]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Enhance the sqlite3_stmt_scanstatus() API and add sqlite3_stmt_scanstatus_v2(). For...
authordan <Dan Kennedy>
Fri, 2 Dec 2022 20:32:22 +0000 (20:32 +0000)
committerdan <Dan Kennedy>
Fri, 2 Dec 2022 20:32:22 +0000 (20:32 +0000)
FossilOrigin-Name: 55800833645739efeddcacef464c623931cb6aeb43f4219b4e4faf473c25c8bb

17 files changed:
manifest
manifest.uuid
src/expr.c
src/select.c
src/shell.c.in
src/sqlite.h.in
src/sqliteInt.h
src/test1.c
src/util.c
src/vdbe.c
src/vdbe.h
src/vdbeInt.h
src/vdbeapi.c
src/vdbeaux.c
src/wherecode.c
test/scanstatus.test
test/scanstatus2.test [new file with mode: 0644]

index 61b48477c12f03d93c9c167a9a5d1944c9841978..deb137d25e12e7b0f950ffb8e468e11239598009 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Minor\sinternal\stweaks\sto\sthe\sOPFS\sVFS.\sResolve\sa\smissing\sresult\scode\swhich\slead\sto\sa\snull\sderef\sin\sxFileSize().
-D 2022-12-02T18:56:37.557
+C Enhance\sthe\ssqlite3_stmt_scanstatus()\sAPI\sand\sadd\ssqlite3_stmt_scanstatus_v2().\sFor\screation\sof\seasier\sto\sread\squery\sperformance\sreports.
+D 2022-12-02T20:32:22.142
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -595,7 +595,7 @@ F src/date.c 94ce83b4cd848a387680a5f920c9018c16655db778c4d36525af0a0f34679ac5
 F src/dbpage.c f1a87f4ebcf22284e0aaf0697862f4ccfc120dcd6db3d8dfa3b049b2580c01d8
 F src/dbstat.c a56a7ad1163a9888d46cd5820be2e65354fb1aa04ed6909f7c3e5831e0ee2c29
 F src/delete.c 86573edae75e3d3e9a8b590d87db8e47222103029df4f3e11fa56044459b514e
-F src/expr.c 9e7fadc664b938c18f006be0d4f6669888f9302756ee204420c7eccaed5435a6
+F src/expr.c 7b44a97e14ebb679c14ced7c3db420fd6293c33f8bd668fef37746e76afdc746
 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
 F src/fkey.c 722f20779f5342a787922deded3628d8c74b5249cab04098cf17ee2f2aaff002
 F src/func.c 7e86074afc4dc702691a29b7801f6dcc191db092b52e8bbe69dcd2f7be52194d
@@ -645,17 +645,17 @@ F src/printf.c e99ee9741e79ae3873458146f59644276657340385ade4e76a5f5d1c25793764
 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
 F src/resolve.c efea4e5fbecfd6d0a9071b0be0d952620991673391b6ffaaf4c277b0bb674633
 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
-F src/select.c 6983de0e6b8b68c97f82f9fca27ffb8f17161cff4d0d48fdf9eb40b213da0cc6
-F src/shell.c.in 9fda74d40b206a707aaa69fc5dc38e2c6a9137a3f4a1dcd7af581d59d92c063c
-F src/sqlite.h.in 3439711b72cf1a541716da3671ac40f8d5957cdecfc192d47d32f7aed94207c2
+F src/select.c b1301741aed7014beafb4cbc524d725865a890c7619d784e4ffb04379259ae6c
+F src/shell.c.in 0e45a91da5426563c5b0c8f66fd51d5a5fcb3cc17faa44cd4ea8cc386bde146b
+F src/sqlite.h.in 3ba99a3c2b414b9ad36d06e2f273ab6077f5f74e4d79630aee047394b92f7c7f
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h c4b9fa7a7e2bcdf850cfeb4b8a91d5ec47b7a00033bc996fd2ee96cbf2741f5f
-F src/sqliteInt.h 4ddd98e423276714479f9f22dbbda050e8ef99aa97e7e26bf0bdf58acef0ca42
+F src/sqliteInt.h 0c9934acd88e0fa14f0d4743233b4cd7bd7bbc84099ba3cad50d8e69676ce2b9
 F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
 F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749
 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
 F src/tclsqlite.c 4e64ba300a5a26e0f1170e09032429faeb65e45e8f3d1a7833e8edb69fc2979e
