1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-07 02:42:48 +03:00

Enhance the sqlite3_stmt_scanstatus() API and add sqlite3_stmt_scanstatus_v2(). For creation of enhanced query performance reports.

FossilOrigin-Name: 4893b4e3eafc7c9c22b24717f90a585862203f987cf108b079ce6e946093e675
This commit is contained in:
dan
2022-12-05 19:16:23 +00:00
18 changed files with 717 additions and 160 deletions

View File

@@ -1,5 +1,5 @@
C Add\stest\scase\sthat\sshould\shave\sbeen\spart\sof\sprevious\scommit. C Enhance\sthe\ssqlite3_stmt_scanstatus()\sAPI\sand\sadd\ssqlite3_stmt_scanstatus_v2().\sFor\screation\sof\senhanced\squery\sperformance\sreports.
D 2022-12-05T14:20:54.124 D 2022-12-05T19:16:23.509
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -595,14 +595,14 @@ F src/date.c 94ce83b4cd848a387680a5f920c9018c16655db778c4d36525af0a0f34679ac5
F src/dbpage.c f1a87f4ebcf22284e0aaf0697862f4ccfc120dcd6db3d8dfa3b049b2580c01d8 F src/dbpage.c f1a87f4ebcf22284e0aaf0697862f4ccfc120dcd6db3d8dfa3b049b2580c01d8
F src/dbstat.c a56a7ad1163a9888d46cd5820be2e65354fb1aa04ed6909f7c3e5831e0ee2c29 F src/dbstat.c a56a7ad1163a9888d46cd5820be2e65354fb1aa04ed6909f7c3e5831e0ee2c29
F src/delete.c 86573edae75e3d3e9a8b590d87db8e47222103029df4f3e11fa56044459b514e F src/delete.c 86573edae75e3d3e9a8b590d87db8e47222103029df4f3e11fa56044459b514e
F src/expr.c 9e7fadc664b938c18f006be0d4f6669888f9302756ee204420c7eccaed5435a6 F src/expr.c d0ce060008452653e9c7bcdce8b1e3ff078fbbd5f9f80b530b3ab99bb70a8078
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c 722f20779f5342a787922deded3628d8c74b5249cab04098cf17ee2f2aaff002 F src/fkey.c 722f20779f5342a787922deded3628d8c74b5249cab04098cf17ee2f2aaff002
F src/func.c 7e86074afc4dc702691a29b7801f6dcc191db092b52e8bbe69dcd2f7be52194d F src/func.c 7e86074afc4dc702691a29b7801f6dcc191db092b52e8bbe69dcd2f7be52194d
F src/global.c e06ff8e0acd85aec13563c9ecb44fbbf38232ccf73594998fd880b92d619594b F src/global.c e06ff8e0acd85aec13563c9ecb44fbbf38232ccf73594998fd880b92d619594b
F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19 F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19
F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51
F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
F src/insert.c 1b11a2e33ee52db93c02fddac67e39d00161d61b69fac2675b82f2aa68c1b61c F src/insert.c 1b11a2e33ee52db93c02fddac67e39d00161d61b69fac2675b82f2aa68c1b61c
F src/json.c 7749b98c62f691697c7ee536b570c744c0583cab4a89200fdd0fc2aa8cc8cbd6 F src/json.c 7749b98c62f691697c7ee536b570c744c0583cab4a89200fdd0fc2aa8cc8cbd6
@@ -645,17 +645,17 @@ F src/printf.c e99ee9741e79ae3873458146f59644276657340385ade4e76a5f5d1c25793764
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
F src/resolve.c efea4e5fbecfd6d0a9071b0be0d952620991673391b6ffaaf4c277b0bb674633 F src/resolve.c efea4e5fbecfd6d0a9071b0be0d952620991673391b6ffaaf4c277b0bb674633
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
F src/select.c 6983de0e6b8b68c97f82f9fca27ffb8f17161cff4d0d48fdf9eb40b213da0cc6 F src/select.c 321f29e431fbb71e594cc7026391a827a0270237597d2e2401244e7602dea8bd
F src/shell.c.in f6ab148f150dc0c8460be74a61566d37c65d43311e84963cc1a58df3fc277511 F src/shell.c.in e7fc8db64df14d1893a86dce488770efdbb1007e44ec8cd5549769533bb419f2
F src/sqlite.h.in 14d1273e84a8a4d7cbfc044592c4f97ea04ecf59b2a684f3c7c1b2ab9e48ae0e F src/sqlite.h.in 1fe1836879ecbb2e28f00f44eb6092db09a2a06bf072af351c6c2466bd515496
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h c4b9fa7a7e2bcdf850cfeb4b8a91d5ec47b7a00033bc996fd2ee96cbf2741f5f F src/sqlite3ext.h c4b9fa7a7e2bcdf850cfeb4b8a91d5ec47b7a00033bc996fd2ee96cbf2741f5f
F src/sqliteInt.h 4ddd98e423276714479f9f22dbbda050e8ef99aa97e7e26bf0bdf58acef0ca42 F src/sqliteInt.h 0c9934acd88e0fa14f0d4743233b4cd7bd7bbc84099ba3cad50d8e69676ce2b9
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657 F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749 F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
F src/tclsqlite.c 4e64ba300a5a26e0f1170e09032429faeb65e45e8f3d1a7833e8edb69fc2979e F src/tclsqlite.c 4e64ba300a5a26e0f1170e09032429faeb65e45e8f3d1a7833e8edb69fc2979e
F src/test1.c 09ad3f2bf5f0efe70eb16e460054954b36be8ed09dfcafa0642b37d25d379f6f F src/test1.c 98f4a4525e10b0df164ba7f33950824348bb29fd582101e0f93b1af72f493154
F src/test2.c 827446e259a3b7ab949da1542953edda7b5117982576d3e6f1c24a0dd20a5cef F src/test2.c 827446e259a3b7ab949da1542953edda7b5117982576d3e6f1c24a0dd20a5cef
F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644 F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644
F src/test4.c 4533b76419e7feb41b40582554663ed3cd77aaa54e135cf76b3205098cd6e664 F src/test4.c 4533b76419e7feb41b40582554663ed3cd77aaa54e135cf76b3205098cd6e664
@@ -715,13 +715,13 @@ F src/trigger.c 5e68b790f022b8dafbfb0eb244786512a95c9575fc198719d2557d73e5795858
F src/update.c 5b0302c47cf31b533d5dff04c497ca1d8b9d89c39727e633fbe7b882fd5ac5aa F src/update.c 5b0302c47cf31b533d5dff04c497ca1d8b9d89c39727e633fbe7b882fd5ac5aa
F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145 F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
F src/util.c 4e42c338d982e3f139004467bf0f2e9d679d519c7c75718fcf4ff1811401b03c F src/util.c 313f3154e2b85a447326f5dd15de8d31a4df6ab0c3579bd58f426ff634ec9050
F src/vacuum.c 84ce7f01f8a7a08748e107a441db83bcec13970190ddcb0c9ff522adbc1c23fd F src/vacuum.c 84ce7f01f8a7a08748e107a441db83bcec13970190ddcb0c9ff522adbc1c23fd
F src/vdbe.c 00648bd76fb2145c2d890312112bda446569da64ca70aaf1f2282cc6c2b22d14 F src/vdbe.c e744259050ec9bbcd47acf9a03a66fe3305d7429c823995de11ed524ca06d16f
F src/vdbe.h 58675f47dcf3105bab182c3ad3726efd60ffd003e954386904ac9107d0d2b743 F src/vdbe.h 6d921884cf8ec6a53efba99f8b68e32e955367631743e29039840e781aaf547c
F src/vdbeInt.h 17b7461ffcf9ee760d1341731715a419f6b8c763089a7ece25c2e8098d702b3f F src/vdbeInt.h e83be1eea422758d42302941e96e59d93193c373da655a87befdc03a851e0f95
F src/vdbeapi.c b75fe402b8c0db8839aa9968f917bae49361a364ed28069b27b19ac7761e48b0 F src/vdbeapi.c 959cef4c1e5cb6589e67b3295fe9d3e45cbd109ae6618d57b74c900dbd6be0cd
F src/vdbeaux.c 8ebe337e82d99cf3b01cd4fd67103a5b0054d53fee8456b90dbeba46ebf97ceb F src/vdbeaux.c 0b81f317c86ed9f4ba822af66a52777fed6e8180edc79d4fc62ffe75c8e52d80
F src/vdbeblob.c 5e61ce31aca17db8fb60395407457a8c1c7fb471dde405e0cd675974611dcfcd F src/vdbeblob.c 5e61ce31aca17db8fb60395407457a8c1c7fb471dde405e0cd675974611dcfcd
F src/vdbemem.c 6cfed43758d57b6e3b99d9cdedfeccd86e45a07e427b22d8487cbdbebb6c522a F src/vdbemem.c 6cfed43758d57b6e3b99d9cdedfeccd86e45a07e427b22d8487cbdbebb6c522a
F src/vdbesort.c 43756031ca7430f7aec3ef904824a7883c4ede783e51f280d99b9b65c0796e35 F src/vdbesort.c 43756031ca7430f7aec3ef904824a7883c4ede783e51f280d99b9b65c0796e35
@@ -734,7 +734,7 @@ F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
F src/where.c 20f4f51d2d5fb19b984e6ea381b26cf627cc93e64dd9b2ce6a94531aec2f5916 F src/where.c 20f4f51d2d5fb19b984e6ea381b26cf627cc93e64dd9b2ce6a94531aec2f5916
F src/whereInt.h e25203e5bfee149f5f1225ae0166cfb4f1e65490c998a024249e98bb0647377c F src/whereInt.h e25203e5bfee149f5f1225ae0166cfb4f1e65490c998a024249e98bb0647377c
F src/wherecode.c ee52c2781c36004d23c85bf111063b78fc16e5e1b6a0d424326af8bf90babb0b F src/wherecode.c d3146e215f3a716c0e153ded68bfcc747aad550e114a79729bda887cf4ea6f00
F src/whereexpr.c 05295b44b54eea76d1ba766f0908928d0e20e990c249344c9521454d3d09c7ae F src/whereexpr.c 05295b44b54eea76d1ba766f0908928d0e20e990c249344c9521454d3d09c7ae
F src/window.c 14836767adb26573b50f528eb37f8b1336f2c430ab38de7cead1e5c546bb4d8c F src/window.c 14836767adb26573b50f528eb37f8b1336f2c430ab38de7cead1e5c546bb4d8c
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
@@ -1457,7 +1457,8 @@ F test/savepoint5.test 0735db177e0ebbaedc39812c8d065075d563c4fd
F test/savepoint6.test f41279c5e137139fa5c21485773332c7adb98cd7 F test/savepoint6.test f41279c5e137139fa5c21485773332c7adb98cd7
F test/savepoint7.test cde525ea3075283eb950cdcdefe23ead4f700daa F test/savepoint7.test cde525ea3075283eb950cdcdefe23ead4f700daa
F test/savepointfault.test f044eac64b59f09746c7020ee261734de82bf9b2 F test/savepointfault.test f044eac64b59f09746c7020ee261734de82bf9b2
F test/scanstatus.test 9a0ed37ab6d57b50567282788fffdf832d9b16739ecc41bff9d77a8d767cf317 F test/scanstatus.test 7dbcfd6adc6a8df6abc59f83d6da5a27e1bce0b2f6fa55147c8176d7c44e0450
F test/scanstatus2.test cc0be2f645c32ede50affa7d4ecfaffd2b4572a4fd7f723082397c65a910a2b5
F test/schema.test 5dd11c96ba64744de955315d2e4f8992e447533690153b93377dffb2a5ef5431 F test/schema.test 5dd11c96ba64744de955315d2e4f8992e447533690153b93377dffb2a5ef5431
F test/schema2.test 906408621ea881fdb496d878b1822572a34e32c5 F test/schema2.test 906408621ea881fdb496d878b1822572a34e32c5
F test/schema3.test 8ed4ae66e082cdd8b1b1f22d8549e1e7a0db4527a8e6ee8b6193053ee1e5c9ce F test/schema3.test 8ed4ae66e082cdd8b1b1f22d8549e1e7a0db4527a8e6ee8b6193053ee1e5c9ce
@@ -2066,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 15f0be8a640e7bfa4130edd4650a745337bd96083b119a1553f9abf9ff066806 P dc7dd2d3e50e7cc474b22f1b5b219da32bcd7aa1ba56864d1dbcf0d3a6fa06f2 009462f2344b1f468cf9440343a47fec68d783a2bfb4fa6168bb227ec910b918
R 0d5d7f5190e8e855ad343ae2d8e33bd7 R 6f58eb8b4613cbdc0da9cabf0de9170c
U dan U dan
Z 56bab95316a0c053d879db2cb26ee05d Z dd8e28132ec997579f72120c72f627ae
# Remove this line to create a well-formed Fossil manifest. # Remove this line to create a well-formed Fossil manifest.

