Android Database(SQLite)參數綁定問題初探

來源:互聯網
上載者:User

標籤:reference   問題   data   java代碼   color   tab   generic   pat   gdb   

不廢話,先貼一下測試代碼:

    void test001() {        final String path = "/sdcard/test.db";        File file = new File(path);        if(file.exists()) file.delete();        android.database.sqlite.SQLiteDatabase db = android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(path, null);        db.execSQL("create table t(_id integer, mid integer, data text);");        db.execSQL("create view v  as select IFNULL(mid, _id) as _id, data from t;");        db.execSQL("create view v2 as select mid as _id, data from t;");        db.execSQL("create view v3 as select mid+0 as _id, data from t;");        db.execSQL("insert into t values(10001, null, ‘dudu‘);");        db.execSQL("insert into t values(10002, 1000, ‘bird‘);");        Cursor csr = null;                Log.e(TAG, "------------------------------test001 -----------------------------------");                csr = db.query("v", new String[]{"data"}, "_id=1000", null, null, null, null);        Log.i(TAG,"#1 csr.getCount() = " + csr.getCount());        while(csr.moveToNext()) {            Log.i(TAG, "#1 data = " + csr.getString(0));        }        csr.close();        Log.d(TAG, "-------------------------------------------------------------------------");                csr = db.query("v", new String[]{"data"}, "_id=?", new String[]{"1000"}, null, null, null);        Log.i(TAG,"#2 csr.getCount() = " + csr.getCount());        while(csr.moveToNext()) {            Log.i(TAG, "#2 data = " + csr.getString(0));        }        csr.close();        Log.d(TAG, "-------------------------------------------------------------------------");                csr = db.query("v2", new String[]{"data"}, "_id=1000", null, null, null, null);        Log.i(TAG,"#3 csr.getCount() = " + csr.getCount());        while(csr.moveToNext()) {            Log.i(TAG, "#3 data = " + csr.getString(0));        }        csr.close();        Log.d(TAG, "-------------------------------------------------------------------------");                csr = db.query("v2", new String[]{"data"}, "_id=?", new String[]{"1000"}, null, null, null);        Log.i(TAG,"#4 csr.getCount() = " + csr.getCount());        while(csr.moveToNext()) {            Log.i(TAG, "#4 data = " + csr.getString(0));        }        csr.close();        Log.d(TAG, "-------------------------------------------------------------------------");                csr = db.query("v3", new String[]{"data"}, "_id=1000", null, null, null, null);        Log.i(TAG,"#5 csr.getCount() = " + csr.getCount());        while(csr.moveToNext()) {            Log.i(TAG, "#5 data = " + csr.getString(0));        }        csr.close();        Log.d(TAG, "-------------------------------------------------------------------------");                csr = db.query("v3", new String[]{"data"}, "_id=?", new String[]{"1000"}, null, null, null);        Log.i(TAG,"#6 csr.getCount() = " + csr.getCount());        while(csr.moveToNext()) {            Log.i(TAG, "#6 data = " + csr.getString(0));        }        csr.close();        Log.d(TAG, "-------------------------------------------------------------------------");                db.close();    }

6個查詢的正常的返回結果應該都是 csr.getCount() = 1 , 看一下實際測試結果:

10-14 12:02:09.442: E/JUST(22524): ------------------------------test001 -----------------------------------
10-14 12:02:09.443: I/JUST(22524): #1 csr.getCount() = 1
10-14 12:02:09.443: I/JUST(22524): #1 data = bird
10-14 12:02:09.443: D/JUST(22524): -------------------------------------------------------------------------
10-14 12:02:09.446: I/JUST(22524): #2 csr.getCount() = 0
10-14 12:02:09.447: D/JUST(22524): -------------------------------------------------------------------------
10-14 12:02:09.449: I/JUST(22524): #3 csr.getCount() = 1
10-14 12:02:09.449: I/JUST(22524): #3 data = bird
10-14 12:02:09.449: D/JUST(22524): -------------------------------------------------------------------------
10-14 12:02:09.450: I/JUST(22524): #4 csr.getCount() = 1
10-14 12:02:09.450: I/JUST(22524): #4 data = bird
10-14 12:02:09.450: D/JUST(22524): -------------------------------------------------------------------------
10-14 12:02:09.450: I/JUST(22524): #5 csr.getCount() = 1
10-14 12:02:09.451: I/JUST(22524): #5 data = bird
10-14 12:02:09.451: D/JUST(22524): -------------------------------------------------------------------------
10-14 12:02:09.451: I/JUST(22524): #6 csr.getCount() = 0
10-14 12:02:09.452: D/JUST(22524): -------------------------------------------------------------------------

這個就神奇了!

看一下查詢函數的聲明:

Cursor android.database.sqlite.SQLiteDatabase.query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)注意到 selectionArgs 的說明:selectionArgs You may include ?s in selection, which will be replaced by the values from selectionArgs, in order that they appear in the selection. The values will be bound as Strings.

資料庫表中列類型是Int的,而Java代碼中“粗暴”的全部綁定為String類型了,SQLite有很強大的弱類型支援,這裡為什麼會出現這個結果呢?

這個需要研究一下綁定參數SQL間的區別了:

 

對比查詢計劃:
explain select data from v2 where _id=?; 與 explain select data from v where _id=?;
發現Ne指令的P5參數比較可疑:
4 Ne 2 7 1 (BINARY) 54 if r[1]!=r[2] goto 7
6 Ne 2 9 1 51 if r[1]!=r[2] goto 9

 