-F src/test1.c 09ad3f2bf5f0efe70eb16e460054954b36be8ed09dfcafa0642b37d25d379f6f
+F src/test1.c 98f4a4525e10b0df164ba7f33950824348bb29fd582101e0f93b1af72f493154
 F src/test2.c 827446e259a3b7ab949da1542953edda7b5117982576d3e6f1c24a0dd20a5cef
 F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644
 F src/test4.c 4533b76419e7feb41b40582554663ed3cd77aaa54e135cf76b3205098cd6e664
@@ -715,13 +715,13 @@ F src/trigger.c 5e68b790f022b8dafbfb0eb244786512a95c9575fc198719d2557d73e5795858
 F src/update.c 5b0302c47cf31b533d5dff04c497ca1d8b9d89c39727e633fbe7b882fd5ac5aa
 F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145
 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
-F src/util.c 4e42c338d982e3f139004467bf0f2e9d679d519c7c75718fcf4ff1811401b03c
+F src/util.c 313f3154e2b85a447326f5dd15de8d31a4df6ab0c3579bd58f426ff634ec9050
 F src/vacuum.c 84ce7f01f8a7a08748e107a441db83bcec13970190ddcb0c9ff522adbc1c23fd
-F src/vdbe.c 00648bd76fb2145c2d890312112bda446569da64ca70aaf1f2282cc6c2b22d14
-F src/vdbe.h 58675f47dcf3105bab182c3ad3726efd60ffd003e954386904ac9107d0d2b743
-F src/vdbeInt.h 17b7461ffcf9ee760d1341731715a419f6b8c763089a7ece25c2e8098d702b3f
-F src/vdbeapi.c b75fe402b8c0db8839aa9968f917bae49361a364ed28069b27b19ac7761e48b0
-F src/vdbeaux.c 8ebe337e82d99cf3b01cd4fd67103a5b0054d53fee8456b90dbeba46ebf97ceb
+F src/vdbe.c 5f5cc749c999d65e2115bb3ec555f7ac0ecfbda7f47526272d53a032e9d083b1
+F src/vdbe.h 247b4ffaad9ba2b2944a2b3084936ae5df684a42151552f32f074ec847c1402b
+F src/vdbeInt.h 9628718a289cf3bf490db1435019773ccc0874e1a505502ea477c5a98e649128
+F src/vdbeapi.c 592c3d7be7c23d202cf5bfb96fc970ced7aaaa1b51052868f1deb91bbbb0add8
+F src/vdbeaux.c 600edaa2d20eb28aea9707148f4edcf12b349418fd2e3f1de92e5d23fdfc0eb8
 F src/vdbeblob.c 5e61ce31aca17db8fb60395407457a8c1c7fb471dde405e0cd675974611dcfcd
 F src/vdbemem.c 6cfed43758d57b6e3b99d9cdedfeccd86e45a07e427b22d8487cbdbebb6c522a
 F src/vdbesort.c 43756031ca7430f7aec3ef904824a7883c4ede783e51f280d99b9b65c0796e35
@@ -734,7 +734,7 @@ F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
 F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
 F src/where.c bf470b5d1ba03af8d558a0c98cc1fa97b330e03a198a7af61895e5a2e8d93f20
 F src/whereInt.h e25203e5bfee149f5f1225ae0166cfb4f1e65490c998a024249e98bb0647377c
