SQLite rawQuery selectionArgs和Integers字段

reg*_*sxp 9 sqlite android

正如Android文档所说,rawQuery方法的selectionArgs参数被解析为字符串.

SQLiteDatabase.rawQuery(String sql,String [] selectionArgs)

selectionArgs:您可以在查询中的where子句中包含?s,它将被selectionArgs中的值替换.这些值将绑定为字符串.

但是今天,我遇到了一个问题,这个问题花了我很大的一天.想象一下以下查询:

SELECT * FROM TABLE_A WHERE IFNULL(COLUMN_A, 0) >= 15
Run Code Online (Sandbox Code Playgroud)

COLUMN_A是INTEGER.该表有大约10行符合该标准.在数据库编辑器上运行查询,结果始终是正确的,但是,在智能手机上,语句始终不返回任何行.

一段时间后,将查询更改为:

SELECT * FROM TABLE_A WHERE IFNULL(COLUMN_A, 0) >= '15'
Run Code Online (Sandbox Code Playgroud)

并且编辑器没有返回任何行,就像Android一样.因此,将查询更改为:

SELECT * FROM TABLE_A WHERE CAST(IFNULL(COLUMN_A, 0) as INTEGER) >= '15'
Run Code Online (Sandbox Code Playgroud)

解决了这个问题.另一项测试是:

SELECT * FROM TABLE_A WHERE COLUMN_A >= '15'
Run Code Online (Sandbox Code Playgroud)

还,返回了正确的结果.

这似乎是一个问题,涉及Android使用IFNULL子句将参数绑定到查询(作为字符串)的方式.

那么,有人知道为什么会这样吗?如果不在查询中使用CAST,是否有任何建议可以解决这个问题?

Luk*_*uth 10

将值绑定到查询的原因是为了防止SQL注入攻击.

基本上,您将查询(包括占位符)发送到您的数据库并说"我的下一个查询将是这种形式,而不是其他!".当攻击者然后注入不同的查询字符串(例如,通过表单字段)时,数据库会说"嘿,那不是你要发送的查询!" 并向你抛出一个错误.

由于命令(可以在链接文章中显示的实际查询中注入)是字符串,因此string-datatype更"危险".如果用户试图在您的字段中注入一些代码,这些代码只应该接受数字,并且您尝试将输入转换/解析为整数(在将值放入查询之前),您将立即得到异常.使用字符串,没有这种安全性.因此,他们可能必须逃脱.这可能是绑定值都被解释为字符串的原因.

以上是假的!如果您绑定的参数是字符串或整数并不重要,它们都同样危险.此外,在代码中预先检查您的值会产生大量的样板代码,容易出错并且不灵活!


为了防止您的应用程序进行SQL注入并加速多个数据库写入操作(使用具有不同值的相同查询),您应该使用" 预准备语句".在Android SDK中写入数据库的正确类是SQLiteStatement.

要创建一个事先准备好的声明,您使用compileStatement()-方法您的SQLiteDatabase-object并绑定(将与被替换的相应值?使用正确的-marks在查询)bindXX()-方法(这是从继承SQLiteProgram):

SQLiteDatabase db = dbHelper.getWritableDatabase();
SQLiteStatement stmt = db.compileStatement("INSERT INTO SomeTable (name, age) values (?,?)");
// Careful! The index begins at 1, not 0 !!
stmt.bindString(1, "Jon");
stmt.bindLong(2, 48L);
stmt.execute();
// Also important! Clean up after yourself.
stmt.close();
Run Code Online (Sandbox Code Playgroud)

从这个较老的问题中获取的示例:如何在Android中的SQlite中使用预准备语句?


可悲的是,SQLiteStatement没有返回a的重载Cursor,所以你不能将它用于SELECT-statements.对于那些,你可以使用rawQuery(String, String[])-method SQLiteDatabase:

int number = getNumberFromUser();
String[] arguments = new String[]{String.valueOf(number)};
db.rawQuery("SELECT * FROM TABLE_A WHERE IFNULL(COLUMN_A, 0) >= ?", arguments);
Run Code Online (Sandbox Code Playgroud)

请注意,rawQuery()-method为参数值采用String数组.这实际上没关系,SQLite会自动转换为正确的类型.只要字符串表示与SQL查询中的预期相同,就可以了.