Index: ext/misc/stmt.c ================================================================== --- ext/misc/stmt.c +++ ext/misc/stmt.c @@ -29,11 +29,11 @@ #include #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 */ char *zSql; /* column "sql" */ int aCol[STMT_NUM_INTEGER_COLUMN+1]; /* all other column values */ @@ -93,15 +93,16 @@ #define STMT_COLUMN_NAIDX 6 /* SQLITE_STMTSTATUS_AUTOINDEX */ #define STMT_COLUMN_NSTEP 7 /* SQLITE_STMTSTATUS_VM_STEP */ #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; if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(*pNew)); @@ -173,10 +174,14 @@ ){ stmt_cursor *pCur = (stmt_cursor*)cur; 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]); } return SQLITE_OK; } @@ -250,10 +255,13 @@ pNew->aCol[STMT_COLUMN_RUN] = sqlite3_stmt_status( p, SQLITE_STMTSTATUS_RUN, 0 ); 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: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -2789,10 +2789,11 @@ zType = "table"; zType2 = "TABLE"; #ifndef SQLITE_OMIT_VIEW }else{ /* A view */ + sqlite3VdbeRunOnlyOnce(v); zType = "view"; zType2 = "VIEW"; #endif } Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -916,10 +916,20 @@ va_list ap; int rc; sqlite3_mutex_enter(db->mutex); 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); + } + if( pszNew ) *pszNew = (int)db->mxCache; + rc = SQLITE_OK; + break; + } case SQLITE_DBCONFIG_MAINDBNAME: { /* IMP: R-06824-28531 */ /* IMP: R-36257-52125 */ db->aDb[0].zDbSName = va_arg(ap,char*); rc = SQLITE_OK; @@ -1212,10 +1222,11 @@ if( db->mTrace & SQLITE_TRACE_CLOSE ){ db->trace.xV2(SQLITE_TRACE_CLOSE, db->pTraceArg, db, 0); } /* Force xDisconnect calls on all virtual tables */ + sqlite3VdbeChangeStmtCacheSize(db, 0); disconnectAllVtab(db); /* If a transaction is open, the disconnectAllVtab() call above ** will not have called the xDisconnect() method on any virtual ** tables in the db->aVTrans[] array. The following sqlite3VtabRollback() @@ -3250,10 +3261,11 @@ memcpy(db->aLimit, aHardLimit, sizeof(db->aLimit)); db->aLimit[SQLITE_LIMIT_WORKER_THREADS] = SQLITE_DEFAULT_WORKER_THREADS; 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 /* Beginning with version 3.37.0, using the VFS xFetch() API to memory-map ** the temporary files used to do external sorts (see code in vdbesort.c) Index: src/prepare.c ================================================================== --- src/prepare.c +++ src/prepare.c @@ -684,10 +684,11 @@ const char **pzTail /* OUT: End of parsed string */ ){ 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); memset(PARSE_TAIL(&sParse), 0, PARSE_TAIL_SZ); sParse.pOuterParse = db->pParse; @@ -695,15 +696,28 @@ sParse.db = db; sParse.pReprepare = pReprepare; assert( ppStmt && *ppStmt==0 ); if( db->mallocFailed ) sqlite3ErrorMsg(&sParse, "out of memory"); assert( sqlite3_mutex_held(db->mutex) ); + + if( db->nCache>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 avoid the use of ** lookaside memory. */ - if( prepFlags & SQLITE_PREPARE_PERSISTENT ){ + if( prepFlags & (SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_CACHE) ){ sParse.disableLookaside++; DisableLookaside; } sParse.prepFlags = prepFlags & 0xff; @@ -774,11 +788,12 @@ if( pzTail ){ *pzTail = sParse.zTail; } 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; sParse.checkSchema = 0; } Index: src/shell.c.in ================================================================== --- src/shell.c.in +++ src/shell.c.in @@ -1110,10 +1110,11 @@ int inputNesting; /* Track nesting level of .read and other redirects */ int outCount; /* Revert to stdout when reaching zero */ int cnt; /* Number of records displayed so far */ int lineno; /* Line number of last line read from in */ int openFlags; /* Additional flags to open. (SQLITE_OPEN_NOFOLLOW) */ + int prepFlags; /* Flags for sqlite3_prepare_v3() */ FILE *in; /* Read commands from this stream */ FILE *out; /* Write results here */ FILE *traceOut; /* Output for sqlite3_trace() */ int nErr; /* Number of errors seen */ int mode; /* An output mode setting */ @@ -2660,11 +2661,11 @@ sqlite3_stmt *pSelect; int rc; int nResult; int i; const char *z; - rc = sqlite3_prepare_v2(p->db, zSelect, -1, &pSelect, 0); + rc = sqlite3_prepare_v3(p->db, zSelect, -1, p->prepFlags, &pSelect, 0); if( rc!=SQLITE_OK || !pSelect ){ char *zContext = shell_error_context(zSelect, p->db); utf8_printf(p->out, "/**** ERROR: (%d) %s *****/\n%s", rc, sqlite3_errmsg(p->db), zContext); sqlite3_free(zContext); @@ -3856,11 +3857,11 @@ } #endif while( zSql[0] && (SQLITE_OK == rc) ){ static const char *zStmtSql; - rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover); + rc = sqlite3_prepare_v3(db, zSql, -1, pArg->prepFlags, &pStmt, &zLeftover); if( SQLITE_OK != rc ){ if( pzErrMsg ){ *pzErrMsg = save_err_msg(db, "in prepare", rc, zSql); } }else{ @@ -3922,11 +3923,11 @@ } if( pArg->autoEQP>=AUTOEQP_trigger && triggerEQP==0 ){ sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 0, 0); /* Reprepare pStmt before reactiving trace modes */ sqlite3_finalize(pStmt); - sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + sqlite3_prepare_v3(db, zSql, -1, pArg->prepFlags, &pStmt, 0); if( pArg ) pArg->pStmt = pStmt; } restore_debug_trace_modes(); } @@ -4021,11 +4022,11 @@ int preserveRowid = ShellHasFlag(p, SHFLG_PreserveRowid); int rc; zSql = sqlite3_mprintf("PRAGMA table_info=%Q", zTab); shell_check_oom(zSql); - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); + rc = sqlite3_prepare_v3(p->db, zSql, -1, p->prepFlags, &pStmt, 0); sqlite3_free(zSql); if( rc ) return 0; while( sqlite3_step(pStmt)==SQLITE_ROW ){ if( nCol>=nAlloc-2 ){ nAlloc = nAlloc*2 + nCol + 10; @@ -4066,11 +4067,11 @@ ** no "pk" index if the PRIMARY KEY really is an alias for the ROWID. */ zSql = sqlite3_mprintf("SELECT 1 FROM pragma_index_list(%Q)" " WHERE origin='pk'", zTab); shell_check_oom(zSql); - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); + rc = sqlite3_prepare_v3(p->db, zSql, -1, p->prepFlags, &pStmt, 0); sqlite3_free(zSql); if( rc ){ freeColumnList(azCol); return 0; } @@ -8318,12 +8319,14 @@ raw_printf(stderr, "Usage: .binary on|off\n"); rc = 1; } }else - /* The undocumented ".breakpoint" command causes a call to the no-op - ** routine named test_breakpoint(). + /* UNDOCUMENTED COMMAND: .breakpoint + ** + ** Causes a call to the no-op routine named test_breakpoint(). This provides + ** a convenient function on which to set a breakpoint. */ if( c=='b' && n>=3 && strncmp(azArg[0], "breakpoint", n)==0 ){ test_breakpoint(); }else @@ -8498,23 +8501,34 @@ { "legacy_alter_table", SQLITE_DBCONFIG_LEGACY_ALTER_TABLE }, { "legacy_file_format", SQLITE_DBCONFIG_LEGACY_FILE_FORMAT }, { "load_extension", SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION }, { "no_ckpt_on_close", SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE }, { "reset_database", SQLITE_DBCONFIG_RESET_DATABASE }, + { "stmtcache_size", SQLITE_DBCONFIG_STMTCACHE_SIZE }, { "trigger_eqp", SQLITE_DBCONFIG_TRIGGER_EQP }, { "trusted_schema", SQLITE_DBCONFIG_TRUSTED_SCHEMA }, { "writable_schema", SQLITE_DBCONFIG_WRITABLE_SCHEMA }, }; int ii, v; open_db(p, 0); for(ii=0; ii1 && strcmp(azArg[1], aDbConfig[ii].zName)!=0 ) continue; + op = aDbConfig[ii].op; if( nArg>=3 ){ - sqlite3_db_config(p->db, aDbConfig[ii].op, booleanValue(azArg[2]), 0); + if( op==SQLITE_DBCONFIG_STMTCACHE_SIZE ){ + sqlite3_db_config(p->db, op, (int)integerValue(azArg[2]), 0); + }else{ + sqlite3_db_config(p->db, op, booleanValue(azArg[2]), 0); + } } - sqlite3_db_config(p->db, aDbConfig[ii].op, -1, &v); - utf8_printf(p->out, "%19s %s\n", aDbConfig[ii].zName, v ? "on" : "off"); + sqlite3_db_config(p->db, op, -1, &v); + if( op==SQLITE_DBCONFIG_STMTCACHE_SIZE ){ + utf8_printf(p->out, "%19s %d\n", aDbConfig[ii].zName, v); + }else{ + utf8_printf(p->out, "%19s %s\n", aDbConfig[ii].zName, v ? "on" : "off"); + } if( nArg>1 ) break; } if( nArg>1 && ii==ArraySize(aDbConfig) ){ utf8_printf(stderr, "Error: unknown dbconfig \"%s\"\n", azArg[1]); utf8_printf(stderr, "Enter \".dbconfig\" with no arguments for a list\n"); @@ -9923,10 +9937,51 @@ }else /* If no command name matches, show a syntax error */ parameter_syntax_error: showHelp(p->out, "parameter"); }else + + /* UNDOCUMENTED COMMAND: .prepflags (+|-)FLAG ... + ** + ** For SQLite core debugging use only. + ** + ** Change the value of ShellState.prepFlags which becomes the flags + ** parameter to sqlite3_prepare_v3() in some calls in this application. + */ + if( c=='p' && n>=3 && strncmp(azArg[0], "prepflags", n)==0 ){ + int i; + for(i=1; iout, "unknown option: \"%s\"\n", azArg[i]); + rc = 1; + goto meta_command_exit; + } + if( strcmp(azArg[i]+1, "persist")==0 ){ + op = SQLITE_PREPARE_PERSISTENT; + }else if( strcmp(azArg[i]+1, "no-vtab")==0 ){ + op = SQLITE_PREPARE_NO_VTAB; + }else if( strcmp(azArg[i]+1, "cache")==0 ){ + op = SQLITE_PREPARE_CACHE; + }else{ + utf8_printf(p->out, "option \"%s\" should be one of: \"%cpersist\"" + "\"%cno-vtab\" \"%ccache\"\n", + azArg[i], azArg[i][0],azArg[i][0],azArg[i][0]); + rc = 1; + goto meta_command_exit; + } + if( azArg[i][0]=='+' ){ + p->prepFlags |= op; + }else{ + p->prepFlags &= ~op; + } + } + utf8_printf(p->out, " prepflags:"); + utf8_printf(p->out, " %cpersist", (p->prepFlags & SQLITE_PREPARE_PERSISTENT)?'+':'-'); + utf8_printf(p->out, " %cno-vtab", (p->prepFlags & SQLITE_PREPARE_NO_VTAB)?'+':'-'); + utf8_printf(p->out, " %ccache\n", (p->prepFlags & SQLITE_PREPARE_CACHE)?'+':'-'); + }else if( c=='p' && n>=3 && strncmp(azArg[0], "print", n)==0 ){ int i; for(i=1; i1 ) raw_printf(p->out, " "); @@ -10240,10 +10295,17 @@ }else{ rc = 0; } }else + /* UNDOCUMENTED COMMAND: .treetrace N + ** + ** For SQLite core debugging use only. + ** + ** Set the treetrace mask to N. Also called ".selectrace" for historical + ** compatibility. + */ if( (c=='s' && n==11 && strncmp(azArg[0], "selecttrace", n)==0) || (c=='t' && n==9 && strncmp(azArg[0], "treetrace", n)==0) ){ unsigned int x = nArg>=2 ? (unsigned int)integerValue(azArg[1]) : 0xffffffff; sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 1, &x); @@ -10741,10 +10803,13 @@ if( nArg!=1 ){ raw_printf(stderr, "Usage: .show\n"); rc = 1; goto meta_command_exit; } + utf8_printf(p->out,"%12.12s: ", "colseparator"); + output_c_string(p->out, p->colSeparator); + raw_printf(p->out, "\n"); utf8_printf(p->out, "%12.12s: %s\n","echo", azBool[ShellHasFlag(p, SHFLG_Echo)]); utf8_printf(p->out, "%12.12s: %s\n","eqp", azBool[p->autoEQP&3]); utf8_printf(p->out, "%12.12s: %s\n","explain", p->mode==MODE_Explain ? "on" : p->autoExplain ? "auto" : "off"); @@ -10763,13 +10828,14 @@ utf8_printf(p->out, "%12.12s: ", "nullvalue"); output_c_string(p->out, p->nullValue); raw_printf(p->out, "\n"); utf8_printf(p->out,"%12.12s: %s\n","output", strlen30(p->outfile) ? p->outfile : "stdout"); - utf8_printf(p->out,"%12.12s: ", "colseparator"); - output_c_string(p->out, p->colSeparator); - raw_printf(p->out, "\n"); + utf8_printf(p->out,"%12.12s:", "prepflags"); + utf8_printf(p->out," %cpersist",(p->prepFlags&SQLITE_PREPARE_PERSISTENT)?'+':'-'); + utf8_printf(p->out," %cno-vtab",(p->prepFlags&SQLITE_PREPARE_NO_VTAB)?'+':'-'); + utf8_printf(p->out," %ccache\n",(p->prepFlags&SQLITE_PREPARE_CACHE)?'+':'-'); utf8_printf(p->out,"%12.12s: ", "rowseparator"); output_c_string(p->out, p->rowSeparator); raw_printf(p->out, "\n"); switch( p->statsOn ){ case 0: zOut = "off"; break; @@ -11379,10 +11445,16 @@ sqlite3_free(zVfsName); } } }else + /* UNDOCUMENTED COMMMAND: .wheretrace NUMBER + ** + ** For SQLite core debugging use only. + ** + ** See the wheretrace mask to NUMBER. + */ if( c=='w' && strncmp(azArg[0], "wheretrace", n)==0 ){ unsigned int x = nArg>=2 ? (unsigned int)integerValue(azArg[1]) : 0xffffffff; sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 3, &x); }else Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -2126,11 +2126,65 @@ ** should check the return code from [sqlite3_db_config()] to make sure that ** the call worked. ^The [sqlite3_db_config()] interface will return a ** 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: +** +**
    +**
  • [SQLITE_DBCONFIG_MAINDBNAME] +**
  • [SQLITE_DBCONFIG_LOOKASIDE] +**
  • [SQLITE_DBCONFIG_STMTCACHE_SIZE] +**
