2012-07-03 167 views
7

正如Android文檔所述,rawQuery方法的selectionArgs參數被解析爲字符串。SQLite rawQuery selectionArgs和Integers字段

SQLiteDatabase.rawQuery(字符串SQL,字符串[] selectionArgs兩個)

selectionArgs兩個:您可能包括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?

+0

我有類似的問題,我花了一個小時試圖找到我的應用程序中的錯誤。然後我找到了你的帖子。因此,你寫在這裏真的很棒。另一件事是我永遠不會理解Android哲學,爲什麼這個CAST是必須在這裏... – user2707175

回答

9

爲什麼你綁定你的值到查詢中的第一位是,你想防止SQL-injection attack

基本上,你發送你的查詢(包括佔位符)到你的數據庫,並說「我的下一個查詢將是這種形式,而不是其他!」。當攻擊者注入不同的查詢字符串(例如通過表單字段)時,數據庫會顯示「嘿,那不是你說你會發送的查詢!」並向您拋出錯誤。

由於命令(可注入到你的實際查詢的節目鏈接的文章)都是字符串,字符串,數據類型是更「危險」之一。 如果用戶試圖將一些代碼注入到只應用數字的字段中,並且嘗試將輸入轉換爲整型(在將值放入查詢之前),您將立即得到異常。用字符串,沒有這樣的安全。因此,他們必須逃脫。 可能是綁定值全部解釋爲字符串的原因。

以上是bongus!如果你綁定的參數是字符串或整數,它們不會發生混淆,它們同樣危險。此外,預先檢查代碼中的值會導致很多樣板代碼,這很容易出錯並且不靈活!


爲了防止SQL-注射您的應用程序,也加快了多個數據庫寫作操作(使用相同的命令,但不同的值),你應該使用「準備好的語句」。 寫入Android SDK中的數據庫的正確類是SQLiteStatement

要創建一個準備好的語句,你可以使用你的SQLiteDatabase -object的compileStatement()-method並綁定使用正確的bindXX() - 方法(這是從SQLiteProgram繼承(後者將與? -marks在查詢中被替換)的相應值):

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[]) - 方法的SQLiteDatabase

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

注意,rawQuery() - 方法需要一個字符串數組參數值。實際上,這並不意味着什麼,SQLite也不會在意這種類型。只要字符串表示等於您在SQL查詢中所期望的內容,就沒問題。

+1

嗨@lukas。感謝您的回覆,但正如文檔中所述,準備好的語句不能用於返回遊標。 – regisxp

+0

@regisxp我不知道爲什麼我沒有看到。我更新了我的答案。一探究竟。 –

+0

爲什麼Android將整數轉換爲'String'?你不能用數字注入SQL查詢,對吧?我希望''''''''''''''''''''''''''''''''''''''我希望''''''''''''參數是一個Object的數組, –

1

我有完全相同的問題,我猜。你要提到的是你不能對sqlite數據庫進行實際的計算。我發現問題比你提到的問題要大。 SQLite數據庫似乎不瞭解任何有關字段值的字段類型。這意味着如果您嘗試在INTEGER字段類型中插入字符串值,則插入操作不會發生抱怨。

所以問題更大,因爲你可以看到。雖然我已經看到如果你有一個只有整數的列,並且你做了一個where語句,如:where id = 1 without''那麼就有一個結果數據集。所以我可能會問你是否確定這個聲明不起作用:「SELECT * FROM TABLE_A WHERE IFNULL(COLUMN_A,0)> = 15」。但是,where id> ='15'確實起作用,因爲它採用了id的字符串表示形式,它是實際的2個Unicode字符(!!!),並嘗試使運算符> =爲'15',它適用。

我第一次遇到這些問題令我感到驚訝,我決定動態地創建SQL,而不綁定參數並作爲一個整體執行String。我知道這不是訪問數據庫的最佳方式,沒有綁定參數,但它是一個解決方案,也是一個好的方法,因爲安全原因並不重要,儘管您的數據庫是安全的,並且您的訪問方法對您的應用程序是私有的。如果「入侵者」能夠通過根電話訪問數據庫,他可以將其放入SQLITE Studio中,然後完成。

+0

Hi @ 10s。謝謝你的評論。我的應用程序的「第一個版本」使用與您所說的相同的技術,手動替換參數。我決定改變,因爲我從操作系統收到一些警報,要求我使用查詢參數來改進SQLite語句緩存。你也收到了這個建議嗎? – regisxp

+0

不,幸運還是沒有我還沒有遇到過這種警報,我正在研究一個商業應用程序,它需要大量的手動替換SQL參數,用非常「重」的查詢和連接,子查詢等。所以我想我已經完全測試了android中的SQLite功能。你可以發佈這些警報,以給我一個提示? – 10s

+0

確認?討論? – 10s