6

I've been looking at the official documentation (http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html) and cross-referencing with StackOverflow posts and actual observed behavior, and the documentation seems to be misleading and possibly just wrong in several respects.

I am trying to perform parameterized queries to insert/update/delete, just as one might do for selecting with rawQuery(String sql, String[] arguments). I need to do it via a query rather than by using the insert/update/delete methods on the SQLiteDatabase class because in many cases the queries span multiple tables and in any case we have written a large set of queries that we use for this app across all our platforms (also written for SQLite), and it would be highly preferable to use it as-is.

Here's what I find confusing/misleading:

  • The doc says for rawQuery that statements must not be terminated by a semicolon, but it doesn't actually seem to make any difference. This matters to me because we have a huge XML document filled with queries I use in my app across several platforms and I'd prefer to keep it identical if possible.

  • rawQuery doesn't seem to work for inserts (yes, I've tried with and without the semicolon). The doc don't say anything about this. I do see that it returns a Cursor, which I suppose could be an oblique hint that it only works with a select, but not necessarily -- it could simply return a zero-length or null Cursor when they was no result set.

  • execSQL(String sql, Object[] bindArgs) explicitly says that it does not work with inserts, but in fact it does!

  • Furthermore, although execSQL(String, Object[]) specifically tells you not to try CRUD operations, its parameterless version contains no such warning, and also works fine for that purpose (with the disadvantage of not allowing SQL parameters).

The result of all of this is that the only way I can find to successfully execute inserts with parametrized arguments is to use the one working method that the docs explicitly instruct you not to use for that purpose.

Also, it's really a bummer that rawQuery won't work for inserts/updates/deletes, because we have a generalized data layer and it would be more elegant if we could use a single, unified API call to run all our CRUD queries on the database.

So what is going on here? Is this documentation hopelessly out of date? Is it okay to use execSql to do inserts, updates, and so on? Has anyone succeeded in doing inserts with rawQuery?


Appendix:

Running against OS: Android 4.3.

Sample queries (what's working, what's not)

// Works
db.execSql("INSERT into MyTable (Col1, Col2) VALUES (?, ?)", new String[] { "Foo", "Bar" });
db.execSql("INSERT into MyTable (Col1, Col2) VALUES (?, ?);", new String[] { "Foo", "Bar" });

// No exception thrown, but no changes made to the database
db.rawQuery("INSERT into MyTable (Col1, Col2) VALUES (?, ?)", new String[] { "Foo", "Bar" });
db.rawQuery("INSERT into MyTable (Col1, Col2) VALUES (?, ?);", new String[] { "Foo", "Bar" });
Brian Rak
  • 4,912
  • 6
  • 34
  • 44
  • Why do you think `rawQuery` could be used for non-query operations? A query specifically refers to a `SELECT` operation – ianhanniballake Nov 21 '13 at 00:52
  • @ianhanniballake, in my experience, "query" is often used to refer to any SQL statement that is ready to be sent to the database, regardless of whether it is a SELECT or not. After all, it is called Structured Query Language. So it's common to speak of an "UPDATE query." Here's a Wikipedia article that uses this phraseology (search for 'query'): http://en.wikipedia.org/wiki/Update_%28SQL%29 – Brian Rak Nov 21 '13 at 01:39
  • I ask because the Android documentation is very clear that query refers only to SELECT so I'm not sure what Android docs led you astray? – ianhanniballake Nov 21 '13 at 02:25
  • Maybe I'm reading the wrong documentation, but I don't see anything in the link I posted that says that you can only run SELECTs in rawQuery. On the contrary, it specifically says you *cannot* do CRUD operations in execSQL. So where else would you do it? – Brian Rak Nov 21 '13 at 02:38
  • Hello, how can we know insertion is success? – K.Sopheak Dec 06 '16 at 08:27

2 Answers2

6

