Add the experimental sqlite3_stmt_scanstatus() API.

FossilOrigin-Name: 6a9bab34aeb6a01b612211a28c140de60a3e883c
This commit is contained in:
dan 2014-10-31 20:11:32 +00:00
parent 0fb5daed34
commit 04489b6dce
12 changed files with 394 additions and 31 deletions

View File

@ -1,5 +1,5 @@
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
@ -229,15 +229,15 @@ F src/resolve.c 4965007d6497b6a4d7a6d98751cc39712885f952
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
@ -289,11 +289,11 @@ F src/update.c 3c4ecc282accf12d39edb8d524cf089645e55a13
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
@ -302,7 +302,7 @@ F src/vtab.c 2a30791bbd7926b589401bd09c3abb33de563793
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
@ -801,6 +801,7 @@ F test/savepoint4.test c8f8159ade6d2acd9128be61e1230f1c1edc6cc0
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
@ -1210,7 +1211,10 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
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

View File

@ -1 +1 @@
67f0d469da28c023200239a1f3d0c6cef9ef0e45
6a9bab34aeb6a01b612211a28c140de60a3e883c

View File

@ -7407,6 +7407,42 @@ int sqlite3_vtab_on_conflict(sqlite3 *);
#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

View File

@ -3641,6 +3641,45 @@ static int db_use_legacy_prepare_cmd(
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
/*
@ -3760,6 +3799,9 @@ static void init_all(Tcl_Interp *interp){
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);

View File

@ -2301,6 +2301,75 @@ static int test_stmt_status(
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
**
@ -6868,6 +6937,10 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "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;

View File

@ -167,6 +167,18 @@ int sqlite3_found_count = 0;
/* 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.
@ -3556,6 +3568,7 @@ case OP_SeekGT: { /* jump, in3 */
#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
@ -3664,6 +3677,8 @@ case OP_SeekGT: { /* jump, in3 */
VdbeBranchTaken(res!=0,2);
if( res ){
pc = pOp->p2 - 1;
}else{
IncrementExplainCounter(pC, nVisit);
}
break;
}
@ -4449,6 +4464,8 @@ case OP_Last: { /* jump */
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;
@ -4488,9 +4505,9 @@ case OP_Sort: { /* jump */
**
** 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
@ -4518,11 +4535,14 @@ case OP_Rewind: { /* jump */
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;
}
@ -4633,6 +4653,7 @@ next_tail:
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]++;
@ -6055,7 +6076,10 @@ case OP_VFilter: { /* jump */
VdbeBranchTaken(res!=0,2);
if( res ){
pc = pOp->p2 - 1;
}else{
IncrementExplainCounter(pCur, nVisit);
}
IncrementExplainCounter(pCur, nLoop);
}
pCur->nullRow = 0;
@ -6148,6 +6172,7 @@ case OP_VNext: { /* jump */
if( !res ){
/* If there is data, jump to P2 */
pc = pOp->p2 - 1;
IncrementExplainCounter(pCur, nVisit);
}
goto check_for_interrupt;
}
@ -6347,6 +6372,19 @@ case OP_Init: { /* jump */
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 * * * * *
**

View File

@ -25,6 +25,7 @@
** of this structure.
*/
typedef struct Vdbe Vdbe;
typedef struct ExplainArg ExplainArg;
/*
** The names of the following types declared in vdbeInt.h are required
@ -59,6 +60,7 @@ struct VdbeOp {
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
@ -100,6 +102,19 @@ struct VdbeOpList {
};
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
*/
@ -119,6 +134,7 @@ typedef struct VdbeOpList VdbeOpList;
#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

View File

@ -83,6 +83,7 @@ struct VdbeCursor {
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
@ -291,6 +292,7 @@ struct Explain {
char zBase[100]; /* Initial space */
};
/* A bitfield type for use inside of structures. Always follow with :N where
** N is the number of bits.
*/
@ -368,6 +370,8 @@ struct Vdbe {
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 */
};
/*

View File

@ -1475,3 +1475,40 @@ int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){
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;
}
}

View File

@ -672,6 +672,7 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){
if( p4 ){
assert( db );
switch( p4type ){
case P4_EXPLAIN:
case P4_REAL:
case P4_INT64:
case P4_DYNAMIC:
@ -820,6 +821,13 @@ void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){
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;
@ -1103,6 +1111,10 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
zTemp[0] = 0;
break;
}
case P4_EXPLAIN: {
zP4 = (char*)pOp->p4.pExplain->zExplain;
break;
}
default: {
zP4 = pOp->p4.z;
if( zP4==0 ){
@ -2685,6 +2697,7 @@ void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){
sqlite3DbFree(db, p->aColName);
sqlite3DbFree(db, p->zSql);
sqlite3DbFree(db, p->pFree);
sqlite3DbFree(db, p->apExplain);
}
/*

View File

@ -2820,7 +2820,7 @@ static void explainOneScan(
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
{
@ -2831,20 +2831,38 @@ static void explainOneScan(
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);
@ -2901,15 +2919,28 @@ static void explainOneScan(
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

69
test/scanstatus.test Normal file
View File

@ -0,0 +1,69 @@
# 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