#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 */
#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;
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]);
}
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;
-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
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
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
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
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
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
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
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.
-34b8ea31877ae8b40729d37b3f51ae7e15f38be841881ea4a37c9c8f0a52896d
\ No newline at end of file
+c217b763b148e8bece9d1f72525fd5026d3f710b337c010ffd0306fde4f9add8
\ No newline at end of file
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 */
}
/* Force xDisconnect calls on all virtual tables */
+ sqlite3VdbeChangeStmtCacheSize(db, 0);
disconnectAllVtab(db);
/* If a transaction is open, the disconnectAllVtab() call above
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
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);
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;
}
}
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;
** 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
** 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
** </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* */
** <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
#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
/*
# 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.
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() */
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*);
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 */
#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
#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)
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);
}
if( p->bIsReader ) db->nVdbeRead++;
p->pc = 0;
p->eVdbeState = VDBE_RUN_STATE;
+ sqlite3VdbeCheckActiveVdbeCount(db);
}else
if( ALWAYS(p->eVdbeState==VDBE_HALT_STATE) ){
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;
/*
** 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 ){
}
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.
** 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;
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
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. */
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;
}
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;
+ }
}
/*
" --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"
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 */
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));
}
}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));
}
#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
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
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"
;;