You are right. The documentation is confusing. Overall, the design attempts to provide a convenient Java wrapper around the sqlite3 C API. For most of the time it works fine if you use it the way the designers intended, e.g. using the convenience methods for CRUD operations. But they also needed to provide raw query methods execSQL() and rawQuery() for cases where the convenience CRUD methods are not powerful enough or not applicable at all (CREATE TABLE and so on). This causes leaky abstractions.

The doc says for rawQuery that statements must not be terminated by a semicolon, but it doesn't actually seem to make any difference. This matters to me because we have a huge XML document filled with queries I use in my app across several platforms and I'd prefer to keep it identical if possible.

The docs are bad. In fact, Android SQLiteDatabase itself calls rawQuery with a semicolon-terminated query string.

rawQuery doesn't seem to work for inserts (yes, I've tried with and without the semicolon). The doc don't say anything about this. I do see that it returns a Cursor, which I suppose could be an oblique hint that it only works with a select, but not necessarily -- it could simply return a zero-length or null Cursor when they was no result set.

It does work but you'll need to understand how it works on native level.

Think of execSQL() as sqlite3_exec() that runs the query and returns either success or an error code.

Think of rawQuery() as sqlite3_prepare() that compiles the query but does not run it yet. To actually run it, use one of the moveTo...() methods on the Cursor. Think of this as sqlite3_step(). Combining any rawQuery() with moveTo...() will actually alter the database.

execSQL(String sql, Object[] bindArgs) explicitly says that it does not work with selects, but in fact it does!

Furthermore, although execSQL(String, Object[]) specifically tells you not to try CRUD operations, its parameterless version contains no such warning, and also works fine for that purpose (with the disadvantage of not allowing SQL parameters).

It works fine for all CRUD operations. For the R read part of CRUD, there's just no way to get the selected data.

Community
  • 1
  • 1
laalto
  • 150,114
  • 66
  • 286
  • 303
  • Thank you for the thorough and illuminating answer. I tried an insert with `rawQuery()` followed by `moveToFirst()`, which I figured would be safe for general consumers of my data layer, and it worked! There is a part of me that worries that building on top of knowledge of the internal implementation is dangerous, but I figure no matter which one I use, I'm ignoring the documentation in some way. :-) Thanks again! I'm sure this discussion will help others as well. – Brian Rak Nov 21 '13 at 17:53
  • Quick follow-up for anyone thinking of using my strategy. Must have been too early in the morning....Calling `moveToFirst()` in order to execute a query before returning a `Cursor` to an unsuspecting consumer will obviously cause them to miss the first row. However, you can call `moveToPosition(-1)` instead to execute the query without disturbing the Cursor's position on the result set. In this way, you can use `rawQuery(...)` for any kind of CRUD operation. – Brian Rak Nov 21 '13 at 18:20
  • IMPORTANT FOLLOW-UP: Calling `rawQuery(...)` followed by `moveToPosition(-1)` throws an exception on Android 4.0.3 and 4.0.4 when the query is BEGIN ..., COMMIT ..., or ROLLBACK TRANSACTION. For these queries, you should use `execSQL` instead. – Brian Rak Oct 10 '14 at 16:44
0

What you want is execSQL() for inserts. Or you can do it this way:

ContentValues values = new ContentValues();
values.put ("col1", "foo");
values.put ("cols2", "bar");
getDb().insertOrThrow ("MyTable", null, values);

For queries, you can use rawQuery.

CL.
  • 173,858
  • 17
  • 217
  • 259
Peri Hartman
  • 19,314
  • 18
  • 55
  • 101
  • So it sounds like you're saying that the documentation is wrong (which I am totally fine with -- I'm just verifying). Or are you referring only to the parameterless version of execSQL()? – Brian Rak Nov 21 '13 at 01:41
  • @BrianRak The documentation is wrong; it says what its designers want you to do, not what you need to do to get things done. – CL. Nov 21 '13 at 10:10