View File

@@ -1 +1 @@
dc7dd2d3e50e7cc474b22f1b5b219da32bcd7aa1ba56864d1dbcf0d3a6fa06f2 4893b4e3eafc7c9c22b24717f90a585862203f987cf108b079ce6e946093e675

View File

@@ -3257,6 +3257,9 @@ int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
SelectDest dest; /* How to deal with SELECT result */ SelectDest dest; /* How to deal with SELECT result */
int nReg; /* Registers to allocate */ int nReg; /* Registers to allocate */
Expr *pLimit; /* New limit expression */ Expr *pLimit; /* New limit expression */
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
int addrExplain; /* Address of OP_Explain instruction */
#endif
Vdbe *v = pParse->pVdbe; Vdbe *v = pParse->pVdbe;
assert( v!=0 ); assert( v!=0 );
@@ -3309,8 +3312,9 @@ int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
** In both cases, the query is augmented with "LIMIT 1". Any ** In both cases, the query is augmented with "LIMIT 1". Any
** preexisting limit is discarded in place of the new LIMIT 1. ** 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)); addrOnce?"":"CORRELATED ", pSel->selId));
sqlite3VdbeScanStatusCounters(v, addrExplain, addrExplain, -1);
nReg = pExpr->op==TK_SELECT ? pSel->pEList->nExpr : 1; nReg = pExpr->op==TK_SELECT ? pSel->pEList->nExpr : 1;
sqlite3SelectDestInit(&dest, 0, pParse->nMem+1); sqlite3SelectDestInit(&dest, 0, pParse->nMem+1);
pParse->nMem += nReg; pParse->nMem += nReg;
@@ -3353,6 +3357,7 @@ int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
if( addrOnce ){ if( addrOnce ){
sqlite3VdbeJumpHere(v, addrOnce); sqlite3VdbeJumpHere(v, addrOnce);
} }
sqlite3VdbeScanStatusRange(v, addrExplain, addrExplain, -1);
/* Subroutine return */ /* Subroutine return */
assert( ExprUseYSub(pExpr) ); assert( ExprUseYSub(pExpr) );

View File

@@ -48,9 +48,9 @@
#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) #elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__))
__inline__ sqlite_uint64 sqlite3Hwtime(void){ __inline__ sqlite_uint64 sqlite3Hwtime(void){
unsigned long val; unsigned int lo, hi;
__asm__ __volatile__ ("rdtsc" : "=A" (val)); __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
return val; return (sqlite_uint64)hi << 32 | lo;
} }
#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) #elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__))

View File

