SQLite rawQuery selectionArgs和Integers字段

时间:2012-07-03 23:07:34

标签: android sqlite

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

  

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

     

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

但是今天,我遇到了一个问题,这个问题占据了我的一天。想象一下以下查询:

SELECT * FROM TABLE_A WHERE IFNULL(COLUMN_A, 0) >= 15

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

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

SELECT * FROM TABLE_A WHERE IFNULL(COLUMN_A, 0) >= '15'

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

SELECT * FROM TABLE_A WHERE CAST(IFNULL(COLUMN_A, 0) as INTEGER) >= '15'

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

SELECT * FROM TABLE_A WHERE COLUMN_A >= '15'

也返回了正确的结果。

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

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

2 个答案:

答案 0 :(得分:10)

绑定您的值绑定到查询的原因是为了阻止SQL-injection attack

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

  

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

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


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

要创建预准备语句,请使用SQLiteDatabase - 对象的compileStatement()-method并绑定相应的值(将使用查询中的? - 标记替换)正确的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();

从这个较早的问题中提取的示例:How do I use prepared statements in SQlite in Android?


遗憾的是,SQLiteStatement没有返回Cursor的重载,因此您无法将其用于SELECT - 语句。对于这些,您可以使用rawQuery(String, String[])的{​​{1}}方法:

SQLiteDatabase

请注意,int number = getNumberFromUser(); String[] arguments = new String[]{String.valueOf(number)}; db.rawQuery("SELECT * FROM TABLE_A WHERE IFNULL(COLUMN_A, 0) >= ?", arguments); - 方法为参数值采用String数组。这实际上没关系,SQLite会自动转换为正确的类型。只要字符串表示与SQL查询中的预期相同,就可以了。

答案 1 :(得分:1)

我猜是完全相同的问题。您要提到的是您无法对sqlite数据库进行实际计算。我发现问题比你提到的问题更大。 SQLite数据库似乎不了解有关字段值的任何字段类型。这意味着如果您尝试在INTEGER字段类型中插入String值,则插入不会抱怨。

所以问题就更大了,你可以看到。虽然我已经看到如果你有一个只有Integers的列,并且你创建了一个where语句,如:where id = 1 without''那么就有一个结果数据集。所以我可能会问你是否确定这个陈述不起作用:“SELECT * FROM TABLE_A WHERE IFNULL(COLUMN_A,0)> = 15”。但是id> ='15'的地方确实有效,因为它采用id的字符串表示形式,它是实际的2个unicode字符(!!!),并尝试将运算符> =设置为'15',这些都适用。

第一次遇到这些问题让我感到惊讶,我决定动态创建SQL而不绑定参数并作为整个String执行。我知道这不是访问数据库的最佳方式,没有绑定参数,但它是一个解决方案而且是一个好的解决方案,因为安全性原因并不重要,尽管您的数据库是安全的,并且您的访问方法对您的应用程序是私有的。如果“入侵者”能够通过root手机访问数据库,他可以将它放在SQLITE Studio中并完成。