+** **
+** [[SQLITE_DBCONFIG_STMTCACHE_SIZE]]
SQLITE_DBCONFIG_STMTCACHE_SIZE
+**
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. +**

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. +**

+** +** [[SQLITE_DBCONFIG_MAINDBNAME]]
SQLITE_DBCONFIG_MAINDBNAME
+**
^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. +**
+** ** [[SQLITE_DBCONFIG_LOOKASIDE]] **
SQLITE_DBCONFIG_LOOKASIDE
**
^This option takes three additional arguments that determine the ** [lookaside memory allocator] configuration for the [database connection]. ** ^The first argument (the third parameter to [sqlite3_db_config()] is a @@ -2227,19 +2281,10 @@ ** is written 0 or 1 to indicate whether [sqlite3_load_extension()] interface ** is disabled or enabled following this call. The second parameter may ** be a NULL pointer, in which case the new setting is not reported back. **
** -** [[SQLITE_DBCONFIG_MAINDBNAME]]
SQLITE_DBCONFIG_MAINDBNAME
-**
^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. -**
-** ** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]] **
SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE
**
Usually, when a database in wal mode is closed or detached from a ** database handle, SQLite checks if this will mean that there are now no ** connections at all to the database. If so, it performs a checkpoint @@ -2394,10 +2439,11 @@ ** not considered a bug since SQLite versions 3.3.0 and earlier do not support ** either generated columns or decending indexes. **
**
*/ +#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* */ #define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1004 /* int int* */ @@ -4045,15 +4091,23 @@ ** ** [[SQLITE_PREPARE_NO_VTAB]]
SQLITE_PREPARE_NO_VTAB
**
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]] SQLITE_PREPARE_CACHE +**
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]. ** */ #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 ** KEYWORDS: {SQL statement compiler} ** METHOD: sqlite3 @@ -8557,10 +8611,19 @@ **
^SQLITE_STMTSTATUS_FILTER_HIT is the number of times that a join ** step was bypassed because a Bloom filter returned not-found. The ** corresponding SQLITE_STMTSTATUS_FILTER_MISS value is the number of ** times that the Bloom filter returned a find, and thus the join step ** had to be processed as normal. +** +** [[SQLITE_STMTSTATUS_STATE]]
SQLITE_STMTSTATUS_STATE
+**
During its lifetime, a prepare statement transitions through +** various "states". This interface returns the current state of the +** prepared statement, as an integer. This information is intended +** for use by SQLite core developers only. The internal state numbers +** for prepared statements can and do change from one release of +** SQLite to the next. Hence the value returned for this property +** code can change from one release of SQLite to the next. ** ** [[SQLITE_STMTSTATUS_MEMUSED]]
SQLITE_STMTSTATUS_MEMUSED
**
^This is the approximate number of bytes of heap memory ** used to store the prepared statement. ^This value is not actually ** a counter, and so the resetFlg parameter to sqlite3_stmt_status() @@ -8574,10 +8637,11 @@ #define SQLITE_STMTSTATUS_VM_STEP 4 #define SQLITE_STMTSTATUS_REPREPARE 5 #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 /* ** CAPI3REF: Custom Page Cache Object ** Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -350,10 +350,20 @@ */ #if !defined(SQLITE_DEFAULT_MEMSTATUS) # 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. ** ** SQLITE_SYSTEM_MALLOC // Use normal system malloc() @@ -1563,10 +1573,11 @@ u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */ u8 mTrace; /* zero or more SQLITE_TRACE flags */ 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() */ int aLimit[SQLITE_N_LIMIT]; /* Limits */ int nMaxSorterMmap; /* Maximum size of regions mapped by sorter */ Index: src/trigger.c ================================================================== --- src/trigger.c +++ src/trigger.c @@ -370,10 +370,11 @@ /* Make an entry in the sqlite_schema table */ v = sqlite3GetVdbe(pParse); if( v==0 ) goto triggerfinish_cleanup; sqlite3BeginWriteOperation(pParse, 0, iDb); + sqlite3VdbeRunOnlyOnce(v); z = sqlite3DbStrNDup(db, (char*)pAll->z, pAll->n); testcase( z==0 ); sqlite3NestedParse(pParse, "INSERT INTO %Q." LEGACY_SCHEMA_TABLE " VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')", Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -751,18 +751,24 @@ nProgressLimit = db->nProgressOps - (iPrior % db->nProgressOps); }else{ nProgressLimit = LARGEST_UINT64; } #endif - if( p->rc==SQLITE_NOMEM ){ - /* This happens if a malloc() inside a call to sqlite3_column_text() or - ** sqlite3_column_text16() failed. */ - goto no_mem; - } - assert( p->rc==SQLITE_OK || (p->rc&0xff)==SQLITE_BUSY ); - testcase( p->rc!=SQLITE_OK ); - p->rc = SQLITE_OK; + if( p->rc ){ + if( p->rc==SQLITE_NOMEM ){ + /* This happens if a malloc() inside a call to sqlite3_column_text() or + ** sqlite3_column_text16() failed. */ + goto no_mem; + } + if( p->rc==SQLITE_TOOBIG ){ + /* This happens if the SQLITE_LENGTH is reduced on a cached prepared + ** statement. */ + goto too_big; + } + assert( (p->rc&0xff)==SQLITE_BUSY ); + p->rc = SQLITE_OK; + } assert( p->bIsReader || p->readOnly!=0 ); p->iCurrentTime = 0; assert( p->explain==0 ); p->pResultSet = 0; db->busyHandler.nBusy = 0; @@ -6629,10 +6635,11 @@ case OP_CreateBtree: { /* out2 */ Pgno pgno; Db *pDb; sqlite3VdbeIncrWriteCounter(p, 0); + p->hSql = 0; pOut = out2Prerelease(p, pOp); pgno = 0; assert( pOp->p3==BTREE_INTKEY || pOp->p3==BTREE_BLOBKEY ); assert( pOp->p1>=0 && pOp->p1nDb ); assert( DbMaskTest(p->btreeMask, pOp->p1) ); @@ -7700,10 +7707,11 @@ if( !pOp->p1 ){ sqlite3ExpirePreparedStatements(db, pOp->p2); }else{ p->expired = pOp->p2+1; } + p->hSql = 0; break; } /* Opcode: CursorLock P1 * * * * ** Index: src/vdbe.h ================================================================== --- src/vdbe.h +++ src/vdbe.h @@ -260,11 +260,13 @@ void sqlite3VdbeSetNumCols(Vdbe*,int); 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*); #endif void sqlite3VdbeSwap(Vdbe*,Vdbe*); Index: src/vdbeInt.h ================================================================== --- src/vdbeInt.h +++ src/vdbeInt.h @@ -468,10 +468,12 @@ bft readOnly:1; /* True for statements that do not write */ bft bIsReader:1; /* True for statements that read */ 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. 0 if non-cacheable. */ + 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 */ DblquoteStr *pDblStr; /* List of double-quoted string literals */ #endif @@ -494,10 +496,11 @@ */ #define VDBE_INIT_STATE 0 /* Prepared statement under construction */ #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 ** sqlite3_preupdate_*() API functions. */ @@ -651,13 +654,15 @@ 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) void sqlite3VdbeEnter(Vdbe*); #else Index: src/vdbeapi.c ================================================================== --- src/vdbeapi.c +++ src/vdbeapi.c @@ -108,11 +108,23 @@ if( vdbeSafety(v) ) return SQLITE_MISUSE_BKPT; sqlite3_mutex_enter(db->mutex); checkProfileCallback(db, v); assert( v->eVdbeState>=VDBE_READY_STATE ); rc = sqlite3VdbeReset(v); - sqlite3VdbeDelete(v); + if( 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); } return rc; } @@ -687,10 +699,11 @@ db->nVdbeActive++; if( p->readOnly==0 ) db->nVdbeWrite++; if( p->bIsReader ) db->nVdbeRead++; p->pc = 0; p->eVdbeState = VDBE_RUN_STATE; + sqlite3VdbeCheckActiveVdbeCount(db); }else if( ALWAYS(p->eVdbeState==VDBE_HALT_STATE) ){ /* We used to require that sqlite3_reset() be called before retrying ** sqlite3_step() after any error or after SQLITE_DONE. But beginning @@ -1840,10 +1853,12 @@ db->lookaside.pEnd = db->lookaside.pStart; sqlite3VdbeDelete(pVdbe); 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; } return (int)v; Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -61,23 +61,92 @@ sqlite3DbFree(p->db, p->zErrMsg); va_start(ap, zFormat); p->zErrMsg = sqlite3VMPrintf(p->db, zFormat, ap); va_end(ap); } + +/* +** Compute a hash on the SQL used to generate a prepared statement. +*/ +static u32 sqlHash(const char *zSql, int nSql){ + u32 hSql = 0; + int i; + for(i=0; iprepFlags = prepFlags; if( (prepFlags & SQLITE_PREPARE_SAVESQL)==0 ){ p->expmask = 0; } assert( p->zSql==0 ); p->zSql = sqlite3DbStrNDup(p->db, z, n); + p->nSql = n; + if( prepFlags & SQLITE_PREPARE_CACHE ){ + if( hSql==0 ) hSql = sqlHash(z, n); + p->hSql = hSql; + }else{ + p->hSql = 0; + } } + +/* +** 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 */ +){ + Vdbe *pCache; /* Candidate statement */ + u32 hSql; /* A hash of the SQL input */ + + assert( nSql>=0 ); + hSql = sqlHash(zSql, nSql); + 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,11 +3077,11 @@ ** step. ** ** 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; int nRead = 0; p = db->pVdbe; @@ -3026,13 +3095,39 @@ } assert( cnt==db->nVdbeActive ); 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 ** SAVEPOINT_RELEASE. If it is SAVEPOINT_ROLLBACK, then the statement @@ -3158,11 +3253,11 @@ assert( p->eVdbeState==VDBE_RUN_STATE ); if( db->mallocFailed ){ 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. */ if( p->bIsReader ){ int mrc; /* Primary error code from p->rc */ @@ -3317,11 +3412,11 @@ if( p->bIsReader ) db->nVdbeRead--; assert( db->nVdbeActive>=db->nVdbeRead ); 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; } /* If the auto-commit flag is set to true, then any locks that were held @@ -5104,10 +5199,16 @@ void sqlite3ExpirePreparedStatements(sqlite3 *db, int iCode){ Vdbe *p; 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; + } } /* ** Return the database associated with the Vdbe. */ Index: test/speedtest1.c ================================================================== --- test/speedtest1.c +++ test/speedtest1.c @@ -23,10 +23,11 @@ " --nosync Set PRAGMA synchronous=OFF\n" " --notnull Add NOT NULL constraints to table columns\n" " --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" " --reserve N Reserve N bytes on each database page\n" " --script FILE Write an SQL script for the test into FILE\n" @@ -35,10 +36,11 @@ " --sqlonly No-op. Only show the SQL that would have been run.\n" " --shrink-memory Invoke sqlite3_db_release_memory() frequently.\n" " --size N Relative test size. Default=100\n" " --strict Use STRICT table where appropriate\n" " --stats Show statistics at the end\n" + " --stmtcache N Use a statement cache of size N\n" " --temp N N from 0 to 9. 0: no temp table. 9: all temp tables\n" " --testset T Run test-set T (main, cte, rtree, orm, fp, debug)\n" " --trace Turn on SQL tracing\n" " --threads N Use up to N threads for sorting\n" " --utf16be Set text encoding to UTF-16BE\n" @@ -63,10 +65,14 @@ #define ISDIGIT(X) isdigit((unsigned char)(X)) #if SQLITE_VERSION_NUMBER<3005000 # define sqlite3_int64 sqlite_int64 #endif + +#ifndef SQLITE_PREPARE_CACHE +# define SQLITE_PREPARE_CACHE 0x40000000 +#endif typedef sqlite3_uint64 u64; /* ** State structure for a Hash hash in progress @@ -86,19 +92,21 @@ sqlite3_stmt *pStmt; /* Current SQL statement */ sqlite3_int64 iStart; /* Start-time for the current test */ 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 */ int bMemShrink; /* Call sqlite3_db_release_memory() often */ int eTemp; /* 0: no TEMP. 9: always TEMP. */ int szTest; /* Scale factor for test iterations */ int nRepeat; /* Repeat selects this many times */ int doCheckpoint; /* Run PRAGMA wal_checkpoint after each trans */ int nReserve; /* Reserve bytes */ + int szStmtCache; /* Size of the statement cache */ const char *zWR; /* Might be WITHOUT ROWID */ const char *zNN; /* Might be NOT NULL */ const char *zPK; /* Might be UNIQUE or PRIMARY KEY */ unsigned int x, y; /* Pseudo-random number generator state */ u64 nResByte; /* Total number of result bytes */ @@ -500,11 +508,11 @@ zSql = sqlite3_vmprintf(zFormat, ap); va_end(ap); 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)); } if( g.pScript ){ char *z = sqlite3_expanded_sql(pStmt); @@ -532,11 +540,11 @@ if( g.bSqlOnly ){ printSql(zSql); }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)); } } sqlite3_free(zSql); @@ -600,12 +608,20 @@ } } #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_CACHE ){ + 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 { sqlite3_reset(g.pStmt); @@ -2287,10 +2303,12 @@ g.zPK = "PRIMARY KEY"; }else if( strcmp(z,"repeat")==0 ){ 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 }else if( strcmp(z,"serialized")==0 ){ sqlite3_config(SQLITE_CONFIG_SERIALIZED); @@ -2309,10 +2327,14 @@ }else if( strcmp(z,"shrink-memory")==0 ){ g.bMemShrink = 1; }else if( strcmp(z,"size")==0 ){ if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); g.szTest = integerValue(argv[++i]); + }else if( strcmp(z,"stmtcache")==0 ){ + if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); + g.szStmtCache = integerValue(argv[++i]); + g.mPrepFlags |= SQLITE_PREPARE_CACHE; }else if( strcmp(z,"stats")==0 ){ showStats = 1; }else if( strcmp(z,"temp")==0 ){ if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); i++; @@ -2403,10 +2425,18 @@ pLook = malloc( nLook*szLook ); rc = sqlite3_db_config(g.db, SQLITE_DBCONFIG_LOOKASIDE,pLook,szLook,nLook); if( rc ) fatal_error("lookaside configuration failed: %d\n", rc); } #endif +#ifdef SQLITE_DBCONFIG_STMTCACHE_SIZE + if( g.mPrepFlags & SQLITE_PREPARE_CACHE ){ + int nCache = 0; + rc = sqlite3_db_config(g.db, SQLITE_DBCONFIG_STMTCACHE_SIZE, + g.szStmtCache, &nCache); + if( rc ) fatal_error("statement cache configuration failed: %d\n", rc); + } +#endif /* SQLITE_DBCONFIG_STMTCACHE_SIZE */ if( g.nReserve>0 ){ sqlite3_file_control(g.db, 0, SQLITE_FCNTL_RESERVE_BYTES, &g.nReserve); } /* Set database connection options */