]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
First attempt at adding a built-in prepared statement cache. This is
authordrh <>
Wed, 24 Aug 2022 17:55:27 +0000 (17:55 +0000)
committerdrh <>
Wed, 24 Aug 2022 17:55:27 +0000 (17:55 +0000)
mostly working, but still has a few obscure faults.

FossilOrigin-Name: c217b763b148e8bece9d1f72525fd5026d3f710b337c010ffd0306fde4f9add8

13 files changed:
ext/misc/stmt.c
manifest
manifest.uuid
src/main.c
src/prepare.c
src/sqlite.h.in
src/sqliteInt.h
src/vdbe.h
src/vdbeInt.h
src/vdbeapi.c
src/vdbeaux.c
test/speedtest1.c
tool/speed-check.sh

index 1687f419782ba8f35552cff52237e73e87406191..a89eccf29559ee96a35a9cfdb7b00ffed9f55777 100644 (file)
@@ -31,7 +31,7 @@ SQLITE_EXTENSION_INIT1
 #ifndef SQLITE_OMIT_VIRTUALTABLE
 
 
-#define STMT_NUM_INTEGER_COLUMN 10
+#define STMT_NUM_INTEGER_COLUMN 11
 typedef struct StmtRow StmtRow;
 struct StmtRow {
   sqlite3_int64 iRowid;                /* Rowid value */
@@ -95,11 +95,12 @@ static int stmtConnect(
 #define STMT_COLUMN_REPREP  8   /* SQLITE_STMTSTATUS_REPREPARE */
 #define STMT_COLUMN_RUN     9   /* SQLITE_STMTSTATUS_RUN */
 #define STMT_COLUMN_MEM    10   /* SQLITE_STMTSTATUS_MEMUSED */
+#define STMT_COLUMN_STATE  11   /* SQLITE_STMTSTATUS_STATE */
 
 
   rc = sqlite3_declare_vtab(db,
      "CREATE TABLE x(sql,ncol,ro,busy,nscan,nsort,naidx,nstep,"
-                    "reprep,run,mem)");
+                    "reprep,run,mem,state)");
   if( rc==SQLITE_OK ){
     pNew = sqlite3_malloc64( sizeof(*pNew) );
     *ppVtab = (sqlite3_vtab*)pNew;
@@ -175,6 +176,10 @@ static int stmtColumn(
   StmtRow *pRow = pCur->pRow;
   if( i==STMT_COLUMN_SQL ){
     sqlite3_result_text(ctx, pRow->zSql, -1, SQLITE_TRANSIENT);
+  }else if( i==STMT_COLUMN_STATE ){
+    const char *azStateName[] = { "INIT", "READY", "RUN", "HALT", "CACHE" };
+    int j = pRow->aCol[i];
+    sqlite3_result_text(ctx, azStateName[j%5], -1, SQLITE_STATIC);
   }else{
     sqlite3_result_int(ctx, pRow->aCol[i]);
   }
@@ -253,6 +258,9 @@ static int stmtFilter(
     pNew->aCol[STMT_COLUMN_MEM] = sqlite3_stmt_status(
         p, SQLITE_STMTSTATUS_MEMUSED, 0
     );
+    pNew->aCol[STMT_COLUMN_STATE] = sqlite3_stmt_status(
+        p, SQLITE_STMTSTATUS_STATE, 0
+    );
     pNew->iRowid = iRowid++;
     *ppRow = pNew;
     ppRow = &pNew->pNext;
index 18912fab5b428cd67dc3671e481741cf3170f2e2..9c1bc75ff2d8dd10fe1ddb0de6ba68c75f664b4d 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Change\sthe\snames\sof\sthe\sVdbe.pVNext\sand\sVdbe.ppVPrev\sfields\sto\smake\sthem\nunique.\s\sChange\sto\sppVPrev\sto\ssave\sa\sfew\sbytes\sand\sa\sfew\sCPU\scycles.
-D 2022-08-23T20:11:01.058
+C First\sattempt\sat\sadding\sa\sbuilt-in\sprepared\sstatement\scache.\s\sThis\sis\nmostly\sworking,\sbut\sstill\shas\sa\sfew\sobscure\sfaults.
+D 2022-08-24T17:55:27.625
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -328,7 +328,7 @@ F ext/misc/shathree.c 7b17615869a495659f1569ada1d8d3d21b4a24614f2746d93cc87ef7c0
 F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
 F ext/misc/spellfix.c 94df9bbfa514a563c1484f684a2df3d128a2f7209a84ca3ca100c68a0163e29f
 F ext/misc/sqlar.c 0ace5d3c10fe736dc584bf1159a36b8e2e60fab309d310cd8a0eecd9036621b6
-F ext/misc/stmt.c ed05ad78013edccd43f4fc33d8244001f6f886eb2dfd2106ba33616ac514c6fb
+F ext/misc/stmt.c 36ad8e9a5352e4795dd1da1d04c6ab9680c58d1dd648bdb26a53123c976a0f2b
 F ext/misc/templatevtab.c 8a16a91a5ceaccfcbd6aaaa56d46828806e460dd194965b3f77bf38f14b942c4
 F ext/misc/totype.c fa4aedeb07f66169005dffa8de3b0a2b621779fd44f85c103228a42afa71853b
 F ext/misc/uint.c 053fed3bce2e89583afcd4bf804d75d659879bbcedac74d0fa9ed548839a030b
@@ -552,7 +552,7 @@ F src/insert.c aea5361767817f917b0f0f647a1f0b1621bd858938ae6ae545c3b6b9814b798f
 F src/json.c 7749b98c62f691697c7ee536b570c744c0583cab4a89200fdd0fc2aa8cc8cbd6
 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
 F src/loadext.c 853385cc7a604157e137585097949252d5d0c731768e16b044608e5c95c3614b
-F src/main.c 211f5fd4110b0565fc2135d60ed12b4ef8a29d634414f471b5f957aa7b7f2afa
+F src/main.c 5c82ba33d2032f099c90fbb2a7a05ed684164a0ddecb7aaeda4374d97f4055b5
 F src/malloc.c b7a3430cbe91d3e8e04fc10c2041b3a19794e63556ad2441a13d8dadd9b2bafc
 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
 F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
@@ -583,17 +583,17 @@ F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
 F src/pcache1.c 0b4245cd4964e635f2630908c2533cd8e9da7af3ca592e23ae8730aa25ae5eb9
 F src/pragma.c b57a859a366472131194a9ad35cd76d5920577226b04c884b1b9085605faa280
 F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7
-F src/prepare.c 971d5819a4bda88038c2283d71fc0a2974ffc3dd480f9bd941341017abacfd1b
+F src/prepare.c 54839876dd40924279004e98ba35e38a64aed728b9be5ba8641f10a9a5203496
 F src/printf.c e99ee9741e79ae3873458146f59644276657340385ade4e76a5f5d1c25793764
 F src/random.c 546d6feb15ec69c1aafe9bb351a277cbb498fd5410e646add673acb805714960
 F src/resolve.c efea4e5fbecfd6d0a9071b0be0d952620991673391b6ffaaf4c277b0bb674633
 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
 F src/select.c ccce37e7fbe71089cf6aec91e7134c9c0c1d4840cff9f02587bbc71240d914a5
 F src/shell.c.in 269f682249c1bce2962883e5b99c8702b16a488a43b9ae186daa178713a93c5d
-F src/sqlite.h.in b9b7fd73239d94db20332bb6e504688001e5564b655e1318a4427a1caef4b99e
+F src/sqlite.h.in f34d1ca4091c969e1b4b4744839f415c7963618bb10b34bbe1ef6a356ace0840
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h a988810c9b21c0dc36dc7a62735012339dc76fc7ab448fb0792721d30eacb69d
-F src/sqliteInt.h ebf18764e404a2cef39ae5bfc8dd4a83bf0d70be1c444a4fbd8539eb35ef6ffd
+F src/sqliteInt.h 71316870f7ef94fd8326d435c2b2fb6a5e374133f128fd8c0132502ad6d18b6f
 F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
 F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749
 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
@@ -661,10 +661,10 @@ F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
 F src/util.c 602fe229f32a96ceccae4f40824129669582096f7c355f53dbac156c9fecef23
 F src/vacuum.c bb346170b0b54c6683bba4a5983aea40485597fdf605c87ec8bc2e199fe88cd8
 F src/vdbe.c 0150d16da21cb96b2b0f2880aad6acd80ddedf93a9f3eb2c5200aef864854fe6
-F src/vdbe.h 64619af62603dc3c4f5ff6ff6d2c8f389abd667a29ce6007ed44bd22b3211cd0
-F src/vdbeInt.h 17b7461ffcf9ee760d1341731715a419f6b8c763089a7ece25c2e8098d702b3f
-F src/vdbeapi.c fc3183daf72808b4311b228989120fdbc2dc44972fb0d77d5c453460cc0e5b2c
-F src/vdbeaux.c 1f33d20fc19f13ad781ee083276ffad2204a182617859895db9daa3412456457
+F src/vdbe.h c4c9defdf2ad9465f9c9c7f79c3b03555f47aa930ea2744f358d9b27269763c7
+F src/vdbeInt.h e332f7d165b2cb984772c425c45f67f1d57e3c032d8dbf74a9ef8f1cebfa4bb2
+F src/vdbeapi.c 8087dba84836f59d0d5340a6c554948cf4fef03ace62a8d7dd3c8f310107e3f4
+F src/vdbeaux.c fdbf1df03cfcf4d11c49178be30b23d2b750082edd4d7b6aa663479e7dc65f7c
 F src/vdbeblob.c 5e61ce31aca17db8fb60395407457a8c1c7fb471dde405e0cd675974611dcfcd
 F src/vdbemem.c c3ce80af15e2ff5c2824a8db881681cbf511376f13613da020bac6d320c535b1
 F src/vdbesort.c 43756031ca7430f7aec3ef904824a7883c4ede783e51f280d99b9b65c0796e35
@@ -1480,7 +1480,7 @@ F test/speed3.test 694affeb9100526007436334cf7d08f3d74b85ef
 F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715
 F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
 F test/speed4p.test 377a0c48e5a92e0b11c1c5ebb1bc9d83a7312c922bc0cb05970ef5d6a96d1f0c
-F test/speedtest1.c 61f8a72bbcc80edb0b95e957f108feb4013ff3d08721cc87ae1865fd4d20652d
+F test/speedtest1.c d37467faf7a43711b134f05e9cde7abfd1504bf8f16f6220f67d8db1bc0fc51f
 F test/spellfix.test 951a6405d49d1a23d6b78027d3877b4a33eeb8221dcab5704b499755bb4f552e
 F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3
 F test/spellfix3.test 0f9efaaa502a0e0a09848028518a6fb096c8ad33
@@ -1957,7 +1957,7 @@ F tool/showstat4.c 0682ebea7abf4d3657f53c4a243f2e7eab48eab344ed36a94bb75dcd19a5c
 F tool/showwal.c 0253c187ae16fdae9cde89e63e1dfcd3bb35e5416d066415f99e2f8cac6ab03d
 F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe
 F tool/spaceanal.tcl 1b5be34c6223cb1af06da2a10fb77863eb869b1962d055820b0a11cf2336ab45
-F tool/speed-check.sh ff74a68bb95a0341275f4d3c9a7d8a3800bd278aceecf1913295a1f0175bc3e6
+F tool/speed-check.sh fc224ee49062fedf3bbef87779197bb5103f6f796603e4255ea558633360e8ba
 F tool/speedtest.tcl 06c76698485ccf597b9e7dbb1ac70706eb873355
 F tool/speedtest16.c ecb6542862151c3e6509bbc00509b234562ae81e
 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
@@ -1999,8 +1999,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 4e0a07fc6f96e6e7726506b7a5ee942461c1381501f253893fa94d76023b634f
-R 72b6d89c208e8d4296a211e6207c4905
+P 34b8ea31877ae8b40729d37b3f51ae7e15f38be841881ea4a37c9c8f0a52896d
+R 08d11f60d527a9ebc32a98574ca5f0f9
+T *branch * stmt-cache
+T *sym-stmt-cache *
+T -sym-trunk *
 U drh
-Z 0897b771860025dba1bfebc2fa438b5b
+Z c8d2ef694a3736c7eee4ba211f6abfcb
 # Remove this line to create a well-formed Fossil manifest.
index d5837ab0a93995ff76bebb94299900b9d0cc8572..9e6622d10d3c7a1c6fb365c7fb82c2206e9d704e 100644 (file)
@@ -1 +1 @@
-34b8ea31877ae8b40729d37b3f51ae7e15f38be841881ea4a37c9c8f0a52896d
\ No newline at end of file
+c217b763b148e8bece9d1f72525fd5026d3f710b337c010ffd0306fde4f9add8
\ No newline at end of file
index d982c3dfb7bbe199df17bbc0e012f317d3174580..ac09341eb8a56bae99155030f0884aa7594e9b2a 100644 (file)
@@ -917,6 +917,16 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){
   int rc;
   va_start(ap, op);
   switch( op ){
+    case SQLITE_DBCONFIG_STMTCACHE_SIZE: {
+      int szDesired = va_arg(ap,int);
+      int *pszNew = va_arg(ap,int*);
+      if( szDesired>=0 ){
+        sqlite3VdbeChangeStmtCacheSize(db, szDesired);
+      }
+      *pszNew = (int)db->mxCache;
+      rc = SQLITE_OK;
+      break;
+    }
     case SQLITE_DBCONFIG_MAINDBNAME: {
       /* IMP: R-06824-28531 */
       /* IMP: R-36257-52125 */
@@ -1212,6 +1222,7 @@ static int sqlite3Close(sqlite3 *db, int forceZombie){
   }
 
   /* Force xDisconnect calls on all virtual tables */
+  sqlite3VdbeChangeStmtCacheSize(db, 0);
   disconnectAllVtab(db);
 
   /* If a transaction is open, the disconnectAllVtab() call above
@@ -3250,6 +3261,7 @@ static int openDatabase(
   db->autoCommit = 1;
   db->nextAutovac = -1;
   db->szMmap = sqlite3GlobalConfig.szMmap;
+  db->mxCache = SQLITE_DEFAULT_STMTCACHE_SIZE;
   db->nextPagesize = 0;
   db->init.azInit = sqlite3StdType; /* Any array of string ptrs will do */
 #ifdef SQLITE_ENABLE_SORTER_MMAP
index f66c366b990825425d6a4d800466b1ac010c621b..0ac4c8d97be163a814b4192871f5f17e72e863fa 100644 (file)
@@ -686,6 +686,7 @@ static int sqlite3Prepare(
   int rc = SQLITE_OK;       /* Result code */
   int i;                    /* Loop counter */
   Parse sParse;             /* Parsing context */
+  u32 hSql = 0;             /* Hash of the input SQL */
 
   /* sqlite3ParseObjectInit(&sParse, db); // inlined for performance */
   memset(PARSE_HDR(&sParse), 0, PARSE_HDR_SZ);
@@ -698,10 +699,24 @@ static int sqlite3Prepare(
   if( db->mallocFailed ) sqlite3ErrorMsg(&sParse, "out of memory");
   assert( sqlite3_mutex_held(db->mutex) );
 
-  /* For a long-term use prepared statement avoid the use of
-  ** lookaside memory.
-  */
   if( prepFlags & SQLITE_PREPARE_PERSISTENT ){
+    /* Check to see if the prepared statement can be resolved from cache. */
+    if( db->mxCache>0 ){
+      Vdbe *pCache;
+      if( nBytes<0 ) nBytes = sqlite3Strlen30(zSql);
+      pCache = sqlite3VdbeFindInStmtCache(db, zSql, nBytes, &hSql);
+      if( pCache ){
+        *ppStmt = (sqlite3_stmt*)pCache;
+        if( pzTail ){
+          *pzTail = &zSql[nBytes];
+        }
+        goto end_prepare;
+      }
+    }
+
+    /* For a long-term use prepared statement should avoid the use of
+    ** lookaside memory.
+    */
     sParse.disableLookaside++;
     DisableLookaside;
   }
@@ -776,7 +791,8 @@ static int sqlite3Prepare(
   }
 
   if( db->init.busy==0 ){
-    sqlite3VdbeSetSql(sParse.pVdbe, zSql, (int)(sParse.zTail-zSql), prepFlags);
+    sqlite3VdbeSetSql(sParse.pVdbe, zSql, (int)(sParse.zTail-zSql),
+                      prepFlags, hSql);
   }
   if( db->mallocFailed ){
     sParse.rc = SQLITE_NOMEM_BKPT;
index d862c4fc00a364eb615d46737cc0f624e84da18f..8ebf904cb925d3e38d22b5dc0120c0dba59c8589 100644 (file)
@@ -2128,7 +2128,61 @@ struct sqlite3_mem_methods {
 ** non-zero [error code] if a discontinued or unsupported configuration option
 ** is invoked.
 **
+** Most of these configuration options are used to set, clear, or query flags.
+** All such options take two arguments:  And integer (X) and a pointer to an
+** integer (P).  If the X argument is greater than zero, the flag is set.  If
+** X is equal to zero, the flag is cleared.  If X is less than zero, the flag
+** remains unchanged.  In all cases, the value of the flag after any changes
+** (either 0 or 1) is written into the integer pointed to by P.  All of the
+** configuration options listed below operate this way, with the following
+** exceptions:
+**
+** <ul>
+** <li> [SQLITE_DBCONFIG_MAINDBNAME]
+** <li> [SQLITE_DBCONFIG_LOOKASIDE]
+** <li> [SQLITE_DBCONFIG_STMTCACHE_SIZE]
+** </ul>
+**
 ** <dl>
+** [[SQLITE_DBCONFIG_STMTCACHE_SIZE]] <dt>SQLITE_DBCONFIG_STMTCACHE_SIZE</dt>
+** <dd> The SQLITE_DBCONFIG_STMTCACHE_SIZE option is used to query or change
+** the maximum number of [prepared statements] that SQLite will cache.  Prepared
+** statements are only candidates for caching if they are prepared using
+** [sqlite3_prepare_v3()] with the [SQLITE_PREPARE_PERSISTENT] option.  If
+** SQLite chooses to cache a perpared statement, that means that when
+** [sqlite3_finalize()] is called on the prepared statement, the statement is
+** not immediately deleted, but instead is put into the cache.  Subsequent
+** calls to any of the [sqlite3_prepare()] ineterfaces with the exact same
+** SQL text may reuse the cached prepared statement rather than creating a new
+** prepared statement.  This setting adjusts the maximum number of prepared
+** statements that SQLite will keep in cache.  The default value is
+** SQLITE_DEFAULT_STMTCACHE_SIZE which is 10 unless overridden at compile-time.
+** Setting the maximum cache size to zero disables the cache.  The cache is
+** automatically trimmed back to its maximum size when the cache size is
+** reduced.  Hence the cache can be flushed, by set the cache size to zero
+** then set it back to its original value.
+** <p>This configuration option takes two parameters. The first parameter (N) is
+** an integer which is the desired new size of the statement cache.  A negative
+** value for N leaves the cache size unchanged.  The second parameter (P) 
+** is a pointer to an integer into which the new cache size is written.
+** The database connection is free to limit or truncate the N value that is
+** passed in, so the new cache size returned in P is not necessarily the same
+** as the N argument.
+** </dd>
+**
+** [[SQLITE_DBCONFIG_MAINDBNAME]] <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
+** <dd> ^This option is used to change the name of the "main" database
+** schema.  ^The sole argument is a pointer to a constant UTF8 string
+** which will become the new schema name in place of "main".  ^SQLite
+** does not make a copy of the new main schema name string, so the application
+** must ensure that the argument passed into this DBCONFIG option is unchanged
+** until after the database connection closes.  The application is responsible
+** for managing the storage associated with the string passed in as the
+** argument.  SQLite does not attempt to free the string, or do anything else
+** to reclaim storage space associated with the string, when the database
+** connection closes.
+** </dd>
+**
 ** [[SQLITE_DBCONFIG_LOOKASIDE]]
 ** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
 ** <dd> ^This option takes three additional arguments that determine the 
@@ -2229,15 +2283,6 @@ struct sqlite3_mem_methods {
 ** be a NULL pointer, in which case the new setting is not reported back.
 ** </dd>
 **
-** [[SQLITE_DBCONFIG_MAINDBNAME]] <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
-** <dd> ^This option is used to change the name of the "main" database
-** schema.  ^The sole argument is a pointer to a constant UTF8 string
-** which will become the new schema name in place of "main".  ^SQLite
-** does not make a copy of the new main schema name string, so the application
-** must ensure that the argument passed into this DBCONFIG option is unchanged
-** until after the database connection closes.
-** </dd>
-**
 ** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]] 
 ** <dt>SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE</dt>
 ** <dd> Usually, when a database in wal mode is closed or detached from a 
@@ -2396,6 +2441,7 @@ struct sqlite3_mem_methods {
 ** </dd>
 ** </dl>
 */
+#define SQLITE_DBCONFIG_STMTCACHE_SIZE         999 /* int int* */
 #define SQLITE_DBCONFIG_MAINDBNAME            1000 /* const char* */
 #define SQLITE_DBCONFIG_LOOKASIDE             1001 /* void* int int */
 #define SQLITE_DBCONFIG_ENABLE_FKEY           1002 /* int int* */
@@ -4047,11 +4093,19 @@ int sqlite3_limit(sqlite3*, int id, int newVal);
 ** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler
 ** to return an error (error code SQLITE_ERROR) if the statement uses
 ** any virtual tables.
+**
+** [[SQLITE_PREPARE_CACHE]] <td>SQLITE_PREPARE_CACHE</dt>
+** <dd>The SQLITE_PREPARE_CACHE flag gives SQLite permission to attempt
+** to cache the prepared statement after it is
+** [sqlite3_finalized|finalized] and attempt to reuse it as the
+** return value for the next call to a [sqlite3_prepare()] that has
+** the same SQL.  This flag also implies [SQLITE_PREPARE_PERSISTENT].
 ** </dl>
 */
 #define SQLITE_PREPARE_PERSISTENT              0x01
 #define SQLITE_PREPARE_NORMALIZE               0x02
 #define SQLITE_PREPARE_NO_VTAB                 0x04
+#define SQLITE_PREPARE_CACHE                   0x08
 
 /*
 ** CAPI3REF: Compiling An SQL Statement
@@ -8576,6 +8630,7 @@ int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
 #define SQLITE_STMTSTATUS_RUN               6
 #define SQLITE_STMTSTATUS_FILTER_MISS       7
 #define SQLITE_STMTSTATUS_FILTER_HIT        8
+#define SQLITE_STMTSTATUS_STATE             98
 #define SQLITE_STMTSTATUS_MEMUSED           99
 
 /*
index 21d15378f17a092428d2054610935f7b72fb7357..b7fe75855f28ea92aa9ccea71e49d54a9243e914 100644 (file)
 # define SQLITE_DEFAULT_MEMSTATUS 1
 #endif
 
+/*
+** Set the default maximum statement cache size
+*/
+#if !defined(SQLITE_DEFAULT_STMTCACHE_SIZE)
+# define SQLITE_DEFAULT_STMTCACHE_SIZE 10
+#endif
+#if SQLITE_DEFAULT_STMTCACHE_SIZE<0 || SQLITE_DEFAULT_STMTCACHE_SIZE>254
+# error SQLITE_DEFAULT_STMTCACHE_SIZE must be between 0 and 254
+#endif
+
 /*
 ** Exactly one of the following macros must be defined in order to
 ** specify which memory allocation subsystem to use.
@@ -1560,6 +1570,7 @@ struct sqlite3 {
   u8 noSharedCache;             /* True if no shared-cache backends */
   u8 nSqlExec;                  /* Number of pending OP_SqlExec opcodes */
   u8 eOpenState;                /* Current condition of the connection */
+  u8 mxCache, nCache;           /* Max and current size of statement cache */
   int nextPagesize;             /* Pagesize after VACUUM if >0 */
   i64 nChange;                  /* Value returned by sqlite3_changes() */
   i64 nTotalChange;             /* Value returned by sqlite3_total_changes() */
index eb1445f1db676597525d55f08c250abb391d1c14..324e811e08365564c8b4aa810e593caf9a44abdc 100644 (file)
@@ -262,7 +262,9 @@ int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, void(*)(void*));
 void sqlite3VdbeCountChanges(Vdbe*);
 sqlite3 *sqlite3VdbeDb(Vdbe*);
 u8 sqlite3VdbePrepareFlags(Vdbe*);
-void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, u8);
+void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, u8, u32);
+Vdbe *sqlite3VdbeFindInStmtCache(sqlite3*,const char*,int,u32*);
+void sqlite3VdbeChangeStmtCacheSize(sqlite3*,int);
 #ifdef SQLITE_ENABLE_NORMALIZE
 void sqlite3VdbeAddDblquoteStr(sqlite3*,Vdbe*,const char*);
 int sqlite3VdbeUsesDoubleQuotedString(Vdbe*,const char*);
index 0c6301c8f1666492d7d000cf75088051c75d5ac4..a17e1e3ba86f06299fd191222999be93c6ff881c 100644 (file)
@@ -470,6 +470,8 @@ struct Vdbe {
   yDbMask btreeMask;      /* Bitmask of db->aDb[] entries referenced */
   yDbMask lockMask;       /* Subset of btreeMask that requires a lock */
   u32 aCounter[9];        /* Counters used by sqlite3_stmt_status() */
+  u32 hSql;               /* Hash for zSql */
+  int nSql;               /* Number of bytes in zSql[] */
   char *zSql;             /* Text of the SQL statement that generated this */
 #ifdef SQLITE_ENABLE_NORMALIZE
   char *zNormSql;         /* Normalization of the associated SQL statement */
@@ -496,6 +498,7 @@ struct Vdbe {
 #define VDBE_READY_STATE    1   /* Ready to run but not yet started */
 #define VDBE_RUN_STATE      2   /* Run in progress */
 #define VDBE_HALT_STATE     3   /* Finished.  Need reset() or finalize() */
+#define VDBE_CACHE_STATE    4   /* In statement cache */
 
 /*
 ** Structure used to store the context required by the 
@@ -653,9 +656,11 @@ int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *);
 #ifdef SQLITE_DEBUG
   void sqlite3VdbeIncrWriteCounter(Vdbe*, VdbeCursor*);
   void sqlite3VdbeAssertAbortable(Vdbe*);
+  void sqlite3VdbeCheckActiveVdbeCount(sqlite3*);
 #else
 # define sqlite3VdbeIncrWriteCounter(V,C)
 # define sqlite3VdbeAssertAbortable(V)
+# define sqlite3VdbeCheckActiveVdbeCount(D)
 #endif
 
 #if !defined(SQLITE_OMIT_SHARED_CACHE) 
index 6c5f7562b32979dec48d73995b88eb9d04e4c4c7..f42d8ca6f3b559a4bbe746c1275a3ffc0179c395 100644 (file)
@@ -110,7 +110,20 @@ int sqlite3_finalize(sqlite3_stmt *pStmt){
     checkProfileCallback(db, v);
     assert( v->eVdbeState>=VDBE_READY_STATE );
     rc = sqlite3VdbeReset(v);
-    sqlite3VdbeDelete(v);
+    if( rc==SQLITE_OK
+     && v->hSql>0
+     && db->mxCache>0
+     && v->expired==0
+    ){
+      v->eVdbeState = VDBE_CACHE_STATE;
+      db->nCache++;
+      if( db->nCache>db->mxCache){
+        sqlite3VdbeChangeStmtCacheSize(db, db->mxCache);
+      }
+    }else{
+      sqlite3VdbeDelete(v);
+    }
+    sqlite3VdbeCheckActiveVdbeCount(db);
     rc = sqlite3ApiExit(db, rc);
     sqlite3LeaveMutexAndCloseZombie(db);
   }
@@ -689,6 +702,7 @@ static int sqlite3Step(Vdbe *p){
       if( p->bIsReader ) db->nVdbeRead++;
       p->pc = 0;
       p->eVdbeState = VDBE_RUN_STATE;
+      sqlite3VdbeCheckActiveVdbeCount(db);
     }else
 
     if( ALWAYS(p->eVdbeState==VDBE_HALT_STATE) ){
@@ -1842,6 +1856,8 @@ int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){
     db->pnBytesFreed = 0;
     db->lookaside.pEnd = db->lookaside.pTrueEnd;
     sqlite3_mutex_leave(db->mutex);
+  }else if( op==SQLITE_STMTSTATUS_STATE ){
+    v = pVdbe->eVdbeState;
   }else{
     v = pVdbe->aCounter[op];
     if( resetFlag ) pVdbe->aCounter[op] = 0;
index a4b4516c04987c67cb7bd030e46d989865a67c0d..916b2277e3ef4c2c7c9544635e0cf2f7e57bd285 100644 (file)
@@ -67,7 +67,13 @@ void sqlite3VdbeError(Vdbe *p, const char *zFormat, ...){
 /*
 ** Remember the SQL string for a prepared statement.
 */
-void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, u8 prepFlags){
+void sqlite3VdbeSetSql(
+  Vdbe *p,               /* The VDBE whose SQL text is to be set */
+  const char *z,         /* the SQL text */
+  int n,                 /* Number of bytes in z[] */
+  u8 prepFlags,          /* prepFlags */
+  u32 hSql               /* Hash of z[] used for fast cache lookups */
+){
   if( p==0 ) return;
   p->prepFlags = prepFlags;
   if( (prepFlags & SQLITE_PREPARE_SAVESQL)==0 ){
@@ -75,8 +81,61 @@ void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, u8 prepFlags){
   }
   assert( p->zSql==0 );
   p->zSql = sqlite3DbStrNDup(p->db, z, n);
+  p->nSql = n;
+  p->hSql = hSql;
+}
+
+/*
+** Search for a cached VDBE that that implements the given SQL.
+*/
+Vdbe *sqlite3VdbeFindInStmtCache(
+  sqlite3 *db,          /* The database connection holding the cache */
+  const char *zSql,     /* Input SQL text */
+  int nSql,             /* Size of zSql[] in bytes */
+  u32 *phSql            /* Write the hash value here if not found */
+){
+  int i;                /* Loop counter */
+  Vdbe *pCache;         /* Candidate statement */
+  u32 c;                /* One character of SQL input */
+  u32 hSql;             /* A hash of the SQL input */
+  int n;                /* Number of SQL input characters to be hashed */
+
+  hSql = 0;
+  n = nSql;
+  if( n>100 ) n = 100;
+  for(i=0; i<n && (c = ((u8*)zSql)[i])!=';'; i++){
+    hSql = (hSql + c) * 0x9e3779b1;
+  }
+  hSql |= 1;
+  if( db->nCache ){
+    for(pCache = db->pVdbe; pCache; pCache=pCache->pVNext){
+      if( pCache->hSql==hSql
+       && pCache->eVdbeState==VDBE_CACHE_STATE
+       && pCache->nSql==nSql
+       && strncmp(zSql, pCache->zSql, nSql)==0
+      ){
+        pCache->eVdbeState = VDBE_READY_STATE;
+        db->nCache--;
+        if( pCache!=db->pVdbe ){
+          if( pCache->pVNext ){
+            pCache->pVNext->ppVPrev = pCache->ppVPrev;
+          }
+          *pCache->ppVPrev = pCache->pVNext;
+          pCache->pVNext = db->pVdbe;
+          pCache->ppVPrev = &db->pVdbe;
+          db->pVdbe->ppVPrev = &pCache->pVNext;
+          db->pVdbe = pCache;
+        }
+        sqlite3VdbeCheckActiveVdbeCount(db);
+        return pCache;
+      }
+    }
+  }
+  *phSql = hSql;
+  return 0;
 }
 
+
 #ifdef SQLITE_ENABLE_NORMALIZE
 /*
 ** Add a new element to the Vdbe->pDblStr list.
@@ -3008,7 +3067,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
 ** This is a no-op if NDEBUG is defined.
 */
 #ifndef NDEBUG
-static void checkActiveVdbeCnt(sqlite3 *db){
+void sqlite3VdbeCheckActiveVdbeCount(sqlite3 *db){
   Vdbe *p;
   int cnt = 0;
   int nWrite = 0;
@@ -3026,10 +3085,36 @@ static void checkActiveVdbeCnt(sqlite3 *db){
   assert( nWrite==db->nVdbeWrite );
   assert( nRead==db->nVdbeRead );
 }
-#else
-#define checkActiveVdbeCnt(x)
 #endif
 
+/*
+** Attempt to change the statement cache size.  If the statement cache
+** size is reduced remove excess elements from the cache.
+**
+** The argument is the *desired* new statement cache size.  The new
+** statement cache size will not necessarily be the same size.
+*/
+void sqlite3VdbeChangeStmtCacheSize(sqlite3 *db, int szDesired){
+  assert( szDesired>=0 );
+  if( szDesired>100 ) szDesired = 100;
+  if( db->nCache>szDesired ){
+    Vdbe *p, *pNext;
+    int n = szDesired;
+    for(p=db->pVdbe; p; p=pNext){
+      pNext = p->pVNext;
+      if( p->hSql==0 ) continue;
+      if( p->eVdbeState!=VDBE_CACHE_STATE ) continue;
+      if( n>0 ){ n--; continue; }
+      sqlite3VdbeDelete(p);
+      assert( db->nCache>szDesired );
+      db->nCache--;
+    }
+    assert( db->nCache==szDesired );
+  }
+  db->mxCache = szDesired;
+  sqlite3VdbeCheckActiveVdbeCount(db);
+}
+
 /*
 ** If the Vdbe passed as the first argument opened a statement-transaction,
 ** close it now. Argument eOp must be either SAVEPOINT_ROLLBACK or
@@ -3158,7 +3243,7 @@ int sqlite3VdbeHalt(Vdbe *p){
     p->rc = SQLITE_NOMEM_BKPT;
   }
   closeAllCursors(p);
-  checkActiveVdbeCnt(db);
+  sqlite3VdbeCheckActiveVdbeCount(db);
 
   /* No commit or rollback needed if the program never started or if the
   ** SQL statement does not read or write a database file.  */
@@ -3317,7 +3402,7 @@ int sqlite3VdbeHalt(Vdbe *p){
   assert( db->nVdbeRead>=db->nVdbeWrite );
   assert( db->nVdbeWrite>=0 );
   p->eVdbeState = VDBE_HALT_STATE;
-  checkActiveVdbeCnt(db);
+  sqlite3VdbeCheckActiveVdbeCount(db);
   if( db->mallocFailed ){
     p->rc = SQLITE_NOMEM_BKPT;
   }
@@ -5104,6 +5189,12 @@ void sqlite3ExpirePreparedStatements(sqlite3 *db, int iCode){
   for(p = db->pVdbe; p; p=p->pVNext){
     p->expired = iCode+1;
   }
+  if( db->nCache>0 ){
+    u8 mxCache = db->mxCache;
+    sqlite3VdbeChangeStmtCacheSize(db, 0);
+    assert( db->nCache==0 );
+    db->mxCache = mxCache;
+  }
 }
 
 /*
index 83c503c8c789a7b8290cc13dc09219a0a6a2c1ae..e782e7313c6588439c29015d80506d51379a7198 100644 (file)
@@ -25,6 +25,7 @@ static const char zHelp[] =
   "  --output FILE       Store SQL output in FILE\n"
   "  --pagesize N        Set the page size to N\n"
   "  --pcache N SZ       Configure N pages of pagecache each of size SZ bytes\n"
+  "  --persist           Use the SQLITE_PREPARE_PERSISTENT flag\n"
   "  --primarykey        Use PRIMARY KEY instead of UNIQUE where appropriate\n"
   "  --repeat N          Repeat each SELECT N times (default: 1)\n"
   "  --reprepare         Reprepare each statement upon every invocation\n"
@@ -87,6 +88,7 @@ static struct Global {
   sqlite3_int64 iTotal;      /* Total time */
   int bWithoutRowid;         /* True for --without-rowid */
   int bReprepare;            /* True to reprepare the SQL on each rerun */
+  unsigned int mPrepFlags;   /* Flags passed into sqlite3_prepare_v3() */
   int bSqlOnly;              /* True to print the SQL once only */
   int bExplain;              /* Print SQL with EXPLAIN prefix */
   int bVerify;               /* Try to verify that results are correct */
@@ -496,7 +498,7 @@ char *speedtest1_once(const char *zFormat, ...){
   if( g.bSqlOnly ){
     printSql(zSql);
   }else{
-    int rc = sqlite3_prepare_v2(g.db, zSql, -1, &pStmt, 0);
+    int rc = sqlite3_prepare_v3(g.db, zSql, -1, 0, &pStmt, 0);
     if( rc ){
       fatal_error("SQL error: %s\n", sqlite3_errmsg(g.db));
     }
@@ -523,7 +525,7 @@ void speedtest1_prepare(const char *zFormat, ...){
   }else{
     int rc;
     if( g.pStmt ) sqlite3_finalize(g.pStmt);
-    rc = sqlite3_prepare_v2(g.db, zSql, -1, &g.pStmt, 0);
+    rc = sqlite3_prepare_v3(g.db, zSql, -1, g.mPrepFlags, &g.pStmt, 0);
     if( rc ){
       fatal_error("SQL error: %s\n", sqlite3_errmsg(g.db));
     }
@@ -586,8 +588,16 @@ void speedtest1_run(void){
 #if SQLITE_VERSION_NUMBER>=3006001
   if( g.bReprepare ){
     sqlite3_stmt *pNew;
-    sqlite3_prepare_v2(g.db, sqlite3_sql(g.pStmt), -1, &pNew, 0);
-    sqlite3_finalize(g.pStmt);
+    if( g.mPrepFlags & SQLITE_PREPARE_PERSISTENT ){
+      char zBuf[1000];
+      strncpy(zBuf, sqlite3_sql(g.pStmt), sizeof(zBuf));
+      zBuf[sizeof(zBuf)-1] = 0;
+      sqlite3_finalize(g.pStmt);
+      sqlite3_prepare_v3(g.db, zBuf, -1, g.mPrepFlags, &pNew, 0);
+    }else{
+      sqlite3_prepare_v2(g.db, sqlite3_sql(g.pStmt), -1, &pNew, 0);
+      sqlite3_finalize(g.pStmt);
+    }
     g.pStmt = pNew;
   }else
 #endif
@@ -2273,6 +2283,8 @@ int main(int argc, char **argv){
         if( i>=argc-1 ) fatal_error("missing arguments on %s\n", argv[i]);
         g.nRepeat = integerValue(argv[i+1]);
         i += 1;
+      }else if( strcmp(z,"persist")==0 ){
+        g.mPrepFlags |= SQLITE_PREPARE_PERSISTENT;
       }else if( strcmp(z,"reprepare")==0 ){
         g.bReprepare = 1;
 #if SQLITE_VERSION_NUMBER>=3006000
index 6b0fbeb43a4e8ca984b308e7c9c90f3717890299..6d69ea032060c0453737090c5457bee204c8b6de 100644 (file)
@@ -143,6 +143,9 @@ while test "$1" != ""; do
         SPEEDTEST_OPTS="$SPEEDTEST_OPTS --testset rtree"
         CC_OPTS="$CC_OPTS -DSQLITE_ENABLE_RTREE"
         ;;
+    --persist)
+        SPEEDTEST_OPTS="$SPEEDTEST_OPTS --persist"
+        ;;
     --orm)
         SPEEDTEST_OPTS="$SPEEDTEST_OPTS --testset orm"
         ;;