銀の弾丸

プログラミングに関して、いろいろ書き残していければと思っております。

SQLiteのSQLのパラメータ化できる箇所

SQLiteSQLiteDatabase.rawQueryで、SQLのパラメタライズが、できる場所とできない場所がありまして、調査結果を記しておきます。

「単なる文字列置換」では無いようです(よくよく考えれば当たり前ですね)。

f:id:takamints:20151104125310p:plain


SQLite入門 第2版
SQLite入門 第2版
posted with amazlet at 15.11.04
西沢 直木
翔泳社
売り上げランキング: 293,597

SQLiteDatabase.rawQuery

概要

単純にSQLをそのまま実行するメソッドSQLのパラメタライズも可能です。

プロトタイプ

Cursor SQLiteDatabase.rawQuery(String sql, String[] paramValues);

引数

  1. sql - SQL文を指定。SELECTのみ。
  2. paramValues - sqlの中の?を置き換える文字列の配列。

使い方例

ざっくり以下のような使い方です。

// android.database.
//     Cursor
//     sqlite.
//         SQLiteOpenHelper
//         SQLiteDatabase
SQLiteDatabase db = openHelper.getWritableDatabase();

Cursor cursor = db.rawQuery(
    "SELECT id, description, amount, date"
    + "FROM t_wallet " /*     v               v   */
    + "WHERE description like ? AND amount >= ?",
        new String[] { "'ポテト%'", "100" });

while(cursor.moveToNext()) {
    int id = cursor.getInt(0);    
    String description = cursor.getString(1);    
    int amount = cursor.getInt(2);
    byte[] blobDate = cursor.getBlob(3);
    //      ・
    //      ・
    //      ・
}

WHERE句の条件にある?を置き換えています。 条件式などの値を置換できます。 しかし単に文字列置換をしているのではなさそうでして、

【できない】文字列リテラルの中身は置換されない

たとえば、description like ?の部分を、description like '%?'とはできません。 あくまでもシングルコーテーションで囲まれている部分は、文字列リテラルとして扱われるので、その中の?は置換されず、単に?で終わっているdescriptionを見つけてきます。ただ、第二引数の要素数が合わない場合は例外が投入されます。

【できる】式の一部の置換はOK

以下のように、式の一部は、置換できます。

rawQuery(
    "SELECT id FROM t_wallet " +
    "WHERE amount >= ? + 30",       /* OK */
    new String[] { "100" })`

※ これ、当初「できない」としていましたが、できました。別の要因で例外が出ていたのかも。すみません。

また、以下のように、単独の値でなくてもOKです。

rawQuery(
    "SELECT id FROM t_wallet " +
    "WHERE amount >= ? + 30",       /* OK */
    new String[] { "(80 + 20)" })`

ところが、次のは駄目でした。「構文エラー」になります。

rawQuery(
    "SELECT id FROM t_wallet " +
    "WHERE amount >= ? ) + 30",       /* NG */
    new String[] { "(80 + 20" })`

?を式として扱って、第1引数のSQL単独で構文エラーになるのは駄目っぽいですね。

【できる】カラム名もOKです

以下のようにSELECT句やWHERE句、ORDER BY句の、カラム名は置換可能でした。

db.rawQuery(
    "SELECT ? FROM t_wallet " +
    "WHERE ? <= ? ORDER BY ? DESC",/* OK */
    new String[] { "description",
                   "amount", "100",  "amount"})`

しかし、

【できない】FROM句のテーブル名はダメでした

予想に反して、テーブル名は置換できないようです。

rawQuery(
    "SELECT description FROM ? " + /* NG */
    "WHERE amount <= 100 " +
    "ORDER BY amount",
    new String[] {"t_wallet"})

そして、

【できない】ORDER BY の DESCもダメですね

これ、できても良い気がしましたが、しっかり例外が投入されました。

rawQuery(
    "SELECT description FROM t_wallet " +
    "WHERE amount <= 100 " +
    "ORDER BY id ?",               /* NG */
    new String[] {"DESC"})`

まとめ

結果をまとめると、以下のようになりました。

置換可能

置換できない

カラム名も構文的には式ですから、?は式を置換できるということで良いのかな。