]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add the experimental sqlite3_reoptimize() API.
authordan <dan@noemail.net>
Thu, 15 Oct 2009 18:35:38 +0000 (18:35 +0000)
committerdan <dan@noemail.net>
Thu, 15 Oct 2009 18:35:38 +0000 (18:35 +0000)
FossilOrigin-Name: 9bd6f3d8864d422fe42074688b191915b27ad8ea

17 files changed:
manifest
manifest.uuid
src/expr.c
src/main.c
src/prepare.c
src/sqlite.h.in
src/sqliteInt.h
src/tclsqlite.c
src/test1.c
src/vdbe.c
src/vdbe.h
src/vdbeInt.h
src/vdbeapi.c
src/vdbeaux.c
src/vdbemem.c
src/where.c
test/analyze3.test [new file with mode: 0644]

index 19154c87adc51b0dd9cb9091e4f982f3277008e1..cb3b026c85277aae866cb36ba48bcd616527dda2 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,8 +1,5 @@
------BEGIN PGP SIGNED MESSAGE-----
-Hash: SHA1
-
-C Version\s3.6.19\srelease\scandidate
-D 2009-10-14T11:33:55
+C Add\sthe\sexperimental\ssqlite3_reoptimize()\sAPI.
+D 2009-10-15T18:35:39
 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
 F Makefile.in 4ca3f1dd6efa2075bcb27f4dc43eef749877740d
 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -117,7 +114,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 +127,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 +155,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 +206,16 @@ F src/update.c 2c8a64237e4fae604468d14380b877d169545b63
 F src/utf.c 99cf927eabb104621ba889ac0dd075fc1657ad30
 F src/util.c 59d4e9456bf1fe581f415a783fa0cee6115c8f35
 F src/vacuum.c 869d08eaab64e2a4eaf4ef9ea34b851892b65a75
-F src/vdbe.c 93fb0b490db0e27291d5240b00320a378199e0b8
-F src/vdbe.h 7d5075e3fa4e5587a9be8d5e503857c825490cef
-F src/vdbeInt.h 7afb76c0296f9a2310e565803fa66798ef47e9d5
-F src/vdbeapi.c 524d79eb17bbcbe31c37c908b8e01edc5c684a90
-F src/vdbeaux.c 6834737c119f5662c9e6d147ddb4f72523a31aea
+F src/vdbe.c a03993ed188c9b2c575766dfe2b8cdc9f0bd2262
+F src/vdbe.h 1fb725c38df7f79dc60e9a61cb368152d9457e3c
+F src/vdbeInt.h aafda2e9761298e12ef0a3e8b5caed9aaf9c7592
+F src/vdbeapi.c 7de617aceb19563b2409c101ee4b2d6702e64d1b
+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 +226,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 030efdbb019e0246b43ff0040559ab8da2cd1d35
 F test/async.test 8c75d31b8330f8b70cf2571b014d4476a063efdb
 F test/async2.test bf5e2ca2c96763b4cba3d016249ad7259a5603b6
 F test/async3.test 93edaa9122f498e56ea98c36c72abc407f4fb11e
@@ -760,14 +758,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
-P ac19dbc6a208be2313e47cbcc0b2a0d8d04bae4c
-R 989a9468e4db33fc4015d0bb3c927c8c
-U drh
-Z c68f3421f3f8fb97175ced03120a9234
------BEGIN PGP SIGNATURE-----
-Version: GnuPG v1.4.6 (GNU/Linux)
-
-iD8DBQFK1bcmoxKgR168RlERAqQ5AJ9xUtHzhAMNU+bXzeO3SQg1Qu58qgCZARPR
-8o61exV/iSWvw1xrcHTiCMo=
-=4tvm
------END PGP SIGNATURE-----
+P c1d499afc50d54b376945b4efb65c56c787a073d
+R e61b2301a45df21e4dc30b9f4d342cf1
+U dan
+Z 1f7bbf5e059f514df66c16d9a82a79d8
index bb516efbbc82f08cd1341203b5bd8144b5b4b28a..7bd30591dae836571c8eef63607cc1f1e4aee86e 100644 (file)
@@ -1 +1 @@
-c1d499afc50d54b376945b4efb65c56c787a073d
\ No newline at end of file
+9bd6f3d8864d422fe42074688b191915b27ad8ea
\ No newline at end of file
index 06db149016d4010462d3b051b24e6607d4406d53..f592fc75a913ea5e1fe22faec4b94469f1ed68d2 100644 (file)
@@ -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;
index 6f5e98e8a3cd61bd518e1d011e3e2f7f1b2d2849..cdb4152864480dfc3be22e56eaaeb392faadfc95 100644 (file)
@@ -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
index b4bb6512d51f0e26708fea00f5a6b55c4daf53ad..5970b6fcc91c3d83b39e4f4713fde41e88b8a851 100644 (file)
@@ -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 ){
index 877c2c7f286f34907e98910b2033fd6a53a98668..b8367b07183d9d4de63ac75e3b6e48fdd99d1d51 100644 (file)
@@ -5743,6 +5743,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.
index 6a82cb7c45c05793bc609f9921ac5324808306bf..c2c947d12d85a31c227e3eec8e9ee1ab0b2b1fd8 100644 (file)
@@ -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 */
index 5f5517ac2b841a5eb5879755ebc11fd64e3bc1ce..3e7a513e77db0f5345d197a5e0a682cf3c21aace 100644 (file)
@@ -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;
 }
 