-F src/wherecode.c ee52c2781c36004d23c85bf111063b78fc16e5e1b6a0d424326af8bf90babb0b
+F src/wherecode.c f7121f4c29e45ba7d38c91448e250ae5197a4b75e9fa75996ba6d5c81a3cc6e5
 F src/whereexpr.c 05295b44b54eea76d1ba766f0908928d0e20e990c249344c9521454d3d09c7ae
 F src/window.c 14836767adb26573b50f528eb37f8b1336f2c430ab38de7cead1e5c546bb4d8c
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
@@ -1456,7 +1456,8 @@ F test/savepoint5.test 0735db177e0ebbaedc39812c8d065075d563c4fd
 F test/savepoint6.test f41279c5e137139fa5c21485773332c7adb98cd7
 F test/savepoint7.test cde525ea3075283eb950cdcdefe23ead4f700daa
 F test/savepointfault.test f044eac64b59f09746c7020ee261734de82bf9b2
-F test/scanstatus.test 9a0ed37ab6d57b50567282788fffdf832d9b16739ecc41bff9d77a8d767cf317
+F test/scanstatus.test 7dbcfd6adc6a8df6abc59f83d6da5a27e1bce0b2f6fa55147c8176d7c44e0450
+F test/scanstatus2.test c618c5843257e290647dd63672c16b0f6488faebc64ba9b2ec5b4de3bb39a7cc
 F test/schema.test 5dd11c96ba64744de955315d2e4f8992e447533690153b93377dffb2a5ef5431
 F test/schema2.test 906408621ea881fdb496d878b1822572a34e32c5
 F test/schema3.test 8ed4ae66e082cdd8b1b1f22d8549e1e7a0db4527a8e6ee8b6193053ee1e5c9ce
@@ -2065,8 +2066,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 0d36021d107d3afca190ad61c3380536ad0cc2d493d345d48f9f9c1191741128
-R 6eba0a795b491027bc2604925d67afb7
-U stephan
-Z 379ecd060fe213536d8ac858396e84c5
+P 57dd593ef0efa17dfb3a9f4eac36d5b8b879e271de817d8cd94a8c8b56d31870
+R 49b81422f59a6a7338c393b5956057f1
+T *branch * scanstatus_v2
+T *sym-scanstatus_v2 *
+T -sym-trunk *
+U dan
+Z e87234282222fcab550cd1e86b10e65b
 # Remove this line to create a well-formed Fossil manifest.
index 2c043a64327e7b31cbeff7366c240e621416fda6..23a6edc9141fbafe9fdfdc7f10587e21ce35b94a 100644 (file)
@@ -1 +1 @@
-57dd593ef0efa17dfb3a9f4eac36d5b8b879e271de817d8cd94a8c8b56d31870
\ No newline at end of file
+55800833645739efeddcacef464c623931cb6aeb43f4219b4e4faf473c25c8bb
\ No newline at end of file
index d30ae97668b1ce4d5a198897a41f89285bae09e3..07e44a1876c2661121f8b362dd9b5d52084fb9e4 100644 (file)
@@ -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) );
index 0cd28b543e16ee8e4f9c3b1e4ce42053b2112424..7403725252652eff6c0beace8f5d9a30a3e25aaa 100644 (file)
@@ -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 ){
index a829fa2387a1942e4b0ebaa663b181789a4b2467..7cbd6b3c47a4fd968a8f6c7e97d564fabac5fb30 100644 (file)
@@ -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 ){
index 369d6b60222e0089e0f3283ce66038209dab9e2b..91466399be6daf8540cb2e6972a60ba639e05811 100644 (file)
@@ -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
index 4711e4f094c9ef54a48226477f4d5e5df952eba3..5a19058bd108a5c14baa28b7c648cc2df57ff7b4 100644 (file)
@@ -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
 
index c127d49870f1e8651af15c34d797fa0263aa31aa..a314e90d848b70e451c10947ec6c85291999011b 100644 (file)
@@ -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;
 }
