SQLiteDirectCursorDriver usage of SelectionArgs or workaround - using placeholders - android

I have a rawQuery string running but SQlite throws this warning :
SQLiteDirectCursorDriver(1058): Found SQL string that ends in ;
because of quote issues. So I'm trying to use SelectionArgs in a query where I do not have really replacement by values, like this :
String query = "SELECT Min(?) as oldest, Max(?) as newest, COUNT(?) as nb_data FROM ? WHERE ? BETWEEN (SELECT Min(?) FROM ?) AND (SELECT Max(?) FROM ?)";
String[] values = new String[] { TS, TS, col_2, table, TS, TS, table, TS, table };
Cursor mCount = database.rawQuery(query, values);
I can't get it working, even using fewer SelectionArgs strings (only on the "WHERE" part of the query) as there is not really value replacement requirements but placeholders.
Is there a solution to set this prepared statement (using placeholders rather than using quotes or rewriting the original query) to avoid getting the SQLiteDirectCursorDriver warning ?

You can use parameters only for expressions, not for identifiers.
To prevent this warning, just omit the semicolon from the end of your SQL query strings.

Related

JDBC to android.sqlite: SELECT/DELETE … WHERE field IN (?)

I am just porting some JDBC code (intended for use with HSQLDB) to Android’s own SQLite implementation. I have a snippet where I delete records based on a particular field matching one of the values in a String[] array in my Java code.
Here is the JDBC code for the DELETE statement:
String[] ids = getIdsSomehow();
PreparedStatement stmtD = db.prepareStatement("delete from message where id in (unnest(?))");
Array delIdArray = stmtD.getConnection().createArrayOf("VARCHAR", ids);
stmtD.setArray(1, delIdArray);
stmtD.execute();
stmtD.close();
Another snippet does a SELECT instead of DELETE and has the values in a List<String> instead of an array.
How would I accomplish this with the methods offered by SQLiteDatabase, preferably in a way that does not open up any SQL injection vulnerabilities?
The main “ingredients” to make this work with HSQLDB over JDBC, namely the unnest() function of the DBMS, and the ability to pass array values to SQL statements, are not available with the android.sqlite stack, making a workaround necessary:
String[] ids = getIdsSomehow();
String whereClause = "id in (" + TextUtils.join(",", Collections.nCopies(ids.size(), "?")) + ")";
db.delete("message", whereClause, ids);
This builds a where clause à la id in (?,?,?,?) with the correct number of question marks.
For the selection, use rawQuery() with a SELECT statement built in the same manner. The List<String> can be converted to an array like this:
ids.toArray(new String[]{})
Input came from this answer and its comments.

What exactly is where?whereval in android sqlite?

There are two pieces of code,which i thought were similar ,but one gives me an error and one doesn't,so I have a few questions to ask
What exactly is "where","whereval"?
The below code gives me an error
String where = android.provider.MediaStore.Audio.Media.ALBUM
+ "="+img.get(position).Album;//here is the difference
Cursor cursor = managedQuery(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, columns,
where, null , orderBy);//whereval is null here
The below code doesn't give me any error
String where = android.provider.MediaStore.Audio.Media.ALBUM
+ "=?";here is the difference
String whereVal[] = {img.get(position).Album};here is the difference
Cursor cursor = managedQuery(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, columns,
where, whereVal , orderBy);//i have included whereVal
Please explain me the difference between the two pieces of code ??
Also how can i use two conditions in sqlite ??
In SQL, string literals are quoted 'like this'. If you insert a string literal in SQL without the quotes, you'll likely get an SQL syntax error. It's the same as in Java where "any string" without the quotes is unlikely to be valid Java code but it's valid as a string literal.
? is a placeholder for a literal. The SQL is compiled with the placeholder in place and the literal value is bound to the compiled statement. Think of it as a variable in programming languages. The value for the variable comes from the bind arguments. In Android Java sqlite wrapper, the bind args are supplied in the string array.
Further reading: http://www.sqlite.org/lang_expr.html#varparam - though the Android wrapper code adds some leaky abstractions on top of sqlite C API.
suppose i have to use the code1 to achieve the result that i am getting from code 2..what should be the syntax ??
Quote the string literal with '...':
String where = android.provider.MediaStore.Audio.Media.ALBUM
+ "='"+img.get(position).Album+"'";
Based on the comments you want a conjuction expression. Just add an AND between subexpressions, like
String where = "foo='bar' AND baz='xyzzy'";

Selecting int values using a cursor query

I am new to Android, and having some basic problems. One of them is the use of queries.
I store a boolean value as either 1 or 0 in the table (INTEGER field). However, when I select either on 1 or 0 using the query below I get no results. What am I doing wrong?
Cursor cursor = _db.query(_objectName, _fields.keySet().toArray(new String[0]), "parentId=? AND published=?", new String[] {String.valueOf(menuItem), String.valueOf(1)}, null, null, "level");
There is nothing wrong with your query. The problem must be elsewhere. Check your code and table structure. Maybe you are not sending the right values for parentId and published columns or the data in the table is not in the format you expected.
Use raw query
"Select * from "+TABLE_NAME+" where published = '"+String.valueOf(1)+"'";
You can put your integer value insted of 1

Queries with prepared statements in Android?

