Index: Makefile.in ================================================================== --- Makefile.in +++ Makefile.in @@ -179,11 +179,11 @@ memjournal.lo \ mutex.lo mutex_noop.lo mutex_unix.lo mutex_w32.lo \ notify.lo opcodes.lo os.lo os_unix.lo os_win.lo \ pager.lo parse.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \ random.lo resolve.lo rowset.lo rtree.lo \ - sqlite3session.lo select.lo sqlite3rbu.lo status.lo \ + sqlite3session.lo select.lo sqlite3rbu.lo status.lo stmts.lo \ table.lo threads.lo tokenize.lo treeview.lo trigger.lo \ update.lo util.lo vacuum.lo \ vdbe.lo vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbesort.lo \ vdbetrace.lo wal.lo walker.lo where.lo wherecode.lo whereexpr.lo \ utf.lo vtab.lo @@ -348,11 +348,12 @@ $(TOP)/ext/session/sqlite3session.h SRC += \ $(TOP)/ext/rbu/sqlite3rbu.h \ $(TOP)/ext/rbu/sqlite3rbu.c SRC += \ - $(TOP)/ext/misc/json1.c + $(TOP)/ext/misc/json1.c \ + $(TOP)/ext/misc/stmts.c # Generated source code files # SRC += \ keywordhash.h \ @@ -562,10 +563,11 @@ # SHELL_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS4 # SHELL_OPT += -DSQLITE_ENABLE_FTS5 SHELL_OPT += -DSQLITE_ENABLE_EXPLAIN_COMMENTS SHELL_OPT += -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION +SHELL_OPT += -DSQLITE_ENABLE_STMTSVTAB FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1 FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5 -DSQLITE_OSS_FUZZ FUZZCHECK_OPT += -DSQLITE_MAX_MEMORY=50000000 FUZZCHECK_SRC = $(TOP)/test/fuzzcheck.c $(TOP)/test/ossfuzz.c DBFUZZ_OPT = @@ -1032,10 +1034,13 @@ $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/session/sqlite3session.c json1.lo: $(TOP)/ext/misc/json1.c $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/misc/json1.c +stmts.lo: $(TOP)/ext/misc/stmts.c + $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/misc/stmts.c + # FTS5 things # FTS5_SRC = \ $(TOP)/ext/fts5/fts5.h \ $(TOP)/ext/fts5/fts5Int.h \ Index: Makefile.msc ================================================================== --- Makefile.msc +++ Makefile.msc @@ -1292,11 +1292,12 @@ $(TOP)\ext\fts3\fts3_write.c \ $(TOP)\ext\icu\icu.c \ $(TOP)\ext\rtree\rtree.c \ $(TOP)\ext\session\sqlite3session.c \ $(TOP)\ext\rbu\sqlite3rbu.c \ - $(TOP)\ext\misc\json1.c + $(TOP)\ext\misc\json1.c \ + $(TOP)\ext\misc\stmts.c # Extension header files, part 1. # SRC08 = \ $(TOP)\ext\fts1\fts1.h \ @@ -1503,11 +1504,11 @@ # Additional compiler options for the shell. These are only effective # when the shell is not being dynamically linked. # !IF $(DYNAMIC_SHELL)==0 && $(FOR_WIN10)==0 -SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_SHELL_JSON1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS +SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_SHELL_JSON1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_STMTSVTAB !ENDIF # <> # Extra compiler options for various test tools. # Index: ext/misc/stmts.c ================================================================== --- ext/misc/stmts.c +++ ext/misc/stmts.c @@ -18,11 +18,14 @@ ** .load ./stmts ** .mode line ** .header on ** SELECT * FROM stmts; */ +#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTSVTAB) +#if !defined(SQLITEINT_H) #include "sqlite3ext.h" +#endif SQLITE_EXTENSION_INIT1 #include #include #ifndef SQLITE_OMIT_VIRTUALTABLE @@ -87,24 +90,27 @@ ){ stmts_vtab *pNew; int rc; /* Column numbers */ -#define STMTS_COLUMN_PTR 0 /* Numeric value of the statement pointer */ -#define STMTS_COLUMN_SQL 1 /* SQL for the statement */ -#define STMTS_COLUMN_NCOL 2 /* Number of result columns */ -#define STMTS_COLUMN_RO 3 /* True if read-only */ -#define STMTS_COLUMN_BUSY 4 /* True if currently busy */ -#define STMTS_COLUMN_NSCAN 5 /* SQLITE_STMTSTATUS_FULLSCAN_STEP */ -#define STMTS_COLUMN_NSORT 6 /* SQLITE_STMTSTATUS_SORT */ -#define STMTS_COLUMN_NAIDX 7 /* SQLITE_STMTSTATUS_AUTOINDEX */ -#define STMTS_COLUMN_NSTEP 8 /* SQLITE_STMTSTATUS_VM_STEP */ -#define STMTS_COLUMN_MEM 9 /* SQLITE_STMTSTATUS_MEMUSED */ +#define STMTS_COLUMN_PTR 0 /* Numeric value of the statement pointer */ +#define STMTS_COLUMN_SQL 1 /* SQL for the statement */ +#define STMTS_COLUMN_NCOL 2 /* Number of result columns */ +#define STMTS_COLUMN_RO 3 /* True if read-only */ +#define STMTS_COLUMN_BUSY 4 /* True if currently busy */ +#define STMTS_COLUMN_NSCAN 5 /* SQLITE_STMTSTATUS_FULLSCAN_STEP */ +#define STMTS_COLUMN_NSORT 6 /* SQLITE_STMTSTATUS_SORT */ +#define STMTS_COLUMN_NAIDX 7 /* SQLITE_STMTSTATUS_AUTOINDEX */ +#define STMTS_COLUMN_NSTEP 8 /* SQLITE_STMTSTATUS_VM_STEP */ +#define STMTS_COLUMN_REPREP 9 /* SQLITE_STMTSTATUS_REPREPARE */ +#define STMTS_COLUMN_RUN 10 /* SQLITE_STMTSTATUS_RUN */ +#define STMTS_COLUMN_MEM 11 /* SQLITE_STMTSTATUS_MEMUSED */ rc = sqlite3_declare_vtab(db, - "CREATE TABLE x(ptr,sql,ncol,ro,busy,nscan,nsort,naidx,nstep,mem)"); + "CREATE TABLE x(ptr,sql,ncol,ro,busy,nscan,nsort,naidx,nstep," + "reprep,run,mem)"); if( rc==SQLITE_OK ){ pNew = sqlite3_malloc( sizeof(*pNew) ); *ppVtab = (sqlite3_vtab*)pNew; if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(*pNew)); @@ -181,16 +187,22 @@ break; } case STMTS_COLUMN_BUSY: { sqlite3_result_int(ctx, sqlite3_stmt_busy(pCur->pStmt)); break; + } + case STMTS_COLUMN_MEM: { + i = SQLITE_STMTSTATUS_MEMUSED + + STMTS_COLUMN_NSCAN - SQLITE_STMTSTATUS_FULLSCAN_STEP; + /* Fall thru */ } case STMTS_COLUMN_NSCAN: case STMTS_COLUMN_NSORT: case STMTS_COLUMN_NAIDX: case STMTS_COLUMN_NSTEP: - case STMTS_COLUMN_MEM: { + case STMTS_COLUMN_REPREP: + case STMTS_COLUMN_RUN: { sqlite3_result_int(ctx, sqlite3_stmt_status(pCur->pStmt, i-STMTS_COLUMN_NSCAN+SQLITE_STMTSTATUS_FULLSCAN_STEP, 0)); break; } } @@ -275,10 +287,19 @@ 0, /* xRename */ }; #endif /* SQLITE_OMIT_VIRTUALTABLE */ +int sqlite3StmtsVtabInit(sqlite3 *db){ + int rc = SQLITE_OK; +#ifndef SQLITE_OMIT_VIRTUALTABLE + rc = sqlite3_create_module(db, "stmts", &stmtsModule, 0); +#endif + return rc; +} + +#ifndef SQLITE_CORE #ifdef _WIN32 __declspec(dllexport) #endif int sqlite3_stmts_init( sqlite3 *db, @@ -291,9 +312,11 @@ if( sqlite3_libversion_number()<3008012 ){ *pzErrMsg = sqlite3_mprintf( "generate_stmts() requires SQLite 3.8.12 or later"); return SQLITE_ERROR; } - rc = sqlite3_create_module(db, "stmts", &stmtsModule, 0); + rc = sqlite3StmtsVtabInit(db); #endif return rc; } +#endif /* SQLITE_CORE */ +#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTSVTAB) */ Index: main.mk ================================================================== --- main.mk +++ main.mk @@ -66,11 +66,12 @@ main.o malloc.o mem0.o mem1.o mem2.o mem3.o mem5.o \ memjournal.o \ mutex.o mutex_noop.o mutex_unix.o mutex_w32.o \ notify.o opcodes.o os.o os_unix.o os_win.o \ pager.o pcache.o pcache1.o pragma.o prepare.o printf.o \ - random.o resolve.o rowset.o rtree.o select.o sqlite3rbu.o status.o \ + random.o resolve.o rowset.o rtree.o \ + select.o sqlite3rbu.o status.o stmts.o \ table.o threads.o tokenize.o treeview.o trigger.o \ update.o userauth.o util.o vacuum.o \ vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \ vdbetrace.o wal.o walker.o where.o wherecode.o whereexpr.o \ utf.o vtab.o @@ -232,11 +233,12 @@ $(TOP)/ext/userauth/sqlite3userauth.h SRC += \ $(TOP)/ext/rbu/sqlite3rbu.c \ $(TOP)/ext/rbu/sqlite3rbu.h SRC += \ - $(TOP)/ext/misc/json1.c + $(TOP)/ext/misc/json1.c \ + $(TOP)/ext/misc/stmts.c # FTS5 things # FTS5_HDR = \ @@ -473,10 +475,11 @@ # Extra compiler options for various shell tools # SHELL_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 SHELL_OPT += -DSQLITE_ENABLE_EXPLAIN_COMMENTS SHELL_OPT += -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION +SHELL_OPT += -DSQLITE_ENABLE_STMTSVTAB FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1 FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5 FUZZCHECK_OPT += -DSQLITE_MAX_MEMORY=50000000 DBFUZZ_OPT = KV_OPT = -DSQLITE_THREADSAFE=0 -DSQLITE_DIRECT_OVERFLOW_READ @@ -716,10 +719,13 @@ $(TCCX) -DSQLITE_CORE -c fts5.c json1.o: $(TOP)/ext/misc/json1.c $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/misc/json1.c +stmts.o: $(TOP)/ext/misc/stmts.c + $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/misc/stmts.c + rtree.o: $(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR) $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/rtree/rtree.c Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -26,10 +26,13 @@ # include "sqliteicu.h" #endif #ifdef SQLITE_ENABLE_JSON1 int sqlite3Json1Init(sqlite3*); #endif +#ifdef SQLITE_ENABLE_STMTSVTAB +int sqlite3StmtsVtabInit(sqlite3*); +#endif #ifdef SQLITE_ENABLE_FTS5 int sqlite3Fts5Init(sqlite3*); #endif #ifndef SQLITE_AMALGAMATION @@ -3055,10 +3058,16 @@ #ifdef SQLITE_ENABLE_JSON1 if( !db->mallocFailed && rc==SQLITE_OK){ rc = sqlite3Json1Init(db); } #endif + +#ifdef SQLITE_ENABLE_STMTSVTAB + if( !db->mallocFailed && rc==SQLITE_OK){ + rc = sqlite3StmtsVtabInit(db); + } +#endif /* -DSQLITE_DEFAULT_LOCKING_MODE=1 makes EXCLUSIVE the default locking ** mode. -DSQLITE_DEFAULT_LOCKING_MODE=0 make NORMAL the default locking ** mode. Doing nothing at all also makes NORMAL the default. */ Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -7145,10 +7145,22 @@ ** by the prepared statement if that number is less than or equal ** to 2147483647. The number of virtual machine operations can be ** used as a proxy for the total work done by the prepared statement. ** If the number of virtual machine operations exceeds 2147483647 ** then the value returned by this statement status code is undefined. +** +** [[SQLITE_STMTSTATUS_REPREPARE]]
SQLITE_STMTSTATUS_REPREPARE
+**
^This is the number of times that the prepare statement has been +** automatically regenerated due to schema changes or change to +** [bound parameters] that might affect the query plan. +** +** [[SQLITE_STMTSTATUS_RUN]]
SQLITE_STMTSTATUS_RUN
+**
^This is the number of times that the prepared statement has +** been run. A single "run" for the purposes of this counter is one +** or more calls to [sqlite3_step()] followed by a call to [sqlite3_reset()]. +** The counter is incremented on the first [sqlite3_step()] call of each +** cycle. ** ** [[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() @@ -7158,11 +7170,13 @@ */ #define SQLITE_STMTSTATUS_FULLSCAN_STEP 1 #define SQLITE_STMTSTATUS_SORT 2 #define SQLITE_STMTSTATUS_AUTOINDEX 3 #define SQLITE_STMTSTATUS_VM_STEP 4 -#define SQLITE_STMTSTATUS_MEMUSED 5 +#define SQLITE_STMTSTATUS_REPREPARE 5 +#define SQLITE_STMTSTATUS_RUN 6 +#define SQLITE_STMTSTATUS_MEMUSED 99 /* ** CAPI3REF: Custom Page Cache Object ** ** The sqlite3_pcache type is opaque. It is implemented by Index: src/test1.c ================================================================== --- src/test1.c +++ src/test1.c @@ -2137,10 +2137,13 @@ } aOp[] = { { "SQLITE_STMTSTATUS_FULLSCAN_STEP", SQLITE_STMTSTATUS_FULLSCAN_STEP }, { "SQLITE_STMTSTATUS_SORT", SQLITE_STMTSTATUS_SORT }, { "SQLITE_STMTSTATUS_AUTOINDEX", SQLITE_STMTSTATUS_AUTOINDEX }, { "SQLITE_STMTSTATUS_VM_STEP", SQLITE_STMTSTATUS_VM_STEP }, + { "SQLITE_STMTSTATUS_REPREPARE", SQLITE_STMTSTATUS_REPREPARE }, + { "SQLITE_STMTSTATUS_RUN", SQLITE_STMTSTATUS_RUN }, + { "SQLITE_STMTSTATUS_MEMUSED", SQLITE_STMTSTATUS_MEMUSED }, }; if( objc!=4 ){ Tcl_WrongNumArgs(interp, 1, objv, "STMT PARAMETER RESETFLAG"); return TCL_ERROR; } Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -7077,10 +7077,11 @@ if( p->aOp[i].opcode==OP_Once ) p->aOp[i].p1 = 0; } pOp->p1 = 0; } pOp->p1++; + p->aCounter[SQLITE_STMTSTATUS_RUN]++; goto jump_to_p2; } #ifdef SQLITE_ENABLE_CURSOR_HINTS /* Opcode: CursorHint P1 * * P4 * Index: src/vdbeInt.h ================================================================== --- src/vdbeInt.h +++ src/vdbeInt.h @@ -391,11 +391,11 @@ bft readOnly:1; /* True for statements that do not write */ bft bIsReader:1; /* True for statements that read */ bft isPrepareV2:1; /* True if prepared with prepare_v2() */ yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */ yDbMask lockMask; /* Subset of btreeMask that requires a lock */ - u32 aCounter[5]; /* Counters used by sqlite3_stmt_status() */ + u32 aCounter[7]; /* Counters used by sqlite3_stmt_status() */ char *zSql; /* Text of the SQL statement that generated this */ void *pFree; /* Free this when deleting the vdbe */ VdbeFrame *pFrame; /* Parent frame */ VdbeFrame *pDelFrame; /* List of frame objects to free on VM reset */ int nFrame; /* Number of frames in pFrame list */ Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -85,10 +85,13 @@ zTmp = pA->zSql; pA->zSql = pB->zSql; pB->zSql = zTmp; pB->isPrepareV2 = pA->isPrepareV2; pB->expmask = pA->expmask; + memcpy(pB->aCounter, pA->aCounter, sizeof(pB->aCounter)); + pB->aCounter[SQLITE_STMTSTATUS_REPREPARE]++; + } /* ** Resize the Vdbe.aOp array so that it is at least nOp elements larger ** than its current size. nOp is guaranteed to be less than or equal Index: tool/mksqlite3c.tcl ================================================================== --- tool/mksqlite3c.tcl +++ tool/mksqlite3c.tcl @@ -387,10 +387,11 @@ sqlite3rbu.c dbstat.c sqlite3session.c json1.c fts5.c + stmts.c } { copy_file tsrc/$file } close $out