-C Change\sthe\scommand-line\sshell\sman-page\sto\suse\sthe\s".tr"\stroff\sdirective\ninstead\sof\s".cc"\sfor\sescaping\sthe\sinitial\s"."\scharacters\sin\sthe\s".help"\noutput.
-D 2014-10-31T14:46:51.896
+C Add\sthe\sexperimental\ssqlite3_stmt_scanstatus()\sAPI.
+D 2014-10-31T20:11:32.562
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in cf57f673d77606ab0f2d9627ca52a9ba1464146a
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
F src/select.c 428165951748151e87a15295b7357221433e311b
F src/shell.c 282f8f5278e0c78eb442217531172ec9e1538796
-F src/sqlite.h.in 737b7dd0f3f81fe183646d22828b39f85ef3e68c
+F src/sqlite.h.in 6e90cdb404e4fa8c0eb149ca79c11c6608a97ece
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
F src/sqlite3ext.h 17d487c3c91b0b8c584a32fbeb393f6f795eea7d
F src/sqliteInt.h 4f86ac648ea398c1bb3db036062934cde257ea23
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c 81712116e826b0089bb221b018929536b2b5406f
F src/table.c f142bba7903e93ca8d113a5b8877a108ad1a27dc
-F src/tclsqlite.c c67d310c833046cccc192125d64ad422ab882684
-F src/test1.c 63d4b1707c4052cf9c05c1cbb4a62666d70a0b48
+F src/tclsqlite.c 7cdd4dd3c2a4183483feca260070d73d6e22cd47
+F src/test1.c b53f4da2f386efa5c248716cd4bdc8344a87e952
F src/test2.c 98049e51a17dc62606a99a9eb95ee477f9996712
F src/test3.c 1c0e5d6f080b8e33c1ce8b3078e7013fdbcd560c
F src/test4.c 9b32d22f5f150abe23c1830e2057c4037c45b3df
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
F src/util.c 3b627daa45c7308c1e36e3dbaa3f9ce7e5c7fa73
F src/vacuum.c 59f03f92bcff57faa6a8ca256eb29ccddfb0614a
-F src/vdbe.c 49e659bc165e99b28492004b440e22146dc898ab
-F src/vdbe.h 09f5b4e3719fa454f252322b1cdab5cf1f361327
-F src/vdbeInt.h acc36ac461f973f46ac7942f86abdd93d2f8cfbc
-F src/vdbeapi.c 02d8afcff710eb35e3d9e49cb677308296b00009
-F src/vdbeaux.c 3d6b2b412ef2193aa4729922dfc5df1efadbf6df
+F src/vdbe.c 80329be857cff3c2d6e8c1f594ac689166c44ae7
+F src/vdbe.h f8e5388173dbf8408da4ae1dcbf75911caf2253d
+F src/vdbeInt.h 284b2294c188474daa9b5ce2bd0200560cb0940d
+F src/vdbeapi.c 2afa2e162f290879ca9c51e8795a377035f1662a
+F src/vdbeaux.c 1e2561eeb94749f6b8c0a1eb02b5fecb645ed48c
F src/vdbeblob.c 8b5442ff0954c44b45cbabbe2e94091a2e16fdef
F src/vdbemem.c 31d8eabb0cd78bfeab4e5124c7363c3e9e54db9f
F src/vdbesort.c 975aeffa99acb0991b2f288d30294756bff41438
F src/wal.c 10e7de7ce90865a68153f001a61f1d985cd17983
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804
-F src/where.c 5665df88cbd2b38eb72b4b94c8892c8afb360181
+F src/where.c 344beac082f8f4f69ec7c4a669d74294475e5abe
F src/whereInt.h 19279cd0664ce1d90b9ad3ef0108cb494acfe455
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
F test/savepoint5.test 0735db177e0ebbaedc39812c8d065075d563c4fd
F test/savepoint6.test f41279c5e137139fa5c21485773332c7adb98cd7
F test/savepoint7.test fbf319a7b2dda089ec5be30a424a0e95f121d423
+F test/scanstatus.test a62dad3311fc51eab1d6977b082738bd2148fe07
F test/schema.test 8f7999be894260f151adf15c2c7540f1c6d6a481
F test/schema2.test 906408621ea881fdb496d878b1822572a34e32c5
F test/schema3.test 1bc1008e1f8cb5654b248c55f27249366eb7ed38
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P a07078b60007e88adea67bec5f0caf91f707ad78
-R f904ce78baced6aeb6bcb91c631714db
-U drh
-Z c463256d5d45e3bce4a212ca4dd9f3e4
+P 67f0d469da28c023200239a1f3d0c6cef9ef0e45
+R 4c76f3e7b97f67c1dd65bda3a6e96149
+T *branch * scanstatus
+T *sym-scanstatus *
+T -sym-trunk *
+U dan
+Z 4994ca943ee260eeca8f493b4f59766a
-67f0d469da28c023200239a1f3d0c6cef9ef0e45
\ No newline at end of file
+6a9bab34aeb6a01b612211a28c140de60a3e883c
\ No newline at end of file
#define SQLITE_REPLACE 5
+/*
+** Return status data for a single loop within query pStmt.
+**
+** Parameter "idx" identifies the specific loop to retrieve statistics for.
+** Loops are numbered starting from zero. If idx is out of range - less than
+** zero or greater than or equal to the total number of loops used to implement
+** the statement - a non-zero value is returned. In this case the final value
+** of all five output parameters is undefined. Otherwise, if idx is in range,
+** the output parameters are populated and zero returned.
+**
+** Statistics may not be available for all loops in all statements. In cases
+** where there exist loops with no available statistics, this function ignores
+** them completely.
+**
+** This API is only available if the library is built with pre-processor
+** symbol SQLITE_ENABLE_STMT_SCANSTATUS defined.
+*/
+SQLITE_EXPERIMENTAL int sqlite3_stmt_scanstatus(
+ sqlite3_stmt *pStmt,
+ int idx, /* Index of loop to report on */
+ sqlite3_int64 *pnLoop, /* OUT: Number of times loop was run */
+ sqlite3_int64 *pnVisit, /* OUT: Number of rows visited (all loops) */
+ sqlite3_int64 *pnEst, /* OUT: Number of rows estimated (per loop) */
+ const char **pzName, /* OUT: Object name (table or index) */
+ const char **pzExplain /* OUT: EQP string */
+);
+
+
+/*
+** Zero all sqlite3_stmt_scanstatus() related event counters.
+**
+** This API is only available if the library is built with pre-processor
+** symbol SQLITE_ENABLE_STMT_SCANSTATUS defined.
+*/
+SQLITE_EXPERIMENTAL void sqlite3_stmt_scanstatus_reset(sqlite3_stmt*);
+
/*
** Undo the hack that converts floating point types to integer for
Tcl_ResetResult(interp);
return TCL_OK;
}
+
+/*
+** Tclcmd: db_last_stmt_ptr DB
+**
+** If the statement cache associated with database DB is not empty,
+** return the text representation of the most recently used statement
+** handle.
+*/
+static int db_last_stmt_ptr(
+ ClientData cd,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
+ Tcl_CmdInfo cmdInfo;
+ SqliteDb *pDb;
+ sqlite3_stmt *pStmt = 0;
+ char zBuf[100];
+
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DB");
+ return TCL_ERROR;
+ }
+
+ if( !Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo) ){
+ Tcl_AppendResult(interp, "no such db: ", Tcl_GetString(objv[1]), (char*)0);
+ return TCL_ERROR;
+ }
+ pDb = (SqliteDb*)cmdInfo.objClientData;
+
+ if( pDb->stmtList ) pStmt = pDb->stmtList->pStmt;
+ if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ){
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, zBuf, TCL_VOLATILE);
+
+ return TCL_OK;
+}
#endif
/*
Tcl_CreateObjCommand(
interp, "db_use_legacy_prepare", db_use_legacy_prepare_cmd, 0, 0
);
+ Tcl_CreateObjCommand(
+ interp, "db_last_stmt_ptr", db_last_stmt_ptr, 0, 0
+ );
#ifdef SQLITE_SSE
Sqlitetestsse_Init(interp);
return TCL_OK;
}
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+/*
+** Usage: sqlite3_stmt_scanstatus STMT IDX
+*/
+static int test_stmt_scanstatus(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt; /* First argument */
+ int idx; /* Second argument */
+
+ const char *zName;
+ const char *zExplain;
+ sqlite3_int64 nLoop;
+ sqlite3_int64 nVisit;
+ sqlite3_int64 nEst;
+ int res;
+
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "STMT IDX");
+ return TCL_ERROR;
+ }
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR;
+
+ res = sqlite3_stmt_scanstatus(
+ pStmt, idx, &nLoop, &nVisit, &nEst, &zName, &zExplain
+ );
+ if( res==0 ){
+ Tcl_Obj *pRet = Tcl_NewObj();
+ Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("nLoop", -1));
+ Tcl_ListObjAppendElement(0, pRet, Tcl_NewWideIntObj(nLoop));
+ Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("nVisit", -1));
+ Tcl_ListObjAppendElement(0, pRet, Tcl_NewWideIntObj(nVisit));
+ Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("nEst", -1));
+ Tcl_ListObjAppendElement(0, pRet, Tcl_NewWideIntObj(nEst));
+ Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("zName", -1));
+ Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj(zName, -1));
+ Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("zExplain", -1));
+ Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj(zExplain, -1));
+ Tcl_SetObjResult(interp, pRet);
+ }else{
+ Tcl_ResetResult(interp);
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_stmt_scanstatus_reset STMT
+*/
+static int test_stmt_scanstatus_reset(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt; /* First argument */
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "STMT");
+ return TCL_ERROR;
+ }
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+ sqlite3_stmt_scanstatus_reset(pStmt);
+ return TCL_OK;
+}
+#endif
+
/*
** Usage: sqlite3_next_stmt DB STMT
**
{ "sqlite3_user_change", test_user_change, 0 },
{ "sqlite3_user_delete", test_user_delete, 0 },
#endif
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ { "sqlite3_stmt_scanstatus", test_stmt_scanstatus, 0 },
+ { "sqlite3_stmt_scanstatus_reset", test_stmt_scanstatus_reset, 0 },
+#endif
};
static int bitmask_size = sizeof(Bitmask)*8;
/* Return true if the cursor was opened using the OP_OpenSorter opcode. */
#define isSorter(x) ((x)->pSorter!=0)
+/*
+** The first argument passed to the IncrementExplainCounter() macro must
+** be a non-NULL pointer to an object of type VdbeCursor. The second
+** argument must be either "nLoop" or "nVisit" (without the double-quotes).
+*/
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+# define IncrementExplainCounter(pCsr, counter) \
+ if( (pCsr)->pExplain ) (pCsr)->pExplain->counter++
+#else
+# define IncrementExplainCounter(pCsr, counter)
+#endif
+
/*
** Allocate VdbeCursor number iCur. Return a pointer to it. Return NULL
** if we run out of memory.
#ifdef SQLITE_DEBUG
pC->seekOp = pOp->opcode;
#endif
+ IncrementExplainCounter(pC, nLoop);
if( pC->isTable ){
/* The input value in P3 might be of any type: integer, real, string,
** blob, or NULL. But it needs to be an integer before we can do
VdbeBranchTaken(res!=0,2);
if( res ){
pc = pOp->p2 - 1;
+ }else{
+ IncrementExplainCounter(pC, nVisit);
}
break;
}
res = 0;
assert( pCrsr!=0 );
rc = sqlite3BtreeLast(pCrsr, &res);
+ IncrementExplainCounter(pC, nLoop);
+ if( res==0 ) IncrementExplainCounter(pC, nVisit);
pC->nullRow = (u8)res;
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
**
** The next use of the Rowid or Column or Next instruction for P1
** will refer to the first entry in the database table or index.
-** If the table or index is empty and P2>0, then jump immediately to P2.
-** If P2 is 0 or if the table or index is not empty, fall through
-** to the following instruction.
+** If the table or index is empty, jump immediately to P2.
+** If the table or index is not empty, fall through to the following
+** instruction.
**
** This opcode leaves the cursor configured to move in forward order,
** from the beginning toward the end. In other words, the cursor is
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
}
+ IncrementExplainCounter(pC, nLoop);
pC->nullRow = (u8)res;
assert( pOp->p2>0 && pOp->p2<p->nOp );
VdbeBranchTaken(res!=0,2);
if( res ){
pc = pOp->p2 - 1;
+ }else{
+ IncrementExplainCounter(pC, nVisit);
}
break;
}
pC->cacheStatus = CACHE_STALE;
VdbeBranchTaken(res==0,2);
if( res==0 ){
+ IncrementExplainCounter(pC, nVisit);
pC->nullRow = 0;
pc = pOp->p2 - 1;
p->aCounter[pOp->p5]++;
VdbeBranchTaken(res!=0,2);
if( res ){
pc = pOp->p2 - 1;
+ }else{
+ IncrementExplainCounter(pCur, nVisit);
}
+ IncrementExplainCounter(pCur, nLoop);
}
pCur->nullRow = 0;
if( !res ){
/* If there is data, jump to P2 */
pc = pOp->p2 - 1;
+ IncrementExplainCounter(pCur, nVisit);
}
goto check_for_interrupt;
}
break;
}
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+case OP_Explain: {
+ ExplainArg *pArg;
+ VdbeCursor *pCur;
+ if( pOp->p4type==P4_EXPLAIN && (pArg = pOp->p4.pExplain)->iCsr>=0 ){
+ pArg = pOp->p4.pExplain;
+ pCur = p->apCsr[pArg->iCsr];
+ pCur->pExplain = pArg;
+ }
+ break;
+}
+#endif
+
/* Opcode: Noop * * * * *
**
** of this structure.
*/
typedef struct Vdbe Vdbe;
+typedef struct ExplainArg ExplainArg;
/*
** The names of the following types declared in vdbeInt.h are required
KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */
int *ai; /* Used when p4type is P4_INTARRAY */
SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */
+ ExplainArg *pExplain; /* Used when p4type is P4_EXPLAIN */
int (*xAdvance)(BtCursor *, int *);
} p4;
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
};
typedef struct VdbeOpList VdbeOpList;
+/*
+** Structure used as the P4 parameter for OP_Explain opcodes.
+*/
+struct ExplainArg {
+ int iCsr; /* Cursor number this applies to */
+ i64 nLoop; /* Number of times loop has run */
+ i64 nVisit; /* Total number of rows visited */
+ i64 nEst; /* Estimated number of rows per scan */
+ const char *zName; /* Name of table/index being scanned */
+ const char *zExplain; /* EQP text for this loop */
+};
+
+
/*
** Allowed values of VdbeOp.p4type
*/
#define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */
#define P4_SUBPROGRAM (-18) /* P4 is a pointer to a SubProgram structure */
#define P4_ADVANCE (-19) /* P4 is a pointer to BtreeNext() or BtreePrev() */
+#define P4_EXPLAIN (-20) /* P4 is a pointer to an instance of ExplainArg */
/* Error message codes for OP_Halt */
#define P5_ConstraintNotNull 1
i64 seqCount; /* Sequence counter */
i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */
VdbeSorter *pSorter; /* Sorter object for OP_SorterOpen cursors */
+ ExplainArg *pExplain; /* Object to store seek/visit counts (may be NULL) */
/* Cached information about the header for the data record that the
** cursor is currently pointing to. Only valid if cacheStatus matches
char zBase[100]; /* Initial space */
};
+
/* A bitfield type for use inside of structures. Always follow with :N where
** N is the number of bits.
*/
int nOnceFlag; /* Size of array aOnceFlag[] */
u8 *aOnceFlag; /* Flags for OP_Once */
AuxData *pAuxData; /* Linked list of auxdata allocations */
+ ExplainArg **apExplain; /* Array of pointers to P4_EXPLAIN p4 values */
+ int nExplain; /* Number of entries in array apExplain */
};
/*
if( resetFlag ) pVdbe->aCounter[op] = 0;
return (int)v;
}
+
+/*
+** Return status data for a single loop within query pStmt.
+*/
+int sqlite3_stmt_scanstatus(
+ sqlite3_stmt *pStmt,
+ int idx, /* Index of loop to report on */
+ sqlite3_int64 *pnLoop, /* OUT: Number of times loop was run */
+ sqlite3_int64 *pnVisit, /* OUT: Number of rows visited (all loops) */
+ sqlite3_int64 *pnEst, /* OUT: Number of rows estimated (per loop) */
+ const char **pzName, /* OUT: Object name (table or index) */
+ const char **pzExplain /* OUT: EQP string */
+){
+ Vdbe *p = (Vdbe*)pStmt;
+ ExplainArg *pExplain;
+ if( idx<0 || idx>=p->nExplain ) return 1;
+ pExplain = p->apExplain[idx];
+ if( pnLoop ) *pnLoop = pExplain->nLoop;
+ if( pnVisit ) *pnVisit = pExplain->nVisit;
+ if( pnEst ) *pnEst = pExplain->nEst;
+ if( *pzName ) *pzName = pExplain->zName;
+ if( *pzExplain ) *pzExplain = pExplain->zExplain;
+ return 0;
+}
+
+/*
+** Zero all counters associated with the sqlite3_stmt_scanstatus() data.
+*/
+void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){
+ Vdbe *p = (Vdbe*)pStmt;
+ int i;
+ for(i=0; i<p->nExplain; i++){
+ p->apExplain[i]->nLoop = 0;
+ p->apExplain[i]->nVisit = 0;
+ }
+}
+
if( p4 ){
assert( db );
switch( p4type ){
+ case P4_EXPLAIN:
case P4_REAL:
case P4_INT64:
case P4_DYNAMIC:
pOp->p4type = P4_VTAB;
sqlite3VtabLock((VTable *)zP4);
assert( ((VTable *)zP4)->db==p->db );
+ }else if( n==P4_EXPLAIN ){
+ pOp->p4.p = (void*)zP4;
+ pOp->p4type = P4_EXPLAIN;
+ p->apExplain = (ExplainArg**)sqlite3DbReallocOrFree(
+ p->db, p->apExplain, sizeof(ExplainArg*) * p->nExplain + 1
+ );
+ if( p->apExplain ) p->apExplain[p->nExplain++] = (ExplainArg*)zP4;
}else if( n<0 ){
pOp->p4.p = (void*)zP4;
pOp->p4type = (signed char)n;
zTemp[0] = 0;
break;
}
+ case P4_EXPLAIN: {
+ zP4 = (char*)pOp->p4.pExplain->zExplain;
+ break;
+ }
default: {
zP4 = pOp->p4.z;
if( zP4==0 ){
sqlite3DbFree(db, p->aColName);
sqlite3DbFree(db, p->zSql);
sqlite3DbFree(db, p->pFree);
+ sqlite3DbFree(db, p->apExplain);
}
/*
int iFrom, /* Value for "from" column of output */
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
){
-#ifndef SQLITE_DEBUG
+#if !defined(SQLITE_DEBUG) && !defined(SQLITE_ENABLE_STMT_SCANSTATUS)
if( pParse->explain==2 )
#endif
{
int isSearch; /* True for a SEARCH. False for SCAN. */
WhereLoop *pLoop; /* The controlling WhereLoop object */
u32 flags; /* Flags that describe this loop */
- char *zMsg; /* Text to add to EQP output */
StrAccum str; /* EQP output string */
char zBuf[100]; /* Initial space for EQP output string */
+ const char *zObj;
+ ExplainArg *pExplain;
+ i64 nEstRow; /* Estimated rows per scan of pLevel */
pLoop = pLevel->pWLoop;
flags = pLoop->wsFlags;
if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_ONETABLE_ONLY) ) return;
+ sqlite3StrAccumInit(&str, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
+ str.db = db;
+
+ /* Reserve space at the start of the buffer managed by the StrAccum
+ ** object for *pExplain. */
+ assert( sizeof(*pExplain)<=sizeof(zBuf) );
+ str.nChar = sizeof(*pExplain);
+
+ /* Append the object (table or index) name to the buffer. */
+ if( pItem->pSelect ){
+ zObj = "";
+ }else if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){
+ zObj = pLoop->u.btree.pIndex->zName;
+ }else{
+ zObj = pItem->zName;
+ }
+ sqlite3StrAccumAppend(&str, zObj, sqlite3Strlen30(zObj)+1);
+
+ /* Append the EQP text to the buffer */
isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0
|| ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0))
|| (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));
-
- sqlite3StrAccumInit(&str, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
- str.db = db;
sqlite3StrAccumAppendAll(&str, isSearch ? "SEARCH" : "SCAN");
if( pItem->pSelect ){
sqlite3XPrintf(&str, 0, " SUBQUERY %d", pItem->iSelectId);
pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr);
}
#endif
+ nEstRow = pLoop->nOut>=10 ? sqlite3LogEstToInt(pLoop->nOut) : 1;
#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS
- if( pLoop->nOut>=10 ){
- sqlite3XPrintf(&str, 0, " (~%llu rows)", sqlite3LogEstToInt(pLoop->nOut));
- }else{
- sqlite3StrAccumAppend(&str, " (~1 row)", 9);
- }
+ sqlite3XPrintf(&str, 0, " (~%llu rows)", nEstRow);
#endif
- zMsg = sqlite3StrAccumFinish(&str);
- sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg, P4_DYNAMIC);
+ pExplain = (ExplainArg*)sqlite3StrAccumFinish(&str);
+ assert( pExplain || db->mallocFailed );
+ if( pExplain ){
+ memset(pExplain, 0, sizeof(*pExplain));
+ if( pLoop->wsFlags & WHERE_INDEXED ){
+ pExplain->iCsr = pLevel->iIdxCur;
+ }else if( pItem->pSelect==0 ){
+ pExplain->iCsr = pLevel->iTabCur;
+ }else{
+ pExplain->iCsr = -1;
+ }
+ pExplain->nEst = nEstRow;
+ pExplain->zName = (const char*)&pExplain[1];
+ pExplain->zExplain = &pExplain->zName[sqlite3Strlen30(pExplain->zName)+1];
+ sqlite3VdbeAddOp4(v, OP_Explain,
+ iId, iLevel, iFrom, (char*)pExplain,P4_EXPLAIN
+ );
+ }
}
}
#else
--- /dev/null
+# 2014 November 1
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix scanstatus
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a, b);
+ CREATE TABLE t2(x, y);
+ INSERT INTO t1 VALUES(1, 2);
+ INSERT INTO t1 VALUES(3, 4);
+ INSERT INTO t2 VALUES('a', 'b');
+ INSERT INTO t2 VALUES('c', 'd');
+ INSERT INTO t2 VALUES('e', 'f');
+}
+
+proc do_scanstatus_test {tn res} {
+ set stmt [db_last_stmt_ptr db]
+ set idx 0
+ set ret [list]
+ while {1} {
+ set r [sqlite3_stmt_scanstatus $stmt $idx]
+ if {[llength $r]==0} break
+ lappend ret {*}$r
+ incr idx
+ }
+
+ uplevel [list do_test $tn [list set {} $ret] [list {*}$res]]
+}
+
+do_execsql_test 1.1 { SELECT count(*) FROM t1, t2; } 6
+do_scanstatus_test 1.2 {
+ nLoop 1 nVisit 2 nEst 1048576 zName t1 zExplain {SCAN TABLE t1}
+ nLoop 2 nVisit 6 nEst 1048576 zName t2 zExplain {SCAN TABLE t2}
+}
+
+do_execsql_test 1.3 {
+ ANALYZE;
+ SELECT count(*) FROM t1, t2;
+} 6
+do_scanstatus_test 1.4 {
+ nLoop 1 nVisit 2 nEst 2 zName t1 zExplain {SCAN TABLE t1}
+ nLoop 2 nVisit 6 nEst 3 zName t2 zExplain {SCAN TABLE t2}
+}
+
+do_execsql_test 1.5 {
+ ANALYZE;
+ SELECT count(*) FROM t1, t2 WHERE t2.rowid>1;
+} 4
+do_scanstatus_test 1.6 {
+ nLoop 1 nVisit 2 nEst 2 zName t2 zExplain
+ {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid>?)}
+ nLoop 2 nVisit 4 nEst 2 zName t1 zExplain {SCAN TABLE t1}
+}
+
+
+
+
+finish_test