diff options
-rw-r--r-- | manifest | 45 | ||||
-rw-r--r-- | manifest.uuid | 2 | ||||
-rw-r--r-- | src/expr.c | 13 | ||||
-rw-r--r-- | src/main.c | 4 | ||||
-rw-r--r-- | src/prepare.c | 15 | ||||
-rw-r--r-- | src/sqlite.h.in | 9 | ||||
-rw-r--r-- | src/sqliteInt.h | 4 | ||||
-rw-r--r-- | src/tclsqlite.c | 10 | ||||
-rw-r--r-- | src/test1.c | 31 | ||||
-rw-r--r-- | src/vdbe.c | 22 | ||||
-rw-r--r-- | src/vdbe.h | 2 | ||||
-rw-r--r-- | src/vdbeInt.h | 4 | ||||
-rw-r--r-- | src/vdbeapi.c | 37 | ||||
-rw-r--r-- | src/vdbeaux.c | 41 | ||||
-rw-r--r-- | src/vdbemem.c | 3 | ||||
-rw-r--r-- | src/where.c | 115 | ||||
-rw-r--r-- | test/analyze3.test | 599 |
17 files changed, 880 insertions, 76 deletions
@@ -1,8 +1,8 @@ -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 -C Comment\scleanup\sin\sbtree.c. -D 2009-10-16T15:05:19 +C Merge\sthe\ssqlite3_reoptimize()\schanges\sinto\sthe\strunk. +D 2009-10-16T16:21:52 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in 4ca3f1dd6efa2075bcb27f4dc43eef749877740d F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -117,7 +117,7 @@ F src/callback.c 10d237171472865f58fb07d515737238c9e06688 F src/complete.c 5ad5c6cd4548211867c204c41a126d73a9fbcea0 F src/date.c 657ff12ca0f1195b531561afacbb38b772d16638 F src/delete.c 308e300d599d2d11b838687e2cf7309d42f29a1a -F src/expr.c c7f3f718bd5c392344ec8694a41c1824f30cf375 +F src/expr.c f14ffa54e4eea3909b1ac8d5f6d25fe053611804 F src/fault.c dc88c821842157460750d2d61a8a8b4197d047ff F src/fkey.c 41219cba186bcf0a053e42327dfa23aaba4f834a F src/func.c e536218d193b8d326aab91120bc4c6f28aa2b606 @@ -130,7 +130,7 @@ F src/journal.c e00df0c0da8413ab6e1bb7d7cab5665d4a9000d0 F src/legacy.c 303b4ffcf1ae652fcf5ef635846c563c254564f6 F src/lempar.c 0c4d1ab0a5ef2b0381eb81a732c54f68f27a574d F src/loadext.c 0e88a335665db0b2fb4cece3e49dcb65d832635a -F src/main.c 45346f57dc031711aaa57cf786f6fafa9c59375d +F src/main.c 25d44525b06c395975bf9dd633e39dd902f8f75d F src/malloc.c b1725183bcc4ce2e569f1b65da844dc3e4c7a643 F src/mem0.c f2f84062d1f35814d6535c9f9e33de3bfb3b132c F src/mem1.c e6d5c23941288df8191b8a98c28e3f57771e2270 @@ -158,21 +158,21 @@ F src/pcache.c c92ffd4f3e1279b3766854c6d18b5bf4aac0d1fa F src/pcache.h 435ef324197f79391f9c92b71d7f92b548ad7a36 F src/pcache1.c 211295a9ff6a5b30f1ca50516731a5cf3e9bf82c F src/pragma.c c25d0d15dd0bbc5ec34e9760629353358705a447 -F src/prepare.c 9803fc01f0db29ac4a17fa662902af285f37c06b +F src/prepare.c 665d52303135833c53b9be03e68533e249e1de54 F src/printf.c 508a1c59433353552b6553cba175eaa7331f8fc1 F src/random.c 676b9d7ac820fe81e6fb2394ac8c10cff7f38628 F src/resolve.c 941843301f6fda6c6350839c6955a172441a0782 F src/rowset.c c64dafba1f9fd876836c8db8682966b9d197eb1f F src/select.c 1d0a13137532321b4364f964e46f057d271691e3 F src/shell.c 270231b3f587f1f86391b9994fdfcd5d472c3fdf -F src/sqlite.h.in 8e1ccddb12b5e983d0a17e1f1477c72b0a72dd84 +F src/sqlite.h.in f5173e894cc775a30557ec08935d633eecf7c340 F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17 -F src/sqliteInt.h 4bacf6fd6986579a2c6d0f4eb7ecff312eb4acf5 +F src/sqliteInt.h cd893f92cc5605943ccf6ac46b46713d5f571e1e F src/sqliteLimit.h 38b2fffcd01faeaeaadea71b2b47695a81580c8b F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76 F src/table.c cc86ad3d6ad54df7c63a3e807b5783c90411a08d -F src/tclsqlite.c b91a03d52d39eda4392931ac4ebd421b9234c2be -F src/test1.c 4da992ff460cba2167e67df3ba28ad66afebfe91 +F src/tclsqlite.c 00cefd8c37cc934e13cb5238ed53f090391aa99a +F src/test1.c 92ccdb2e68f8c5b529bfbf833b759b5fa39a88f7 F src/test2.c 0de743ec8890ca4f09e0bce5d6d5a681f5957fec F src/test3.c 2445c2beb5e7a0c91fd8136dc1339ec369a24898 F src/test4.c b5fd530f02a6a0dbffb23be202168a690985dedd @@ -209,16 +209,16 @@ F src/update.c 2c8a64237e4fae604468d14380b877d169545b63 F src/utf.c 99cf927eabb104621ba889ac0dd075fc1657ad30 F src/util.c 59d4e9456bf1fe581f415a783fa0cee6115c8f35 F src/vacuum.c f2347520907ee4ec867c9b804d24456b0fd912a7 -F src/vdbe.c b00293fa34fe0e065610f355fd95988fa357068a -F src/vdbe.h 7d5075e3fa4e5587a9be8d5e503857c825490cef -F src/vdbeInt.h 7afb76c0296f9a2310e565803fa66798ef47e9d5 -F src/vdbeapi.c 524d79eb17bbcbe31c37c908b8e01edc5c684a90 -F src/vdbeaux.c 6834737c119f5662c9e6d147ddb4f72523a31aea +F src/vdbe.c f0d6e7dbd4515758c188c9dd7025eb9dfcf021e0 +F src/vdbe.h 1fb725c38df7f79dc60e9a61cb368152d9457e3c +F src/vdbeInt.h aafda2e9761298e12ef0a3e8b5caed9aaf9c7592 +F src/vdbeapi.c a7669f434f1fb53457343e7e85d06d695f7bb4e8 +F src/vdbeaux.c bd26b3c765dff7c0bc089f550d82510a37f24d6a F src/vdbeblob.c 9bfaeab22e261a6a7b6df04e7faaf7d6dfdbef5a -F src/vdbemem.c 0ff2b209fccade3ff6709286057b82ed7f6c1e70 +F src/vdbemem.c 7055a2941a7802094f4704cedc7a28cc88a23749 F src/vtab.c 3e54fe39374e5feb8b174de32a90e7a21966025d F src/walker.c 1edca756275f158b80f20eb6f104c8d3fcc96a04 -F src/where.c 53e2620985fc691b62e811cb25c46cd6f4dd7f77 +F src/where.c 10b4796d864701376054cd5aad1c71c87cb76bd9 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 F test/all.test 14165b3e32715b700b5f0cbf8f6e3833dda0be45 @@ -229,6 +229,7 @@ F test/alter4.test 9386ffd1e9c7245f43eca412b2058d747509cc1f F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc F test/analyze.test ad5329098fe4de4a96852231d53e3e9e6283ad4b F test/analyze2.test a2ad7b0a4e13801ee3968fe70f22aff52326569c +F test/analyze3.test 2f00779be9c1637dd72793de755a17722995d836 F test/async.test 8c75d31b8330f8b70cf2571b014d4476a063efdb F test/async2.test bf5e2ca2c96763b4cba3d016249ad7259a5603b6 F test/async3.test 93edaa9122f498e56ea98c36c72abc407f4fb11e @@ -761,14 +762,14 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 174477bca05d019e663fd2b7cd031189ab2e010a -R 86490e3083ac0bc0fd081f22ec541c2a +P 32966ba4796e70d0afcff6abdda9bdcba08b098a 9f0937066184421f23453ceb451fd726c75cb593 61174aea74db59f6792e275aa366b7f0e1f2270b +R 61972980031ca23020de60e9d0fbe40d U drh -Z b17767b2b8f9c8009e027786b6c3af45 +Z 1bd496ba22406bf1b4185ebae8272187 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) -iD8DBQFK2IuyoxKgR168RlERAtgCAJ98+WY1Pr2TifLYL6T7GXUUzCZi6wCfVaBD -oWFTM8wlOhaD8eBE8rtZdkU= -=6Cef +iD8DBQFK2J2moxKgR168RlERAnBlAJ0TOp4dztyQ8x3H8zZLjMwtg9ofCQCeM/sa +FdOVdtjuYXVkiT+lGN/mPWo= +=+vEN -----END PGP SIGNATURE----- diff --git a/manifest.uuid b/manifest.uuid index 5c2e9174b..96ecc2564 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -32966ba4796e70d0afcff6abdda9bdcba08b098a
\ No newline at end of file +50136840d54674c239613265ebbacaabf215f4e2
\ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 06db14901..f592fc75a 100644 --- a/src/expr.c +++ b/src/expr.c @@ -571,12 +571,12 @@ void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){ if( z[1]==0 ){ /* Wildcard of the form "?". Assign the next variable number */ assert( z[0]=='?' ); - pExpr->iTable = ++pParse->nVar; + pExpr->iColumn = ++pParse->nVar; }else if( z[0]=='?' ){ /* Wildcard of the form "?nnn". Convert "nnn" to an integer and ** use it as the variable number */ int i; - pExpr->iTable = i = atoi((char*)&z[1]); + pExpr->iColumn = i = atoi((char*)&z[1]); testcase( i==0 ); testcase( i==1 ); testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 ); @@ -600,12 +600,12 @@ void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){ Expr *pE = pParse->apVarExpr[i]; assert( pE!=0 ); if( memcmp(pE->u.zToken, z, n)==0 && pE->u.zToken[n]==0 ){ - pExpr->iTable = pE->iTable; + pExpr->iColumn = pE->iColumn; break; } } if( i>=pParse->nVarExpr ){ - pExpr->iTable = ++pParse->nVar; + pExpr->iColumn = ++pParse->nVar; if( pParse->nVarExpr>=pParse->nVarExprAlloc-1 ){ pParse->nVarExprAlloc += pParse->nVarExprAlloc + 10; pParse->apVarExpr = @@ -2164,7 +2164,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ assert( pExpr->u.zToken[0]!=0 ); if( pExpr->u.zToken[1]==0 && (pOp = sqlite3VdbeGetOp(v, -1))->opcode==OP_Variable - && pOp->p1+pOp->p3==pExpr->iTable + && pOp->p1+pOp->p3==pExpr->iColumn && pOp->p2+pOp->p3==target && pOp->p4.z==0 ){ @@ -2175,7 +2175,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ */ pOp->p3++; }else{ - sqlite3VdbeAddOp3(v, OP_Variable, pExpr->iTable, target, 1); + sqlite3VdbeAddOp3(v, OP_Variable, pExpr->iColumn, target, 1); if( pExpr->u.zToken[1]!=0 ){ sqlite3VdbeChangeP4(v, -1, pExpr->u.zToken, 0); } @@ -2801,6 +2801,7 @@ int sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){ iMem = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Copy, inReg, iMem); pExpr->iTable = iMem; + pExpr->op2 = pExpr->op; pExpr->op = TK_REGISTER; } return inReg; diff --git a/src/main.c b/src/main.c index 6f5e98e8a..cdb415286 100644 --- a/src/main.c +++ b/src/main.c @@ -1481,8 +1481,8 @@ static const int aHardLimit[] = { #if SQLITE_MAX_LIKE_PATTERN_LENGTH<1 # error SQLITE_MAX_LIKE_PATTERN_LENGTH must be at least 1 #endif -#if SQLITE_MAX_VARIABLE_NUMBER<1 -# error SQLITE_MAX_VARIABLE_NUMBER must be at least 1 +#if SQLITE_MAX_VARIABLE_NUMBER<1 || SQLITE_MAX_VARIABLE_NUMBER>32767 +# error SQLITE_MAX_VARIABLE_NUMBER must be between 1 and 32767 #endif #if SQLITE_MAX_COLUMN>32767 # error SQLITE_MAX_COLUMN must not exceed 32767 diff --git a/src/prepare.c b/src/prepare.c index b4bb6512d..5970b6fcc 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -525,6 +525,7 @@ static int sqlite3Prepare( const char *zSql, /* UTF-8 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ int saveSqlFlag, /* True to copy SQL text into the sqlite3_stmt */ + Vdbe *pReprepare, /* VM being reprepared */ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ const char **pzTail /* OUT: End of parsed string */ ){ @@ -539,6 +540,7 @@ static int sqlite3Prepare( rc = SQLITE_NOMEM; goto end_prepare; } + pParse->pReprepare = pReprepare; if( sqlite3SafetyOn(db) ){ rc = SQLITE_MISUSE; @@ -696,6 +698,7 @@ static int sqlite3LockAndPrepare( const char *zSql, /* UTF-8 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ int saveSqlFlag, /* True to copy SQL text into the sqlite3_stmt */ + Vdbe *pOld, /* VM being reprepared */ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ const char **pzTail /* OUT: End of parsed string */ ){ @@ -707,10 +710,10 @@ static int sqlite3LockAndPrepare( } sqlite3_mutex_enter(db->mutex); sqlite3BtreeEnterAll(db); - rc = sqlite3Prepare(db, zSql, nBytes, saveSqlFlag, ppStmt, pzTail); + rc = sqlite3Prepare(db, zSql, nBytes, saveSqlFlag, pOld, ppStmt, pzTail); if( rc==SQLITE_SCHEMA ){ sqlite3_finalize(*ppStmt); - rc = sqlite3Prepare(db, zSql, nBytes, saveSqlFlag, ppStmt, pzTail); + rc = sqlite3Prepare(db, zSql, nBytes, saveSqlFlag, pOld, ppStmt, pzTail); } sqlite3BtreeLeaveAll(db); sqlite3_mutex_leave(db->mutex); @@ -736,7 +739,7 @@ int sqlite3Reprepare(Vdbe *p){ assert( zSql!=0 ); /* Reprepare only called for prepare_v2() statements */ db = sqlite3VdbeDb(p); assert( sqlite3_mutex_held(db->mutex) ); - rc = sqlite3LockAndPrepare(db, zSql, -1, 0, &pNew, 0); + rc = sqlite3LockAndPrepare(db, zSql, -1, 0, p, &pNew, 0); if( rc ){ if( rc==SQLITE_NOMEM ){ db->mallocFailed = 1; @@ -770,7 +773,7 @@ int sqlite3_prepare( const char **pzTail /* OUT: End of parsed string */ ){ int rc; - rc = sqlite3LockAndPrepare(db,zSql,nBytes,0,ppStmt,pzTail); + rc = sqlite3LockAndPrepare(db,zSql,nBytes,0,0,ppStmt,pzTail); assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); /* VERIFY: F13021 */ return rc; } @@ -782,7 +785,7 @@ int sqlite3_prepare_v2( const char **pzTail /* OUT: End of parsed string */ ){ int rc; - rc = sqlite3LockAndPrepare(db,zSql,nBytes,1,ppStmt,pzTail); + rc = sqlite3LockAndPrepare(db,zSql,nBytes,1,0,ppStmt,pzTail); assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); /* VERIFY: F13021 */ return rc; } @@ -816,7 +819,7 @@ static int sqlite3Prepare16( sqlite3_mutex_enter(db->mutex); zSql8 = sqlite3Utf16to8(db, zSql, nBytes); if( zSql8 ){ - rc = sqlite3LockAndPrepare(db, zSql8, -1, saveSqlFlag, ppStmt, &zTail8); + rc = sqlite3LockAndPrepare(db, zSql8, -1, saveSqlFlag, 0, ppStmt, &zTail8); } if( zTail8 && pzTail ){ diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 877c2c7f2..b8367b071 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -5744,6 +5744,15 @@ int sqlite3_unlock_notify( int sqlite3_strnicmp(const char *, const char *, int); /* +** CAPI3REF: Optimizing for Bound Parameters +** EXPERIMENTAL +** +** If possible, optimize the SQL statement passed as the only argument +** for the values currently bound to the statements SQL variables. +*/ +int sqlite3_reoptimize(sqlite3_stmt *pStmt); + +/* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 6a82cb7c4..c2c947d12 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1590,7 +1590,8 @@ struct Expr { int iTable; /* TK_COLUMN: cursor number of table holding column ** TK_REGISTER: register number ** TK_TRIGGER: 1 -> new, 0 -> old */ - i16 iColumn; /* TK_COLUMN: column index. -1 for rowid */ + i16 iColumn; /* TK_COLUMN: column index. -1 for rowid. + ** TK_VARIABLE: variable number (always >= 1). */ i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */ i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */ u8 flags2; /* Second set of flags. EP2_... */ @@ -2132,6 +2133,7 @@ struct Parse { int nVarExpr; /* Number of used slots in apVarExpr[] */ int nVarExprAlloc; /* Number of allocated slots in apVarExpr[] */ Expr **apVarExpr; /* Pointers to :aaa and $aaaa wildcard expressions */ + Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */ int nAlias; /* Number of aliased result set columns */ int nAliasAlloc; /* Number of allocated slots for aAlias[] */ int *aAlias; /* Register used to hold aliased result */ diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 5f5517ac2..3e7a513e7 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -1128,6 +1128,16 @@ static int dbPrepareAndBind( } pPreStmt->nParm = iParm; *ppPreStmt = pPreStmt; + + /* Call sqlite3_reoptimize() to optimize the statement according to + ** the values just bound to it. If SQLITE_ENABLE_STAT2 is not defined + ** or the statement will not benefit from re-optimization, this + ** call is a no-op. */ + if( SQLITE_OK!=sqlite3_reoptimize(pPreStmt->pStmt) ){ + Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db))); + return TCL_ERROR; + } + return TCL_OK; } diff --git a/src/test1.c b/src/test1.c index ac4f27282..7c0b2ad3d 100644 --- a/src/test1.c +++ b/src/test1.c @@ -2050,6 +2050,34 @@ static int test_stmt_status( } /* +** Usage: sqlite3_reoptimize STMT +** +** Call sqlite3_reoptimize() on the statement handle passed as the +** only parameter. Return a string representing the value returned by +** sqlite3_reoptimize - "SQLITE_OK", "SQLITE_MISUSE" etc. +*/ +static int test_reoptimize( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3_stmt *pStmt; + int rc; + + if( objc!=2 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", + Tcl_GetString(objv[0]), " STMT", 0); + return TCL_ERROR; + } + if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; + rc = sqlite3_reoptimize(pStmt); + Tcl_ResetResult(interp); + Tcl_SetResult(interp, (char *)t1ErrorName(rc), 0); + return TCL_OK; +} + +/* ** Usage: sqlite3_next_stmt DB STMT ** ** Return the next statment in sequence after STMT. @@ -3303,6 +3331,7 @@ static int test_prepare( if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR; rc = sqlite3_prepare(db, zSql, bytes, &pStmt, objc>=5 ? &zTail : 0); + Tcl_ResetResult(interp); if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR; if( zTail && objc>=5 ){ if( bytes>=0 ){ @@ -3360,6 +3389,7 @@ static int test_prepare_v2( rc = sqlite3_prepare_v2(db, zSql, bytes, &pStmt, objc>=5 ? &zTail : 0); assert(rc==SQLITE_OK || pStmt==0); + Tcl_ResetResult(interp); if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR; if( zTail && objc>=5 ){ if( bytes>=0 ){ @@ -5001,6 +5031,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "sqlite3_step", test_step ,0 }, { "sqlite3_sql", test_sql ,0 }, { "sqlite3_next_stmt", test_next_stmt ,0 }, + { "sqlite3_reoptimize", test_reoptimize ,0 }, { "sqlite3_release_memory", test_release_memory, 0}, { "sqlite3_soft_heap_limit", test_soft_heap_limit, 0}, diff --git a/src/vdbe.c b/src/vdbe.c index 19d24af20..6847b9027 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -151,12 +151,10 @@ int sqlite3_found_count = 0; /* ** Argument pMem points at a register that will be passed to a ** user-defined function or returned to the user as the result of a query. -** The second argument, 'db_enc' is the text encoding used by the vdbe for -** register variables. This routine sets the pMem->enc and pMem->type -** variables used by the sqlite3_value_*() routines. +** This routine sets the pMem->type variable used by the sqlite3_value_*() +** routines. */ -#define storeTypeInfo(A,B) _storeTypeInfo(A) -static void _storeTypeInfo(Mem *pMem){ +void sqlite3VdbeMemStoreType(Mem *pMem){ int flags = pMem->flags; if( flags & MEM_Null ){ pMem->type = SQLITE_NULL; @@ -327,7 +325,7 @@ static void applyAffinity( int sqlite3_value_numeric_type(sqlite3_value *pVal){ Mem *pMem = (Mem*)pVal; applyNumericAffinity(pMem); - storeTypeInfo(pMem, 0); + sqlite3VdbeMemStoreType(pMem); return pMem->type; } @@ -1021,7 +1019,7 @@ case OP_Variable: { n = pOp->p3; assert( p1>=0 && p1+n<=p->nVar ); assert( p2>=1 && p2+n-1<=p->nMem ); - assert( pOp->p4.z==0 || pOp->p3==1 ); + assert( pOp->p4.z==0 || pOp->p3==1 || pOp->p3==0 ); while( n-- > 0 ){ pVar = &p->aVar[p1++]; @@ -1168,7 +1166,7 @@ case OP_ResultRow: { pMem = p->pResultSet = &p->aMem[pOp->p1]; for(i=0; i<pOp->p2; i++){ sqlite3VdbeMemNulTerminate(&pMem[i]); - storeTypeInfo(&pMem[i], encoding); + sqlite3VdbeMemStoreType(&pMem[i]); REGISTER_TRACE(pOp->p1+i, &pMem[i]); } if( db->mallocFailed ) goto no_mem; @@ -1387,7 +1385,7 @@ case OP_Function: { pArg = &p->aMem[pOp->p2]; for(i=0; i<n; i++, pArg++){ apVal[i] = pArg; - storeTypeInfo(pArg, encoding); + sqlite3VdbeMemStoreType(pArg); REGISTER_TRACE(pOp->p2, pArg); } @@ -5057,7 +5055,7 @@ case OP_AggStep: { assert( apVal || n==0 ); for(i=0; i<n; i++, pRec++){ apVal[i] = pRec; - storeTypeInfo(pRec, encoding); + sqlite3VdbeMemStoreType(pRec); } ctx.pFunc = pOp->p4.pFunc; assert( pOp->p3>0 && pOp->p3<=p->nMem ); @@ -5345,7 +5343,7 @@ case OP_VFilter: { /* jump */ apArg = p->apArg; for(i = 0; i<nArg; i++){ apArg[i] = &pArgc[i+1]; - storeTypeInfo(apArg[i], 0); + sqlite3VdbeMemStoreType(apArg[i]); } if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; @@ -5549,7 +5547,7 @@ case OP_VUpdate: { apArg = p->apArg; pX = &p->aMem[pOp->p3]; for(i=0; i<nArg; i++){ - storeTypeInfo(pX, 0); + sqlite3VdbeMemStoreType(pX); apArg[i] = pX; pX++; } diff --git a/src/vdbe.h b/src/vdbe.h index 36844346e..1ff4d610a 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -202,6 +202,8 @@ void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, int); void sqlite3VdbeSwap(Vdbe*,Vdbe*); VdbeOp *sqlite3VdbeTakeOpArray(Vdbe*, int*, int*); void sqlite3VdbeProgramDelete(sqlite3 *, SubProgram *, int); +sqlite3_value *sqlite3VdbeGetValue(Vdbe*, int, u8); +void sqlite3VdbeSetVarmask(Vdbe*,int,int); #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT int sqlite3VdbeReleaseMemory(int); diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 91c3ad25d..fabd78606 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -323,6 +323,9 @@ struct Vdbe { #endif VdbeFrame *pFrame; /* Parent frame */ int nFrame; /* Number of frames in pFrame list */ + u8 optimizable; /* True if VM may benefit from sqlite3_reoptimize() */ + u32 optmask; /* Bitmask of vars that may be used by reoptimize() */ + u32 expmask; /* Binding to these vars invalidates VM */ }; /* @@ -388,6 +391,7 @@ int sqlite3VdbeFrameRestore(VdbeFrame *); #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT int sqlite3VdbeReleaseBuffers(Vdbe *p); #endif +void sqlite3VdbeMemStoreType(Mem *pMem); #ifndef SQLITE_OMIT_FOREIGN_KEY int sqlite3VdbeCheckFk(Vdbe *, int); diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 66dfd7152..107c1eb6c 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -914,6 +914,21 @@ static int vdbeUnbind(Vdbe *p, int i){ sqlite3VdbeMemRelease(pVar); pVar->flags = MEM_Null; sqlite3Error(p->db, SQLITE_OK, 0); + + /* If the bit corresponding to this variable is set in Vdbe.opmask, set + ** the optimizable flag before returning. This tells the sqlite3_reoptimize() + ** function that the VM program may benefit from recompilation. + ** + ** If the bit in Vdbe.expmask is set, then binding a new value to this + ** variable invalidates the current query plan. This comes about when the + ** variable is the RHS of a LIKE or GLOB operator and the LIKE/GLOB is + ** able to use an index. */ + if( (i<32 && p->optmask & ((u32)1 << i)) || p->optmask==0xffffffff ){ + p->optimizable = 1; + } + if( (i<32 && p->expmask & ((u32)1 << i)) || p->expmask==0xffffffff ){ + p->expired = 1; + } return SQLITE_OK; } @@ -1205,3 +1220,25 @@ int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){ if( resetFlag ) pVdbe->aCounter[op-1] = 0; return v; } + +/* +** If possible, optimize the statement for the current bindings. +*/ +int sqlite3_reoptimize(sqlite3_stmt *pStmt){ + int rc = SQLITE_OK; + Vdbe *v = (Vdbe *)pStmt; + sqlite3 *db = v->db; + + sqlite3_mutex_enter(db->mutex); + if( v->isPrepareV2==0 || v->pc>0 ){ + rc = SQLITE_MISUSE; + }else if( v->optimizable ){ + rc = sqlite3Reprepare(v); + rc = sqlite3ApiExit(db, rc); + } + assert( rc!=SQLITE_OK || v->optimizable==0 ); + sqlite3_mutex_leave(db->mutex); + + return rc; +} + diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 3db26b29e..d25b633dd 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -3022,3 +3022,44 @@ void sqlite3ExpirePreparedStatements(sqlite3 *db){ sqlite3 *sqlite3VdbeDb(Vdbe *v){ return v->db; } + +/* +** Return a pointer to an sqlite3_value structure containing the value bound +** parameter iVar of VM v. Except, if the value is an SQL NULL, return +** 0 instead. Unless it is NULL, apply affinity aff (one of the SQLITE_AFF_* +** constants) to the value before returning it. +** +** The returned value must be freed by the caller using sqlite3ValueFree(). +*/ +sqlite3_value *sqlite3VdbeGetValue(Vdbe *v, int iVar, u8 aff){ + assert( iVar>0 ); + if( v ){ + Mem *pMem = &v->aVar[iVar-1]; + if( 0==(pMem->flags & MEM_Null) ){ + sqlite3_value *pRet = sqlite3ValueNew(v->db); + if( pRet ){ + sqlite3VdbeMemCopy((Mem *)pRet, pMem); + sqlite3ValueApplyAffinity(pRet, aff, SQLITE_UTF8); + sqlite3VdbeMemStoreType((Mem *)pRet); + } + return pRet; + } + } + return 0; +} + +/* +** Configure SQL variable iVar so that binding a new value to it signals +** to sqlite3_reoptimize() that re-preparing the statement may result +** in a better query plan. +*/ +void sqlite3VdbeSetVarmask(Vdbe *v, int iVar, int isExpire){ + u32 *mask = (isExpire ? &v->expmask : &v->optmask); + assert( iVar>0 ); + if( iVar>32 ){ + *mask = 0xffffffff; + }else{ + *mask |= ((u32)1 << (iVar-1)); + } +} + diff --git a/src/vdbemem.c b/src/vdbemem.c index 45a175f1f..83483244b 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -1053,6 +1053,9 @@ int sqlite3ValueFromExpr( } #endif + if( pVal ){ + sqlite3VdbeMemStoreType(pVal); + } *ppVal = pVal; return SQLITE_OK; diff --git a/src/where.c b/src/where.c index c67a56964..85617bf05 100644 --- a/src/where.c +++ b/src/where.c @@ -625,11 +625,11 @@ static void exprAnalyzeAll( static int isLikeOrGlob( Parse *pParse, /* Parsing and code generating context */ Expr *pExpr, /* Test this expression */ - int *pnPattern, /* Number of non-wildcard prefix characters */ + Expr **ppPrefix, /* Pointer to TK_STRING expression with pattern prefix */ int *pisComplete, /* True if the only wildcard is % in the last character */ int *pnoCase /* True if uppercase is equivalent to lowercase */ ){ - const char *z; /* String on RHS of LIKE operator */ + const char *z = 0; /* String on RHS of LIKE operator */ Expr *pRight, *pLeft; /* Right and left size of LIKE operator */ ExprList *pList; /* List of operands to the LIKE operator */ int c; /* One character in z[] */ @@ -637,6 +637,8 @@ static int isLikeOrGlob( char wc[3]; /* Wildcard characters */ CollSeq *pColl; /* Collating sequence for LHS */ sqlite3 *db = pParse->db; /* Database connection */ + sqlite3_value *pVal = 0; + int op; /* Opcode of pRight */ if( !sqlite3IsLikeFunction(db, pExpr, pnoCase, wc) ){ return 0; @@ -645,10 +647,6 @@ static int isLikeOrGlob( if( *pnoCase ) return 0; #endif pList = pExpr->x.pList; - pRight = pList->a[0].pExpr; - if( pRight->op!=TK_STRING ){ - return 0; - } pLeft = pList->a[1].pExpr; if( pLeft->op!=TK_COLUMN ){ return 0; @@ -661,19 +659,54 @@ static int isLikeOrGlob( return 0; } if( sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT ) return 0; - z = pRight->u.zToken; - if( ALWAYS(z) ){ + + pRight = pList->a[0].pExpr; + op = pRight->op; + if( op==TK_REGISTER ){ + op = pRight->op2; + } + if( op==TK_VARIABLE ){ + Vdbe *pReprepare = pParse->pReprepare; + pVal = sqlite3VdbeGetValue(pReprepare, pRight->iColumn, SQLITE_AFF_NONE); + if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){ + z = (char *)sqlite3_value_text(pVal); + } + sqlite3VdbeSetVarmask(pParse->pVdbe, pRight->iColumn, 0); + assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER ); + }else if( op==TK_STRING ){ + z = pRight->u.zToken; + } + if( z ){ cnt = 0; while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){ cnt++; } if( cnt!=0 && c!=0 && 255!=(u8)z[cnt-1] ){ + Expr *pPrefix; *pisComplete = z[cnt]==wc[0] && z[cnt+1]==0; - *pnPattern = cnt; - return 1; + pPrefix = sqlite3Expr(db, TK_STRING, z); + if( pPrefix ) pPrefix->u.zToken[cnt] = 0; + *ppPrefix = pPrefix; + if( op==TK_VARIABLE ){ + Vdbe *v = pParse->pVdbe; + sqlite3VdbeSetVarmask(v, pRight->iColumn, 1); + if( *pisComplete && pRight->u.zToken[1] ){ + /* If the rhs of the LIKE expression is a variable, and the current + ** value of the variable means there is no need to invoke the LIKE + ** function, then no OP_Variable will be added to the program. + ** This causes problems for the sqlite3_bind_parameter_name() + ** API. To workaround them, add a dummy OP_Variable here. */ + sqlite3ExprCodeTarget(pParse, pRight, 1); + sqlite3VdbeChangeP3(v, sqlite3VdbeCurrentAddr(v)-1, 0); + } + } + }else{ + z = 0; } } - return 0; + + sqlite3ValueFree(pVal); + return (z!=0); } #endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */ @@ -1055,12 +1088,12 @@ static void exprAnalyze( Bitmask prereqLeft; /* Prerequesites of the pExpr->pLeft */ Bitmask prereqAll; /* Prerequesites of pExpr */ Bitmask extraRight = 0; - int nPattern; int isComplete; int noCase; int op; /* Top-level operator. pExpr->op */ Parse *pParse = pWC->pParse; /* Parsing context */ sqlite3 *db = pParse->db; /* Database connection */ + Expr *pStr1; if( db->mallocFailed ){ return; @@ -1192,21 +1225,19 @@ static void exprAnalyze( ** The last character of the prefix "abc" is incremented to form the ** termination condition "abd". */ - if( isLikeOrGlob(pParse, pExpr, &nPattern, &isComplete, &noCase) - && pWC->op==TK_AND ){ - Expr *pLeft, *pRight; - Expr *pStr1, *pStr2; + if( pWC->op==TK_AND + && isLikeOrGlob(pParse, pExpr, &pStr1, &isComplete, &noCase) + ){ + Expr *pLeft; + Expr *pStr2; Expr *pNewExpr1, *pNewExpr2; int idxNew1, idxNew2; pLeft = pExpr->x.pList->a[1].pExpr; - pRight = pExpr->x.pList->a[0].pExpr; - pStr1 = sqlite3Expr(db, TK_STRING, pRight->u.zToken); - if( pStr1 ) pStr1->u.zToken[nPattern] = 0; pStr2 = sqlite3ExprDup(db, pStr1, 0); if( !db->mallocFailed ){ u8 c, *pC; /* Last character before the first wildcard */ - pC = (u8*)&pStr2->u.zToken[nPattern-1]; + pC = (u8*)&pStr2->u.zToken[sqlite3Strlen30(pStr2->u.zToken)-1]; c = *pC; if( noCase ){ /* The point is to increment the last character before the first @@ -1985,6 +2016,39 @@ static int whereRangeRegion( #endif /* #ifdef SQLITE_ENABLE_STAT2 */ /* +** If expression pExpr represents a literal value, set *pp to point to +** an sqlite3_value structure containing the same value, with affinity +** aff applied to it, before returning. It is the responsibility of the +** caller to eventually release this structure by passing it to +** sqlite3ValueFree(). +** +** If the current parse is a recompile (sqlite3Reprepare()) and pExpr +** is an SQL variable that currently has a non-NULL value bound to it, +** create an sqlite3_value structure containing this value, again with +** affinity aff applied to it, instead. +** +** If neither of the above apply, set *pp to NULL. +** +** If an error occurs, return an error code. Otherwise, SQLITE_OK. +*/ +static int valueFromExpr( + Parse *pParse, + Expr *pExpr, + u8 aff, + sqlite3_value **pp +){ + if( (pExpr->op==TK_VARIABLE) + || (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE) + ){ + int iVar = pExpr->iColumn; + sqlite3VdbeSetVarmask(pParse->pVdbe, iVar, 0); + *pp = sqlite3VdbeGetValue(pParse->pReprepare, iVar, aff); + return SQLITE_OK; + } + return sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, aff, pp); +} + +/* ** This function is used to estimate the number of rows that will be visited ** by scanning an index for a range of values. The range may have an upper ** bound, a lower bound, or both. The WHERE clause terms that set the upper @@ -2036,23 +2100,22 @@ static int whereRangeScanEst( int rc = SQLITE_OK; #ifdef SQLITE_ENABLE_STAT2 - sqlite3 *db = pParse->db; - sqlite3_value *pLowerVal = 0; - sqlite3_value *pUpperVal = 0; if( nEq==0 && p->aSample ){ + sqlite3_value *pLowerVal = 0; + sqlite3_value *pUpperVal = 0; int iEst; int iLower = 0; int iUpper = SQLITE_INDEX_SAMPLES; - u8 aff = p->pTable->aCol[0].affinity; + u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity; if( pLower ){ Expr *pExpr = pLower->pExpr->pRight; - rc = sqlite3ValueFromExpr(db, pExpr, SQLITE_UTF8, aff, &pLowerVal); + rc = valueFromExpr(pParse, pExpr, aff, &pLowerVal); } if( rc==SQLITE_OK && pUpper ){ Expr *pExpr = pUpper->pExpr->pRight; - rc = sqlite3ValueFromExpr(db, pExpr, SQLITE_UTF8, aff, &pUpperVal); + rc = valueFromExpr(pParse, pExpr, aff, &pUpperVal); } if( rc!=SQLITE_OK || (pLowerVal==0 && pUpperVal==0) ){ diff --git a/test/analyze3.test b/test/analyze3.test new file mode 100644 index 000000000..2c56ff742 --- /dev/null +++ b/test/analyze3.test @@ -0,0 +1,599 @@ +# 2009 August 06 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# This file implements regression tests for SQLite library. This file +# implements tests for the sqlite3_reoptimize() functionality. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !stat2 { + finish_test + return +} + +#---------------------------------------------------------------------- +# Test Organization: +# +# analyze3-1.*: Test that the values of bound parameters are considered +# in the same way as constants when planning queries that +# use range constraints. +# +# analyze3-2.*: Test that the values of bound parameters are considered +# in the same way as constants when planning queries that +# use LIKE expressions in the WHERE clause. +# +# analyze3-3.*: Test that sqlite3_reoptimize() is a no-op when there is +# no way for re-preparing the query to produce a superior +# query plan. +# +# analyze3-4.*: Test that SQL or authorization callback errors occuring +# within sqlite3_reoptimize() are handled correctly. +# + +proc getvar {varname} { uplevel #0 set $varname } +db function var getvar + +proc eqp {sql {db db}} { + uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db +} + +proc sf_execsql {sql {db db}} { + set ::sqlite_search_count 0 + set r [uplevel [list execsql $sql $db]] + + concat $::sqlite_search_count [$db status step] $r +} + +#------------------------------------------------------------------------- +# +# analyze3-1.1.1: +# Create a table with two columns. Populate the first column (affinity +# INTEGER) with integer values from 100 to 1100. Create an index on this +# column. ANALYZE the table. +# +# analyze3-1.1.2 - 3.1.3 +# Show that there are two possible plans for querying the table with +# a range constraint on the indexed column - "full table scan" or "use +# the index". When the range is specified using literal values, SQLite +# is able to pick the best plan based on the samples in sqlite_stat2. +# +# analyze3-1.1.4 - 3.1.9 +# Show that using SQL variables produces the same results as using +# literal values to constrain the range scan. This works because the +# Tcl interface always calls [sqlite3_reoptimize] after binding values. +# +# These tests also check that the compiler code considers column +# affinities when estimating the number of rows scanned by the "use +# index strategy". +# +do_test analyze3-1.1.1 { + execsql { + BEGIN; + CREATE TABLE t1(x INTEGER, y); + CREATE INDEX i1 ON t1(x); + } + for {set i 0} {$i < 1000} {incr i} { + execsql { INSERT INTO t1 VALUES($i+100, $i) } + } + execsql { + COMMIT; + ANALYZE; + } +} {} + +do_test analyze3-1.1.2 { + eqp { SELECT sum(y) FROM t1 WHERE x>200 AND x<300 } +} {0 0 {TABLE t1 WITH INDEX i1}} +do_test analyze3-1.1.3 { + eqp { SELECT sum(y) FROM t1 WHERE x>0 AND x<1100 } +} {0 0 {TABLE t1}} + +do_test analyze3-1.1.4 { + sf_execsql { SELECT sum(y) FROM t1 WHERE x>200 AND x<300 } +} {199 0 14850} +do_test analyze3-1.1.5 { + set l [string range "200" 0 end] + set u [string range "300" 0 end] + sf_execsql { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u } +} {199 0 14850} +do_test analyze3-1.1.6 { + set l [expr int(200)] + set u [expr int(300)] + sf_execsql { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u } +} {199 0 14850} +do_test analyze3-1.1.7 { + sf_execsql { SELECT sum(y) FROM t1 WHERE x>0 AND x<1100 } +} {999 999 499500} +do_test analyze3-1.1.8 { + set l [string range "0" 0 end] + set u [string range "1100" 0 end] + sf_execsql { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u } +} {999 999 499500} +do_test analyze3-1.1.9 { + set l [expr int(0)] + set u [expr int(1100)] + sf_execsql { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u } +} {999 999 499500} + + +# The following tests are similar to the block above. The difference is +# that the indexed column has TEXT affinity in this case. In the tests +# above the affinity is INTEGER. +# +do_test analyze3-1.2.1 { + execsql { + BEGIN; + CREATE TABLE t2(x TEXT, y); + INSERT INTO t2 SELECT * FROM t1; + CREATE INDEX i2 ON t2(x); + COMMIT; + ANALYZE; + } +} {} +do_test analyze3-1.2.2 { + eqp { SELECT sum(y) FROM t2 WHERE x>1 AND x<2 } +} {0 0 {TABLE t2 WITH INDEX i2}} +do_test analyze3-1.2.3 { + eqp { SELECT sum(y) FROM t2 WHERE x>0 AND x<99 } +} {0 0 {TABLE t2}} +do_test analyze3-1.2.4 { + sf_execsql { SELECT sum(y) FROM t2 WHERE x>12 AND x<20 } +} {161 0 4760} +do_test analyze3-1.2.5 { + set l [string range "12" 0 end] + set u [string range "20" 0 end] + sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u} +} {161 0 text text 4760} +do_test analyze3-1.2.6 { + set l [expr int(12)] + set u [expr int(20)] + sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u} +} {161 0 integer integer 4760} +do_test analyze3-1.2.7 { + sf_execsql { SELECT sum(y) FROM t2 WHERE x>0 AND x<99 } +} {999 999 490555} +do_test analyze3-1.2.8 { + set l [string range "0" 0 end] + set u [string range "99" 0 end] + sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u} +} {999 999 text text 490555} +do_test analyze3-1.2.9 { + set l [expr int(0)] + set u [expr int(99)] + sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u} +} {999 999 integer integer 490555} + +# Same tests a third time. This time, column x has INTEGER affinity and +# is not the leftmost column of the table. This triggered a bug causing +# SQLite to use sub-optimal query plans in 3.6.18 and earlier. +# +do_test analyze3-1.3.1 { + execsql { + BEGIN; + CREATE TABLE t3(y TEXT, x INTEGER); + INSERT INTO t3 SELECT y, x FROM t1; + CREATE INDEX i3 ON t3(x); + COMMIT; + ANALYZE; + } +} {} +do_test analyze3-1.3.2 { + eqp { SELECT sum(y) FROM t3 WHERE x>200 AND x<300 } +} {0 0 {TABLE t3 WITH INDEX i3}} +do_test analyze3-1.3.3 { + eqp { SELECT sum(y) FROM t3 WHERE x>0 AND x<1100 } +} {0 0 {TABLE t3}} + +do_test analyze3-1.3.4 { + sf_execsql { SELECT sum(y) FROM t3 WHERE x>200 AND x<300 } +} {199 0 14850} +do_test analyze3-1.3.5 { + set l [string range "200" 0 end] + set u [string range "300" 0 end] + sf_execsql { SELECT sum(y) FROM t3 WHERE x>$l AND x<$u } +} {199 0 14850} +do_test analyze3-1.3.6 { + set l [expr int(200)] + set u [expr int(300)] + sf_execsql { SELECT sum(y) FROM t3 WHERE x>$l AND x<$u } +} {199 0 14850} +do_test analyze3-1.3.7 { + sf_execsql { SELECT sum(y) FROM t3 WHERE x>0 AND x<1100 } +} {999 999 499500} +do_test analyze3-1.3.8 { + set l [string range "0" 0 end] + set u [string range "1100" 0 end] + sf_execsql { SELECT sum(y) FROM t3 WHERE x>$l AND x<$u } +} {999 999 499500} +do_test analyze3-1.3.9 { + set l [expr int(0)] + set u [expr int(1100)] + sf_execsql { SELECT sum(y) FROM t3 WHERE x>$l AND x<$u } +} {999 999 499500} + +#------------------------------------------------------------------------- +# Test that the values of bound SQL variables may be used for the LIKE +# optimization. +# +drop_all_tables +do_test analyze3-2.1 { + execsql { + PRAGMA case_sensitive_like=off; + BEGIN; + CREATE TABLE t1(a, b TEXT COLLATE nocase); + CREATE INDEX i1 ON t1(b); + } + for {set i 0} {$i < 1000} {incr i} { + set t "" + append t [lindex {a b c d e f g h i j} [expr $i/100]] + append t [lindex {a b c d e f g h i j} [expr ($i/10)%10]] + append t [lindex {a b c d e f g h i j} [expr ($i%10)]] + execsql { INSERT INTO t1 VALUES($i, $t) } + } + execsql COMMIT +} {} +do_test analyze3-2.2 { + eqp { SELECT count(a) FROM t1 WHERE b LIKE 'a%' } +} {0 0 {TABLE t1 WITH INDEX i1}} +do_test analyze3-2.3 { + eqp { SELECT count(a) FROM t1 WHERE b LIKE '%a' } +} {0 0 {TABLE t1}} + +do_test analyze3-2.4 { + sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE 'a%' } +} {101 0 100} +do_test analyze3-2.5 { + sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE '%a' } +} {999 999 100} + +do_test analyze3-2.4 { + set like "a%" + sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE $like } +} {101 0 100} +do_test analyze3-2.5 { + set like "%a" + sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE $like } +} {999 999 100} + + +#------------------------------------------------------------------------- +# This block of tests checks that sqlite3_reoptimize() is a no-op if +# the values bound to any parameters that may affect the query plan +# have not changed since the statement was last compiled. +# +# It is possible to tell if sqlite3_reoptimize() is a no-op by registering +# an authorization callback. If the auth callback is not invoked from +# within a give call to reoptimize(), then it must have been a no-op. +# +# Also test that: +# +# * sqlite3_reoptimize() returns SQLITE_MISUSE if called on a statement +# that was prepared using the legacy sqlite3_prepare() interface, +# +# * sqlite3_reoptimize() returns SQLITE_MISUSE if called on a statement +# that is not in the "reset" state. +# +drop_all_tables +db auth auth +proc auth {args} { + set ::auth 1 + return SQLITE_OK +} + +# Return true if calling reoptimize() on the statement handle passed +# as an argument causes the statement to be recompiled. +# +proc test_reoptimize {stmt} { + set ::auth 0 + sqlite3_reoptimize $stmt + set ::auth +} + +do_test analyze3-3.1 { + execsql { + BEGIN; + CREATE TABLE t1(a, b, c); + CREATE INDEX i1 ON t1(b); + } + for {set i 0} {$i < 100} {incr i} { + execsql { INSERT INTO t1 VALUES($i, $i, $i) } + } + execsql COMMIT + execsql ANALYZE +} {} + +do_test analyze3-3.2.1 { + set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE b>?" -1 dummy] + test_reoptimize $S +} {0} +do_test analyze3-3.2.2 { + sqlite3_bind_text $S 1 "abc" 3 + test_reoptimize $S +} {1} +do_test analyze3-3.2.3 { + test_reoptimize $S +} {0} +do_test analyze3-3.2.4 { + sqlite3_finalize $S +} {SQLITE_OK} + +do_test analyze3-3.2.1 { + set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE b=?" -1 dummy] + test_reoptimize $S +} {0} +do_test analyze3-3.2.2 { + sqlite3_bind_text $S 1 "abc" 3 + test_reoptimize $S +} {0} +do_test analyze3-3.2.3 { + sqlite3_finalize $S +} {SQLITE_OK} + +do_test analyze3-3.3.1 { + set S [sqlite3_prepare db "SELECT * FROM t1 WHERE b=?" -1 dummy] + sqlite3_reoptimize $S +} {SQLITE_MISUSE} +do_test analyze3-3.3.2 { + sqlite3_finalize $S +} {SQLITE_OK} + +do_test analyze3-3.3.1 { + set S [sqlite3_prepare_v2 db "SELECT * FROM t1" -1 dummy] + sqlite3_reoptimize $S +} {SQLITE_OK} +do_test analyze3-3.3.2 { + sqlite3_step $S +} {SQLITE_ROW} +do_test analyze3-3.3.3 { + sqlite3_reoptimize $S +} {SQLITE_MISUSE} +do_test analyze3-3.3.4 { + while {"SQLITE_ROW" == [sqlite3_step $S]} {} + sqlite3_reoptimize $S +} {SQLITE_MISUSE} +do_test analyze3-3.3.5 { + sqlite3_finalize $S +} {SQLITE_OK} + +do_test analyze3-3.4.1 { + set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE a=? AND b>?" -1 dummy] + test_reoptimize $S +} {0} +do_test analyze3-3.4.2 { + sqlite3_bind_text $S 1 "abc" 3 + test_reoptimize $S +} {0} +do_test analyze3-3.4.3 { + sqlite3_bind_text $S 2 "def" 3 + test_reoptimize $S +} {1} +do_test analyze3-3.4.4 { + sqlite3_bind_text $S 2 "ghi" 3 + test_reoptimize $S +} {1} +do_test analyze3-3.4.5 { + test_reoptimize $S +} {0} +do_test analyze3-3.4.6 { + sqlite3_finalize $S +} {SQLITE_OK} + +do_test analyze3-3.5.1 { + set S [sqlite3_prepare_v2 db { + SELECT * FROM t1 WHERE a IN ( + ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, + ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, + ?21, ?22, ?23, ?24, ?25, ?26, ?27, ?28, ?29, ?30, ?31 + ) AND b>?32; + } -1 dummy] + test_reoptimize $S +} {0} +do_test analyze3-3.5.2 { + sqlite3_bind_text $S 31 "abc" 3 + test_reoptimize $S +} {0} +do_test analyze3-3.5.3 { + sqlite3_bind_text $S 32 "def" 3 + test_reoptimize $S +} {1} +do_test analyze3-3.5.4 { + test_reoptimize $S +} {0} +do_test analyze3-3.5.5 { + sqlite3_finalize $S +} {SQLITE_OK} + +do_test analyze3-3.6.1 { + set S [sqlite3_prepare_v2 db { + SELECT * FROM t1 WHERE a IN ( + ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, + ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, + ?21, ?22, ?23, ?24, ?25, ?26, ?27, ?28, ?29, ?30, ?31, ?32 + ) AND b>?33; + } -1 dummy] + test_reoptimize $S +} {0} +do_test analyze3-3.6.2 { + sqlite3_bind_text $S 32 "abc" 3 + test_reoptimize $S +} {1} +do_test analyze3-3.6.3 { + sqlite3_bind_text $S 33 "def" 3 + test_reoptimize $S +} {1} +do_test analyze3-3.6.4 { + test_reoptimize $S +} {0} +do_test analyze3-3.6.5 { + sqlite3_finalize $S +} {SQLITE_OK} + +do_test analyze3-3.7.1 { +breakpoint + set S [sqlite3_prepare_v2 db { + SELECT * FROM t1 WHERE a IN ( + ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?33, + ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, + ?21, ?22, ?23, ?24, ?25, ?26, ?27, ?28, ?29, ?30, ?31, ?32 + ) AND b>?10; + } -1 dummy] + test_reoptimize $S +} {0} +do_test analyze3-3.7.2 { + sqlite3_bind_text $S 32 "abc" 3 + test_reoptimize $S +} {0} +do_test analyze3-3.7.3 { + sqlite3_bind_text $S 33 "def" 3 + test_reoptimize $S +} {0} +do_test analyze3-3.7.4 { + sqlite3_bind_text $S 10 "def" 3 + test_reoptimize $S +} {1} +do_test analyze3-3.7.5 { + test_reoptimize $S +} {0} +do_test analyze3-3.7.6 { + sqlite3_finalize $S +} {SQLITE_OK} + +do_test analyze3-3.8.1 { + execsql { + CREATE TABLE t4(x, y TEXT COLLATE NOCASE); + CREATE INDEX i4 ON t4(y); + } +} {} +do_test analyze3-3.8.2 { + set S [sqlite3_prepare_v2 db { + SELECT * FROM t4 WHERE x != ? AND y LIKE ? + } -1 dummy] + test_reoptimize $S +} {0} +do_test analyze3-3.8.3 { + sqlite3_bind_text $S 1 "abc" 3 + test_reoptimize $S +} {0} +do_test analyze3-3.8.4 { + sqlite3_bind_text $S 2 "def" 3 + test_reoptimize $S +} {1} +do_test analyze3-3.8.5 { + test_reoptimize $S +} {0} +do_test analyze3-3.8.6 { + sqlite3_expired $S +} {0} +do_test analyze3-3.8.7 { + sqlite3_bind_text $S 2 "ghi%" 4 + sqlite3_expired $S +} {0} +do_test analyze3-3.8.8 { + test_reoptimize $S +} {1} +do_test analyze3-3.8.9 { + sqlite3_bind_text $S 2 "ghi%def" 7 + sqlite3_expired $S +} {1} +do_test analyze3-3.8.10 { + test_reoptimize $S +} {1} +do_test analyze3-3.8.11 { + sqlite3_bind_text $S 2 "%ab" 3 + sqlite3_expired $S +} {1} +do_test analyze3-3.8.12 { + test_reoptimize $S +} {1} +do_test analyze3-3.8.12 { + sqlite3_bind_text $S 2 "%de" 3 + sqlite3_expired $S +} {0} +do_test analyze3-3.8.13 { + test_reoptimize $S +} {1} +do_test analyze3-3.8.14 { + sqlite3_finalize $S +} {SQLITE_OK} + +#------------------------------------------------------------------------- +# These tests check that errors encountered while repreparing an SQL +# statement within sqlite3_reoptimize() are handled correctly. +# + +# Check an schema error. +# +do_test analyze3-4.1.1 { + set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE a=? AND b>?" -1 dummy] + sqlite3_reoptimize $S +} {SQLITE_OK} +do_test analyze3-4.1.2 { + sqlite3_bind_text $S 2 "abc" 3 + execsql { DROP TABLE t1 } + sqlite3_reoptimize $S +} {SQLITE_SCHEMA} +do_test analyze3-4.1.3 { + sqlite3_step $S +} {SQLITE_SCHEMA} +do_test analyze3-4.1.4 { + sqlite3_finalize $S +} {SQLITE_SCHEMA} + +# Check an authorization error. +# +do_test analyze3-4.2.1 { + execsql { + BEGIN; + CREATE TABLE t1(a, b, c); + CREATE INDEX i1 ON t1(b); + } + for {set i 0} {$i < 100} {incr i} { + execsql { INSERT INTO t1 VALUES($i, $i, $i) } + } + execsql COMMIT + execsql ANALYZE + set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE a=? AND b>?" -1 dummy] + sqlite3_reoptimize $S +} {SQLITE_OK} +db auth auth +proc auth {args} { + if {[lindex $args 0] == "SQLITE_READ"} {return SQLITE_DENY} + return SQLITE_OK +} +do_test analyze3-4.2.2 { + sqlite3_bind_text $S 2 "abc" 3 + sqlite3_reoptimize $S +} {SQLITE_SCHEMA} +do_test analyze3-4.2.3 { + sqlite3_step $S +} {SQLITE_SCHEMA} +do_test analyze3-4.2.4 { + sqlite3_finalize $S +} {SQLITE_SCHEMA} + +# Check the effect of an authorization error that occurs in a re-prepare +# performed by sqlite3_step() is the same as one that occurs within +# sqlite3_reoptimize(). +# +do_test analyze3-4.3.1 { + db auth {} + set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE a=? AND b>?" -1 dummy] + execsql { CREATE TABLE t2(d, e, f) } + db auth auth + sqlite3_step $S +} {SQLITE_SCHEMA} +do_test analyze3-4.3.2 { + sqlite3_finalize $S +} {SQLITE_SCHEMA} + +finish_test |