Add the experimental sqlite3_stmt_scanstatus() API.
FossilOrigin-Name: 6a9bab34aeb6a01b612211a28c140de60a3e883c
This commit is contained in:
parent
0fb5daed34
commit
04489b6dce
34
manifest
34
manifest
@ -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
|
||||
|
@ -1 +1 @@
|
||||
67f0d469da28c023200239a1f3d0c6cef9ef0e45
|
||||
6a9bab34aeb6a01b612211a28c140de60a3e883c
|
@ -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
|
||||
|
@ -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);
|
||||
|
73
src/test1.c
73
src/test1.c
@ -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;
|
||||
|
44
src/vdbe.c
44
src/vdbe.c
@ -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 * * * * *
|
||||
**
|
||||
|
16
src/vdbe.h
16
src/vdbe.h
@ -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
|
||||
|
@ -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 */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
55
src/where.c
55
src/where.c
@ -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
69
test/scanstatus.test
Normal 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
|
Loading…
x
Reference in New Issue
Block a user