@@ -65,6 +65,10 @@ struct SortCtx {
} aDefer[4]; } aDefer[4];
#endif #endif
struct RowLoadInfo *pDeferredRowLoad; /* Deferred row loading info or NULL */ struct RowLoadInfo *pDeferredRowLoad; /* Deferred row loading info or NULL */
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
int addrPush; /* First instruction to push data into sorter */
int addrPushEnd; /* Last instruction that pushes data into sorter */
#endif
}; };
#define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */ #define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */
@@ -721,6 +725,10 @@ static void pushOntoSorter(
*/ */
assert( nData==1 || regData==regOrigData || regOrigData==0 ); assert( nData==1 || regData==regOrigData || regOrigData==0 );
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
pSort->addrPush = sqlite3VdbeCurrentAddr(v);
#endif
if( nPrefixReg ){ if( nPrefixReg ){
assert( nPrefixReg==nExpr+bSeq ); assert( nPrefixReg==nExpr+bSeq );
regBase = regData - nPrefixReg; regBase = regData - nPrefixReg;
@@ -821,6 +829,9 @@ static void pushOntoSorter(
sqlite3VdbeChangeP2(v, iSkip, sqlite3VdbeChangeP2(v, iSkip,
pSort->labelOBLopt ? pSort->labelOBLopt : sqlite3VdbeCurrentAddr(v)); pSort->labelOBLopt ? pSort->labelOBLopt : sqlite3VdbeCurrentAddr(v));
} }
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
pSort->addrPushEnd = sqlite3VdbeCurrentAddr(v)-1;
#endif
} }
/* /*
@@ -1647,6 +1658,16 @@ static void generateSortTail(
int bSeq; /* True if sorter record includes seq. no. */ int bSeq; /* True if sorter record includes seq. no. */
int nRefKey = 0; int nRefKey = 0;
struct ExprList_item *aOutEx = p->pEList->a; struct ExprList_item *aOutEx = p->pEList->a;
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
int addrExplain; /* Address of OP_Explain instruction */
#endif
ExplainQueryPlan2(addrExplain, (pParse, 0,
"USE TEMP B-TREE FOR %sORDER BY", pSort->nOBSat>0?"RIGHT PART OF ":"")
);
sqlite3VdbeScanStatusRange(v, addrExplain,pSort->addrPush,pSort->addrPushEnd);
sqlite3VdbeScanStatusCounters(v, addrExplain, addrExplain, pSort->addrPush);
assert( addrBreak<0 ); assert( addrBreak<0 );
if( pSort->labelBkOut ){ if( pSort->labelBkOut ){
@@ -1759,6 +1780,7 @@ static void generateSortTail(
VdbeComment((v, "%s", aOutEx[i].zEName)); VdbeComment((v, "%s", aOutEx[i].zEName));
} }
} }
sqlite3VdbeScanStatusRange(v, addrExplain, addrExplain, -1);
switch( eDest ){ switch( eDest ){
case SRT_Table: case SRT_Table:
case SRT_EphemTab: { case SRT_EphemTab: {
@@ -1820,6 +1842,7 @@ static void generateSortTail(
}else{ }else{
sqlite3VdbeAddOp2(v, OP_Next, iTab, addr); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Next, iTab, addr); VdbeCoverage(v);
} }
sqlite3VdbeScanStatusRange(v, addrExplain, sqlite3VdbeCurrentAddr(v)-1, -1);
if( pSort->regReturn ) sqlite3VdbeAddOp1(v, OP_Return, pSort->regReturn); if( pSort->regReturn ) sqlite3VdbeAddOp1(v, OP_Return, pSort->regReturn);
sqlite3VdbeResolveLabel(v, addrBreak); sqlite3VdbeResolveLabel(v, addrBreak);
} }
@@ -7277,6 +7300,9 @@ int sqlite3Select(
** the same view can reuse the materialization. */ ** the same view can reuse the materialization. */
int topAddr; int topAddr;
int onceAddr = 0; int onceAddr = 0;
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
int addrExplain;
#endif
pItem->regReturn = ++pParse->nMem; pItem->regReturn = ++pParse->nMem;
topAddr = sqlite3VdbeAddOp0(v, OP_Goto); topAddr = sqlite3VdbeAddOp0(v, OP_Goto);
@@ -7292,7 +7318,8 @@ int sqlite3Select(
VdbeNoopComment((v, "materialize %!S", pItem)); VdbeNoopComment((v, "materialize %!S", pItem));
} }
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); 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); dest.zAffSdst = sqlite3TableAffinityStr(db, pItem->pTab);
sqlite3Select(pParse, pSub, &dest); sqlite3Select(pParse, pSub, &dest);
sqlite3DbFree(db, dest.zAffSdst); sqlite3DbFree(db, dest.zAffSdst);
@@ -7301,6 +7328,7 @@ int sqlite3Select(
if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr); if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr);
sqlite3VdbeAddOp2(v, OP_Return, pItem->regReturn, topAddr+1); sqlite3VdbeAddOp2(v, OP_Return, pItem->regReturn, topAddr+1);
VdbeComment((v, "end %!S", pItem)); VdbeComment((v, "end %!S", pItem));
sqlite3VdbeScanStatusRange(v, addrExplain, addrExplain, -1);
sqlite3VdbeJumpHere(v, topAddr); sqlite3VdbeJumpHere(v, topAddr);
sqlite3ClearTempRegCache(pParse); sqlite3ClearTempRegCache(pParse);
if( pItem->fg.isCte && pItem->fg.isCorrelated==0 ){ if( pItem->fg.isCte && pItem->fg.isCorrelated==0 ){
@@ -8055,8 +8083,6 @@ int sqlite3Select(
** and send them to the callback one by one. ** and send them to the callback one by one.
*/ */
if( sSort.pOrderBy ){ if( sSort.pOrderBy ){
explainTempTable(pParse,
sSort.nOBSat>0 ? "RIGHT PART OF ORDER BY":"ORDER BY");
assert( p->pEList==pEList ); assert( p->pEList==pEList );
generateSortTail(pParse, p, &sSort, pEList->nExpr, pDest); generateSortTail(pParse, p, &sSort, pEList->nExpr, pDest);
} }

View File

@@ -2082,7 +2082,7 @@ static void eqp_render_level(ShellState *p, int iEqpId){
/* /*
** Display and reset the EXPLAIN QUERY PLAN data ** 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; EQPGraphRow *pRow = p->sGraph.pRow;
if( pRow ){ if( pRow ){
if( pRow->zText[0]=='-' ){ if( pRow->zText[0]=='-' ){
@@ -2093,6 +2093,8 @@ static void eqp_render(ShellState *p){
utf8_printf(p->out, "%s\n", pRow->zText+3); utf8_printf(p->out, "%s\n", pRow->zText+3);
p->sGraph.pRow = pRow->pNext; p->sGraph.pRow = pRow->pNext;
sqlite3_free(pRow); sqlite3_free(pRow);
}else if( nCycle>0 ){
utf8_printf(p->out, "QUERY PLAN (cycles=%lld [100%%])\n", nCycle);
}else{ }else{
utf8_printf(p->out, "QUERY PLAN\n"); utf8_printf(p->out, "QUERY PLAN\n");
} }
@@ -2970,6 +2972,35 @@ static int display_stats(
return 0; 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. ** Display scan stats.
*/ */
@@ -2981,40 +3012,70 @@ static void display_scanstats(
UNUSED_PARAMETER(db); UNUSED_PARAMETER(db);
UNUSED_PARAMETER(pArg); UNUSED_PARAMETER(pArg);
#else #else
int i, k, n, mx; static const int f = SQLITE_SCANSTAT_COMPLEX;
raw_printf(pArg->out, "-------- scanstats --------\n"); sqlite3_stmt *p = pArg->pStmt;
mx = 0; int ii = 0;
for(k=0; k<=mx; k++){ i64 nTotal = 0;
double rEstLoop = 1.0; int nWidth = 0;
for(i=n=0; 1; i++){ eqp_reset(pArg);
sqlite3_stmt *p = pArg->pStmt;
sqlite3_int64 nLoop, nVisit; for(ii=0; 1; ii++){
double rEst; const char *z = 0;
int iSid; int n = 0;
const char *zExplain; if( sqlite3_stmt_scanstatus_v2(p,ii,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&z) ){
if( sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_NLOOP, (void*)&nLoop) ){ break;
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 && nTotal>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( nLoop>=0 ){
if( iSid>mx ) mx = iSid; z = sqlite3_mprintf("%z%sloops=%lld", z, z ? " " : "", nLoop);
if( iSid!=k ) continue;
if( n==0 ){
rEstLoop = (double)nLoop;
if( k>0 ) raw_printf(pArg->out, "-------- subquery %d -------\n", k);
} }
n++; if( nRow>=0 ){
sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_NVISIT, (void*)&nVisit); z = sqlite3_mprintf("%z%srows=%lld", z, z ? " " : "", nRow);
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); zText = sqlite3_mprintf(
rEstLoop *= rEst; "% *z (%z)", -1*(nWidth-scanStatsHeight(p, ii)*3), zText, z
raw_printf(pArg->out,
" nLoop=%-8lld nRow=%-8lld estRow=%-8lld estRow/Loop=%-8g\n",
nLoop, nVisit, (sqlite3_int64)(rEstLoop+0.5), rEst
); );
} }
eqp_append(pArg, iId, iPid, zText);
sqlite3_free(zText);
} }
raw_printf(pArg->out, "---------------------------\n");
eqp_render(pArg, nTotal);
#endif #endif
} }
@@ -3938,10 +3999,10 @@ static int shell_exec(
int iEqpId = sqlite3_column_int(pExplain, 0); int iEqpId = sqlite3_column_int(pExplain, 0);
int iParentId = sqlite3_column_int(pExplain, 1); int iParentId = sqlite3_column_int(pExplain, 1);
if( zEQPLine==0 ) zEQPLine = ""; if( zEQPLine==0 ) zEQPLine = "";
if( zEQPLine[0]=='-' ) eqp_render(pArg); if( zEQPLine[0]=='-' ) eqp_render(pArg, 0);
eqp_append(pArg, iEqpId, iParentId, zEQPLine); eqp_append(pArg, iEqpId, iParentId, zEQPLine);
} }
eqp_render(pArg); eqp_render(pArg, 0);
} }
sqlite3_finalize(pExplain); sqlite3_finalize(pExplain);
sqlite3_free(zEQP); sqlite3_free(zEQP);
@@ -3990,7 +4051,7 @@ static int shell_exec(
bind_prepared_stmt(pArg, pStmt); bind_prepared_stmt(pArg, pStmt);
exec_prepared_stmt(pArg, pStmt); exec_prepared_stmt(pArg, pStmt);
explain_data_delete(pArg); explain_data_delete(pArg);
eqp_render(pArg); eqp_render(pArg, 0);
/* print usage stats if stats on */ /* print usage stats if stats on */
if( pArg && pArg->statsOn ){ if( pArg && pArg->statsOn ){

View File

@@ -9886,6 +9886,10 @@ int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **ppVal);
** managed by the prepared statement S and will be automatically freed when ** managed by the prepared statement S and will be automatically freed when
** S is finalized. ** S is finalized.
** **
** Not all values are available for all query elements. When a value is
** not available, the output variable is set to -1 if the value is numeric,
** or to NULL if it is a string (SQLITE_SCANSTAT_NAME).
**
** <dl> ** <dl>
** [[SQLITE_SCANSTAT_NLOOP]] <dt>SQLITE_SCANSTAT_NLOOP</dt> ** [[SQLITE_SCANSTAT_NLOOP]] <dt>SQLITE_SCANSTAT_NLOOP</dt>
** <dd>^The [sqlite3_int64] variable pointed to by the V parameter will be ** <dd>^The [sqlite3_int64] variable pointed to by the V parameter will be
@@ -9913,13 +9917,25 @@ int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **ppVal);
** to a zero-terminated UTF-8 string containing the [EXPLAIN QUERY PLAN] ** to a zero-terminated UTF-8 string containing the [EXPLAIN QUERY PLAN]
** description for the X-th loop. ** description for the X-th loop.
** **
** [[SQLITE_SCANSTAT_SELECTID]] <dt>SQLITE_SCANSTAT_SELECT</dt> ** [[SQLITE_SCANSTAT_SELECTID]] <dt>SQLITE_SCANSTAT_SELECTID</dt>
** <dd>^The "int" variable pointed to by the V parameter will be set to the ** <dd>^The "int" variable pointed to by the V parameter will be set to the
** "select-id" for the X-th loop. The select-id identifies which query or ** id for the X-th query plan element. The id value is unique within the
** subquery the loop is part of. The main query has a select-id of zero. ** statement. The select-id is the same value as is output in the first
** The select-id is the same value as is output in the first column ** column of an [EXPLAIN QUERY PLAN] query.
** of an [EXPLAIN QUERY PLAN] query.
** </dl> ** </dl>
**
** [[SQLITE_SCANSTAT_PARENTID]] <dt>SQLITE_SCANSTAT_PARENTID</dt>
** <dd>The "int" variable pointed to by the V parameter will be set to the
** the id of the parent of the current query element, if applicable, or
** to zero if the query element has no parent. This is the same value as
** returned in the second column of an [EXPLAIN QUERY PLAN] query.
**
** [[SQLITE_SCANSTAT_NCYCLE]] <dt>SQLITE_SCANSTAT_NCYCLE</dt>
** <dd>The sqlite3_int64 output value is set to the number of cycles,
** according to the processor time-stamp counter, that elapsed while the
** query element was being processed. This value is not available for
** all query elements - if it is unavailable the output variable is
** set to -1.
*/ */
#define SQLITE_SCANSTAT_NLOOP 0 #define SQLITE_SCANSTAT_NLOOP 0
#define SQLITE_SCANSTAT_NVISIT 1 #define SQLITE_SCANSTAT_NVISIT 1
@@ -9927,12 +9943,14 @@ int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **ppVal);
#define SQLITE_SCANSTAT_NAME 3 #define SQLITE_SCANSTAT_NAME 3
#define SQLITE_SCANSTAT_EXPLAIN 4 #define SQLITE_SCANSTAT_EXPLAIN 4
#define SQLITE_SCANSTAT_SELECTID 5 #define SQLITE_SCANSTAT_SELECTID 5
#define SQLITE_SCANSTAT_PARENTID 6
#define SQLITE_SCANSTAT_NCYCLE 7
/* /*
** CAPI3REF: Prepared Statement Scan Status ** CAPI3REF: Prepared Statement Scan Status
** METHOD: sqlite3_stmt ** METHOD: sqlite3_stmt
** **
** This interface returns information about the predicted and measured ** These interfaces return information about the predicted and measured
** performance for pStmt. Advanced applications can use this ** performance for pStmt. Advanced applications can use this
** interface to compare the predicted and the measured performance and ** interface to compare the predicted and the measured performance and
** issue warnings and/or rerun [ANALYZE] if discrepancies are found. ** issue warnings and/or rerun [ANALYZE] if discrepancies are found.
@@ -9943,19 +9961,25 @@ int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **ppVal);
** **
** The "iScanStatusOp" parameter determines which status information to return. ** The "iScanStatusOp" parameter determines which status information to return.
** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior ** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior
** of this interface is undefined. ** of this interface is undefined. ^The requested measurement is written into
** ^The requested measurement is written into a variable pointed to by ** a variable pointed to by the "pOut" parameter.
** the "pOut" parameter.
** Parameter "idx" identifies the specific loop to retrieve statistics for.
** Loops are numbered starting from zero. ^If idx is out of range - less than
** zero or greater than or equal to the total number of loops used to implement
** the statement - a non-zero value is returned and the variable that pOut
** points to is unchanged.
** **
** ^Statistics might not be available for all loops in all statements. ^In cases ** The "flags" parameter must be passed a mask of flags. At present only
** where there exist loops with no available statistics, this function behaves ** one flag is defined - SQLITE_SCANSTAT_COMPLEX. If SQLITE_SCANSTAT_COMPLEX
** as if the loop did not exist - it returns non-zero and leave the variable ** is specified, then status information is available for all elements
** that pOut points to unchanged. ** of a query plan that are reported by "EXPLAIN QUERY PLAN" output. If
** SQLITE_SCANSTAT_COMPLEX is not specified, then only query plan elements
** that correspond to query loops (the "SCAN..." and "SEARCH..." elements of
** the EXPLAIN QUERY PLAN output) are available. Invoking API
** sqlite3_stmt_scanstatus() is equivalent to calling
** sqlite3_stmt_scanstatus_v2() with a zeroed flags parameter.
**
** Parameter "idx" identifies the specific query element to retrieve statistics
** for. Query elements are numbered starting from zero. A value of -1 may be
** to query for statistics regarding the entire query. ^If idx is out of range
** - less than -1 or greater than or equal to the total number of query
** elements used to implement the statement - a non-zero value is returned and
** the variable that pOut points to is unchanged.
** **
** See also: [sqlite3_stmt_scanstatus_reset()] ** See also: [sqlite3_stmt_scanstatus_reset()]
*/ */
@@ -9965,6 +9989,19 @@ int sqlite3_stmt_scanstatus(
int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */
void *pOut /* Result written here */ 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 */
);
/*
** CAPI3REF: Prepared Statement Scan Status
** KEYWORDS: {scan status flags}
*/
#define SQLITE_SCANSTAT_COMPLEX 0x0001
/* /*
** CAPI3REF: Zero Scan-Status Counters ** CAPI3REF: Zero Scan-Status Counters

View File

@@ -5565,7 +5565,9 @@ const char **sqlite3CompileOptions(int *pnOpt);
int sqlite3KvvfsInit(void); int sqlite3KvvfsInit(void);
#endif #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); sqlite3_uint64 sqlite3Hwtime(void);
#endif #endif

View File

@@ -2188,7 +2188,7 @@ static int SQLITE_TCLAPI test_stmt_status(
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS #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( static int SQLITE_TCLAPI test_stmt_scanstatus(
void * clientData, void * clientData,
@@ -2203,36 +2203,99 @@ static int SQLITE_TCLAPI test_stmt_scanstatus(
const char *zExplain; const char *zExplain;
sqlite3_int64 nLoop; sqlite3_int64 nLoop;
sqlite3_int64 nVisit; sqlite3_int64 nVisit;
sqlite3_int64 nCycle;
double rEst; double rEst;
int res; int res;
int flags = 0;
int iSelectId = 0;
int iParentId = 0;
if( objc!=3 ){ if( objc==5 ){
Tcl_WrongNumArgs(interp, 1, objv, "STMT IDX"); 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; 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( idx<0 ){
if( res==0 ){
Tcl_Obj *pRet = Tcl_NewObj(); Tcl_Obj *pRet = Tcl_NewObj();
Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("nLoop", -1)); res = sqlite3_stmt_scanstatus_v2(
Tcl_ListObjAppendElement(0, pRet, Tcl_NewWideIntObj(nLoop)); pStmt, -1, SQLITE_SCANSTAT_NCYCLE, flags, (void*)&nCycle
sqlite3_stmt_scanstatus(pStmt, idx, SQLITE_SCANSTAT_NVISIT, (void*)&nVisit); );
Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("nVisit", -1)); sqlite3_stmt_scanstatus_v2(
Tcl_ListObjAppendElement(0, pRet, Tcl_NewWideIntObj(nVisit)); pStmt, idx, SQLITE_SCANSTAT_NCYCLE, flags, (void*)&nCycle);
sqlite3_stmt_scanstatus(pStmt, idx, SQLITE_SCANSTAT_EST, (void*)&rEst); Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("nCycle", -1));
Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("nEst", -1)); Tcl_ListObjAppendElement(0, pRet, Tcl_NewWideIntObj(nCycle));
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));
Tcl_SetObjResult(interp, pRet); Tcl_SetObjResult(interp, pRet);
}else{ }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; return TCL_OK;
} }

View File

@@ -1717,6 +1717,8 @@ int sqlite3VListNameToNum(VList *pIn, const char *zName, int nName){
/* /*
** High-resolution hardware timer used for debugging and testing only. ** 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" # include "hwtime.h"
#endif #endif

View File

@@ -617,7 +617,6 @@ void sqlite3VdbeRegisterDump(Vdbe *v){
# define REGISTER_TRACE(R,M) # define REGISTER_TRACE(R,M)
#endif #endif
#ifndef NDEBUG #ifndef NDEBUG
/* /*
** This function is only called from within an assert() expression. It ** 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 *aOp = p->aOp; /* Copy of p->aOp */
Op *pOp = aOp; /* Current operation */ 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 #ifdef SQLITE_DEBUG
Op *pOrigOp; /* Value of pOp at the top of the loop */
int nExtraDelete = 0; /* Verifies FORDELETE and AUXDELETE flags */ int nExtraDelete = 0; /* Verifies FORDELETE and AUXDELETE flags */
#endif #endif
int rc = SQLITE_OK; /* Value to return */ int rc = SQLITE_OK; /* Value to return */
@@ -727,8 +724,8 @@ int sqlite3VdbeExec(
Mem *pIn2 = 0; /* 2nd input operand */ Mem *pIn2 = 0; /* 2nd input operand */
Mem *pIn3 = 0; /* 3rd input operand */ Mem *pIn3 = 0; /* 3rd input operand */
Mem *pOut = 0; /* Output operand */ Mem *pOut = 0; /* Output operand */
#ifdef VDBE_PROFILE #if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
u64 start; /* CPU clock count at start of opcode */ u64 *pnCycle = 0;
#endif #endif
/*** INSERT STACK UNION HERE ***/ /*** INSERT STACK UNION HERE ***/
@@ -791,12 +788,17 @@ int sqlite3VdbeExec(
assert( rc==SQLITE_OK ); assert( rc==SQLITE_OK );
assert( pOp>=aOp && pOp<&aOp[p->nOp]); assert( pOp>=aOp && pOp<&aOp[p->nOp]);
#ifdef VDBE_PROFILE
start = sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime();
#endif
nVmStep++; nVmStep++;
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS #if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
if( p->anExec ) p->anExec[(int)(pOp-aOp)]++; 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 #endif
/* Only allow tracing if SQLITE_DEBUG is defined. /* Only allow tracing if SQLITE_DEBUG is defined.
@@ -858,7 +860,7 @@ int sqlite3VdbeExec(
} }
} }
#endif #endif
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) #ifdef SQLITE_DEBUG
pOrigOp = pOp; pOrigOp = pOp;
#endif #endif
@@ -7150,8 +7152,9 @@ case OP_Program: { /* jump */
pFrame->aOp = p->aOp; pFrame->aOp = p->aOp;
pFrame->nOp = p->nOp; pFrame->nOp = p->nOp;
pFrame->token = pProgram->token; pFrame->token = pProgram->token;
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS #if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
pFrame->anExec = p->anExec; pFrame->anExec = p->anExec;
pFrame->anCycle = p->anCycle;
#endif #endif
#ifdef SQLITE_DEBUG #ifdef SQLITE_DEBUG
pFrame->iFrameMagic = SQLITE_FRAME_MAGIC; pFrame->iFrameMagic = SQLITE_FRAME_MAGIC;
@@ -7189,8 +7192,9 @@ case OP_Program: { /* jump */
memset(pFrame->aOnce, 0, (pProgram->nOp + 7)/8); memset(pFrame->aOnce, 0, (pProgram->nOp + 7)/8);
p->aOp = aOp = pProgram->aOp; p->aOp = aOp = pProgram->aOp;
p->nOp = pProgram->nOp; p->nOp = pProgram->nOp;
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS #if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
p->anExec = 0; p->anExec = 0;
p->anCycle = 0;
#endif #endif
#ifdef SQLITE_DEBUG #ifdef SQLITE_DEBUG
/* Verify that second and subsequent executions of the same trigger do not /* Verify that second and subsequent executions of the same trigger do not
@@ -8727,11 +8731,17 @@ default: { /* This is really OP_Noop, OP_Explain */
*****************************************************************************/ *****************************************************************************/
} }
#ifdef VDBE_PROFILE #if defined(VDBE_PROFILE)
{ assert( pnCycle );
u64 endTime = sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime(); if( pnCycle ){
if( endTime>start ) pOrigOp->cycles += endTime - start; *pnCycle += sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime();
pOrigOp->cnt++; assert( *pnCycle < (((u64)1) << 48) );
pnCycle = 0;
}
#elif defined(SQLITE_ENABLE_STMT_SCANSTATUS)
if( pnCycle ){
*pnCycle += sqlite3Hwtime();
pnCycle = 0;
} }
#endif #endif
@@ -8808,6 +8818,19 @@ abort_due_to_error:
** release the mutexes on btrees that were acquired at the ** release the mutexes on btrees that were acquired at the
** top. */ ** top. */
vdbe_return: vdbe_return:
#if defined(VDBE_PROFILE)
if( pnCycle ){
*pnCycle += sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime();
assert( *pnCycle < (((u64)1) << 48) );
pnCycle = 0;
}
#elif defined(SQLITE_ENABLE_STMT_SCANSTATUS)
if( pnCycle ){
*pnCycle += sqlite3Hwtime();
pnCycle = 0;
}
#endif
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK #ifndef SQLITE_OMIT_PROGRESS_CALLBACK
while( nVmStep>=nProgressLimit && db->xProgress!=0 ){ while( nVmStep>=nProgressLimit && db->xProgress!=0 ){
nProgressLimit += db->nProgressOps; nProgressLimit += db->nProgressOps;

View File

@@ -67,10 +67,6 @@ struct VdbeOp {
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
char *zComment; /* Comment to improve readability */ char *zComment; /* Comment to improve readability */
#endif #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 #ifdef SQLITE_VDBE_COVERAGE
u32 iSrcLine; /* Source-code line that generated this opcode u32 iSrcLine; /* Source-code line that generated this opcode
** with flags in the upper 8 bits */ ** with flags in the upper 8 bits */
@@ -205,14 +201,20 @@ void sqlite3VdbeEndCoroutine(Vdbe*,int);
#endif #endif
VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp,int iLineno); VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp,int iLineno);
#ifndef SQLITE_OMIT_EXPLAIN #ifndef SQLITE_OMIT_EXPLAIN
void sqlite3VdbeExplain(Parse*,u8,const char*,...); int sqlite3VdbeExplain(Parse*,u8,const char*,...);
void sqlite3VdbeExplainPop(Parse*); void sqlite3VdbeExplainPop(Parse*);
int sqlite3VdbeExplainParent(Parse*); int sqlite3VdbeExplainParent(Parse*);
# define ExplainQueryPlan(P) sqlite3VdbeExplain P # 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 ExplainQueryPlanPop(P) sqlite3VdbeExplainPop(P)
# define ExplainQueryPlanParent(P) sqlite3VdbeExplainParent(P) # define ExplainQueryPlanParent(P) sqlite3VdbeExplainParent(P)
#else #else
# define ExplainQueryPlan(P) # define ExplainQueryPlan(P)
# define ExplainQueryPlan2(V,P)
# define ExplainQueryPlanPop(P) # define ExplainQueryPlanPop(P)
# define ExplainQueryPlanParent(P) 0 # define ExplainQueryPlanParent(P) 0
# define sqlite3ExplainBreakpoint(A,B) /*no-op*/ # define sqlite3ExplainBreakpoint(A,B) /*no-op*/
@@ -385,8 +387,12 @@ int sqlite3VdbeBytecodeVtabInit(sqlite3*);
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS #ifdef SQLITE_ENABLE_STMT_SCANSTATUS
void sqlite3VdbeScanStatus(Vdbe*, int, int, int, LogEst, const char*); void sqlite3VdbeScanStatus(Vdbe*, int, int, int, LogEst, const char*);
void sqlite3VdbeScanStatusRange(Vdbe*, int, int, int);
void sqlite3VdbeScanStatusCounters(Vdbe*, int, int, int);
#else #else
# define sqlite3VdbeScanStatus(a,b,c,d,e) # define sqlite3VdbeScanStatus(a,b,c,d,e,f)
# define sqlite3VdbeScanStatusRange(a,b,c,d)
# define sqlite3VdbeScanStatusCounters(a,b,c,d)
#endif #endif
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) #if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)

View File

@@ -172,6 +172,7 @@ struct VdbeFrame {
VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */ VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */
Op *aOp; /* Program instructions for parent frame */ Op *aOp; /* Program instructions for parent frame */
i64 *anExec; /* Event counters from 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 */ Mem *aMem; /* Array of memory cells for parent frame */
VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */ VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */
u8 *aOnce; /* Bitmask used by OP_Once */ u8 *aOnce; /* Bitmask used by OP_Once */
@@ -387,10 +388,19 @@ typedef unsigned bft; /* Bit Field Type */
/* The ScanStatus object holds a single value for the /* The ScanStatus object holds a single value for the
** sqlite3_stmt_scanstatus() interface. ** sqlite3_stmt_scanstatus() interface.
**
** aAddrRange[]:
** This array is used by ScanStatus elements associated with EQP
** notes that make an SQLITE_SCANSTAT_NCYCLE value available. It is
** an array of up to 3 ranges of VM addresses for which the Vdbe.anCycle[]
** values should be summed to calculate the NCYCLE value. Each pair of
** integer addresses is a start and end address (both inclusive) for a range
** instructions. A start value of 0 indicates an empty range.
*/ */
typedef struct ScanStatus ScanStatus; typedef struct ScanStatus ScanStatus;
struct ScanStatus { struct ScanStatus {
int addrExplain; /* OP_Explain for loop */ int addrExplain; /* OP_Explain for loop */
int aAddrRange[6];
int addrLoop; /* Address of "loops" counter */ int addrLoop; /* Address of "loops" counter */
int addrVisit; /* Address of "rows visited" counter */ int addrVisit; /* Address of "rows visited" counter */
int iSelectID; /* The "Select-ID" for this loop */ int iSelectID; /* The "Select-ID" for this loop */
@@ -482,8 +492,11 @@ struct Vdbe {
u32 expmask; /* Binding to these vars invalidates VM */ u32 expmask; /* Binding to these vars invalidates VM */
SubProgram *pProgram; /* Linked list of all sub-programs used by VM */ SubProgram *pProgram; /* Linked list of all sub-programs used by VM */
AuxData *pAuxData; /* Linked list of auxdata allocations */ 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 */ 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[] */ int nScan; /* Entries in aScan[] */
ScanStatus *aScan; /* Scan definitions for sqlite3_stmt_scanstatus() */ ScanStatus *aScan; /* Scan definitions for sqlite3_stmt_scanstatus() */
#endif #endif

View 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. ** 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 */ 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 iScanStatusOp, /* Which metric to return */
int flags,
void *pOut /* OUT: Write the answer here */ void *pOut /* OUT: Write the answer here */
){ ){
Vdbe *p = (Vdbe*)pStmt; Vdbe *p = (Vdbe*)pStmt;
ScanStatus *pScan; ScanStatus *pScan;
if( idx<0 || idx>=p->nScan ) return 1; int idx;
pScan = &p->aScan[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->zName ){
iScan--;
if( iScan<0 ) break;
}
}
}
if( idx>=p->nScan ) return 1;
switch( iScanStatusOp ){ switch( iScanStatusOp ){
case SQLITE_SCANSTAT_NLOOP: { 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; break;
} }
case SQLITE_SCANSTAT_NVISIT: { 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; break;
} }
case SQLITE_SCANSTAT_EST: { case SQLITE_SCANSTAT_EST: {
@@ -2160,6 +2197,48 @@ int sqlite3_stmt_scanstatus(
} }
break; 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 = 0;
if( pScan->aAddrRange[0]==0 ){
res = -1;
}else{
int ii;
for(ii=0; ii<ArraySize(pScan->aAddrRange); ii+=2){
int iIns = pScan->aAddrRange[ii];
int iEnd = pScan->aAddrRange[ii+1];
if( iIns==0 ) break;
if( iIns>0 ){
while( iIns<=iEnd ){
res += p->anCycle[iIns];
iIns++;
}
}else{
int iOp;
for(iOp=0; iOp<p->nOp; iOp++){
Op *pOp = &p->aOp[iOp];
if( pOp->p1!=iEnd ) continue;
if( pOp->opcode!=OP_VFilter && pOp->opcode!=OP_VColumn
&& pOp->opcode!=OP_Rowid && pOp->opcode!=OP_VOpen
&& pOp->opcode!=OP_VNext
){
continue;
}
res += p->anCycle[iOp];
}
}
}
}
*(i64*)pOut = res;
break;
}
default: { default: {
return 1; return 1;
} }
@@ -2167,11 +2246,24 @@ int sqlite3_stmt_scanstatus(
return 0; 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. ** Zero all counters associated with the sqlite3_stmt_scanstatus() data.
*/ */
void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){ void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){
Vdbe *p = (Vdbe*)pStmt; Vdbe *p = (Vdbe*)pStmt;
memset(p->anExec, 0, p->nOp * sizeof(i64)); memset(p->anExec, 0, p->nOp * sizeof(i64));
memset(p->anCycle, 0, p->nOp * sizeof(u64));
} }
#endif /* SQLITE_ENABLE_STMT_SCANSTATUS */ #endif /* SQLITE_ENABLE_STMT_SCANSTATUS */

View File

@@ -268,10 +268,6 @@ int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){
test_addop_breakpoint(i, &p->aOp[i]); test_addop_breakpoint(i, &p->aOp[i]);
} }
#endif #endif
#ifdef VDBE_PROFILE
pOp->cycles = 0;
pOp->cnt = 0;
#endif
#ifdef SQLITE_VDBE_COVERAGE #ifdef SQLITE_VDBE_COVERAGE
pOp->iSrcLine = 0; pOp->iSrcLine = 0;
#endif #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 ** If the bPush flag is true, then make this opcode the parent for
** subsequent Explains until sqlite3VdbeExplainPop() is called. ** subsequent Explains until sqlite3VdbeExplainPop() is called.
*/ */
void sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){ int sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){
#ifndef SQLITE_DEBUG int addr = 0;
#if !defined(SQLITE_DEBUG) && !defined(SQLITE_ENABLE_STMT_SCANSTATUS)
/* Always include the OP_Explain opcodes if SQLITE_DEBUG is defined. /* Always include the OP_Explain opcodes if SQLITE_DEBUG is defined.
** But omit them (for performance) during production builds */ ** But omit them (for performance) during production builds */
if( pParse->explain==2 ) if( pParse->explain==2 )
@@ -455,13 +452,15 @@ void sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){
va_end(ap); va_end(ap);
v = pParse->pVdbe; v = pParse->pVdbe;
iThis = v->nOp; iThis = v->nOp;
sqlite3VdbeAddOp4(v, OP_Explain, iThis, pParse->addrExplain, 0, addr = sqlite3VdbeAddOp4(v, OP_Explain, iThis, pParse->addrExplain, 0,
zMsg, P4_DYNAMIC); zMsg, P4_DYNAMIC);
sqlite3ExplainBreakpoint(bPush?"PUSH":"", sqlite3VdbeGetLastOp(v)->p4.z); sqlite3ExplainBreakpoint(bPush?"PUSH":"", sqlite3VdbeGetLastOp(v)->p4.z);
if( bPush){ if( bPush){
pParse->addrExplain = iThis; 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); aNew = (ScanStatus*)sqlite3DbRealloc(p->db, p->aScan, nByte);
if( aNew ){ if( aNew ){
ScanStatus *pNew = &aNew[p->nScan++]; ScanStatus *pNew = &aNew[p->nScan++];
memset(pNew, 0, sizeof(ScanStatus));
pNew->addrExplain = addrExplain; pNew->addrExplain = addrExplain;
pNew->addrLoop = addrLoop; pNew->addrLoop = addrLoop;
pNew->addrVisit = addrVisit; pNew->addrVisit = addrVisit;
@@ -1127,6 +1127,62 @@ void sqlite3VdbeScanStatus(
p->aScan = aNew; p->aScan = aNew;
} }
} }
/*
** Add the range of instructions from addrStart to addrEnd (inclusive) to
** the set of those corresponding to the sqlite3_stmt_scanstatus() counters
** associated with the OP_Explain instruction at addrExplain. The
** sum of the sqlite3Hwtime() values for each of these instructions
** will be returned for SQLITE_SCANSTAT_NCYCLE requests.
*/
void sqlite3VdbeScanStatusRange(
Vdbe *p,
int addrExplain,
int addrStart,
int addrEnd
){
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 ){
if( addrEnd<0 ) addrEnd = sqlite3VdbeCurrentAddr(p)-1;
for(ii=0; ii<ArraySize(pScan->aAddrRange); ii+=2){
if( pScan->aAddrRange[ii]==0 ){
pScan->aAddrRange[ii] = addrStart;
pScan->aAddrRange[ii+1] = addrEnd;
break;
}
}
}
}
/*
** Set the addresses for the SQLITE_SCANSTAT_NLOOP and SQLITE_SCANSTAT_NROW
** counters for the query element associated with the OP_Explain at
** addrExplain.
*/
void sqlite3VdbeScanStatusCounters(
Vdbe *p,
int addrExplain,
int addrLoop,
int addrVisit
){
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->addrLoop = addrLoop;
pScan->addrVisit = addrVisit;
}
}
#endif #endif
@@ -2424,7 +2480,7 @@ static void *allocSpace(
** running it. ** running it.
*/ */
void sqlite3VdbeRewind(Vdbe *p){ void sqlite3VdbeRewind(Vdbe *p){
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) #if defined(SQLITE_DEBUG)
int i; int i;
#endif #endif
assert( p!=0 ); assert( p!=0 );
@@ -2452,10 +2508,8 @@ void sqlite3VdbeRewind(Vdbe *p){
p->iStatement = 0; p->iStatement = 0;
p->nFkConstraint = 0; p->nFkConstraint = 0;
#ifdef VDBE_PROFILE #ifdef VDBE_PROFILE
for(i=0; i<p->nOp; i++){ memset(p->anExec, 0, sizeof(i64)*p->nOp);
p->aOp[i].cnt = 0; memset(p->anCycle, 0, sizeof(u64)*p->nOp);
p->aOp[i].cycles = 0;
}
#endif #endif
} }
@@ -2563,8 +2617,9 @@ void sqlite3VdbeMakeReady(
p->aVar = allocSpace(&x, 0, nVar*sizeof(Mem)); p->aVar = allocSpace(&x, 0, nVar*sizeof(Mem));
p->apArg = allocSpace(&x, 0, nArg*sizeof(Mem*)); p->apArg = allocSpace(&x, 0, nArg*sizeof(Mem*));
p->apCsr = allocSpace(&x, 0, nCursor*sizeof(VdbeCursor*)); 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->anExec = allocSpace(&x, 0, p->nOp*sizeof(i64));
p->anCycle = allocSpace(&x, 0, p->nOp*sizeof(u64));
#endif #endif
if( x.nNeeded ){ if( x.nNeeded ){
x.pSpace = p->pFree = sqlite3DbMallocRawNN(db, x.nNeeded); x.pSpace = p->pFree = sqlite3DbMallocRawNN(db, x.nNeeded);
@@ -2574,8 +2629,9 @@ void sqlite3VdbeMakeReady(
p->aVar = allocSpace(&x, p->aVar, nVar*sizeof(Mem)); p->aVar = allocSpace(&x, p->aVar, nVar*sizeof(Mem));
p->apArg = allocSpace(&x, p->apArg, nArg*sizeof(Mem*)); p->apArg = allocSpace(&x, p->apArg, nArg*sizeof(Mem*));
p->apCsr = allocSpace(&x, p->apCsr, nCursor*sizeof(VdbeCursor*)); 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->anExec = allocSpace(&x, p->anExec, p->nOp*sizeof(i64));
p->anCycle = allocSpace(&x, p->anCycle, p->nOp*sizeof(u64));
#endif #endif
} }
} }
@@ -2591,8 +2647,9 @@ void sqlite3VdbeMakeReady(
p->nMem = nMem; p->nMem = nMem;
initMemArray(p->aMem, nMem, db, MEM_Undefined); initMemArray(p->aMem, nMem, db, MEM_Undefined);
memset(p->apCsr, 0, nCursor*sizeof(VdbeCursor*)); 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->anExec, 0, p->nOp*sizeof(i64));
memset(p->anCycle, 0, p->nOp*sizeof(u64));
#endif #endif
} }
sqlite3VdbeRewind(p); sqlite3VdbeRewind(p);
@@ -2651,8 +2708,9 @@ static void closeCursorsInFrame(Vdbe *p){
int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){ int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
Vdbe *v = pFrame->v; Vdbe *v = pFrame->v;
closeCursorsInFrame(v); closeCursorsInFrame(v);
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS #if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
v->anExec = pFrame->anExec; v->anExec = pFrame->anExec;
v->anCycle = pFrame->anCycle;
#endif #endif
v->aOp = pFrame->aOp; v->aOp = pFrame->aOp;
v->nOp = pFrame->nOp; v->nOp = pFrame->nOp;
@@ -3485,10 +3543,12 @@ int sqlite3VdbeReset(Vdbe *p){
} }
for(i=0; i<p->nOp; i++){ for(i=0; i<p->nOp; i++){
char zHdr[100]; char zHdr[100];
i64 cnt = p->anExec[i];
i64 cycles = p->anCycle[i];
sqlite3_snprintf(sizeof(zHdr), zHdr, "%6u %12llu %8llu ", sqlite3_snprintf(sizeof(zHdr), zHdr, "%6u %12llu %8llu ",
p->aOp[i].cnt, cnt,
p->aOp[i].cycles, cycles,
p->aOp[i].cnt>0 ? p->aOp[i].cycles/p->aOp[i].cnt : 0 cnt>0 ? cycles/cnt : 0
); );
fprintf(out, "%s", zHdr); fprintf(out, "%s", zHdr);
sqlite3VdbePrintOp(out, i, &p->aOp[i]); sqlite3VdbePrintOp(out, i, &p->aOp[i]);

View File

@@ -270,6 +270,8 @@ int sqlite3WhereExplainBloomFilter(
zMsg = sqlite3StrAccumFinish(&str); zMsg = sqlite3StrAccumFinish(&str);
ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v), ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v),
pParse->addrExplain, 0, zMsg,P4_DYNAMIC); pParse->addrExplain, 0, zMsg,P4_DYNAMIC);
sqlite3VdbeScanStatus(v, sqlite3VdbeCurrentAddr(v)-1, 0, 0, 0, 0);
return ret; return ret;
} }
#endif /* SQLITE_OMIT_EXPLAIN */ #endif /* SQLITE_OMIT_EXPLAIN */
@@ -300,6 +302,9 @@ void sqlite3WhereAddScanStatus(
sqlite3VdbeScanStatus( sqlite3VdbeScanStatus(
v, addrExplain, pLvl->addrBody, pLvl->addrVisit, pLoop->nOut, zObj v, addrExplain, pLvl->addrBody, pLvl->addrVisit, pLoop->nOut, zObj
); );
if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){
sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iTabCur);
}
} }
#endif #endif