index 7a58fc8764237e861f609362f0c8452137c4db92..72faafea5f8434ce32ec40b3fb0b689364b065be 100644 (file)
@@ -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
index 857a10dcd5ba0693650c9bb03a5074fb7c5fb36f..81642eb9294bbb6db03b500efb561c61a3fd1f53 100644 (file)
@@ -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;
index a244468b5227539ead59945360e9b8da227ae356..16690f0855ae285f962bc2f90bf506e11035038d 100644 (file)
@@ -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)
index 0c6301c8f1666492d7d000cf75088051c75d5ac4..b8642da508dab1e5aca670b79644c61a2829cfda 100644 (file)
@@ -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
index f67ca828ca90890ca057558d96e947e4bfbeaf7e..90b0cc60c8a373656e27a895f7fc184e9c51a58f 100644 (file)
@@ -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;
     }
@@ -2167,11 +2224,25 @@ int sqlite3_stmt_scanstatus(
   return 0;
 }
 
+/*
+** 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 */
index fd196f37bcd60b8126817c99755eb77eb3e75d6e..e7cd61f05dc079ca9a6999357a9b4acd0d1dac08 100644 (file)
@@ -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]);
index c731cc82f4490e5b0c0927c3b9755ee948e35b88..b744556fa8dd4e9decd79d34c4314993699b5523 100644 (file)
@@ -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 */
index 46249f665d950f2d352e0699442c9307aabfd817..caad70c5b45c158b8f60e654fb9f36525733a886 100644 (file)
@@ -36,7 +36,9 @@ proc do_scanstatus_test {tn res} {
   while {1} {
     set r [sqlite3_stmt_scanstatus $stmt $idx]
     if {[llength $r]==0} break
-    lappend ret {*}$r
+    foreach v {nLoop nVisit nEst zName zExplain} {
+      lappend ret $v [dict get $r $v]
+    }
     incr idx
   }
 
@@ -312,8 +314,8 @@ do_execsql_test 5.1.1 {
   SELECT count(*) FROM t1 WHERE a IN (SELECT b FROM t1 AS ii)
 } {2}
 do_scanstatus_test 5.1.2 { 
-  nLoop 1 nVisit 10 nEst 10.0 zName t1bc 
-  zExplain {SCAN ii USING COVERING INDEX t1bc}
+  nLoop 1 nVisit 10 nEst 10.0 zName t1
+  zExplain {SCAN ii}
   nLoop 1 nVisit 2 nEst 8.0 zName sqlite_autoindex_t1_1
   zExplain {SEARCH t1 USING COVERING INDEX sqlite_autoindex_t1_1 (a=?)}
 }
@@ -341,15 +343,15 @@ do_eqp_test 5.4.1 {
   SELECT count(*) FROM t1, t2 WHERE y = c;
 } {
   QUERY PLAN
-  |--SCAN t1 USING COVERING INDEX t1bc
+  |--SCAN t1
   `--SEARCH t2 USING COVERING INDEX t2xy (ANY(x) AND y=?)
 }
 do_execsql_test 5.4.2 {
   SELECT count(*) FROM t1, t2 WHERE y = c;
 } {200}
 do_scanstatus_test 5.4.3 { 
-  nLoop 1 nVisit 10 nEst 10.0 zName t1bc 
-  zExplain {SCAN t1 USING COVERING INDEX t1bc}
+  nLoop 1 nVisit 10 nEst 10.0 zName t1
+  zExplain {SCAN t1}
   nLoop 10 nVisit 200 nEst 56.0 zName t2xy 
   zExplain {SEARCH t2 USING COVERING INDEX t2xy (ANY(x) AND y=?)}
 }
diff --git a/test/scanstatus2.test b/test/scanstatus2.test
new file mode 100644 (file)
index 0000000..001797d
--- /dev/null
@@ -0,0 +1,137 @@
+# 2014 November 1
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix scanstatus2
+
+ifcapable !scanstatus {
+  finish_test
+  return
+}
+
+do_execsql_test 1.0 {
+  CREATE TABLE t1(a, b);
+  CREATE TABLE t2(x, y);
+  INSERT INTO t1 VALUES(1, 2);
+  INSERT INTO t1 VALUES(3, 4);
+  INSERT INTO t2 VALUES('a', 'b');
+  INSERT INTO t2 VALUES('c', 'd');
+  INSERT INTO t2 VALUES('e', 'f');
+}
+
+proc do_zexplain_test {v2 tn sql res} {
+  db eval $sql
+  set stmt [db version -last-stmt-ptr]
+  set idx 0
+  set ret [list]
+
+  set cmd sqlite3_stmt_scanstatus
+  set f [list]
+  if {$v2} { lappend f complex }
+
+  while {1} {
+    set r [sqlite3_stmt_scanstatus -flags $f $stmt $idx]
+    if {[llength $r]==0} break
+    lappend ret [dict get $r zExplain]
+    incr idx
+  }
+  uplevel [list do_test $tn [list set {} $ret] [list {*}$res]]
+}
+
+proc get_cycles {stmt} {
+  set r [sqlite3_stmt_scanstatus $stmt -1]
+  dict get $r nCycle
+}
+
+proc foreach_scan {varname stmt body} {
+  upvar $varname var
+
+  for {set ii 0} {1} {incr ii} {
+    set r [sqlite3_stmt_scanstatus -flags complex $stmt $ii]
+    if {[llength $r]==0} break
+    array set var $r
+    uplevel $body
+  }
+}
+
+proc get_eqp_graph {stmt iPar nIndent} {
+  set res ""
+  foreach_scan A $stmt {
+    if {$A(iParentId)==$iPar} {
+      set txt $A(zExplain)
+      if {$A(nCycle)>=0} {
+        append txt " (nCycle=$A(nCycle))"
+      }
+      append res "[string repeat - $nIndent]$txt\n"
+      append res [get_eqp_graph $stmt $A(iSelectId) [expr $nIndent+2]]
+    }
+  }
+  set res
+}
+
+proc get_graph {stmt} {
+  set nCycle [get_cycles $stmt]
+  set res "QUERY (nCycle=$nCycle)\n"
+  append res [get_eqp_graph $stmt 0 2]
+}
+
+proc do_graph_test {tn sql res} {
+  db eval $sql
+  set stmt [db version -last-stmt-ptr]
+
+  set graph [string trim [get_graph $stmt]]
+  set graph [regsub -all {nCycle=[0-9]+} $graph nCycle=nnn]
+  uplevel [list do_test $tn [list set {} $graph] [string trim $res]]
+}
+
+
+do_zexplain_test 0 1.1 {
+  SELECT (SELECT a FROM t1 WHERE b=x) FROM t2 WHERE y=2
+} {
+  {SCAN t2}
+  {SCAN t1}
+}
+do_zexplain_test 1 1.2 {
+  SELECT (SELECT a FROM t1 WHERE b=x) FROM t2 WHERE y=2
+} {
+  {SCAN t2}
+  {CORRELATED SCALAR SUBQUERY 1}
+  {SCAN t1}
+}
+
+do_graph_test 1.3 {
+  SELECT (SELECT a FROM t1 WHERE b=x) FROM t2 WHERE y=2
+} {
+QUERY (nCycle=nnn)
+--SCAN t2
+--CORRELATED SCALAR SUBQUERY 1 (nCycle=nnn)
+----SCAN t1
+}
+
+do_graph_test 1.4 {
+  WITH v2(x,y) AS MATERIALIZED (
+    SELECT x,y FROM t2
+  )
+  SELECT * FROM t1, v2 ORDER BY y;
+} {
+QUERY (nCycle=nnn)
+--MATERIALIZE v2 (nCycle=nnn)
+----SCAN t2
+--SCAN v2
+--SCAN t1
+--USE TEMP B-TREE FOR ORDER BY
+}
+
+finish_test
+
+