From: dan Date: Sat, 1 Nov 2014 20:38:06 +0000 (+0000) Subject: If SQLITE_ENABLE_STMT_SCANSTATUS is defined, record the number of times each VDBE... X-Git-Tag: version-3.8.8~192^2~5 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6f9702ed4dd4a6312e8a4e4ec9ae174d931298c8;p=thirdparty%2Fsqlite.git 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 --- diff --git a/manifest b/manifest index 8582d6b5e8..c81e911395 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Minor\sfixes\sand\sdocumentation\simprovements\sfor\ssqlite3_stmt_scanstatus(). -D 2014-11-01T18:08:04.130 +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-01T20:38:06.833 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in cf57f673d77606ab0f2d9627ca52a9ba1464146a F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -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 5240bad20ce0b707afee211f0c21a862b54b1e0b -F src/vdbe.h f8e5388173dbf8408da4ae1dcbf75911caf2253d -F src/vdbeInt.h 284b2294c188474daa9b5ce2bd0200560cb0940d -F src/vdbeapi.c 2afa2e162f290879ca9c51e8795a377035f1662a -F src/vdbeaux.c 1e2561eeb94749f6b8c0a1eb02b5fecb645ed48c +F src/vdbe.c 69d025732d242d7c97282e6570a4e5eb768ebaff +F src/vdbe.h 7d603b93d128e614ba2600f12a6c541435405522 +F src/vdbeInt.h ee8d44cba5998279039bcd91ebddb6d9a2d463b8 +F src/vdbeapi.c 19e433e69fe2b27bfc9337a207b6ebf499f41d03 +F src/vdbeaux.c bad342af7cadb8d3bf2990700a78787d602dcd69 F src/vdbeblob.c 8b5442ff0954c44b45cbabbe2e94091a2e16fdef F src/vdbemem.c 31d8eabb0cd78bfeab4e5124c7363c3e9e54db9f F src/vdbesort.c 975aeffa99acb0991b2f288d30294756bff41438 @@ -302,8 +302,8 @@ F src/vtab.c 2a30791bbd7926b589401bd09c3abb33de563793 F src/wal.c 10e7de7ce90865a68153f001a61f1d985cd17983 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804 -F src/where.c 636ca646c8f2f53fd66f9ed5c773e84cb0c4b762 -F src/whereInt.h 99d324a8f921d7a40c605a8b197350c3cb18977d +F src/where.c fb404e3db40db61c212ffb39022e885a91252498 +F src/whereInt.h a2bc22f4e3e70eeaa57272f354c288bc3b71b80b F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggnested.test b35b4cd69fc913f90d39a575e171e1116c3a4bb7 @@ -801,7 +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 b4b1780bad243e1576329d05597b99f06886e4d2 +F test/scanstatus.test 40c7712c8bc0adc3fb88e0419356880679a3a5fb F test/schema.test 8f7999be894260f151adf15c2c7540f1c6d6a481 F test/schema2.test 906408621ea881fdb496d878b1822572a34e32c5 F test/schema3.test 1bc1008e1f8cb5654b248c55f27249366eb7ed38 @@ -1211,7 +1211,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 6a9bab34aeb6a01b612211a28c140de60a3e883c -R d964f269915d57cf95d9198c6ba22665 +P 8d8cc9608d30bb65fffcfe488e904411cbbc7f41 +R e1e809461d373caeeb0e83f126874426 U dan -Z 082decdd6298ff1a2330407f8d406dfb +Z 5d5debb04affeaeb7378f6b538c8f641 diff --git a/manifest.uuid b/manifest.uuid index 7325586095..f70d7e8aef 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8d8cc9608d30bb65fffcfe488e904411cbbc7f41 \ No newline at end of file +9ea37422a8cc2fce51bb10508e5e90f40fd4b511 \ No newline at end of file diff --git a/src/vdbe.c b/src/vdbe.c index 1abccfeeab..bc49505dd0 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -167,18 +167,6 @@ 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. @@ -620,6 +608,9 @@ int sqlite3VdbeExec( #endif nVmStep++; pOp = &aOp[pc]; +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + if( p->pFrame==0 ) p->anExec[pc]++; +#endif /* Only allow tracing if SQLITE_DEBUG is defined. */ @@ -3568,7 +3559,6 @@ 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 @@ -3677,8 +3667,6 @@ case OP_SeekGT: { /* jump, in3 */ VdbeBranchTaken(res!=0,2); if( res ){ pc = pOp->p2 - 1; - }else{ - IncrementExplainCounter(pC, nVisit); } break; } @@ -3889,7 +3877,6 @@ case OP_NotExists: { /* jump, in3 */ res = 0; iKey = pIn3->u.i; rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res); - IncrementExplainCounter(pC, nLoop); pC->movetoTarget = iKey; /* Used by OP_Delete */ pC->nullRow = 0; pC->cacheStatus = CACHE_STALE; @@ -3897,8 +3884,6 @@ case OP_NotExists: { /* jump, in3 */ VdbeBranchTaken(res!=0,2); if( res!=0 ){ pc = pOp->p2 - 1; - }else{ - IncrementExplainCounter(pC, nVisit); } pC->seekResult = res; break; @@ -4467,8 +4452,6 @@ 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; @@ -4538,14 +4521,11 @@ case OP_Rewind: { /* jump */ pC->deferredMoveto = 0; pC->cacheStatus = CACHE_STALE; } - IncrementExplainCounter(pC, nLoop); pC->nullRow = (u8)res; assert( pOp->p2>0 && pOp->p2nOp ); VdbeBranchTaken(res!=0,2); if( res ){ pc = pOp->p2 - 1; - }else{ - IncrementExplainCounter(pC, nVisit); } break; } @@ -4656,7 +4636,6 @@ 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]++; @@ -6079,10 +6058,7 @@ case OP_VFilter: { /* jump */ VdbeBranchTaken(res!=0,2); if( res ){ pc = pOp->p2 - 1; - }else{ - IncrementExplainCounter(pCur, nVisit); } - IncrementExplainCounter(pCur, nLoop); } pCur->nullRow = 0; @@ -6175,7 +6151,6 @@ case OP_VNext: { /* jump */ if( !res ){ /* If there is data, jump to P2 */ pc = pOp->p2 - 1; - IncrementExplainCounter(pCur, nVisit); } goto check_for_interrupt; } @@ -6375,19 +6350,6 @@ 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 * * * * * ** diff --git a/src/vdbe.h b/src/vdbe.h index 1b470441be..966fdb622e 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -25,7 +25,6 @@ ** of this structure. */ typedef struct Vdbe Vdbe; -typedef struct ExplainArg ExplainArg; /* ** 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 */ 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 @@ -102,19 +100,6 @@ 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 */ @@ -134,7 +119,6 @@ struct ExplainArg { #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 @@ -298,4 +282,10 @@ void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *); # define VDBE_OFFSET_LINENO(x) 0 #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 diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 839108f6bc..9f21ac7ebf 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -83,7 +83,6 @@ 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 @@ -292,12 +291,20 @@ 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. */ 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 ** state of the virtual machine. @@ -370,8 +377,11 @@ 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 */ +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + i64 *anExec; /* Number of times each op has been executed */ + int nScan; /* Entries in aScan[] */ + ScanCounter *aScan; /* Scan definitions for sqlite3_stmt_scanstatus() */ +#endif }; /* diff --git a/src/vdbeapi.c b/src/vdbeapi.c index b09faa1fa5..f2e124dc51 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -1476,6 +1476,7 @@ int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){ return (int)v; } +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS /* ** Return status data for a single loop within query pStmt. */ @@ -1489,14 +1490,20 @@ int sqlite3_stmt_scanstatus( 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; + ScanCounter *pScan; + if( idx<0 || idx>=p->nScan ) return 1; + pScan = &p->aScan[idx]; + if( pnLoop ) *pnLoop = p->anExec[pScan->addrLoop]; + if( pnVisit ) *pnVisit = p->anExec[pScan->addrVisit]; + if( pnEst ) *pnEst = pScan->nEst; + if( *pzName ) *pzName = pScan->zName; + if( *pzExplain ){ + if( pScan->addrExplain ){ + *pzExplain = p->aOp[ pScan->addrExplain ].p4.z; + }else{ + *pzExplain = 0; + } + } return 0; } @@ -1505,10 +1512,7 @@ int sqlite3_stmt_scanstatus( */ void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){ Vdbe *p = (Vdbe*)pStmt; - int i; - for(i=0; inExplain; i++){ - p->apExplain[i]->nLoop = 0; - p->apExplain[i]->nVisit = 0; - } + memset(p->anExec, 0, p->nOp * sizeof(i64)); } +#endif /* SQLITE_ENABLE_STMT_SCANSTATUS */ diff --git a/src/vdbeaux.c b/src/vdbeaux.c index a8223890bd..3f75cfee7a 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -597,6 +597,34 @@ int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp, int iLineno){ 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. ** 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 ){ assert( db ); switch( p4type ){ - case P4_EXPLAIN: case P4_REAL: case P4_INT64: case P4_DYNAMIC: @@ -821,13 +848,6 @@ 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; @@ -1111,10 +1131,6 @@ 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 ){ @@ -1714,6 +1730,10 @@ void sqlite3VdbeMakeReady( zEnd = &zCsr[nByte]; }while( nByte && !db->mallocFailed ); +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + p->anExec = (i64*)sqlite3DbMallocZero(db, p->nOp*sizeof(i64)); +#endif + p->nCursor = nCursor; p->nOnceFlag = nOnce; if( p->aVar ){ @@ -2697,7 +2717,13 @@ void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){ sqlite3DbFree(db, p->aColName); sqlite3DbFree(db, p->zSql); sqlite3DbFree(db, p->pFree); - sqlite3DbFree(db, p->apExplain); +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + sqlite3DbFree(db, p->anExec); + for(i=0; inScan; i++){ + sqlite3DbFree(db, p->aScan[i].zName); + } + sqlite3DbFree(db, p->aScan); +#endif } /* diff --git a/src/where.c b/src/where.c index ae3c12725e..8151a7057b 100644 --- a/src/where.c +++ b/src/where.c @@ -2739,7 +2739,7 @@ static int codeAllEqualityTerms( return regBase; } -#if !defined(SQLITE_OMIT_EXPLAIN) || defined(SQLITE_ENABLE_STMT_SCANSTATUS) +#ifndef SQLITE_OMIT_EXPLAIN /* ** 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 ** pLevel. */ -static void explainOneScan( +static int explainOneScan( Parse *pParse, /* Parse context */ 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 iFrom, /* Value for "from" column of output */ 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 ) #endif { - WhereLevel *pLevel = &pWInfo->a[iLevel]; struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom]; Vdbe *v = pParse->pVdbe; /* VM being constructed */ 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) */ -#endif + int iId = pParse->iSelectId; /* Select id (left-most output column) */ 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) ) 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); + if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_ONETABLE_ONLY) ) return 0; - /* 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 ){ -#ifdef SQLITE_OMIT_EXPLAIN - sqlite3XPrintf(&str, 0, " SUBQUERY 0"); -#else sqlite3XPrintf(&str, 0, " SUBQUERY %d", pItem->iSelectId); -#endif }else{ sqlite3XPrintf(&str, 0, " TABLE %s", pItem->zName); } @@ -2927,33 +2902,48 @@ 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 - sqlite3XPrintf(&str, 0, " (~%llu rows)", nEstRow); -#endif - 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]; - pWInfo->iExplain = sqlite3VdbeAddOp4(v, OP_Explain, - iId, iLevel, pLevel->iFrom, (char*)pExplain,P4_EXPLAIN - ); + if( pLoop->nOut>=10 ){ + sqlite3XPrintf(&str, 0, " (~%llu rows)", sqlite3LogEstToInt(pLoop->nOut)); + }else{ + sqlite3StrAccumAppend(&str, " (~1 row)", 9); } +#endif + zMsg = sqlite3StrAccumFinish(&str); + ret = sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg,P4_DYNAMIC); + } + return ret; +} +#else +# define explainOneScan(u,v,w,x,y,z) 0 +#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 explainOneScan(v,w,x,y,z) -#endif /* !SQLITE_OMIT_EXPLAIN || SQLITE_ENABLE_STMT_SCANSTATUS */ +# define addScanStatus(a, b, c, d) +#endif + /* @@ -3621,14 +3611,10 @@ static Bitmask codeOneLoopStart( assert( pSubWInfo || pParse->nErr || db->mallocFailed ); if( pSubWInfo ){ WhereLoop *pSubLoop; - - /* If an OP_Explain was added for this sub-loop, fix the P2 and - ** P3 parameters to it so that they are relative to the current - ** context. */ - if( pSubWInfo->iExplain!=0 ){ - sqlite3VdbeChangeP2(v, pSubWInfo->iExplain, iLevel); - sqlite3VdbeChangeP3(v, pSubWInfo->iExplain, pLevel->iFrom); - } + int addrExplain = explainOneScan( + pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0 + ); + addScanStatus(v, pOrTab, &pSubWInfo->a[0], addrExplain); /* This is the sub-WHERE clause body. First skip over ** 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 ** computed using the current set of tables. */ @@ -6461,7 +6451,10 @@ WhereInfo *sqlite3WhereBegin( */ notReady = ~(Bitmask)0; for(ii=0; iia[ii]; + wsFlags = pLevel->pWLoop->wsFlags; #ifndef SQLITE_OMIT_AUTOMATIC_INDEX if( (pLevel->pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 ){ constructAutomaticIndex(pParse, &pWInfo->sWC, @@ -6469,10 +6462,15 @@ WhereInfo *sqlite3WhereBegin( if( db->mallocFailed ) goto whereBeginError; } #endif - explainOneScan(pParse, pTabList, pWInfo, ii, wctrlFlags); + addrExplain = explainOneScan( + pParse, pTabList, pLevel, ii, pLevel->iFrom, wctrlFlags + ); pLevel->addrBody = sqlite3VdbeCurrentAddr(v); notReady = codeOneLoopStart(pWInfo, ii, notReady); pWInfo->iContinue = pLevel->addrCont; + if( (wsFlags&WHERE_MULTI_OR)==0 && (wctrlFlags&WHERE_ONETABLE_ONLY)==0 ){ + addScanStatus(v, pTabList, pLevel, addrExplain); + } } /* Done. */ diff --git a/src/whereInt.h b/src/whereInt.h index dcf0e00604..168e11f4c7 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -85,6 +85,9 @@ struct WhereLevel { } u; struct WhereLoop *pWLoop; /* The selected WhereLoop object */ Bitmask notReady; /* FROM entries not usable at this level */ +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + int addrVisit; /* Address at which row is visited */ +#endif }; /* diff --git a/test/scanstatus.test b/test/scanstatus.test index 9df80f9ba8..e4e0bbd4a5 100644 --- a/test/scanstatus.test +++ b/test/scanstatus.test @@ -115,7 +115,7 @@ do_execsql_test 2.4.1 { SELECT * FROM x1 WHERE j<'two' } {4 four 1 one 3 three} 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? AND a? AND b? AND b? AND a