views:

418

answers:

2

Alright, I'm trying to query a sqlite database. I was trying to be good and use the query method of SQLiteDatabase and pass in the values in the selectArgs parameter to ensure everything got properly escaped, but it wouldn't work. I never got any rows returned (no errors, either).

I started getting curious about the SQL that this generated so I did some more poking around and found SQLiteQueryBuilder (and apparently Stack Overflow doesn't handle links with parentheses in them well, so I can't link to the anchor for the buildQuery method), which I assume uses the same logic to generate the SQL statement. I did this:

  SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
  builder.setTables(BarcodeDb.Barcodes.TABLE_NAME);

  String sql = builder.buildQuery(new String[] { BarcodeDb.Barcodes.ID, BarcodeDb.Barcodes.TIMESTAMP, BarcodeDb.Barcodes.TYPE, BarcodeDb.Barcodes.VALUE },
    "? = '?' AND ? = '?'",
    new String[] { BarcodeDb.Barcodes.VALUE, barcode.getValue(), BarcodeDb.Barcodes.TYPE, barcode.getType()},
    null, null, null, null);

  Log.d(tag, "Query is: " + sql);

The SQL that gets logged at this point is:

SELECT _id, timestamp, type, value FROM barcodes WHERE (? = '?' AND ? = '?')

However, here's what the documentation for SQLiteQueryBuilder.buildQuery says about the selectAgs parameter:

You may include ?s in selection, which will be replaced by the values from selectionArgs, in order that they appear in the selection.

...but it isn't working. Any ideas?

A: 

Two things:

  1. The ? substitution will not be done at this point, but only when the query is executed by the SQLiteDatabase.
  2. From what I've seen, ? substitution only works for the right side of comparison clauses. For example, some people have tried to use ? for the table name, which blows up. I haven't seen anyone try using ? for the left side of the comparison clause, so it might work -- I'm just warning you that it might not.
CommonsWare
Alright, I changed the code to only use `?` on the right side of the comparisons with the same result. At the point where I log the SQL, it still has the `?`'s in it, and when I query the database it doesn't return any rows.Have you seen an example where someone actually used the `selectArgs` parameter? I looked and couldn't find one. People seem to just concatenate the value into the `selection` parameter, which can lead to SQL injection.
Daniel
Well, `SQLiteQueryBuilder` is a waste of space, IMHO, except when writing database-backed content providers, and those tend to be clunky examples. However, `selectArgs` is used in `rawQuery()` on `SQLiteDatabase`, and that's where `SQLiteQueryBuilder` eventually goes. `selectArgs` is also used in calls to `update()` and `delete()`. There are lots of examples of those, such as http://github.com/commonsguy/cw-android/tree/master/Database/Constants/ and http://github.com/commonsguy/cw-andtutorials/tree/master/12-Activities/
CommonsWare
A: 

The doc for SQLiteQueryBuilder.buildQuery also says, "The values will be bound as Strings." This tells me that it is doing the straight-forward thing, which is writing the SQL leaving the ? parameter markers in place, which is what you are seeing, and binding the selectArgs as input parameters.

The ? are replaced by sqlite when it runs the query, not in the SQL string. The first string in the array will go where you see the first ?, and so on, when the query actually executes. I would expect the logged SQL to still have the ? markers.

Probably, your query fails because you are quoting the ?. For example, don't use WHERE ID = '?', just use WHERE ID = ?, and make sure the selectArgs is a string that satisfies the query.

Jim Flood