-C Initial\scode\sfor\sa\sproposed\snew\ssqlite3_stmt_mode()\sAPI.\nThis\sis\san\sincomplete\ssnapshot\sof\sa\swork-in-progress.
-D 2020-03-19T21:17:11.830
+C Revamp\sthe\sEXPLAIN\sinfrastructure\sto\sfacilitate\ssqlite3_stmt_mode().\s\sThe\ncurrently\scode\smostly\sworks,\sbut\sthere\sare\stest\sfailures.\s\sThis\sis\san\nincremental\scheck-in.
+D 2020-03-20T16:13:41.030
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F src/utf.c 95fb6e03a5ca679045c5adccd05380f0addccabef5911abddcb06af069500ab7
F src/util.c a285c1e026907b69fa2592bd05047a565a1d8a1aef2b73c924b6a8ffe772871a
F src/vacuum.c 813b510ba887fee6492bcb11f2bf77d7eb58b232b83649136372e0a2fc17f4b9
-F src/vdbe.c ca986d3f9961045256c1b077a6986cd2e9cfa86909adfd0acafb761e6656b5bb
+F src/vdbe.c b7b9ec1d604a12f4cafedf796027a297bc306f0d3afb340948898256cb4473b9
F src/vdbe.h 51282fbe819ee0e8eeeaab176240860d334c20a12b14f3b363e7f1a4e05d60b9
-F src/vdbeInt.h a341e6994ffd865531089f0c94d3910acc66d25d312259c4a45f422693deab9b
-F src/vdbeapi.c e8351c4f351e492e260fb8b0f9f57575772f583fbbe97dca4c31dcadd06760d6
-F src/vdbeaux.c ce2521d1979d6e081c21766523244891149df247092202db3a0e097f4eae40f2
+F src/vdbeInt.h 1a8807b7dfac4142f5b9047767295327be41f02c17f7bb6bd8ffef6219dac588
+F src/vdbeapi.c d840fb3b80bbbd25fd44e1bb2d4a209e297832f527d21ea718fa53502a2a2d97
+F src/vdbeaux.c 734cb197ffdf83322832f9c881e9841b43b27f43677b8c75ad444cd2ccee2802
F src/vdbeblob.c 253ed82894924c362a7fa3079551d3554cd1cdace39aa833da77d3bc67e7c1b1
F src/vdbemem.c 39b942ecca179f4f30a32b54579a85d74ccaefa5af2a0ad2700abe5ef0768b22
F src/vdbesort.c 2be76d26998ce2b3324cdcc9f6443728e54b6c7677c553ad909c7d7cfab587df
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 11e0844f71e8f2d27ce9363fb505e02fd7795c61dae0b3886cf0d8df4484dd97
-R bfd344317fdbc4dd2c74ef078364da12
-T *branch * sqlite3_stmt_mode
-T *sym-sqlite3_stmt_mode *
-T -sym-trunk *
+P 3cf7537b5e14e218218b18b3c0c668c950a71fcddc68a5faf0f197519718a6c2
+R 858f863db42629ad352a7a54d45b01f7
U drh
-Z 4fdf6d3f28007c100776cbe88029096e
+Z acdb8ce72295d433409d053ae8da03ad
# define SQLITE_MAX_SCHEMA_RETRY 50
#endif
+/*
+** Maximum number of columns in any "EXPLAIN" output
+*/
+#define VDBE_EXPLAIN_COLS 9
+
/*
** VDBE_DISPLAY_P4 is true or false depending on whether or not the
** "explain" P4 display logic is enabled.
sqlite3_value *argv[1]; /* Argument set */
};
-/* A bitfield type for use inside of structures. Always follow with :N where
-** N is the number of bits.
-*/
-typedef unsigned bft; /* Bit Field Type */
-
/* The ScanStatus object holds a single value for the
** sqlite3_stmt_scanstatus() interface.
*/
int rcApp; /* errcode set by sqlite3_result_error_code() */
u32 nWrite; /* Number of write operations that have occurred */
#endif
- u16 nResColumn; /* Number of columns in one row of the result set */
+ u16 nResColumn; /* Columns in one row of a normal result set */
+ u16 nRes; /* Columns in the current actual result set */
u8 errorAction; /* Recovery action to do in case of an error */
u8 minWriteFileFormat; /* Minimum file format for writable database files */
u8 prepFlags; /* SQLITE_PREPARE_* flags */
- bft expired:2; /* 1: recompile VM immediately 2: when convenient */
- bft explain:2; /* True if EXPLAIN present on SQL command */
- bft origExplain:2; /* The original value of explain */
- bft doingRerun:1; /* True if rerunning after an auto-reprepare */
- bft changeCntOn:1; /* True to update the change-counter */
- bft runOnlyOnce:1; /* Automatically expire on reset */
- bft usesStmtJournal:1; /* True if uses a statement journal */
- bft readOnly:1; /* True for statements that do not write */
- bft bIsReader:1; /* True for statements that read */
+ u8 explain:2; /* True if EXPLAIN present on SQL command */
+ u8 origExplain:2; /* The original value of explain */
+ u8 expired:2; /* 1: recompile VM immediately 2: when convenient */
+ u8 doingRerun:1; /* True if rerunning after an auto-reprepare */
+ u8 changeCntOn:1; /* True to update the change-counter */
+ u8 runOnlyOnce:1; /* Automatically expire on reset */
+ u8 usesStmtJournal:1; /* True if uses a statement journal */
+ u8 readOnly:1; /* True for statements that do not write */
+ u8 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[7]; /* Counters used by sqlite3_stmt_status() */
int sqlite3VdbeIdxRowid(sqlite3*, BtCursor*, i64*);
int sqlite3VdbeExec(Vdbe*);
#ifndef SQLITE_OMIT_EXPLAIN
+Mem *sqlite3VdbeSetExplainColumnNames(Vdbe*);
int sqlite3VdbeList(Vdbe*);
#endif
int sqlite3VdbeHalt(Vdbe*);
}
/*
-** Release an array of N Mem elements
+** Release all content from an array of N Mem elements. All elements
+** are left in a state of "MEM_Undefined".
+**
+** This routine does not free the array itself. It just frees the
+** content held by the Mem elements in the array.
*/
static void releaseMemArray(Mem *p, int N){
if( p && N ){
#ifndef SQLITE_OMIT_EXPLAIN
/*
-** Give a listing of the program in the virtual machine.
+** There should are VDBE_EXPLAIN_COLS*2 extra registers on the end of the
+** Vdbe.aColName array for statements that are in one of the
+** "explain" modes. The first VDBE_EXPLAIN_COLS registers hold the names of
+** the columns, and the last VDBE_EXPLAIN_COLS registers are used for to store
+** result set values when running the explain.
**
-** The interface is the same as sqlite3VdbeExec(). But instead of
-** running the code, it invokes the callback once for each instruction.
-** This feature is used to implement "EXPLAIN".
+** This routine makes sure those extra register exist and return
+** a pointer to the first of them. If the extra registers do
+** not exist, then the are allocated.
**
-** When p->explain==1, each instruction is listed. When
-** p->explain==2, only OP_Explain instructions are listed and these
-** are shown in a different format. p->explain==2 is used to implement
-** EXPLAIN QUERY PLAN.
-** 2018-04-24: In p->explain==2 mode, the OP_Init opcodes of triggers
-** are also shown, so that the boundaries between the main program and
-** each trigger are clear.
+** NULL is returned if there is a memory allocation error.
+*/
+Mem *sqlite3VdbeSetExplainColumnNames(Vdbe *p){
+ int n = p->nResColumn*COLNAME_N;
+ if( p->nColName<=n ){
+ static const char * const azColName[] = {
+ "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment",
+ "id", "parent", "notused", "detail",
+ "schema", "type", "name", "rw", "rootpage"
+ };
+ int iFirst, cnt, i;
+ Mem *pNew = sqlite3DbRealloc(p->db, p->aColName,
+ (n+VDBE_EXPLAIN_COLS*2)*sizeof(Mem));
+ if( pNew==0 ){
+ return 0;
+ }
+ p->aColName = pNew;
+ p->nColName = n+VDBE_EXPLAIN_COLS*2;
+ initMemArray(p->aColName+n, VDBE_EXPLAIN_COLS*2, p->db, MEM_Null);
+ if( p->explain==SQLITE_STMTMODE_EXPLAIN ){
+ iFirst = 0;
+ cnt = 8;
+ }else if( p->explain==SQLITE_STMTMODE_EQP ){
+ iFirst = 8;
+ cnt = 4;
+ }else{
+ iFirst = 12;
+ cnt = 5;
+ }
+ for(i=0; i<cnt; i++){
+ sqlite3VdbeMemSetStr(pNew+n+i, azColName[i+iFirst], -1,
+ SQLITE_UTF8, SQLITE_STATIC);
+ }
+ }
+ return p->aColName + n;
+}
+#endif
+
+#ifndef SQLITE_OMIT_EXPLAIN
+/*
+** Give a listing of the program in the virtual machine.
**
-** When p->explain==1, first the main program is listed, then each of
-** the trigger subprograms are listed one by one.
+** The interface is the same as sqlite3VdbeExec(). But instead of
+** running the code, it returns rows based on the bytecode content,
+** rather than on actually running the bytecode.
*/
int sqlite3VdbeList(
Vdbe *p /* The VDBE */
sqlite3 *db = p->db; /* The database connection */
int i; /* Loop counter */
int rc = SQLITE_OK; /* Return code */
- Mem *pMem = &p->aMem[1]; /* First Mem of result set */
+ Mem *pMem; /* First Mem of result set */
int bListSubprogs = (p->explain==1 || (db->flags & SQLITE_TriggerEQP)!=0);
Op *pOp = 0;
- assert( p->explain );
+ assert( p->explain==SQLITE_STMTMODE_EXPLAIN
+ || p->explain==SQLITE_STMTMODE_EQP
+ || p->explain==SQLITE_STMTMODE_TABLELIST );
assert( p->magic==VDBE_MAGIC_RUN );
assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || p->rc==SQLITE_NOMEM );
** the result, result columns may become dynamic if the user calls
** sqlite3_column_text16(), causing a translation to UTF-16 encoding.
*/
- releaseMemArray(pMem, 8);
- p->pResultSet = 0;
+ pMem = sqlite3VdbeSetExplainColumnNames(p);
+ if( pMem==0 ){
+ p->rc = SQLITE_NOMEM;
+ return SQLITE_ERROR;
+ }
+ pMem += VDBE_EXPLAIN_COLS;
+ releaseMemArray(pMem, VDBE_EXPLAIN_COLS);
+ p->pResultSet = pMem;
+ p->nRes = 0;
if( p->rc==SQLITE_NOMEM ){
/* This happens if a malloc() inside a call to sqlite3_column_text() or
*/
nRow = p->nOp;
if( bListSubprogs ){
- /* The first 8 memory cells are used for the result set. So we will
- ** commandeer the 9th cell to use as storage for an array of pointers
- ** to trigger subprograms. The VDBE is guaranteed to have at least 9
- ** cells. */
- assert( p->nMem>9 );
- pSub = &p->aMem[9];
+ pSub = pMem++;
+ p->pResultSet = pMem;
if( pSub->flags&MEM_Blob ){
/* On the first call to sqlite3_step(), pSub will hold a NULL. It is
** initialized to a BLOB by the P4_SUBPROGRAM processing logic below */
/* When an OP_Program opcode is encounter (the only opcode that has
** a P4_SUBPROGRAM argument), expand the size of the array of subprograms
- ** kept in p->aMem[9].z to hold the new program - assuming this subprogram
+ ** to hold the new program - assuming this subprogram
** has not already been seen.
*/
if( bListSubprogs && pOp->p4type==P4_SUBPROGRAM ){
nRow += pOp->p4.pProgram->nOp;
}
}
- if( p->explain<2 ) break;
+ if( p->explain==SQLITE_STMTMODE_EXPLAIN ) break;
if( pOp->opcode==OP_Explain ) break;
if( pOp->opcode==OP_Init && p->pc>1 ) break;
}
sqlite3VdbeError(p, sqlite3ErrStr(p->rc));
}else{
char *zP4;
- if( p->explain==1 ){
+ if( p->explain==SQLITE_STMTMODE_EXPLAIN ){
pMem->flags = MEM_Int;
pMem->u.i = i; /* Program counter */
pMem++;
}
pMem++;
- if( p->explain==1 ){
+ if( p->explain==SQLITE_STMTMODE_EXPLAIN ){
if( sqlite3VdbeMemClearAndResize(pMem, 4) ){
assert( p->db->mallocFailed );
return SQLITE_ERROR;
#else
pMem->flags = MEM_Null; /* Comment */
#endif
+ pMem++;
}
- p->nResColumn = 8 - 4*(p->explain-1);
- p->pResultSet = &p->aMem[1];
+ p->nRes = (int)(pMem - p->pResultSet);
+ assert( p->nRes<=VDBE_EXPLAIN_COLS );
p->rc = SQLITE_OK;
rc = SQLITE_ROW;
}
resolveP2Values(p, &nArg);
p->usesStmtJournal = (u8)(pParse->isMultiWrite && pParse->mayAbort);
if( pParse->explain ){
- static const char * const azColName[] = {
- "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment",
- "id", "parent", "notused", "detail"
- };
- int iFirst, mx, i;
- if( nMem<10 ) nMem = 10;
- if( pParse->explain==2 ){
- sqlite3VdbeSetNumCols(p, 4);
- iFirst = 8;
- mx = 12;
- }else{
- sqlite3VdbeSetNumCols(p, 8);
- iFirst = 0;
- mx = 8;
- }
- for(i=iFirst; i<mx; i++){
- sqlite3VdbeSetColName(p, i-iFirst, COLNAME_NAME,
- azColName[i], SQLITE_STATIC);
- }
p->explain = p->origExplain = pParse->explain;
}
p->expired = 0;
#endif
sqlite3DbFree(db, p->zErrMsg);
p->zErrMsg = 0;
- p->pResultSet = 0;
+ p->nRes = 0;
#ifdef SQLITE_DEBUG
p->nWrite = 0;
#endif