If SQLITE_ENABLE_STMT_SCANSTATUS is defined, record the number of times each VDBE opcode is executed. Derive the values returned by sqlite3_stmt_scanstatus() from these records on demand.
FossilOrigin-Name: 9ea37422a8cc2fce51bb10508e5e90f40fd4b511
This commit is contained in:
parent
89e71646df
commit
6f9702ed4d
26
manifest
26
manifest
@ -1,5 +1,5 @@
|
|||||||
C Minor\sfixes\sand\sdocumentation\simprovements\sfor\ssqlite3_stmt_scanstatus().
|
C If\sSQLITE_ENABLE_STMT_SCANSTATUS\sis\sdefined,\srecord\sthe\snumber\sof\stimes\seach\sVDBE\sopcode\sis\sexecuted.\sDerive\sthe\svalues\sreturned\sby\ssqlite3_stmt_scanstatus()\sfrom\sthese\srecords\son\sdemand.
|
||||||
D 2014-11-01T18:08:04.130
|
D 2014-11-01T20:38:06.833
|
||||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||||
F Makefile.in cf57f673d77606ab0f2d9627ca52a9ba1464146a
|
F Makefile.in cf57f673d77606ab0f2d9627ca52a9ba1464146a
|
||||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||||
@ -289,11 +289,11 @@ F src/update.c 3c4ecc282accf12d39edb8d524cf089645e55a13
|
|||||||
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
|
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
|
||||||
F src/util.c 3b627daa45c7308c1e36e3dbaa3f9ce7e5c7fa73
|
F src/util.c 3b627daa45c7308c1e36e3dbaa3f9ce7e5c7fa73
|
||||||
F src/vacuum.c 59f03f92bcff57faa6a8ca256eb29ccddfb0614a
|
F src/vacuum.c 59f03f92bcff57faa6a8ca256eb29ccddfb0614a
|
||||||
F src/vdbe.c 5240bad20ce0b707afee211f0c21a862b54b1e0b
|
F src/vdbe.c 69d025732d242d7c97282e6570a4e5eb768ebaff
|
||||||
F src/vdbe.h f8e5388173dbf8408da4ae1dcbf75911caf2253d
|
F src/vdbe.h 7d603b93d128e614ba2600f12a6c541435405522
|
||||||
F src/vdbeInt.h 284b2294c188474daa9b5ce2bd0200560cb0940d
|
F src/vdbeInt.h ee8d44cba5998279039bcd91ebddb6d9a2d463b8
|
||||||
F src/vdbeapi.c 2afa2e162f290879ca9c51e8795a377035f1662a
|
F src/vdbeapi.c 19e433e69fe2b27bfc9337a207b6ebf499f41d03
|
||||||
F src/vdbeaux.c 1e2561eeb94749f6b8c0a1eb02b5fecb645ed48c
|
F src/vdbeaux.c bad342af7cadb8d3bf2990700a78787d602dcd69
|
||||||
F src/vdbeblob.c 8b5442ff0954c44b45cbabbe2e94091a2e16fdef
|
F src/vdbeblob.c 8b5442ff0954c44b45cbabbe2e94091a2e16fdef
|
||||||
F src/vdbemem.c 31d8eabb0cd78bfeab4e5124c7363c3e9e54db9f
|
F src/vdbemem.c 31d8eabb0cd78bfeab4e5124c7363c3e9e54db9f
|
||||||
F src/vdbesort.c 975aeffa99acb0991b2f288d30294756bff41438
|
F src/vdbesort.c 975aeffa99acb0991b2f288d30294756bff41438
|
||||||
@ -302,8 +302,8 @@ F src/vtab.c 2a30791bbd7926b589401bd09c3abb33de563793
|
|||||||
F src/wal.c 10e7de7ce90865a68153f001a61f1d985cd17983
|
F src/wal.c 10e7de7ce90865a68153f001a61f1d985cd17983
|
||||||
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
|
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
|
||||||
F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804
|
F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804
|
||||||
F src/where.c 636ca646c8f2f53fd66f9ed5c773e84cb0c4b762
|
F src/where.c fb404e3db40db61c212ffb39022e885a91252498
|
||||||
F src/whereInt.h 99d324a8f921d7a40c605a8b197350c3cb18977d
|
F src/whereInt.h a2bc22f4e3e70eeaa57272f354c288bc3b71b80b
|
||||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||||
F test/aggnested.test b35b4cd69fc913f90d39a575e171e1116c3a4bb7
|
F test/aggnested.test b35b4cd69fc913f90d39a575e171e1116c3a4bb7
|
||||||
@ -801,7 +801,7 @@ F test/savepoint4.test c8f8159ade6d2acd9128be61e1230f1c1edc6cc0
|
|||||||
F test/savepoint5.test 0735db177e0ebbaedc39812c8d065075d563c4fd
|
F test/savepoint5.test 0735db177e0ebbaedc39812c8d065075d563c4fd
|
||||||
F test/savepoint6.test f41279c5e137139fa5c21485773332c7adb98cd7
|
F test/savepoint6.test f41279c5e137139fa5c21485773332c7adb98cd7
|
||||||
F test/savepoint7.test fbf319a7b2dda089ec5be30a424a0e95f121d423
|
F test/savepoint7.test fbf319a7b2dda089ec5be30a424a0e95f121d423
|
||||||
F test/scanstatus.test b4b1780bad243e1576329d05597b99f06886e4d2
|
F test/scanstatus.test 40c7712c8bc0adc3fb88e0419356880679a3a5fb
|
||||||
F test/schema.test 8f7999be894260f151adf15c2c7540f1c6d6a481
|
F test/schema.test 8f7999be894260f151adf15c2c7540f1c6d6a481
|
||||||
F test/schema2.test 906408621ea881fdb496d878b1822572a34e32c5
|
F test/schema2.test 906408621ea881fdb496d878b1822572a34e32c5
|
||||||
F test/schema3.test 1bc1008e1f8cb5654b248c55f27249366eb7ed38
|
F test/schema3.test 1bc1008e1f8cb5654b248c55f27249366eb7ed38
|
||||||
@ -1211,7 +1211,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
|||||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||||
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
|
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
|
||||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||||
P 6a9bab34aeb6a01b612211a28c140de60a3e883c
|
P 8d8cc9608d30bb65fffcfe488e904411cbbc7f41
|
||||||
R d964f269915d57cf95d9198c6ba22665
|
R e1e809461d373caeeb0e83f126874426
|
||||||
U dan
|
U dan
|
||||||
Z 082decdd6298ff1a2330407f8d406dfb
|
Z 5d5debb04affeaeb7378f6b538c8f641
|
||||||
|
@ -1 +1 @@
|
|||||||
8d8cc9608d30bb65fffcfe488e904411cbbc7f41
|
9ea37422a8cc2fce51bb10508e5e90f40fd4b511
|
44
src/vdbe.c
44
src/vdbe.c
@ -167,18 +167,6 @@ int sqlite3_found_count = 0;
|
|||||||
/* Return true if the cursor was opened using the OP_OpenSorter opcode. */
|
/* Return true if the cursor was opened using the OP_OpenSorter opcode. */
|
||||||
#define isSorter(x) ((x)->pSorter!=0)
|
#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
|
** Allocate VdbeCursor number iCur. Return a pointer to it. Return NULL
|
||||||
** if we run out of memory.
|
** if we run out of memory.
|
||||||
@ -620,6 +608,9 @@ int sqlite3VdbeExec(
|
|||||||
#endif
|
#endif
|
||||||
nVmStep++;
|
nVmStep++;
|
||||||
pOp = &aOp[pc];
|
pOp = &aOp[pc];
|
||||||
|
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||||
|
if( p->pFrame==0 ) p->anExec[pc]++;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Only allow tracing if SQLITE_DEBUG is defined.
|
/* Only allow tracing if SQLITE_DEBUG is defined.
|
||||||
*/
|
*/
|
||||||
@ -3568,7 +3559,6 @@ case OP_SeekGT: { /* jump, in3 */
|
|||||||
#ifdef SQLITE_DEBUG
|
#ifdef SQLITE_DEBUG
|
||||||
pC->seekOp = pOp->opcode;
|
pC->seekOp = pOp->opcode;
|
||||||
#endif
|
#endif
|
||||||
IncrementExplainCounter(pC, nLoop);
|
|
||||||
if( pC->isTable ){
|
if( pC->isTable ){
|
||||||
/* The input value in P3 might be of any type: integer, real, string,
|
/* 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
|
** blob, or NULL. But it needs to be an integer before we can do
|
||||||
@ -3677,8 +3667,6 @@ case OP_SeekGT: { /* jump, in3 */
|
|||||||
VdbeBranchTaken(res!=0,2);
|
VdbeBranchTaken(res!=0,2);
|
||||||
if( res ){
|
if( res ){
|
||||||
pc = pOp->p2 - 1;
|
pc = pOp->p2 - 1;
|
||||||
}else{
|
|
||||||
IncrementExplainCounter(pC, nVisit);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -3889,7 +3877,6 @@ case OP_NotExists: { /* jump, in3 */
|
|||||||
res = 0;
|
res = 0;
|
||||||
iKey = pIn3->u.i;
|
iKey = pIn3->u.i;
|
||||||
rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res);
|
rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res);
|
||||||
IncrementExplainCounter(pC, nLoop);
|
|
||||||
pC->movetoTarget = iKey; /* Used by OP_Delete */
|
pC->movetoTarget = iKey; /* Used by OP_Delete */
|
||||||
pC->nullRow = 0;
|
pC->nullRow = 0;
|
||||||
pC->cacheStatus = CACHE_STALE;
|
pC->cacheStatus = CACHE_STALE;
|
||||||
@ -3897,8 +3884,6 @@ case OP_NotExists: { /* jump, in3 */
|
|||||||
VdbeBranchTaken(res!=0,2);
|
VdbeBranchTaken(res!=0,2);
|
||||||
if( res!=0 ){
|
if( res!=0 ){
|
||||||
pc = pOp->p2 - 1;
|
pc = pOp->p2 - 1;
|
||||||
}else{
|
|
||||||
IncrementExplainCounter(pC, nVisit);
|
|
||||||
}
|
}
|
||||||
pC->seekResult = res;
|
pC->seekResult = res;
|
||||||
break;
|
break;
|
||||||
@ -4467,8 +4452,6 @@ case OP_Last: { /* jump */
|
|||||||
res = 0;
|
res = 0;
|
||||||
assert( pCrsr!=0 );
|
assert( pCrsr!=0 );
|
||||||
rc = sqlite3BtreeLast(pCrsr, &res);
|
rc = sqlite3BtreeLast(pCrsr, &res);
|
||||||
IncrementExplainCounter(pC, nLoop);
|
|
||||||
if( res==0 ) IncrementExplainCounter(pC, nVisit);
|
|
||||||
pC->nullRow = (u8)res;
|
pC->nullRow = (u8)res;
|
||||||
pC->deferredMoveto = 0;
|
pC->deferredMoveto = 0;
|
||||||
pC->cacheStatus = CACHE_STALE;
|
pC->cacheStatus = CACHE_STALE;
|
||||||
@ -4538,14 +4521,11 @@ case OP_Rewind: { /* jump */
|
|||||||
pC->deferredMoveto = 0;
|
pC->deferredMoveto = 0;
|
||||||
pC->cacheStatus = CACHE_STALE;
|
pC->cacheStatus = CACHE_STALE;
|
||||||
}
|
}
|
||||||
IncrementExplainCounter(pC, nLoop);
|
|
||||||
pC->nullRow = (u8)res;
|
pC->nullRow = (u8)res;
|
||||||
assert( pOp->p2>0 && pOp->p2<p->nOp );
|
assert( pOp->p2>0 && pOp->p2<p->nOp );
|
||||||
VdbeBranchTaken(res!=0,2);
|
VdbeBranchTaken(res!=0,2);
|
||||||
if( res ){
|
if( res ){
|
||||||
pc = pOp->p2 - 1;
|
pc = pOp->p2 - 1;
|
||||||
}else{
|
|
||||||
IncrementExplainCounter(pC, nVisit);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -4656,7 +4636,6 @@ next_tail:
|
|||||||
pC->cacheStatus = CACHE_STALE;
|
pC->cacheStatus = CACHE_STALE;
|
||||||
VdbeBranchTaken(res==0,2);
|
VdbeBranchTaken(res==0,2);
|
||||||
if( res==0 ){
|
if( res==0 ){
|
||||||
IncrementExplainCounter(pC, nVisit);
|
|
||||||
pC->nullRow = 0;
|
pC->nullRow = 0;
|
||||||
pc = pOp->p2 - 1;
|
pc = pOp->p2 - 1;
|
||||||
p->aCounter[pOp->p5]++;
|
p->aCounter[pOp->p5]++;
|
||||||
@ -6079,10 +6058,7 @@ case OP_VFilter: { /* jump */
|
|||||||
VdbeBranchTaken(res!=0,2);
|
VdbeBranchTaken(res!=0,2);
|
||||||
if( res ){
|
if( res ){
|
||||||
pc = pOp->p2 - 1;
|
pc = pOp->p2 - 1;
|
||||||
}else{
|
|
||||||
IncrementExplainCounter(pCur, nVisit);
|
|
||||||
}
|
}
|
||||||
IncrementExplainCounter(pCur, nLoop);
|
|
||||||
}
|
}
|
||||||
pCur->nullRow = 0;
|
pCur->nullRow = 0;
|
||||||
|
|
||||||
@ -6175,7 +6151,6 @@ case OP_VNext: { /* jump */
|
|||||||
if( !res ){
|
if( !res ){
|
||||||
/* If there is data, jump to P2 */
|
/* If there is data, jump to P2 */
|
||||||
pc = pOp->p2 - 1;
|
pc = pOp->p2 - 1;
|
||||||
IncrementExplainCounter(pCur, nVisit);
|
|
||||||
}
|
}
|
||||||
goto check_for_interrupt;
|
goto check_for_interrupt;
|
||||||
}
|
}
|
||||||
@ -6375,19 +6350,6 @@ case OP_Init: { /* jump */
|
|||||||
break;
|
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 * * * * *
|
/* Opcode: Noop * * * * *
|
||||||
**
|
**
|
||||||
|
22
src/vdbe.h
22
src/vdbe.h
@ -25,7 +25,6 @@
|
|||||||
** of this structure.
|
** of this structure.
|
||||||
*/
|
*/
|
||||||
typedef struct Vdbe Vdbe;
|
typedef struct Vdbe Vdbe;
|
||||||
typedef struct ExplainArg ExplainArg;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** The names of the following types declared in vdbeInt.h are required
|
** The names of the following types declared in vdbeInt.h are required
|
||||||
@ -60,7 +59,6 @@ struct VdbeOp {
|
|||||||
KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */
|
KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */
|
||||||
int *ai; /* Used when p4type is P4_INTARRAY */
|
int *ai; /* Used when p4type is P4_INTARRAY */
|
||||||
SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */
|
SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */
|
||||||
ExplainArg *pExplain; /* Used when p4type is P4_EXPLAIN */
|
|
||||||
int (*xAdvance)(BtCursor *, int *);
|
int (*xAdvance)(BtCursor *, int *);
|
||||||
} p4;
|
} p4;
|
||||||
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
||||||
@ -102,19 +100,6 @@ struct VdbeOpList {
|
|||||||
};
|
};
|
||||||
typedef struct VdbeOpList 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
|
** Allowed values of VdbeOp.p4type
|
||||||
*/
|
*/
|
||||||
@ -134,7 +119,6 @@ struct ExplainArg {
|
|||||||
#define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */
|
#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_SUBPROGRAM (-18) /* P4 is a pointer to a SubProgram structure */
|
||||||
#define P4_ADVANCE (-19) /* P4 is a pointer to BtreeNext() or BtreePrev() */
|
#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 */
|
/* Error message codes for OP_Halt */
|
||||||
#define P5_ConstraintNotNull 1
|
#define P5_ConstraintNotNull 1
|
||||||
@ -298,4 +282,10 @@ void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *);
|
|||||||
# define VDBE_OFFSET_LINENO(x) 0
|
# define VDBE_OFFSET_LINENO(x) 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||||
|
void sqlite3VdbeScanCounter(Vdbe*, int, int, int, i64, const char*);
|
||||||
|
#else
|
||||||
|
# define sqlite3VdbeScanCounter(a,b,c,d,e)
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -83,7 +83,6 @@ struct VdbeCursor {
|
|||||||
i64 seqCount; /* Sequence counter */
|
i64 seqCount; /* Sequence counter */
|
||||||
i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */
|
i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */
|
||||||
VdbeSorter *pSorter; /* Sorter object for OP_SorterOpen cursors */
|
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
|
/* Cached information about the header for the data record that the
|
||||||
** cursor is currently pointing to. Only valid if cacheStatus matches
|
** cursor is currently pointing to. Only valid if cacheStatus matches
|
||||||
@ -292,12 +291,20 @@ struct Explain {
|
|||||||
char zBase[100]; /* Initial space */
|
char zBase[100]; /* Initial space */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/* A bitfield type for use inside of structures. Always follow with :N where
|
/* A bitfield type for use inside of structures. Always follow with :N where
|
||||||
** N is the number of bits.
|
** N is the number of bits.
|
||||||
*/
|
*/
|
||||||
typedef unsigned bft; /* Bit Field Type */
|
typedef unsigned bft; /* Bit Field Type */
|
||||||
|
|
||||||
|
typedef struct ScanCounter ScanCounter;
|
||||||
|
struct ScanCounter {
|
||||||
|
int addrExplain; /* OP_Explain for loop */
|
||||||
|
int addrLoop; /* Address of "loops" counter */
|
||||||
|
int addrVisit; /* Address of "rows visited" counter */
|
||||||
|
i64 nEst; /* Estimated rows per loop */
|
||||||
|
char *zName; /* Name of table or index */
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** An instance of the virtual machine. This structure contains the complete
|
** An instance of the virtual machine. This structure contains the complete
|
||||||
** state of the virtual machine.
|
** state of the virtual machine.
|
||||||
@ -370,8 +377,11 @@ struct Vdbe {
|
|||||||
int nOnceFlag; /* Size of array aOnceFlag[] */
|
int nOnceFlag; /* Size of array aOnceFlag[] */
|
||||||
u8 *aOnceFlag; /* Flags for OP_Once */
|
u8 *aOnceFlag; /* Flags for OP_Once */
|
||||||
AuxData *pAuxData; /* Linked list of auxdata allocations */
|
AuxData *pAuxData; /* Linked list of auxdata allocations */
|
||||||
ExplainArg **apExplain; /* Array of pointers to P4_EXPLAIN p4 values */
|
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||||
int nExplain; /* Number of entries in array apExplain */
|
i64 *anExec; /* Number of times each op has been executed */
|
||||||
|
int nScan; /* Entries in aScan[] */
|
||||||
|
ScanCounter *aScan; /* Scan definitions for sqlite3_stmt_scanstatus() */
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1476,6 +1476,7 @@ int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){
|
|||||||
return (int)v;
|
return (int)v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||||
/*
|
/*
|
||||||
** Return status data for a single loop within query pStmt.
|
** Return status data for a single loop within query pStmt.
|
||||||
*/
|
*/
|
||||||
@ -1489,14 +1490,20 @@ int sqlite3_stmt_scanstatus(
|
|||||||
const char **pzExplain /* OUT: EQP string */
|
const char **pzExplain /* OUT: EQP string */
|
||||||
){
|
){
|
||||||
Vdbe *p = (Vdbe*)pStmt;
|
Vdbe *p = (Vdbe*)pStmt;
|
||||||
ExplainArg *pExplain;
|
ScanCounter *pScan;
|
||||||
if( idx<0 || idx>=p->nExplain ) return 1;
|
if( idx<0 || idx>=p->nScan ) return 1;
|
||||||
pExplain = p->apExplain[idx];
|
pScan = &p->aScan[idx];
|
||||||
if( pnLoop ) *pnLoop = pExplain->nLoop;
|
if( pnLoop ) *pnLoop = p->anExec[pScan->addrLoop];
|
||||||
if( pnVisit ) *pnVisit = pExplain->nVisit;
|
if( pnVisit ) *pnVisit = p->anExec[pScan->addrVisit];
|
||||||
if( pnEst ) *pnEst = pExplain->nEst;
|
if( pnEst ) *pnEst = pScan->nEst;
|
||||||
if( *pzName ) *pzName = pExplain->zName;
|
if( *pzName ) *pzName = pScan->zName;
|
||||||
if( *pzExplain ) *pzExplain = pExplain->zExplain;
|
if( *pzExplain ){
|
||||||
|
if( pScan->addrExplain ){
|
||||||
|
*pzExplain = p->aOp[ pScan->addrExplain ].p4.z;
|
||||||
|
}else{
|
||||||
|
*pzExplain = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1505,10 +1512,7 @@ int sqlite3_stmt_scanstatus(
|
|||||||
*/
|
*/
|
||||||
void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){
|
void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){
|
||||||
Vdbe *p = (Vdbe*)pStmt;
|
Vdbe *p = (Vdbe*)pStmt;
|
||||||
int i;
|
memset(p->anExec, 0, p->nOp * sizeof(i64));
|
||||||
for(i=0; i<p->nExplain; i++){
|
|
||||||
p->apExplain[i]->nLoop = 0;
|
|
||||||
p->apExplain[i]->nVisit = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
#endif /* SQLITE_ENABLE_STMT_SCANSTATUS */
|
||||||
|
|
||||||
|
@ -597,6 +597,34 @@ int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp, int iLineno){
|
|||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(SQLITE_ENABLE_STMT_SCANSTATUS)
|
||||||
|
/*
|
||||||
|
** Add an entry to the array of counters managed by sqlite3_stmt_scanstatus().
|
||||||
|
*/
|
||||||
|
void sqlite3VdbeScanCounter(
|
||||||
|
Vdbe *p, /* VM to add scanstatus() to */
|
||||||
|
int addrExplain, /* Address of OP_Explain (or 0) */
|
||||||
|
int addrLoop, /* Address of loop counter */
|
||||||
|
int addrVisit, /* Address of rows visited counter */
|
||||||
|
i64 nEst, /* Estimated number of rows */
|
||||||
|
const char *zName /* Name of table or index being scanned */
|
||||||
|
){
|
||||||
|
int nByte = (p->nScan+1) * sizeof(ScanCounter);
|
||||||
|
ScanCounter *aNew;
|
||||||
|
aNew = (ScanCounter*)sqlite3DbRealloc(p->db, p->aScan, nByte);
|
||||||
|
if( aNew ){
|
||||||
|
ScanCounter *pNew = &aNew[p->nScan++];
|
||||||
|
pNew->addrExplain = addrExplain;
|
||||||
|
pNew->addrLoop = addrLoop;
|
||||||
|
pNew->addrVisit = addrVisit;
|
||||||
|
pNew->nEst = nEst;
|
||||||
|
pNew->zName = sqlite3DbStrDup(p->db, zName);
|
||||||
|
p->aScan = aNew;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Change the value of the P1 operand for a specific instruction.
|
** Change the value of the P1 operand for a specific instruction.
|
||||||
** This routine is useful when a large program is loaded from a
|
** This routine is useful when a large program is loaded from a
|
||||||
@ -672,7 +700,6 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){
|
|||||||
if( p4 ){
|
if( p4 ){
|
||||||
assert( db );
|
assert( db );
|
||||||
switch( p4type ){
|
switch( p4type ){
|
||||||
case P4_EXPLAIN:
|
|
||||||
case P4_REAL:
|
case P4_REAL:
|
||||||
case P4_INT64:
|
case P4_INT64:
|
||||||
case P4_DYNAMIC:
|
case P4_DYNAMIC:
|
||||||
@ -821,13 +848,6 @@ void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){
|
|||||||
pOp->p4type = P4_VTAB;
|
pOp->p4type = P4_VTAB;
|
||||||
sqlite3VtabLock((VTable *)zP4);
|
sqlite3VtabLock((VTable *)zP4);
|
||||||
assert( ((VTable *)zP4)->db==p->db );
|
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 ){
|
}else if( n<0 ){
|
||||||
pOp->p4.p = (void*)zP4;
|
pOp->p4.p = (void*)zP4;
|
||||||
pOp->p4type = (signed char)n;
|
pOp->p4type = (signed char)n;
|
||||||
@ -1111,10 +1131,6 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
|
|||||||
zTemp[0] = 0;
|
zTemp[0] = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case P4_EXPLAIN: {
|
|
||||||
zP4 = (char*)pOp->p4.pExplain->zExplain;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
default: {
|
||||||
zP4 = pOp->p4.z;
|
zP4 = pOp->p4.z;
|
||||||
if( zP4==0 ){
|
if( zP4==0 ){
|
||||||
@ -1714,6 +1730,10 @@ void sqlite3VdbeMakeReady(
|
|||||||
zEnd = &zCsr[nByte];
|
zEnd = &zCsr[nByte];
|
||||||
}while( nByte && !db->mallocFailed );
|
}while( nByte && !db->mallocFailed );
|
||||||
|
|
||||||
|
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||||
|
p->anExec = (i64*)sqlite3DbMallocZero(db, p->nOp*sizeof(i64));
|
||||||
|
#endif
|
||||||
|
|
||||||
p->nCursor = nCursor;
|
p->nCursor = nCursor;
|
||||||
p->nOnceFlag = nOnce;
|
p->nOnceFlag = nOnce;
|
||||||
if( p->aVar ){
|
if( p->aVar ){
|
||||||
@ -2697,7 +2717,13 @@ void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){
|
|||||||
sqlite3DbFree(db, p->aColName);
|
sqlite3DbFree(db, p->aColName);
|
||||||
sqlite3DbFree(db, p->zSql);
|
sqlite3DbFree(db, p->zSql);
|
||||||
sqlite3DbFree(db, p->pFree);
|
sqlite3DbFree(db, p->pFree);
|
||||||
sqlite3DbFree(db, p->apExplain);
|
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||||
|
sqlite3DbFree(db, p->anExec);
|
||||||
|
for(i=0; i<p->nScan; i++){
|
||||||
|
sqlite3DbFree(db, p->aScan[i].zName);
|
||||||
|
}
|
||||||
|
sqlite3DbFree(db, p->aScan);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
130
src/where.c
130
src/where.c
@ -2739,7 +2739,7 @@ static int codeAllEqualityTerms(
|
|||||||
return regBase;
|
return regBase;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(SQLITE_OMIT_EXPLAIN) || defined(SQLITE_ENABLE_STMT_SCANSTATUS)
|
#ifndef SQLITE_OMIT_EXPLAIN
|
||||||
/*
|
/*
|
||||||
** This routine is a helper for explainIndexRange() below
|
** This routine is a helper for explainIndexRange() below
|
||||||
**
|
**
|
||||||
@ -2812,68 +2812,43 @@ static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop, Table *pTab){
|
|||||||
** record is added to the output to describe the table scan strategy in
|
** record is added to the output to describe the table scan strategy in
|
||||||
** pLevel.
|
** pLevel.
|
||||||
*/
|
*/
|
||||||
static void explainOneScan(
|
static int explainOneScan(
|
||||||
Parse *pParse, /* Parse context */
|
Parse *pParse, /* Parse context */
|
||||||
SrcList *pTabList, /* Table list this loop refers to */
|
SrcList *pTabList, /* Table list this loop refers to */
|
||||||
WhereInfo *pWInfo, /* WHERE clause this loop belongs to */
|
WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
|
||||||
int iLevel, /* Value for "level" column of output */
|
int iLevel, /* Value for "level" column of output */
|
||||||
|
int iFrom, /* Value for "from" column of output */
|
||||||
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
|
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
|
||||||
){
|
){
|
||||||
#if !defined(SQLITE_DEBUG) && !defined(SQLITE_ENABLE_STMT_SCANSTATUS)
|
int ret = 0;
|
||||||
|
#ifndef SQLITE_DEBUG
|
||||||
if( pParse->explain==2 )
|
if( pParse->explain==2 )
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
WhereLevel *pLevel = &pWInfo->a[iLevel];
|
|
||||||
struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
|
struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
|
||||||
Vdbe *v = pParse->pVdbe; /* VM being constructed */
|
Vdbe *v = pParse->pVdbe; /* VM being constructed */
|
||||||
sqlite3 *db = pParse->db; /* Database handle */
|
sqlite3 *db = pParse->db; /* Database handle */
|
||||||
#ifdef SQLITE_OMIT_EXPLAIN
|
|
||||||
int iId = 0; /* Select id (left-most output column) */
|
|
||||||
#else
|
|
||||||
int iId = pParse->iSelectId; /* Select id (left-most output column) */
|
int iId = pParse->iSelectId; /* Select id (left-most output column) */
|
||||||
#endif
|
|
||||||
int isSearch; /* True for a SEARCH. False for SCAN. */
|
int isSearch; /* True for a SEARCH. False for SCAN. */
|
||||||
WhereLoop *pLoop; /* The controlling WhereLoop object */
|
WhereLoop *pLoop; /* The controlling WhereLoop object */
|
||||||
u32 flags; /* Flags that describe this loop */
|
u32 flags; /* Flags that describe this loop */
|
||||||
|
char *zMsg; /* Text to add to EQP output */
|
||||||
StrAccum str; /* EQP output string */
|
StrAccum str; /* EQP output string */
|
||||||
char zBuf[100]; /* Initial space for 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;
|
pLoop = pLevel->pWLoop;
|
||||||
flags = pLoop->wsFlags;
|
flags = pLoop->wsFlags;
|
||||||
if( (flags&WHERE_MULTI_OR) ) return;
|
if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_ONETABLE_ONLY) ) return 0;
|
||||||
|
|
||||||
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
|
isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0
|
||||||
|| ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0))
|
|| ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0))
|
||||||
|| (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));
|
|| (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));
|
||||||
|
|
||||||
|
sqlite3StrAccumInit(&str, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
|
||||||
|
str.db = db;
|
||||||
sqlite3StrAccumAppendAll(&str, isSearch ? "SEARCH" : "SCAN");
|
sqlite3StrAccumAppendAll(&str, isSearch ? "SEARCH" : "SCAN");
|
||||||
if( pItem->pSelect ){
|
if( pItem->pSelect ){
|
||||||
#ifdef SQLITE_OMIT_EXPLAIN
|
|
||||||
sqlite3XPrintf(&str, 0, " SUBQUERY 0");
|
|
||||||
#else
|
|
||||||
sqlite3XPrintf(&str, 0, " SUBQUERY %d", pItem->iSelectId);
|
sqlite3XPrintf(&str, 0, " SUBQUERY %d", pItem->iSelectId);
|
||||||
#endif
|
|
||||||
}else{
|
}else{
|
||||||
sqlite3XPrintf(&str, 0, " TABLE %s", pItem->zName);
|
sqlite3XPrintf(&str, 0, " TABLE %s", pItem->zName);
|
||||||
}
|
}
|
||||||
@ -2927,33 +2902,48 @@ static void explainOneScan(
|
|||||||
pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr);
|
pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
nEstRow = pLoop->nOut>=10 ? sqlite3LogEstToInt(pLoop->nOut) : 1;
|
|
||||||
#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS
|
#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS
|
||||||
sqlite3XPrintf(&str, 0, " (~%llu rows)", nEstRow);
|
if( pLoop->nOut>=10 ){
|
||||||
#endif
|
sqlite3XPrintf(&str, 0, " (~%llu rows)", sqlite3LogEstToInt(pLoop->nOut));
|
||||||
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{
|
}else{
|
||||||
pExplain->iCsr = -1;
|
sqlite3StrAccumAppend(&str, " (~1 row)", 9);
|
||||||
}
|
|
||||||
pExplain->nEst = nEstRow;
|
|
||||||
pExplain->zName = (const char*)&pExplain[1];
|
|
||||||
pExplain->zExplain = &pExplain->zName[sqlite3Strlen30(pExplain->zName)+1];
|
|
||||||
pWInfo->iExplain = sqlite3VdbeAddOp4(v, OP_Explain,
|
|
||||||
iId, iLevel, pLevel->iFrom, (char*)pExplain,P4_EXPLAIN
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
zMsg = sqlite3StrAccumFinish(&str);
|
||||||
|
ret = sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg,P4_DYNAMIC);
|
||||||
}
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
# define explainOneScan(v,w,x,y,z)
|
# define explainOneScan(u,v,w,x,y,z) 0
|
||||||
#endif /* !SQLITE_OMIT_EXPLAIN || SQLITE_ENABLE_STMT_SCANSTATUS */
|
#endif /* SQLITE_OMIT_EXPLAIN */
|
||||||
|
|
||||||
|
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||||
|
static void addScanStatus(
|
||||||
|
Vdbe *v,
|
||||||
|
SrcList *pSrclist,
|
||||||
|
WhereLevel *pLvl,
|
||||||
|
int addrExplain
|
||||||
|
){
|
||||||
|
const char *zObj = 0;
|
||||||
|
i64 nEst = 1;
|
||||||
|
WhereLoop *pLoop = pLvl->pWLoop;
|
||||||
|
if( (pLoop->wsFlags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){
|
||||||
|
zObj = pLoop->u.btree.pIndex->zName;
|
||||||
|
}else{
|
||||||
|
zObj = pSrclist->a[pLvl->iFrom].zName;
|
||||||
|
}
|
||||||
|
if( pLoop->nOut>=10 ){
|
||||||
|
nEst = sqlite3LogEstToInt(pLoop->nOut);
|
||||||
|
}
|
||||||
|
sqlite3VdbeScanCounter(
|
||||||
|
v, addrExplain, pLvl->addrBody, pLvl->addrVisit, nEst, zObj
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
# define addScanStatus(a, b, c, d)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3621,14 +3611,10 @@ static Bitmask codeOneLoopStart(
|
|||||||
assert( pSubWInfo || pParse->nErr || db->mallocFailed );
|
assert( pSubWInfo || pParse->nErr || db->mallocFailed );
|
||||||
if( pSubWInfo ){
|
if( pSubWInfo ){
|
||||||
WhereLoop *pSubLoop;
|
WhereLoop *pSubLoop;
|
||||||
|
int addrExplain = explainOneScan(
|
||||||
/* If an OP_Explain was added for this sub-loop, fix the P2 and
|
pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0
|
||||||
** P3 parameters to it so that they are relative to the current
|
);
|
||||||
** context. */
|
addScanStatus(v, pOrTab, &pSubWInfo->a[0], addrExplain);
|
||||||
if( pSubWInfo->iExplain!=0 ){
|
|
||||||
sqlite3VdbeChangeP2(v, pSubWInfo->iExplain, iLevel);
|
|
||||||
sqlite3VdbeChangeP3(v, pSubWInfo->iExplain, pLevel->iFrom);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This is the sub-WHERE clause body. First skip over
|
/* This is the sub-WHERE clause body. First skip over
|
||||||
** duplicate rows from prior sub-WHERE clauses, and record the
|
** duplicate rows from prior sub-WHERE clauses, and record the
|
||||||
@ -3760,6 +3746,10 @@ static Bitmask codeOneLoopStart(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||||
|
pLevel->addrVisit = sqlite3VdbeCurrentAddr(v);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Insert code to test every subexpression that can be completely
|
/* Insert code to test every subexpression that can be completely
|
||||||
** computed using the current set of tables.
|
** computed using the current set of tables.
|
||||||
*/
|
*/
|
||||||
@ -6461,7 +6451,10 @@ WhereInfo *sqlite3WhereBegin(
|
|||||||
*/
|
*/
|
||||||
notReady = ~(Bitmask)0;
|
notReady = ~(Bitmask)0;
|
||||||
for(ii=0; ii<nTabList; ii++){
|
for(ii=0; ii<nTabList; ii++){
|
||||||
|
int addrExplain;
|
||||||
|
int wsFlags;
|
||||||
pLevel = &pWInfo->a[ii];
|
pLevel = &pWInfo->a[ii];
|
||||||
|
wsFlags = pLevel->pWLoop->wsFlags;
|
||||||
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
|
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
|
||||||
if( (pLevel->pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 ){
|
if( (pLevel->pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 ){
|
||||||
constructAutomaticIndex(pParse, &pWInfo->sWC,
|
constructAutomaticIndex(pParse, &pWInfo->sWC,
|
||||||
@ -6469,10 +6462,15 @@ WhereInfo *sqlite3WhereBegin(
|
|||||||
if( db->mallocFailed ) goto whereBeginError;
|
if( db->mallocFailed ) goto whereBeginError;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
explainOneScan(pParse, pTabList, pWInfo, ii, wctrlFlags);
|
addrExplain = explainOneScan(
|
||||||
|
pParse, pTabList, pLevel, ii, pLevel->iFrom, wctrlFlags
|
||||||
|
);
|
||||||
pLevel->addrBody = sqlite3VdbeCurrentAddr(v);
|
pLevel->addrBody = sqlite3VdbeCurrentAddr(v);
|
||||||
notReady = codeOneLoopStart(pWInfo, ii, notReady);
|
notReady = codeOneLoopStart(pWInfo, ii, notReady);
|
||||||
pWInfo->iContinue = pLevel->addrCont;
|
pWInfo->iContinue = pLevel->addrCont;
|
||||||
|
if( (wsFlags&WHERE_MULTI_OR)==0 && (wctrlFlags&WHERE_ONETABLE_ONLY)==0 ){
|
||||||
|
addScanStatus(v, pTabList, pLevel, addrExplain);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Done. */
|
/* Done. */
|
||||||
|
@ -85,6 +85,9 @@ struct WhereLevel {
|
|||||||
} u;
|
} u;
|
||||||
struct WhereLoop *pWLoop; /* The selected WhereLoop object */
|
struct WhereLoop *pWLoop; /* The selected WhereLoop object */
|
||||||
Bitmask notReady; /* FROM entries not usable at this level */
|
Bitmask notReady; /* FROM entries not usable at this level */
|
||||||
|
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||||
|
int addrVisit; /* Address at which row is visited */
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -115,7 +115,7 @@ do_execsql_test 2.4.1 {
|
|||||||
SELECT * FROM x1 WHERE j<'two'
|
SELECT * FROM x1 WHERE j<'two'
|
||||||
} {4 four 1 one 3 three}
|
} {4 four 1 one 3 three}
|
||||||
do_scanstatus_test 2.4.2 {
|
do_scanstatus_test 2.4.2 {
|
||||||
nLoop 1 nVisit 4 nEst 262144 zName x1j
|
nLoop 1 nVisit 3 nEst 262144 zName x1j
|
||||||
zExplain {SEARCH TABLE x1 USING COVERING INDEX x1j (j<?)}
|
zExplain {SEARCH TABLE x1 USING COVERING INDEX x1j (j<?)}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,7 +152,7 @@ do_execsql_test 2.8.1 {
|
|||||||
SELECT * FROM x2 WHERE i=1 AND j='two'
|
SELECT * FROM x2 WHERE i=1 AND j='two'
|
||||||
}
|
}
|
||||||
do_scanstatus_test 2.8.2 {
|
do_scanstatus_test 2.8.2 {
|
||||||
nLoop 1 nVisit 1 nEst 8 zName x2ij
|
nLoop 1 nVisit 0 nEst 8 zName x2ij
|
||||||
zExplain {SEARCH TABLE x2 USING INDEX x2ij (i=? AND j=?)}
|
zExplain {SEARCH TABLE x2 USING INDEX x2ij (i=? AND j=?)}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +168,7 @@ do_execsql_test 2.10.1 {
|
|||||||
SELECT * FROM x2 WHERE i=3 AND j='three'
|
SELECT * FROM x2 WHERE i=3 AND j='three'
|
||||||
} {3 three {3 three}}
|
} {3 three {3 three}}
|
||||||
do_scanstatus_test 2.10.2 {
|
do_scanstatus_test 2.10.2 {
|
||||||
nLoop 1 nVisit 2 nEst 8 zName x2ij
|
nLoop 1 nVisit 1 nEst 8 zName x2ij
|
||||||
zExplain {SEARCH TABLE x2 USING INDEX x2ij (i=? AND j=?)}
|
zExplain {SEARCH TABLE x2 USING INDEX x2ij (i=? AND j=?)}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,15 +187,48 @@ do_execsql_test 3.1 {
|
|||||||
do_execsql_test 3.2.1 {
|
do_execsql_test 3.2.1 {
|
||||||
SELECT d FROM a1 WHERE (a=4 OR b=13)
|
SELECT d FROM a1 WHERE (a=4 OR b=13)
|
||||||
} {4 13}
|
} {4 13}
|
||||||
|
do_scanstatus_test 3.2.2 {
|
||||||
do_scanstatus_test 2.4 {
|
nLoop 1 nVisit 1 nEst 10 zName a1a
|
||||||
nLoop 1 nVisit 2 nEst 10 zName a1a
|
|
||||||
zExplain {SEARCH TABLE a1 USING INDEX a1a (a=?)}
|
zExplain {SEARCH TABLE a1 USING INDEX a1a (a=?)}
|
||||||
|
nLoop 1 nVisit 1 nEst 10 zName a1bc
|
||||||
nLoop 1 nVisit 2 nEst 10 zName a1bc
|
|
||||||
zExplain {SEARCH TABLE a1 USING INDEX a1bc (b=?)}
|
zExplain {SEARCH TABLE a1 USING INDEX a1bc (b=?)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
do_execsql_test 3.2.1 {
|
||||||
|
SELECT count(*) FROM a1 WHERE (a BETWEEN 4 AND 12) OR (b BETWEEN 40 AND 60)
|
||||||
|
} {30}
|
||||||
|
do_scanstatus_test 3.2.2 {
|
||||||
|
nLoop 1 nVisit 9 nEst 16384 zName a1a
|
||||||
|
zExplain {SEARCH TABLE a1 USING INDEX a1a (a>? AND a<?)}
|
||||||
|
nLoop 1 nVisit 21 nEst 16384 zName a1bc
|
||||||
|
zExplain {SEARCH TABLE a1 USING INDEX a1bc (b>? AND b<?)}
|
||||||
|
}
|
||||||
|
|
||||||
|
do_execsql_test 3.3.1 {
|
||||||
|
SELECT count(*) FROM a1 AS x, a1 AS y
|
||||||
|
WHERE (x.a BETWEEN 4 AND 12) AND (y.b BETWEEN 1 AND 10)
|
||||||
|
} {90}
|
||||||
|
do_scanstatus_test 3.2.2 {
|
||||||
|
nLoop 1 nVisit 10 nEst 16384 zName a1bc
|
||||||
|
zExplain {SEARCH TABLE a1 AS y USING COVERING INDEX a1bc (b>? AND b<?)}
|
||||||
|
nLoop 10 nVisit 90 nEst 16384 zName a1a
|
||||||
|
zExplain {SEARCH TABLE a1 AS x USING COVERING INDEX a1a (a>? AND a<?)}
|
||||||
|
}
|
||||||
|
|
||||||
|
do_execsql_test 3.4.1 {
|
||||||
|
SELECT count(*) FROM a1 WHERE a IN (1, 5, 10, 15);
|
||||||
|
} {4}
|
||||||
|
do_scanstatus_test 3.4.2 {
|
||||||
|
nLoop 1 nVisit 4 nEst 40 zName a1a
|
||||||
|
zExplain {SEARCH TABLE a1 USING COVERING INDEX a1a (a=?)}
|
||||||
|
}
|
||||||
|
|
||||||
|
do_execsql_test 3.4.1 {
|
||||||
|
SELECT count(*) FROM a1 WHERE rowid IN (1, 5, 10, 15);
|
||||||
|
} {4}
|
||||||
|
do_scanstatus_test 3.4.2 {
|
||||||
|
nLoop 1 nVisit 4 nEst 4 zName a1
|
||||||
|
zExplain {SEARCH TABLE a1 USING INTEGER PRIMARY KEY (rowid=?)}
|
||||||
|
}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
Loading…
Reference in New Issue
Block a user