In Android, android.database.sqlite.SQLiteStatement allows me to use prepared statements in SQLite to avoid injection attacks. Its execute method is suitable for create/update/delete operations, but there does not seem to be any method for queries that returns a cursor or the like.
Now in iOS I can create prepared statements of type sqlite3_stmt* and use them for queries, so I know this is not a limitation of SQLite. How can I perform queries with prepared statements in Android?
a prepared statement allows you to do two things
speed up the performance since the database does not need to parse the statement each time
bind & escape arguments in the statement so you are save against injection attacks
I don't know exactly where/when Androids SQLite implementation actually uses sqlite3_prepare (afiak not sqlite3_prepare_v2 - see here) but it does use it otherwise you could not get Reached MAX size for compiled-sql statement cache errors.
So if you want to query the database you have to rely on the implementation there is no way I know of to do it with SQLiteStatement.
Regarding the injection safety, every database query, insert, etc method has (sometimes alternative) versions that allow you to bind arguments.
E.g. if you want to get a Cursor out of
SELECT * FROM table WHERE column1='value1' OR column2='value2'
Cursor SQLiteDatabase#rawQuery(
String sql, : full SELECT statment which can include ? everywhere
String[] selectionArgs : list of values that replace ?, in order they appear
)
Cursor c1 = db.rawQuery(
"SELECT * FROM table WHERE column1=? OR column2=?",
new String[] {"value1", "value2"}
);
Cursor SQLiteDatabase#query (
String table, : table name, can include JOIN etc
String[] columns, : list of the columns required, null = *
String selection, : WHERE clause withouth WHERE can / should include ?
String[] selectionArgs, : list of values that replace ?, in order they appear
String groupBy, : GROUP BY clause w/o GROUP BY
String having, : HAVING clause w/o HAVING
String orderBy : ORDER BY clause w/o ORDER BY
)
Cursor c2 = db.query("table", null,
"column1=? OR column2=?",
new String[] {"value1", "value2"},
null, null, null);
Via ContentProviders - that case is slightly different since you interact with an abstract provider, not a database. There is acutally no guarantee that there is a sqlite database backing the ContentProvider. So unless you know what columns there are / how the provider works internally you should stick to what the documentation says.
Cursor ContentResolver#query(
Uri uri, : an URI representing the data source (internally translated to a table)
String[] projection, : list of the columns required, null = *
String selection, : WHERE clause withouth WHERE can / should include ?
String[] selectionArgs, : list of values that replace ?, in order they appear
String sortOrder : ORDER BY clause w/o ORDER BY
)
Cursor c3 = getContentResolver().query(
Uri.parse("content://provider/table"), null,
"column=? OR column2=?",
new String[] {"value1", "value2"},
null);
Hint: if you want to LIMIT here you can add it to the ORDER BY clause:
String sortOrder = "somecolumn LIMIT 5";
or depending on the implementation of the ContentProvider add it as a parameter to the Uri:
Uri.parse("content://provider/table?limit=5");
// or better via buildUpon()
Uri audio = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
audio.buildUpon().appendQueryParameter("limit", "5");
In all cases ? will be replaced by the escaped version of what you put in the bind argument.
? + "hack'me" = 'hack''me'

rawQuery wild card `"_"` not working

I have been trying to use the character wild card "_" in sqlite where conditions and am close to "jumping off a high place" I have tried both rawQuery and query with a variety of hard and soft coded parameters. It seems to ignores the character wild card "_"and returns all rows or none at all.
The data held is in several columns representing a features of an oblect for example a column may represent the colours of an object
("red,orange,yellow,green,blue,indego,violet,black")
and a row's colour could be "01001000" meaning that it is orange and blue but not red, yellow etc. other columns contain single characters ie size contains s,m or l (mapping small, medium, large ) the database holds several columns of each type, the idea to have as compact a database as possible.
My first intention was to code by passing the 'where' of my select as a string to the rawQuery(myselect,null) where the myselect was compiled in the code in response to several features selected by the user.
ie
the mywhere string is compiled to return :-
colour1 like "_1__10__" and colour2 like "___1_01__" and size ="l"
and passed to the rawQuery
db.rawQuery("SELECT _id , name FROM widgets WHERE " + mywhere , null);
The statement below works fine using Firefox's SQLite Manager
SELECT _id , name FROM widgets WHERE colour1 like "_1__10_" and colour2 like "__1_01___" and size ="m"
In order to investigate I have cut the query down to one column in the where clause
myselect ="SELECT _id ,name,FROM widgets where colour1 like \"1_______\"";
cursor = db.rawQuery(myselect,null);
returns no rows
myselect ="SELECT _id ,name,FROM widgets where colour1 like \"%1_______%\"";
cursor = db.rawQuery(myselect,null);
returns rows but not all have a 1 at the first char ( I expected this )
myselect ="SELECT _id ,name, FROM widgets where colour1 like ?";
String[] whereArguments = { "1_______" };
cursor = db.rawQuery(myselect,whereArguments);
returns no rows
myselect ="SELECT _id ,name, FROM widgets where colour1 like ?";
String[] whereArguments = { "%1_______%" };
cursor = db.rawQuery(myselect,whereArguments);
returns rows but not all have a 1 at the first char
But I dont get any rows from
myselect ="SELECT _id ,name, FROM widgets where colour1 like ?";
String[] whereArguments = { "10000000" };
cursor = db.rawQuery(myselect,whereArguments);
and there are rows containing "10000000"
Does any one have any solutions? I have tried searching but it seems that rawQuery and query have questionable functionality in Android.
Not sure if this is your main source of problems, but the string delimiter in SQLite is the single quote character (') not the double quote one ("). See this, for example.

Categories

Resources