View File

@@ -36,7 +36,9 @@ proc do_scanstatus_test {tn res} {
while {1} { while {1} {
set r [sqlite3_stmt_scanstatus $stmt $idx] set r [sqlite3_stmt_scanstatus $stmt $idx]
if {[llength $r]==0} break if {[llength $r]==0} break
lappend ret {*}$r foreach v {nLoop nVisit nEst zName zExplain} {
lappend ret $v [dict get $r $v]
}
incr idx 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) SELECT count(*) FROM t1 WHERE a IN (SELECT b FROM t1 AS ii)
} {2} } {2}
do_scanstatus_test 5.1.2 { do_scanstatus_test 5.1.2 {
nLoop 1 nVisit 10 nEst 10.0 zName t1bc nLoop 1 nVisit 10 nEst 10.0 zName t1
zExplain {SCAN ii USING COVERING INDEX t1bc} zExplain {SCAN ii}
nLoop 1 nVisit 2 nEst 8.0 zName sqlite_autoindex_t1_1 nLoop 1 nVisit 2 nEst 8.0 zName sqlite_autoindex_t1_1
zExplain {SEARCH t1 USING COVERING INDEX sqlite_autoindex_t1_1 (a=?)} 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; SELECT count(*) FROM t1, t2 WHERE y = c;
} { } {
QUERY PLAN QUERY PLAN
|--SCAN t1 USING COVERING INDEX t1bc |--SCAN t1
`--SEARCH t2 USING COVERING INDEX t2xy (ANY(x) AND y=?) `--SEARCH t2 USING COVERING INDEX t2xy (ANY(x) AND y=?)
} }
do_execsql_test 5.4.2 { do_execsql_test 5.4.2 {
SELECT count(*) FROM t1, t2 WHERE y = c; SELECT count(*) FROM t1, t2 WHERE y = c;
} {200} } {200}
do_scanstatus_test 5.4.3 { do_scanstatus_test 5.4.3 {
nLoop 1 nVisit 10 nEst 10.0 zName t1bc nLoop 1 nVisit 10 nEst 10.0 zName t1
zExplain {SCAN t1 USING COVERING INDEX t1bc} zExplain {SCAN t1}
nLoop 10 nVisit 200 nEst 56.0 zName t2xy nLoop 10 nVisit 200 nEst 56.0 zName t2xy
zExplain {SEARCH t2 USING COVERING INDEX t2xy (ANY(x) AND y=?)} zExplain {SEARCH t2 USING COVERING INDEX t2xy (ANY(x) AND y=?)}
} }

159
test/scanstatus2.test Normal file
View File

@@ -0,0 +1,159 @@
# 2022 December 5
#
# 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]]
}
proc puts_graph {sql} {
db eval $sql
set stmt [db version -last-stmt-ptr]
puts [string trim [get_graph $stmt]]
}
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 (nCycle=nnn)
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE ft USING fts5(a);
INSERT INTO ft VALUES('abc');
INSERT INTO ft VALUES('def');
INSERT INTO ft VALUES('ghi');
}
do_graph_test 2.1 {
SELECT * FROM ft('def')
} {
QUERY (nCycle=nnn)
--SCAN ft VIRTUAL TABLE INDEX 0:M1 (nCycle=nnn)
}
finish_test