SQLite的rawQuery selectionArgs和整数字段

正如Android文档所说,rawQuery方法的selectionArgs参数被parsing为string。

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

selectionArgs:您可以在查询中的where子句中包含?s,它将被来自selectionArgs的值replace。 这些值将被绑定为string。

但今天,我面临着一个我一天中很大一部分的问题。 想象下面的查询:

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' 

解决了这个问题。 另一个testing是:

 SELECT * FROM TABLE_A WHERE COLUMN_A >= '15' 

也是,返回正确的结果。

这看起来是一个问题,涉及到Android将参数绑定到查询(作为string)与IFNULL子句的方式。

那么,有没有人知道这是为什么发生? 有没有任何build议来解决这个问题,而不使用查询CAST?

Solutions Collecting From Web of "SQLite的rawQuery selectionArgs和整数字段"

您首先您的值绑定到查询的原因是,您希望防止SQL注入攻击 。

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

由于命令(可以像链接文章中显示的那样注入到实际查询中)是string,string数据types更危险。 如果一个用户试图向你的字段中注入一些代码,那么这个代码只应该用数字来代替,而且你会试图将input转换成一个整数(在将值存入你的查询之前),你会马上得到一个exception。 用string,没有这样的安全。 因此,他们必须逃走。 可能是绑定值全部被解释为string的原因。

以上是bongus ! 如果你绑定的参数是string或整数,它们不会发生混淆,它们同样危险。 另外,预先检查代码中的值会导致很多样板代码,这很容易出错并且不灵活!


为了防止来自SQL注入的应用程序,并加速多个数据库写入操作(使用相同的命令,但不同的值),您应该使用“准备好的语句”。 在Android SDK中写入数据库的正确类是SQLiteStatement

要创build一个准备好的语句,可以使用SQLiteDatabasecompileStatement()方法 ,并使用正确的bindXX()方法绑定相应的值(将在您的查询中用? -marksreplacebindXX()来自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(); 

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


可悲的是, SQLiteStatement没有返回一个Cursor的重载,所以你不能在SELECT使用它。 对于那些,你可以使用rawQuery(String, String[])方法:

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

请注意, rawQuery()方法为参数值使用一个String数组。 这实际上并没有恶意,SQLite不关心types。 只要string表示等于你在SQL查询中所期望的,你就没事。

我有我完全相同的问题,我猜。 你想提到的是,你不能做有关sqlite数据库的实际计算。 我发现问题比你提到的还要大。 SQLite数据库似乎不了解有关字段值的任何字段types。 这意味着如果你试图在一个INTEGER字段types中插入一个string值,那么这个插入就不会发生抱怨。

所以,如你所见,问题更大。 虽然我已经看到,如果你有一个只有整数的列,你做一个where语句,如:where id = 1 without''那么就有一个结果数据集。 所以我可能会问你是否确定这个语句不起作用:“SELECT * FROM TABLE_A WHERE IFNULL(COLUMN_A,0)> = 15”。 但是where id> ='15'确实起作用,因为它采用实际的2个Unicode字符(!!!)的id的string表示forms,并试图使运算符> =为'15'。

我第一次遇到这些问题让我感到吃惊,我决定dynamic地创buildSQL,而不绑定参数,并作为一个整体执行String。 我知道这不是访问数据库的最好方法,没有绑定参数,但是它是一个解决scheme,也是一个好的方法,因为安全性的原因并不重要,尽pipe你的数据库是安全的,你的访问方法是私有的。 如果“入侵者”能够通过根电话访问数据库,他可以将其放入SQLITE Studio中,然后完成。