index ac4f27282f1dcd17fea50d515254fdc3d00be798..7c0b2ad3d55471bb3a330a2560c249b3ddb78ea0 100644 (file)
@@ -2049,6 +2049,34 @@ static int test_stmt_status(
   return TCL_OK;
 }
 
+/*
+** 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
 **
@@ -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},
index c89dbd7bc44cc45b9e0445e2bc56a165665f2618..bf39585c90f0e4aeac94ddbe2daf30cc6cbefd59 100644 (file)
@@ -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);
   }
 
@@ -5051,7 +5049,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 );
@@ -5339,7 +5337,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;
@@ -5543,7 +5541,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++;
     }
index 36844346eaa1855ebe548f682e83ad4ca9ca96e0..1ff4d610a2dd4e1362f86e84ed6fa6c21bf41b49 100644 (file)
@@ -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);
index 91c3ad25d43eab986e06f98c109f83af0912e317..fabd78606020a27d884cdebf2116a35eae8ff76d 100644 (file)
@@ -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);
index 66dfd7152e29b9f3c6e220905de4c581832240ba..3e7ac71815124c9023f651bc3700a59207757936 100644 (file)
@@ -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,27 @@ 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 ){
+    sqlite3 *db = v->db;
+    sqlite3_mutex_enter(db->mutex);
+    rc = sqlite3Reprepare(v);
+    rc = sqlite3ApiExit(db, rc);
+  }
+  assert( rc!=SQLITE_OK || v->optimizable==0 );
+  sqlite3_mutex_leave(db->mutex);
+
+  return rc;
+}
+
index 3db26b29e0078339d169ddbe52855c0bd48cf69a..d25b633dd423e34dbd72e6479ab1bf661b5ac968 100644 (file)
@@ -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));
+  }
+}
+
index 45a175f1f446d394de56a924b470859554d7784b..83483244bb5cc9a35644f5df641f1f3c946a4f13 100644 (file)
@@ -1053,6 +1053,9 @@ int sqlite3ValueFromExpr(
   }
 #endif
 
+  if( pVal ){
+    sqlite3VdbeMemStoreType(pVal);
+  }
   *ppVal = pVal;
   return SQLITE_OK;
 
index c67a56964ea46b761062f3732df64ce22f5cab59..85617bf054220a08694f5e2d6dab108255a81db6 100644 (file)
@@ -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
@@ -1984,6 +2015,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
@@ -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 (file)
index 0000000..bb50285
--- /dev/null
@@ -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" -1
+  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" -1
+  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" -1
+  test_reoptimize $S
+} {0}
+do_test analyze3-3.4.3 {
+  sqlite3_bind_text $S 2 "def" -1
+  test_reoptimize $S
+} {1}
+do_test analyze3-3.4.4 {
+  sqlite3_bind_text $S 2 "ghi" -1
+  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" -1
+  test_reoptimize $S
+} {0}
+do_test analyze3-3.5.3 {
+  sqlite3_bind_text $S 32 "def" -1
+  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" -1
+  test_reoptimize $S
+} {1}
+do_test analyze3-3.6.3 {
+  sqlite3_bind_text $S 33 "def" -1
+  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" -1
+  test_reoptimize $S
+} {0}
+do_test analyze3-3.7.3 {
+  sqlite3_bind_text $S 33 "def" -1
+  test_reoptimize $S
+} {0}
+do_test analyze3-3.7.4 {
+  sqlite3_bind_text $S 10 "def" -1
+  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" -1
+  test_reoptimize $S
+} {0}
+do_test analyze3-3.8.4 {
+  sqlite3_bind_text $S 2 "def" -1
+  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%" -1
+  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" -1
+  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" -1
+  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" -1
+  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" -1
+  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" -1
+  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