在sqlite3.c(以目前Android主流還在用的3.9.2為例)中定位到Ne指令相關關鍵代碼:

case OP_Ne:               /* same as TK_NE, jump, in1, in3 */    ...    /* Neither operand is NULL.  Do a comparison. */    affinity = pOp->p5 & SQLITE_AFF_MASK;    if( affinity>=SQLITE_AFF_NUMERIC ){      if( (flags1 | flags3)&MEM_Str ){        if( (flags1 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){          applyNumericAffinity(pIn1,0);          testcase( flags3!=pIn3->flags ); /* Possible if pIn1==pIn3 */          flags3 = pIn3->flags;        }        if( (flags3 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){          applyNumericAffinity(pIn3,0);        }      }    ...    res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl);    ...

到了Ne這裡P5已經被改動過了,經過一番有點艱苦的GDB調試定位到:

/*** Generate code for a comparison operator.*/static int codeCompare(  Parse *pParse,    /* The parsing (and code generating) context */  Expr *pLeft,      /* The left operand */  Expr *pRight,     /* The right operand */  int opcode,       /* The comparison opcode */  int in1, int in2, /* Register holding operands */  int dest,         /* Jump here if true.  */  int jumpIfNull    /* If true, jump if either operand is NULL */){  int p5;  int addr;  CollSeq *p4;  p4 = sqlite3BinaryCompareCollSeq(pParse, pLeft, pRight);  p5 = binaryCompareP5(pLeft, pRight, jumpIfNull);// P5在這裡擷取  addr = sqlite3VdbeAddOp4(pParse->pVdbe, opcode, in2, dest, in1,                           (void*)p4, P4_COLLSEQ);  sqlite3VdbeChangeP5(pParse->pVdbe, (u8)p5);// 注意這裡會修改P5的值  return addr;}

所以下面重點查看binaryCompareP5的實現:

/*** Return the P5 value that should be used for a binary comparison** opcode (OP_Eq, OP_Ge etc.) used to compare pExpr1 and pExpr2.*/static u8 binaryCompareP5(Expr *pExpr1, Expr *pExpr2, int jumpIfNull){  u8 aff = (char)sqlite3ExprAffinity(pExpr2);  aff = (u8)sqlite3CompareAffinity(pExpr1, aff) | (u8)jumpIfNull;  return aff;}/*** Return the ‘affinity‘ of the expression pExpr if any.**** If pExpr is a column, a reference to a column via an ‘AS‘ alias,** or a sub-select with a column as the return value, then the ** affinity of that column is returned. Otherwise, 0x00 is returned,** indicating no affinity for the expression.**** i.e. the WHERE clause expressions in the following statements all** have an affinity:**** CREATE TABLE t1(a);** SELECT * FROM t1 WHERE a;** SELECT a AS b FROM t1 WHERE b;** SELECT * FROM t1 WHERE (select a from t1);*/SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr){  int op;  pExpr = sqlite3ExprSkipCollate(pExpr);  if( pExpr->flags & EP_Generic ) return 0;  op = pExpr->op;  if( op==TK_SELECT ){    assert( pExpr->flags&EP_xIsSelect );    return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr);  }  if( op==TK_REGISTER ) op = pExpr->op2;#ifndef SQLITE_OMIT_CAST  if( op==TK_CAST ){    assert( !ExprHasProperty(pExpr, EP_IntValue) );    return sqlite3AffinityType(pExpr->u.zToken, 0);  }#endif  if( (op==TK_AGG_COLUMN || op==TK_COLUMN) && pExpr->pTab ){    return sqlite3TableColumnAffinity(pExpr->pTab, pExpr->iColumn);  }  if( op==TK_SELECT_COLUMN ){    assert( pExpr->pLeft->flags&EP_xIsSelect );    return sqlite3ExprAffinity(        pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr    );  }  return pExpr->affinity;}/*** pExpr is an operand of a comparison operator.  aff2 is the** type affinity of the other operand.  This routine returns the** type affinity that should be used for the comparison operator.*/SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2){  char aff1 = sqlite3ExprAffinity(pExpr);  if( aff1 && aff2 ){      // 注意看下面英文說明就理解了    /* Both sides of the comparison are columns. If one has numeric    ** affinity, use that. Otherwise use no affinity.    */    if( sqlite3IsNumericAffinity(aff1) || sqlite3IsNumericAffinity(aff2) ){      return SQLITE_AFF_NUMERIC; //這裡是關鍵,綁定參數能不能作為數字來比較就靠這個傳回值了!    }else{      return SQLITE_AFF_BLOB;    }  }else if( !aff1 && !aff2 ){    /* Neither side of the comparison is a column.  Compare the    ** results directly.    */    return SQLITE_AFF_BLOB;  }else{    /* One side is a column, the other is not. Use the columns affinity. */    assert( aff1==0 || aff2==0 );    return (aff1 + aff2); //這裡是關鍵,綁定參數能不能作為數字來比較就靠這個傳回值了!  }}

 

問題原因這就算說清楚了,怎們解決呢? 下次有空再寫吧。。。

 

 

備忘:aHR0cCUzQS8vd3d3LmNuYmxvZ3MuY29tL3poaGQv

Android Database(SQLite)參數綁定問題初探

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.