diff options
author | dan <Dan Kennedy> | 2022-12-02 20:32:22 +0000 |
---|---|---|
committer | dan <Dan Kennedy> | 2022-12-02 20:32:22 +0000 |
commit | 231ff4b027b077ce9ac0c829cd9cee4108ca9b7a (patch) | |
tree | 5cf6210ffda1cc351169cef8dc576ac2edc83fc9 /src | |
parent | bb4e4a4840530da37c6fdaabc9769a1996f25809 (diff) | |
download | sqlite-231ff4b027b077ce9ac0c829cd9cee4108ca9b7a.tar.gz sqlite-231ff4b027b077ce9ac0c829cd9cee4108ca9b7a.zip |
Enhance the sqlite3_stmt_scanstatus() API and add sqlite3_stmt_scanstatus_v2(). For creation of easier to read query performance reports.
FossilOrigin-Name: 55800833645739efeddcacef464c623931cb6aeb43f4219b4e4faf473c25c8bb
Diffstat (limited to 'src')
-rw-r--r-- | src/expr.c | 7 | ||||
-rw-r--r-- | src/select.c | 7 | ||||
-rw-r--r-- | src/shell.c.in | 127 | ||||
-rw-r--r-- | src/sqlite.h.in | 12 | ||||
-rw-r--r-- | src/sqliteInt.h | 4 | ||||
-rw-r--r-- | src/test1.c | 107 | ||||
-rw-r--r-- | src/util.c | 4 | ||||
-rw-r--r-- | src/vdbe.c | 55 | ||||
-rw-r--r-- | src/vdbe.h | 16 | ||||
-rw-r--r-- | src/vdbeInt.h | 7 | ||||
-rw-r--r-- | src/vdbeapi.c | 83 | ||||
-rw-r--r-- | src/vdbeaux.c | 55 | ||||
-rw-r--r-- | src/wherecode.c | 2 |
13 files changed, 376 insertions, 110 deletions
diff --git a/src/expr.c b/src/expr.c index d30ae9766..07e44a187 100644 --- a/src/expr.c +++ b/src/expr.c @@ -3257,6 +3257,9 @@ int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ SelectDest dest; /* How to deal with SELECT result */ int nReg; /* Registers to allocate */ Expr *pLimit; /* New limit expression */ +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + int addrExplain; +#endif Vdbe *v = pParse->pVdbe; assert( v!=0 ); @@ -3309,7 +3312,7 @@ int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ ** In both cases, the query is augmented with "LIMIT 1". Any ** preexisting limit is discarded in place of the new LIMIT 1. */ - ExplainQueryPlan((pParse, 1, "%sSCALAR SUBQUERY %d", + ExplainQueryPlan2(addrExplain, (pParse, 1, "%sSCALAR SUBQUERY %d", addrOnce?"":"CORRELATED ", pSel->selId)); nReg = pExpr->op==TK_SELECT ? pSel->pEList->nExpr : 1; sqlite3SelectDestInit(&dest, 0, pParse->nMem+1); @@ -3353,6 +3356,8 @@ int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ if( addrOnce ){ sqlite3VdbeJumpHere(v, addrOnce); } + sqlite3VdbeScanStatusEnd(v, addrExplain); + /* Subroutine return */ assert( ExprUseYSub(pExpr) ); diff --git a/src/select.c b/src/select.c index 0cd28b543..740372525 100644 --- a/src/select.c +++ b/src/select.c @@ -7277,6 +7277,9 @@ int sqlite3Select( ** the same view can reuse the materialization. */ int topAddr; int onceAddr = 0; +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + int addrExplain; +#endif pItem->regReturn = ++pParse->nMem; topAddr = sqlite3VdbeAddOp0(v, OP_Goto); @@ -7292,7 +7295,8 @@ int sqlite3Select( VdbeNoopComment((v, "materialize %!S", pItem)); } sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); - ExplainQueryPlan((pParse, 1, "MATERIALIZE %!S", pItem)); + + ExplainQueryPlan2(addrExplain, (pParse, 1, "MATERIALIZE %!S", pItem)); dest.zAffSdst = sqlite3TableAffinityStr(db, pItem->pTab); sqlite3Select(pParse, pSub, &dest); sqlite3DbFree(db, dest.zAffSdst); @@ -7301,6 +7305,7 @@ int sqlite3Select( if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr); sqlite3VdbeAddOp2(v, OP_Return, pItem->regReturn, topAddr+1); VdbeComment((v, "end %!S", pItem)); + sqlite3VdbeScanStatusEnd(v, addrExplain); sqlite3VdbeJumpHere(v, topAddr); sqlite3ClearTempRegCache(pParse); if( pItem->fg.isCte && pItem->fg.isCorrelated==0 ){ diff --git a/src/shell.c.in b/src/shell.c.in index a829fa238..7cbd6b3c4 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -2082,7 +2082,7 @@ static void eqp_render_level(ShellState *p, int iEqpId){ /* ** Display and reset the EXPLAIN QUERY PLAN data */ -static void eqp_render(ShellState *p){ +static void eqp_render(ShellState *p, i64 nCycle){ EQPGraphRow *pRow = p->sGraph.pRow; if( pRow ){ if( pRow->zText[0]=='-' ){ @@ -2093,6 +2093,8 @@ static void eqp_render(ShellState *p){ utf8_printf(p->out, "%s\n", pRow->zText+3); p->sGraph.pRow = pRow->pNext; sqlite3_free(pRow); + }else if( nCycle>0 ){ + utf8_printf(p->out, "QUERY PLAN (cycles=%lld [100%%])\n", nCycle); }else{ utf8_printf(p->out, "QUERY PLAN\n"); } @@ -2970,6 +2972,35 @@ static int display_stats( return 0; } + +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS +static int scanStatsHeight(sqlite3_stmt *p, int iEntry){ + int iPid = 0; + int ret = 1; + sqlite3_stmt_scanstatus_v2(p, iEntry, + SQLITE_SCANSTAT_SELECTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iPid + ); + while( iPid!=0 ){ + int ii; + for(ii=0; 1; ii++){ + int iId; + int res; + res = sqlite3_stmt_scanstatus_v2(p, ii, + SQLITE_SCANSTAT_SELECTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iId + ); + if( res ) break; + if( iId==iPid ){ + sqlite3_stmt_scanstatus_v2(p, ii, + SQLITE_SCANSTAT_PARENTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iPid + ); + } + } + ret++; + } + return ret; +} +#endif + /* ** Display scan stats. */ @@ -2981,40 +3012,70 @@ static void display_scanstats( UNUSED_PARAMETER(db); UNUSED_PARAMETER(pArg); #else - int i, k, n, mx; - raw_printf(pArg->out, "-------- scanstats --------\n"); - mx = 0; - for(k=0; k<=mx; k++){ - double rEstLoop = 1.0; - for(i=n=0; 1; i++){ - sqlite3_stmt *p = pArg->pStmt; - sqlite3_int64 nLoop, nVisit; - double rEst; - int iSid; - const char *zExplain; - if( sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_NLOOP, (void*)&nLoop) ){ - break; + static const int f = SQLITE_SCANSTAT_COMPLEX; + sqlite3_stmt *p = pArg->pStmt; + int ii = 0; + i64 nTotal = 0; + int nWidth = 0; + eqp_reset(pArg); + + for(ii=0; 1; ii++){ + const char *z = 0; + int n = 0; + if( sqlite3_stmt_scanstatus_v2(p,ii,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&z) ){ + break; + } + n = strlen(z) + scanStatsHeight(p, ii)*3; + if( n>nWidth ) nWidth = n; + } + nWidth += 4; + + sqlite3_stmt_scanstatus_v2(p, -1, SQLITE_SCANSTAT_NCYCLE, f, (void*)&nTotal); + for(ii=0; 1; ii++){ + i64 nLoop = 0; + i64 nRow = 0; + i64 nCycle = 0; + int iId = 0; + int iPid = 0; + const char *z = 0; + char *zText = 0; + double rEst = 0.0; + + if( sqlite3_stmt_scanstatus_v2(p,ii,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&z) ){ + break; + } + sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_EST,f,(void*)&rEst); + sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NLOOP,f,(void*)&nLoop); + sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NVISIT,f,(void*)&nRow); + sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NCYCLE,f,(void*)&nCycle); + sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_SELECTID,f,(void*)&iId); + sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_PARENTID,f,(void*)&iPid); + + zText = sqlite3_mprintf("%s", z); + if( nCycle>=0 || nLoop>=0 || nRow>=0 ){ + char *z = 0; + if( nCycle>=0 ){ + z = sqlite3_mprintf("%zcycles=%lld [%d%%]", z, + nCycle, ((nCycle*100)+nTotal/2) / nTotal + ); } - sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_SELECTID, (void*)&iSid); - if( iSid>mx ) mx = iSid; - if( iSid!=k ) continue; - if( n==0 ){ - rEstLoop = (double)nLoop; - if( k>0 ) raw_printf(pArg->out, "-------- subquery %d -------\n", k); + if( nLoop>=0 ){ + z = sqlite3_mprintf("%z%sloops=%lld", z, z ? " " : "", nLoop); } - n++; - sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_NVISIT, (void*)&nVisit); - sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_EST, (void*)&rEst); - sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_EXPLAIN, (void*)&zExplain); - utf8_printf(pArg->out, "Loop %2d: %s\n", n, zExplain); - rEstLoop *= rEst; - raw_printf(pArg->out, - " nLoop=%-8lld nRow=%-8lld estRow=%-8lld estRow/Loop=%-8g\n", - nLoop, nVisit, (sqlite3_int64)(rEstLoop+0.5), rEst + if( nRow>=0 ){ + z = sqlite3_mprintf("%z%srows=%lld", z, z ? " " : "", nRow); + } + + zText = sqlite3_mprintf( + "% *z (%z)", -1*(nWidth-scanStatsHeight(p, ii)*3), zText, z ); } + + eqp_append(pArg, iId, iPid, zText); + sqlite3_free(zText); } - raw_printf(pArg->out, "---------------------------\n"); + + eqp_render(pArg, nTotal); #endif } @@ -3938,10 +3999,10 @@ static int shell_exec( int iEqpId = sqlite3_column_int(pExplain, 0); int iParentId = sqlite3_column_int(pExplain, 1); if( zEQPLine==0 ) zEQPLine = ""; - if( zEQPLine[0]=='-' ) eqp_render(pArg); + if( zEQPLine[0]=='-' ) eqp_render(pArg, 0); eqp_append(pArg, iEqpId, iParentId, zEQPLine); } - eqp_render(pArg); + eqp_render(pArg, 0); } sqlite3_finalize(pExplain); sqlite3_free(zEQP); @@ -3990,7 +4051,7 @@ static int shell_exec( bind_prepared_stmt(pArg, pStmt); exec_prepared_stmt(pArg, pStmt); explain_data_delete(pArg); - eqp_render(pArg); + eqp_render(pArg, 0); /* print usage stats if stats on */ if( pArg && pArg->statsOn ){ diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 369d6b602..91466399b 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -9946,6 +9946,8 @@ int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **ppVal); #define SQLITE_SCANSTAT_NAME 3 #define SQLITE_SCANSTAT_EXPLAIN 4 #define SQLITE_SCANSTAT_SELECTID 5 +#define SQLITE_SCANSTAT_PARENTID 6 +#define SQLITE_SCANSTAT_NCYCLE 7 /* ** CAPI3REF: Prepared Statement Scan Status @@ -9985,6 +9987,16 @@ int sqlite3_stmt_scanstatus( void *pOut /* Result written here */ ); +int sqlite3_stmt_scanstatus_v2( + sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ + int idx, /* Index of loop to report on */ + int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ + int flags, /* Mask of flags defined below */ + void *pOut /* Result written here */ +); + +#define SQLITE_SCANSTAT_COMPLEX 0x0001 + /* ** CAPI3REF: Zero Scan-Status Counters ** METHOD: sqlite3_stmt diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 4711e4f09..5a19058bd 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -5565,7 +5565,9 @@ const char **sqlite3CompileOptions(int *pnOpt); int sqlite3KvvfsInit(void); #endif -#if defined(VDBE_PROFILE) || defined(SQLITE_PERFORMANCE_TRACE) +#if defined(VDBE_PROFILE) \ + || defined(SQLITE_PERFORMANCE_TRACE) \ + || defined(SQLITE_ENABLE_STMT_SCANSTATUS) sqlite3_uint64 sqlite3Hwtime(void); #endif diff --git a/src/test1.c b/src/test1.c index c127d4987..a314e90d8 100644 --- a/src/test1.c +++ b/src/test1.c @@ -2188,7 +2188,7 @@ static int SQLITE_TCLAPI test_stmt_status( #ifdef SQLITE_ENABLE_STMT_SCANSTATUS /* -** Usage: sqlite3_stmt_scanstatus STMT IDX +** Usage: sqlite3_stmt_scanstatus ?-flags FLAGS? STMT IDX */ static int SQLITE_TCLAPI test_stmt_scanstatus( void * clientData, @@ -2203,36 +2203,99 @@ static int SQLITE_TCLAPI test_stmt_scanstatus( const char *zExplain; sqlite3_int64 nLoop; sqlite3_int64 nVisit; + sqlite3_int64 nCycle; double rEst; int res; + int flags = 0; + int iSelectId = 0; + int iParentId = 0; - if( objc!=3 ){ - Tcl_WrongNumArgs(interp, 1, objv, "STMT IDX"); + if( objc==5 ){ + struct Flag { + const char *zFlag; + int flag; + } aTbl[] = { + {"complex", SQLITE_SCANSTAT_COMPLEX}, + {0, 0} + }; + + Tcl_Obj **aFlag = 0; + int nFlag = 0; + int ii; + + if( Tcl_ListObjGetElements(interp, objv[2], &nFlag, &aFlag) ){ + return TCL_ERROR; + } + for(ii=0; ii<nFlag; ii++){ + int iVal = 0; + int res = Tcl_GetIndexFromObjStruct( + interp, aFlag[ii], aTbl, sizeof(aTbl[0]), "flag", 0, &iVal + ); + if( res ) return TCL_ERROR; + flags |= aTbl[iVal].flag; + } + } + + if( objc!=3 && objc!=5 ){ + Tcl_WrongNumArgs(interp, 1, objv, "-flags FLAGS STMT IDX"); + return TCL_ERROR; + } + if( getStmtPointer(interp, Tcl_GetString(objv[objc-2]), &pStmt) + || Tcl_GetIntFromObj(interp, objv[objc-1], &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, SQLITE_SCANSTAT_NLOOP, (void*)&nLoop); - if( res==0 ){ + if( idx<0 ){ Tcl_Obj *pRet = Tcl_NewObj(); - Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("nLoop", -1)); - Tcl_ListObjAppendElement(0, pRet, Tcl_NewWideIntObj(nLoop)); - sqlite3_stmt_scanstatus(pStmt, idx, SQLITE_SCANSTAT_NVISIT, (void*)&nVisit); - Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("nVisit", -1)); - Tcl_ListObjAppendElement(0, pRet, Tcl_NewWideIntObj(nVisit)); - sqlite3_stmt_scanstatus(pStmt, idx, SQLITE_SCANSTAT_EST, (void*)&rEst); - Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("nEst", -1)); - Tcl_ListObjAppendElement(0, pRet, Tcl_NewDoubleObj(rEst)); - sqlite3_stmt_scanstatus(pStmt, idx, SQLITE_SCANSTAT_NAME, (void*)&zName); - Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("zName", -1)); - Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj(zName, -1)); - sqlite3_stmt_scanstatus(pStmt, idx, SQLITE_SCANSTAT_EXPLAIN, (void*)&zExplain); - Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("zExplain", -1)); - Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj(zExplain, -1)); + res = sqlite3_stmt_scanstatus_v2( + pStmt, -1, SQLITE_SCANSTAT_NCYCLE, flags, (void*)&nCycle + ); + sqlite3_stmt_scanstatus_v2( + pStmt, idx, SQLITE_SCANSTAT_NCYCLE, flags, (void*)&nCycle); + Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("nCycle", -1)); + Tcl_ListObjAppendElement(0, pRet, Tcl_NewWideIntObj(nCycle)); Tcl_SetObjResult(interp, pRet); }else{ - Tcl_ResetResult(interp); + res = sqlite3_stmt_scanstatus_v2( + pStmt, idx, SQLITE_SCANSTAT_NLOOP, flags, (void*)&nLoop + ); + if( res==0 ){ + Tcl_Obj *pRet = Tcl_NewObj(); + Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("nLoop", -1)); + Tcl_ListObjAppendElement(0, pRet, Tcl_NewWideIntObj(nLoop)); + sqlite3_stmt_scanstatus_v2( + pStmt, idx, SQLITE_SCANSTAT_NVISIT, flags, (void*)&nVisit); + Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("nVisit", -1)); + Tcl_ListObjAppendElement(0, pRet, Tcl_NewWideIntObj(nVisit)); + sqlite3_stmt_scanstatus_v2( + pStmt, idx, SQLITE_SCANSTAT_EST, flags, (void*)&rEst); + Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("nEst", -1)); + Tcl_ListObjAppendElement(0, pRet, Tcl_NewDoubleObj(rEst)); + sqlite3_stmt_scanstatus_v2( + pStmt, idx, SQLITE_SCANSTAT_NAME, flags, (void*)&zName); + Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("zName", -1)); + Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj(zName, -1)); + sqlite3_stmt_scanstatus_v2( + pStmt, idx, SQLITE_SCANSTAT_EXPLAIN, flags, (void*)&zExplain); + Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("zExplain", -1)); + Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj(zExplain, -1)); + sqlite3_stmt_scanstatus_v2( + pStmt, idx, SQLITE_SCANSTAT_SELECTID, flags, (void*)&iSelectId); + Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("iSelectId", -1)); + Tcl_ListObjAppendElement(0, pRet, Tcl_NewIntObj(iSelectId)); + sqlite3_stmt_scanstatus_v2( + pStmt, idx, SQLITE_SCANSTAT_PARENTID, flags, (void*)&iParentId); + Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("iParentId", -1)); + Tcl_ListObjAppendElement(0, pRet, Tcl_NewIntObj(iParentId)); + sqlite3_stmt_scanstatus_v2( + pStmt, idx, SQLITE_SCANSTAT_NCYCLE, flags, (void*)&nCycle); + Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("nCycle", -1)); + Tcl_ListObjAppendElement(0, pRet, Tcl_NewWideIntObj(nCycle)); + Tcl_SetObjResult(interp, pRet); + }else{ + Tcl_ResetResult(interp); + } } return TCL_OK; } diff --git a/src/util.c b/src/util.c index 7a58fc876..72faafea5 100644 --- a/src/util.c +++ b/src/util.c @@ -1717,6 +1717,8 @@ int sqlite3VListNameToNum(VList *pIn, const char *zName, int nName){ /* ** High-resolution hardware timer used for debugging and testing only. */ -#if defined(VDBE_PROFILE) || defined(SQLITE_PERFORMANCE_TRACE) +#if defined(VDBE_PROFILE) \ + || defined(SQLITE_PERFORMANCE_TRACE) \ + || defined(SQLITE_ENABLE_STMT_SCANSTATUS) # include "hwtime.h" #endif diff --git a/src/vdbe.c b/src/vdbe.c index 857a10dcd..81642eb92 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -617,7 +617,6 @@ void sqlite3VdbeRegisterDump(Vdbe *v){ # define REGISTER_TRACE(R,M) #endif - #ifndef NDEBUG /* ** This function is only called from within an assert() expression. It @@ -707,10 +706,8 @@ int sqlite3VdbeExec( ){ Op *aOp = p->aOp; /* Copy of p->aOp */ Op *pOp = aOp; /* Current operation */ -#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) - Op *pOrigOp; /* Value of pOp at the top of the loop */ -#endif #ifdef SQLITE_DEBUG + Op *pOrigOp; /* Value of pOp at the top of the loop */ int nExtraDelete = 0; /* Verifies FORDELETE and AUXDELETE flags */ #endif int rc = SQLITE_OK; /* Value to return */ @@ -727,8 +724,8 @@ int sqlite3VdbeExec( Mem *pIn2 = 0; /* 2nd input operand */ Mem *pIn3 = 0; /* 3rd input operand */ Mem *pOut = 0; /* Output operand */ -#ifdef VDBE_PROFILE - u64 start; /* CPU clock count at start of opcode */ +#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) + u64 *pnCycle = 0; #endif /*** INSERT STACK UNION HERE ***/ @@ -791,12 +788,17 @@ int sqlite3VdbeExec( assert( rc==SQLITE_OK ); assert( pOp>=aOp && pOp<&aOp[p->nOp]); -#ifdef VDBE_PROFILE - start = sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime(); -#endif nVmStep++; -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS - if( p->anExec ) p->anExec[(int)(pOp-aOp)]++; +#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) + if( p->anExec ){ + assert( p->anExec && p->anCycle ); + p->anExec[(int)(pOp-aOp)]++; + pnCycle = &p->anCycle[pOp-aOp]; +# ifdef VDBE_PROFILE + if( sqlite3NProfileCnt==0 ) +# endif + *pnCycle -= sqlite3Hwtime(); + } #endif /* Only allow tracing if SQLITE_DEBUG is defined. @@ -858,7 +860,7 @@ int sqlite3VdbeExec( } } #endif -#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) +#ifdef SQLITE_DEBUG pOrigOp = pOp; #endif @@ -7150,8 +7152,9 @@ case OP_Program: { /* jump */ pFrame->aOp = p->aOp; pFrame->nOp = p->nOp; pFrame->token = pProgram->token; -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS +#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) pFrame->anExec = p->anExec; + pFrame->anCycle = p->anCycle; #endif #ifdef SQLITE_DEBUG pFrame->iFrameMagic = SQLITE_FRAME_MAGIC; @@ -7189,8 +7192,9 @@ case OP_Program: { /* jump */ memset(pFrame->aOnce, 0, (pProgram->nOp + 7)/8); p->aOp = aOp = pProgram->aOp; p->nOp = pProgram->nOp; -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS +#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) p->anExec = 0; + p->anCycle = 0; #endif #ifdef SQLITE_DEBUG /* Verify that second and subsequent executions of the same trigger do not @@ -8727,11 +8731,16 @@ default: { /* This is really OP_Noop, OP_Explain */ *****************************************************************************/ } -#ifdef VDBE_PROFILE - { - u64 endTime = sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime(); - if( endTime>start ) pOrigOp->cycles += endTime - start; - pOrigOp->cnt++; +#if defined(VDBE_PROFILE) + assert( pnCycle ); + if( pnCycle ){ + *pnCycle += sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime(); + pnCycle = 0; + } +#elif defined(SQLITE_ENABLE_STMT_SCANSTATUS) + if( pnCycle ){ + *pnCycle += sqlite3Hwtime(); + pnCycle = 0; } #endif @@ -8808,6 +8817,14 @@ abort_due_to_error: ** release the mutexes on btrees that were acquired at the ** top. */ vdbe_return: +#if defined(VDBE_PROFILE) + if( pnCycle ){ + *pnCycle += sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime(); + } +#elif defined(SQLITE_ENABLE_STMT_SCANSTATUS) + if( pnCycle ) *pnCycle += sqlite3Hwtime(); +#endif + #ifndef SQLITE_OMIT_PROGRESS_CALLBACK while( nVmStep>=nProgressLimit && db->xProgress!=0 ){ nProgressLimit += db->nProgressOps; diff --git a/src/vdbe.h b/src/vdbe.h index a244468b5..16690f085 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -67,10 +67,6 @@ struct VdbeOp { #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS char *zComment; /* Comment to improve readability */ #endif -#ifdef VDBE_PROFILE - u32 cnt; /* Number of times this instruction was executed */ - u64 cycles; /* Total time spent executing this instruction */ -#endif #ifdef SQLITE_VDBE_COVERAGE u32 iSrcLine; /* Source-code line that generated this opcode ** with flags in the upper 8 bits */ @@ -205,14 +201,20 @@ void sqlite3VdbeEndCoroutine(Vdbe*,int); #endif VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp,int iLineno); #ifndef SQLITE_OMIT_EXPLAIN - void sqlite3VdbeExplain(Parse*,u8,const char*,...); + int sqlite3VdbeExplain(Parse*,u8,const char*,...); void sqlite3VdbeExplainPop(Parse*); int sqlite3VdbeExplainParent(Parse*); # define ExplainQueryPlan(P) sqlite3VdbeExplain P +# ifdef SQLITE_ENABLE_STMT_SCANSTATUS +# define ExplainQueryPlan2(V,P) (V = sqlite3VdbeExplain P) +# else +# define ExplainQueryPlan2(V,P) ExplainQueryPlan(P) +# endif # define ExplainQueryPlanPop(P) sqlite3VdbeExplainPop(P) # define ExplainQueryPlanParent(P) sqlite3VdbeExplainParent(P) #else # define ExplainQueryPlan(P) +# define ExplainQueryPlan2(V,P) # define ExplainQueryPlanPop(P) # define ExplainQueryPlanParent(P) 0 # define sqlite3ExplainBreakpoint(A,B) /*no-op*/ @@ -385,8 +387,10 @@ int sqlite3VdbeBytecodeVtabInit(sqlite3*); #ifdef SQLITE_ENABLE_STMT_SCANSTATUS void sqlite3VdbeScanStatus(Vdbe*, int, int, int, LogEst, const char*); +void sqlite3VdbeScanStatusEnd(Vdbe*, int); #else -# define sqlite3VdbeScanStatus(a,b,c,d,e) +# define sqlite3VdbeScanStatus(a,b,c,d,e,f) +# define sqlite3VdbeScanStatusEnd(a,b) #endif #if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 0c6301c8f..b8642da50 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -172,6 +172,7 @@ struct VdbeFrame { VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */ Op *aOp; /* Program instructions for parent frame */ i64 *anExec; /* Event counters from parent frame */ + u64 *anCycle; /* Cycle counters from parent frame */ Mem *aMem; /* Array of memory cells for parent frame */ VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */ u8 *aOnce; /* Bitmask used by OP_Once */ @@ -391,6 +392,7 @@ typedef unsigned bft; /* Bit Field Type */ typedef struct ScanStatus ScanStatus; struct ScanStatus { int addrExplain; /* OP_Explain for loop */ + int addrEndRange; /* End of range (inclusive) of times to sum */ int addrLoop; /* Address of "loops" counter */ int addrVisit; /* Address of "rows visited" counter */ int iSelectID; /* The "Select-ID" for this loop */ @@ -482,8 +484,11 @@ struct Vdbe { u32 expmask; /* Binding to these vars invalidates VM */ SubProgram *pProgram; /* Linked list of all sub-programs used by VM */ AuxData *pAuxData; /* Linked list of auxdata allocations */ -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS +#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) i64 *anExec; /* Number of times each op has been executed */ + u64 *anCycle; /* Sum of sqlite3HwTime() spent in each opcode */ +#endif +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS int nScan; /* Entries in aScan[] */ ScanStatus *aScan; /* Scan definitions for sqlite3_stmt_scanstatus() */ #endif diff --git a/src/vdbeapi.c b/src/vdbeapi.c index f67ca828c..90b0cc60c 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -2111,23 +2111,60 @@ int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ /* ** Return status data for a single loop within query pStmt. */ -int sqlite3_stmt_scanstatus( +int sqlite3_stmt_scanstatus_v2( sqlite3_stmt *pStmt, /* Prepared statement being queried */ - int idx, /* Index of loop to report on */ + int iScan, /* Index of loop to report on */ int iScanStatusOp, /* Which metric to return */ + int flags, void *pOut /* OUT: Write the answer here */ ){ Vdbe *p = (Vdbe*)pStmt; ScanStatus *pScan; - if( idx<0 || idx>=p->nScan ) return 1; - pScan = &p->aScan[idx]; + int idx; + + /* If the v2 flag is clear, then this function must ignore any ScanStatus + ** structures with ScanStatus.addrLoop set to 0. */ + if( iScan<0 ){ + int ii; + if( iScanStatusOp==SQLITE_SCANSTAT_NCYCLE ){ + i64 res = 0; + for(ii=0; ii<p->nOp; ii++){ + res += p->anCycle[ii]; + } + *(i64*)pOut = res; + return 0; + } + return 1; + } + if( flags & SQLITE_SCANSTAT_COMPLEX ){ + idx = iScan; + pScan = &p->aScan[idx]; + }else{ + for(idx=0; idx<p->nScan; idx++){ + pScan = &p->aScan[idx]; + if( pScan->addrLoop ){ + iScan--; + if( iScan<0 ) break; + } + } + } + if( idx>=p->nScan ) return 1; + switch( iScanStatusOp ){ case SQLITE_SCANSTAT_NLOOP: { - *(sqlite3_int64*)pOut = p->anExec[pScan->addrLoop]; + if( pScan->addrLoop>0 ){ + *(sqlite3_int64*)pOut = p->anExec[pScan->addrLoop]; + }else{ + *(sqlite3_int64*)pOut = -1; + } break; } case SQLITE_SCANSTAT_NVISIT: { - *(sqlite3_int64*)pOut = p->anExec[pScan->addrVisit]; + if( pScan->addrVisit>0 ){ + *(sqlite3_int64*)pOut = p->anExec[pScan->addrVisit]; + }else{ + *(sqlite3_int64*)pOut = -1; + } break; } case SQLITE_SCANSTAT_EST: { @@ -2160,6 +2197,26 @@ int sqlite3_stmt_scanstatus( } break; } + case SQLITE_SCANSTAT_PARENTID: { + if( pScan->addrExplain ){ + *(int*)pOut = p->aOp[ pScan->addrExplain ].p2; + }else{ + *(int*)pOut = -1; + } + break; + } + case SQLITE_SCANSTAT_NCYCLE: { + i64 res = -1; + if( pScan->addrEndRange ){ + int ii; + res = 0; + for(ii=pScan->addrExplain; ii<=pScan->addrEndRange; ii++){ + res += p->anCycle[ii]; + } + } + *(i64*)pOut = res; + break; + } default: { return 1; } @@ -2168,10 +2225,24 @@ int sqlite3_stmt_scanstatus( } /* +** Return status data for a single loop within query pStmt. +*/ +int sqlite3_stmt_scanstatus( + sqlite3_stmt *pStmt, /* Prepared statement being queried */ + int iScan, /* Index of loop to report on */ + int iScanStatusOp, /* Which metric to return */ + void *pOut /* OUT: Write the answer here */ +){ + return sqlite3_stmt_scanstatus_v2(pStmt, iScan, iScanStatusOp, 0, pOut); +} + + +/* ** Zero all counters associated with the sqlite3_stmt_scanstatus() data. */ void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){ Vdbe *p = (Vdbe*)pStmt; memset(p->anExec, 0, p->nOp * sizeof(i64)); + memset(p->anCycle, 0, p->nOp * sizeof(u64)); } #endif /* SQLITE_ENABLE_STMT_SCANSTATUS */ diff --git a/src/vdbeaux.c b/src/vdbeaux.c index fd196f37b..e7cd61f05 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -268,10 +268,6 @@ int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){ test_addop_breakpoint(i, &p->aOp[i]); } #endif -#ifdef VDBE_PROFILE - pOp->cycles = 0; - pOp->cnt = 0; -#endif #ifdef SQLITE_VDBE_COVERAGE pOp->iSrcLine = 0; #endif @@ -439,8 +435,9 @@ void sqlite3ExplainBreakpoint(const char *z1, const char *z2){ ** If the bPush flag is true, then make this opcode the parent for ** subsequent Explains until sqlite3VdbeExplainPop() is called. */ -void sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){ -#ifndef SQLITE_DEBUG +int sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){ + int addr = 0; +#if !defined(SQLITE_DEBUG) && !defined(SQLITE_ENABLE_STMT_SCANSTATUS) /* Always include the OP_Explain opcodes if SQLITE_DEBUG is defined. ** But omit them (for performance) during production builds */ if( pParse->explain==2 ) @@ -455,13 +452,15 @@ void sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){ va_end(ap); v = pParse->pVdbe; iThis = v->nOp; - sqlite3VdbeAddOp4(v, OP_Explain, iThis, pParse->addrExplain, 0, + addr = sqlite3VdbeAddOp4(v, OP_Explain, iThis, pParse->addrExplain, 0, zMsg, P4_DYNAMIC); sqlite3ExplainBreakpoint(bPush?"PUSH":"", sqlite3VdbeGetLastOp(v)->p4.z); if( bPush){ pParse->addrExplain = iThis; } + sqlite3VdbeScanStatus(v, iThis, 0, 0, 0, 0); } + return addr; } /* @@ -1119,6 +1118,7 @@ void sqlite3VdbeScanStatus( aNew = (ScanStatus*)sqlite3DbRealloc(p->db, p->aScan, nByte); if( aNew ){ ScanStatus *pNew = &aNew[p->nScan++]; + memset(pNew, 0, sizeof(ScanStatus)); pNew->addrExplain = addrExplain; pNew->addrLoop = addrLoop; pNew->addrVisit = addrVisit; @@ -1127,6 +1127,19 @@ void sqlite3VdbeScanStatus( p->aScan = aNew; } } + +void sqlite3VdbeScanStatusEnd(Vdbe *p, int addrExplain){ + ScanStatus *pScan = 0; + int ii; + for(ii=p->nScan-1; ii>=0; ii--){ + pScan = &p->aScan[ii]; + if( pScan->addrExplain==addrExplain ) break; + pScan = 0; + } + if( pScan ){ + pScan->addrEndRange = sqlite3VdbeCurrentAddr(p)-1; + } +} #endif @@ -2424,7 +2437,7 @@ static void *allocSpace( ** running it. */ void sqlite3VdbeRewind(Vdbe *p){ -#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) +#if defined(SQLITE_DEBUG) int i; #endif assert( p!=0 ); @@ -2452,10 +2465,8 @@ void sqlite3VdbeRewind(Vdbe *p){ p->iStatement = 0; p->nFkConstraint = 0; #ifdef VDBE_PROFILE - for(i=0; i<p->nOp; i++){ - p->aOp[i].cnt = 0; - p->aOp[i].cycles = 0; - } + memset(p->anExec, 0, sizeof(i64)*p->nOp); + memset(p->anCycle, 0, sizeof(u64)*p->nOp); #endif } @@ -2563,8 +2574,9 @@ void sqlite3VdbeMakeReady( p->aVar = allocSpace(&x, 0, nVar*sizeof(Mem)); p->apArg = allocSpace(&x, 0, nArg*sizeof(Mem*)); p->apCsr = allocSpace(&x, 0, nCursor*sizeof(VdbeCursor*)); -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS +#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) p->anExec = allocSpace(&x, 0, p->nOp*sizeof(i64)); + p->anCycle = allocSpace(&x, 0, p->nOp*sizeof(u64)); #endif if( x.nNeeded ){ x.pSpace = p->pFree = sqlite3DbMallocRawNN(db, x.nNeeded); @@ -2574,8 +2586,9 @@ void sqlite3VdbeMakeReady( p->aVar = allocSpace(&x, p->aVar, nVar*sizeof(Mem)); p->apArg = allocSpace(&x, p->apArg, nArg*sizeof(Mem*)); p->apCsr = allocSpace(&x, p->apCsr, nCursor*sizeof(VdbeCursor*)); -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS +#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) p->anExec = allocSpace(&x, p->anExec, p->nOp*sizeof(i64)); + p->anCycle = allocSpace(&x, p->anCycle, p->nOp*sizeof(u64)); #endif } } @@ -2591,8 +2604,9 @@ void sqlite3VdbeMakeReady( p->nMem = nMem; initMemArray(p->aMem, nMem, db, MEM_Undefined); memset(p->apCsr, 0, nCursor*sizeof(VdbeCursor*)); -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS +#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) memset(p->anExec, 0, p->nOp*sizeof(i64)); + memset(p->anCycle, 0, p->nOp*sizeof(u64)); #endif } sqlite3VdbeRewind(p); @@ -2651,8 +2665,9 @@ static void closeCursorsInFrame(Vdbe *p){ int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){ Vdbe *v = pFrame->v; closeCursorsInFrame(v); -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS +#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) v->anExec = pFrame->anExec; + v->anCycle = pFrame->anCycle; #endif v->aOp = pFrame->aOp; v->nOp = pFrame->nOp; @@ -3485,10 +3500,12 @@ int sqlite3VdbeReset(Vdbe *p){ } for(i=0; i<p->nOp; i++){ char zHdr[100]; + i64 cnt = p->anExec[i]; + i64 cycles = p->anCycle[i]; sqlite3_snprintf(sizeof(zHdr), zHdr, "%6u %12llu %8llu ", - p->aOp[i].cnt, - p->aOp[i].cycles, - p->aOp[i].cnt>0 ? p->aOp[i].cycles/p->aOp[i].cnt : 0 + cnt, + cycles, + cnt>0 ? cycles/cnt : 0 ); fprintf(out, "%s", zHdr); sqlite3VdbePrintOp(out, i, &p->aOp[i]); diff --git a/src/wherecode.c b/src/wherecode.c index c731cc82f..b744556fa 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -270,6 +270,8 @@ int sqlite3WhereExplainBloomFilter( zMsg = sqlite3StrAccumFinish(&str); ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v), pParse->addrExplain, 0, zMsg,P4_DYNAMIC); + + sqlite3VdbeScanStatus(v, sqlite3VdbeCurrentAddr(v)-1, 0, 0, 0, 0); return ret; } #endif /* SQLITE_OMIT_EXPLAIN */ |