-C Improvements\sto\sthe\sEQP\sdisplay\sfor\scompound\sselect\sstatements.
-D 2018-05-02T02:22:22.993
+C Fix\sa\sdangling-else\sproblem\sthat\swas\scausing\srecursive\sCTEs\sto\smalfunction.\nBegin\sfixing\stest\scases\sto\swork\swith\sthe\snew\sEQP\soutput.
+D 2018-05-02T14:24:34.656
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F Makefile.in 5ce9343cba9c189046f1afe6d2bcc1f68079439febc05267b98aec6ecc752439
F src/pcache1.c 716975564c15eb6679e97f734cec1bfd6c16ac3d4010f05f1f8e509fc7d19880
F src/pragma.c bea56df3ae0637768c0da4fbbb8f2492f780980d95000034a105ff291bf7ca69
F src/pragma.h bb83728944b42f6d409c77f5838a8edbdb0fe83046c5496ffc9602b40340a324
-F src/prepare.c 1492a2bed7bc5770c5850404f09bf887a67d4580985b8cee37bdab2ea809f479
+F src/prepare.c 95a9dba7a5d032039a77775188cb3b6fb17f2fa1a0b7cd915b30b4b823383ffa
F src/printf.c d3b7844ddeb11fbbdd38dd84d09c9c1ac171d21fb038473c3aa97981201cc660
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
F src/resolve.c 6415381a0e9d22c0e7cba33ca4a53f81474190862f5d4838190f5eb5b0b47bc9
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
-F src/select.c 41962df2f21593db4eb5e0d7d8f15848b9ebd3ffed9b7584677033a8db37223b
+F src/select.c e66bb89e00608e50fa03939d5d86213a302487e8b78e0960b646be9ea0ac2f3e
F src/shell.c.in 29309f2ab656c8817fbc3b7910b9af8464557b91cba75277a03669399c8e2730
F src/sqlite.h.in d669de545f18f2f01362de02e309cd7f15185958c71bac8f53cd5438b46d2bea
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 83a3c4ce93d650bedfd1aa558cb85a516bd6d094445ee989740827d0d944368d
-F src/sqliteInt.h 337e4fe0a9e3bef575699bd0063ac2dcc31b73c905206d5425d442fe7fbb2798
+F src/sqliteInt.h 4095263176d49601f27086b7e66ca541923b72a909187923e3b45e60511cfe2a
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
F src/walker.c da987a20d40145c0a03c07d8fefcb2ed363becc7680d0500d9c79915591f5b1f
F src/where.c 7a1c5555c00bcf49c677472ae83bb49bf24c8d8e9a060d475e86dee39be2fb3a
F src/whereInt.h 2610cb87dd95509995b63decc674c60f2757697a206cfe0c085ee53d9c43cfff
-F src/wherecode.c 13b831d258ab8468cb0469a882f0778632b55d787f329751e50d92b8133ea594
+F src/wherecode.c 3368f0797a4b166e0773a4137d270d92ddcbce91618b11d1e9f11f7c39f33068
F src/whereexpr.c e90b2e76dcabc81edff56633bf281bc01d93b71e0c81482dc06925ce39f5844a
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc
F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f
F test/analyze.test b3a9c67d00e1df7588a5b7be9a0292899f94fe8cac1f94a017277474ca2e59df
-F test/analyze3.test 8b3ef8ba6d1096b76c40e0925c0fe51e700d2b779cdda40914580de3f9b9d80f
+F test/analyze3.test ff62d9029e6deb2c914490c6b00caf7fae47cc85cdc046e4a0d0a4d4b87c71d8
F test/analyze4.test eff2df19b8dd84529966420f29ea52edc6b56213
F test/analyze5.test 765c4e284aa69ca172772aa940946f55629bc8c4
F test/analyze6.test f1c552ce39cca4ec922a7e4e0e5d0203d6b3281f
F test/analyze7.test bb1409afc9e8629e414387ef048b8e0e3e0bdc4f
F test/analyze8.test c05a461d0a6b05991106467d0c47480f2e709c82
-F test/analyze9.test dac0bdc7eab965b9ad639ca83564d98717aaf13ce5a776f23d9a3680238cecd8
+F test/analyze9.test 9fbf0e0101eef4f5dc149769aa14e10b76ee06e7c28598264b32173cd1999a54
F test/analyzeA.test 3335697f6700c7052295cfd0067fc5b2aacddf9a
F test/analyzeB.test a4c1c3048f6d9e090eb76e83eecb18bcf6d31a70
F test/analyzeC.test 555a6cc388b9818b6eda6df816f01ce0a75d3a93
F test/enc2.test 83437a79ba1545a55fb549309175c683fb334473
F test/enc3.test 6807f7a7740a00361ca8d0ccd66bc60c8dc5f2b6
F test/enc4.test c8f1ce3618508fd0909945beb8b8831feef2c020
-F test/eqp.test 3fe051af50921284189d1970eb653f9fcf5117d2
+F test/eqp.test 0d06518e010ca5f02bd56b6a45fb70514a29c7eb97d244d72826d164477cfb1e
F test/errmsg.test eae9f091eb39ce7e20305de45d8e5d115b68fa856fba4ea6757b6ca3705ff7f9
F test/eval.test a64c9105d6ff163df7cf09d6ac29cdad5922078c
F test/exclusive.test 1206b87e192497d78c7f35552e86a9d05421498da300fb1cce5ca5351ccde3c3
F test/temptable2.test d2940417496e2b9548e01d09990763fbe88c316504033256d51493e1f1a5ce6a
F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc
-F test/tester.tcl 94901a4625d9a2229666dd5c44120ddf7f0fb639470710ef74a4cefc7b039e07
+F test/tester.tcl aa7558f20fcf7dd9151325f849d9103bd450235093bc078073bf0f080991e3c4
F test/thread001.test b61a29dd87cf669f5f6ac96124a7c97d71b0c80d9012746072055877055cf9ef
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 70b48a7972dfbb44af3ccd8ccd830e984bec88d80a78b3566a5de86a16e7fc14
-R 21b35e2f4fb3267c4b0452fc48ac12e8
+P 699a77e479010a331b0423f157a2fbfc373688e3d0d04ae5e64376c00cb3d488
+R 5835567753900691e5cf824409193ffc
U drh
-Z 26b206c16e8cc64ca80991a86c32cf32
+Z eb3edd9f2f9ef18dad6eafce24ad9bb2
-699a77e479010a331b0423f157a2fbfc373688e3d0d04ae5e64376c00cb3d488
\ No newline at end of file
+82ca44b82fed6814c84440ba8bfaa019488ab956e84ac165180e2fcece6facb2
\ No newline at end of file
if( rc==SQLITE_OK && sParse.pVdbe && sParse.explain ){
static const char * const azColName[] = {
"addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment",
- "selectid", "order", "from", "detail"
+ "id", "parent", "notused", "detail"
};
int iFirst, mx;
if( sParse.explain==2 ){
*/
if( p->pOrderBy ){
return multiSelectOrderBy(pParse, p, pDest);
- }else
+ }else{
#ifndef SQLITE_OMIT_EXPLAIN
- if( pPrior->pPrior==0 ){
- ExplainQueryPlan((pParse, 1, "COMPOUND QUERY"));
- ExplainQueryPlan((pParse, 1, "LEFT-MOST SUBQUERY"));
- ExplainQueryPlanSetId(pParse, pPrior);
- }
+ if( p->pNext==0 ){
+ ExplainQueryPlan((pParse, 1, "COMPOUND QUERY"));
+ }
+ if( pPrior->pPrior==0 ){
+ ExplainQueryPlan((pParse, 1, "LEFT-MOST SUBQUERY"));
+ ExplainQueryPlanSetId(pParse, pPrior);
+ }
#endif
- /* Generate code for the left and right SELECT statements.
- */
- switch( p->op ){
- case TK_ALL: {
- int addr = 0;
- int nLimit;
- assert( !pPrior->pLimit );
- pPrior->iLimit = p->iLimit;
- pPrior->iOffset = p->iOffset;
- pPrior->pLimit = p->pLimit;
- rc = sqlite3Select(pParse, pPrior, &dest);
- p->pLimit = 0;
- if( rc ){
- goto multi_select_end;
- }
- p->pPrior = 0;
- p->iLimit = pPrior->iLimit;
- p->iOffset = pPrior->iOffset;
- if( p->iLimit ){
- addr = sqlite3VdbeAddOp1(v, OP_IfNot, p->iLimit); VdbeCoverage(v);
- VdbeComment((v, "Jump ahead if LIMIT reached"));
- if( p->iOffset ){
- sqlite3VdbeAddOp3(v, OP_OffsetLimit,
- p->iLimit, p->iOffset+1, p->iOffset);
+ /* Generate code for the left and right SELECT statements.
+ */
+ switch( p->op ){
+ case TK_ALL: {
+ int addr = 0;
+ int nLimit;
+ assert( !pPrior->pLimit );
+ pPrior->iLimit = p->iLimit;
+ pPrior->iOffset = p->iOffset;
+ pPrior->pLimit = p->pLimit;
+ rc = sqlite3Select(pParse, pPrior, &dest);
+ p->pLimit = 0;
+ if( rc ){
+ goto multi_select_end;
}
+ p->pPrior = 0;
+ p->iLimit = pPrior->iLimit;
+ p->iOffset = pPrior->iOffset;
+ if( p->iLimit ){
+ addr = sqlite3VdbeAddOp1(v, OP_IfNot, p->iLimit); VdbeCoverage(v);
+ VdbeComment((v, "Jump ahead if LIMIT reached"));
+ if( p->iOffset ){
+ sqlite3VdbeAddOp3(v, OP_OffsetLimit,
+ p->iLimit, p->iOffset+1, p->iOffset);
+ }
+ }
+ ExplainQueryPlan((pParse, 1, "UNION ALL"));
+ ExplainQueryPlanSetId(pParse, p);
+ rc = sqlite3Select(pParse, p, &dest);
+ testcase( rc!=SQLITE_OK );
+ pDelete = p->pPrior;
+ p->pPrior = pPrior;
+ p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
+ if( pPrior->pLimit
+ && sqlite3ExprIsInteger(pPrior->pLimit->pLeft, &nLimit)
+ && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit)
+ ){
+ p->nSelectRow = sqlite3LogEst((u64)nLimit);
+ }
+ if( addr ){
+ sqlite3VdbeJumpHere(v, addr);
+ }
+ break;
}
- ExplainQueryPlan((pParse, 1, "UNION ALL"));
- ExplainQueryPlanSetId(pParse, p);
- rc = sqlite3Select(pParse, p, &dest);
- testcase( rc!=SQLITE_OK );
- pDelete = p->pPrior;
- p->pPrior = pPrior;
- p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
- if( pPrior->pLimit
- && sqlite3ExprIsInteger(pPrior->pLimit->pLeft, &nLimit)
- && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit)
- ){
- p->nSelectRow = sqlite3LogEst((u64)nLimit);
- }
- if( addr ){
- sqlite3VdbeJumpHere(v, addr);
- }
- break;
- }
- case TK_EXCEPT:
- case TK_UNION: {
- int unionTab; /* Cursor number of the temporary table holding result */
- u8 op = 0; /* One of the SRT_ operations to apply to self */
- int priorOp; /* The SRT_ operation to apply to prior selects */
- Expr *pLimit; /* Saved values of p->nLimit */
- int addr;
- SelectDest uniondest;
-
- testcase( p->op==TK_EXCEPT );
- testcase( p->op==TK_UNION );
- priorOp = SRT_Union;
- if( dest.eDest==priorOp ){
- /* We can reuse a temporary table generated by a SELECT to our
- ** right.
+ case TK_EXCEPT:
+ case TK_UNION: {
+ int unionTab; /* Cursor number of the temp table holding result */
+ u8 op = 0; /* One of the SRT_ operations to apply to self */
+ int priorOp; /* The SRT_ operation to apply to prior selects */
+ Expr *pLimit; /* Saved values of p->nLimit */
+ int addr;
+ SelectDest uniondest;
+
+ testcase( p->op==TK_EXCEPT );
+ testcase( p->op==TK_UNION );
+ priorOp = SRT_Union;
+ if( dest.eDest==priorOp ){
+ /* We can reuse a temporary table generated by a SELECT to our
+ ** right.
+ */
+ assert( p->pLimit==0 ); /* Not allowed on leftward elements */
+ unionTab = dest.iSDParm;
+ }else{
+ /* We will need to create our own temporary table to hold the
+ ** intermediate results.
+ */
+ unionTab = pParse->nTab++;
+ assert( p->pOrderBy==0 );
+ addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, unionTab, 0);
+ assert( p->addrOpenEphm[0] == -1 );
+ p->addrOpenEphm[0] = addr;
+ findRightmost(p)->selFlags |= SF_UsesEphemeral;
+ assert( p->pEList );
+ }
+
+ /* Code the SELECT statements to our left
*/
- assert( p->pLimit==0 ); /* Not allowed on leftward elements */
- unionTab = dest.iSDParm;
- }else{
- /* We will need to create our own temporary table to hold the
- ** intermediate results.
+ assert( !pPrior->pOrderBy );
+ sqlite3SelectDestInit(&uniondest, priorOp, unionTab);
+ rc = sqlite3Select(pParse, pPrior, &uniondest);
+ if( rc ){
+ goto multi_select_end;
+ }
+
+ /* Code the current SELECT statement
*/
- unionTab = pParse->nTab++;
+ if( p->op==TK_EXCEPT ){
+ op = SRT_Except;
+ }else{
+ assert( p->op==TK_UNION );
+ op = SRT_Union;
+ }
+ p->pPrior = 0;
+ pLimit = p->pLimit;
+ p->pLimit = 0;
+ uniondest.eDest = op;
+ ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
+ selectOpName(p->op)));
+ ExplainQueryPlanSetId(pParse, p);
+ rc = sqlite3Select(pParse, p, &uniondest);
+ testcase( rc!=SQLITE_OK );
+ /* Query flattening in sqlite3Select() might refill p->pOrderBy.
+ ** Be sure to delete p->pOrderBy, therefore, to avoid a memory leak. */
+ sqlite3ExprListDelete(db, p->pOrderBy);
+ pDelete = p->pPrior;
+ p->pPrior = pPrior;
+ p->pOrderBy = 0;
+ if( p->op==TK_UNION ){
+ p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
+ }
+ sqlite3ExprDelete(db, p->pLimit);
+ p->pLimit = pLimit;
+ p->iLimit = 0;
+ p->iOffset = 0;
+
+ /* Convert the data in the temporary table into whatever form
+ ** it is that we currently need.
+ */
+ assert( unionTab==dest.iSDParm || dest.eDest!=priorOp );
+ if( dest.eDest!=priorOp ){
+ int iCont, iBreak, iStart;
+ assert( p->pEList );
+ iBreak = sqlite3VdbeMakeLabel(v);
+ iCont = sqlite3VdbeMakeLabel(v);
+ computeLimitRegisters(pParse, p, iBreak);
+ sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak); VdbeCoverage(v);
+ iStart = sqlite3VdbeCurrentAddr(v);
+ selectInnerLoop(pParse, p, unionTab,
+ 0, 0, &dest, iCont, iBreak);
+ sqlite3VdbeResolveLabel(v, iCont);
+ sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart); VdbeCoverage(v);
+ sqlite3VdbeResolveLabel(v, iBreak);
+ sqlite3VdbeAddOp2(v, OP_Close, unionTab, 0);
+ }
+ break;
+ }
+ default: assert( p->op==TK_INTERSECT ); {
+ int tab1, tab2;
+ int iCont, iBreak, iStart;
+ Expr *pLimit;
+ int addr;
+ SelectDest intersectdest;
+ int r1;
+
+ /* INTERSECT is different from the others since it requires
+ ** two temporary tables. Hence it has its own case. Begin
+ ** by allocating the tables we will need.
+ */
+ tab1 = pParse->nTab++;
+ tab2 = pParse->nTab++;
assert( p->pOrderBy==0 );
- addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, unionTab, 0);
+
+ addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab1, 0);
assert( p->addrOpenEphm[0] == -1 );
p->addrOpenEphm[0] = addr;
findRightmost(p)->selFlags |= SF_UsesEphemeral;
assert( p->pEList );
- }
-
- /* Code the SELECT statements to our left
- */
- assert( !pPrior->pOrderBy );
- sqlite3SelectDestInit(&uniondest, priorOp, unionTab);
- rc = sqlite3Select(pParse, pPrior, &uniondest);
- if( rc ){
- goto multi_select_end;
- }
-
- /* Code the current SELECT statement
- */
- if( p->op==TK_EXCEPT ){
- op = SRT_Except;
- }else{
- assert( p->op==TK_UNION );
- op = SRT_Union;
- }
- p->pPrior = 0;
- pLimit = p->pLimit;
- p->pLimit = 0;
- uniondest.eDest = op;
- ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
- selectOpName(p->op)));
- ExplainQueryPlanSetId(pParse, p);
- rc = sqlite3Select(pParse, p, &uniondest);
- testcase( rc!=SQLITE_OK );
- /* Query flattening in sqlite3Select() might refill p->pOrderBy.
- ** Be sure to delete p->pOrderBy, therefore, to avoid a memory leak. */
- sqlite3ExprListDelete(db, p->pOrderBy);
- pDelete = p->pPrior;
- p->pPrior = pPrior;
- p->pOrderBy = 0;
- if( p->op==TK_UNION ){
- p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
- }
- sqlite3ExprDelete(db, p->pLimit);
- p->pLimit = pLimit;
- p->iLimit = 0;
- p->iOffset = 0;
-
- /* Convert the data in the temporary table into whatever form
- ** it is that we currently need.
- */
- assert( unionTab==dest.iSDParm || dest.eDest!=priorOp );
- if( dest.eDest!=priorOp ){
- int iCont, iBreak, iStart;
+
+ /* Code the SELECTs to our left into temporary table "tab1".
+ */
+ sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1);
+ rc = sqlite3Select(pParse, pPrior, &intersectdest);
+ if( rc ){
+ goto multi_select_end;
+ }
+
+ /* Code the current SELECT into temporary table "tab2"
+ */
+ addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab2, 0);
+ assert( p->addrOpenEphm[1] == -1 );
+ p->addrOpenEphm[1] = addr;
+ p->pPrior = 0;
+ pLimit = p->pLimit;
+ p->pLimit = 0;
+ intersectdest.iSDParm = tab2;
+ ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
+ selectOpName(p->op)));
+ ExplainQueryPlanSetId(pParse, p);
+ rc = sqlite3Select(pParse, p, &intersectdest);
+ testcase( rc!=SQLITE_OK );
+ pDelete = p->pPrior;
+ p->pPrior = pPrior;
+ if( p->nSelectRow>pPrior->nSelectRow ){
+ p->nSelectRow = pPrior->nSelectRow;
+ }
+ sqlite3ExprDelete(db, p->pLimit);
+ p->pLimit = pLimit;
+
+ /* Generate code to take the intersection of the two temporary
+ ** tables.
+ */
assert( p->pEList );
iBreak = sqlite3VdbeMakeLabel(v);
iCont = sqlite3VdbeMakeLabel(v);
computeLimitRegisters(pParse, p, iBreak);
- sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak); VdbeCoverage(v);
- iStart = sqlite3VdbeCurrentAddr(v);
- selectInnerLoop(pParse, p, unionTab,
+ sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak); VdbeCoverage(v);
+ r1 = sqlite3GetTempReg(pParse);
+ iStart = sqlite3VdbeAddOp2(v, OP_RowData, tab1, r1);
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0);
+ VdbeCoverage(v);
+ sqlite3ReleaseTempReg(pParse, r1);
+ selectInnerLoop(pParse, p, tab1,
0, 0, &dest, iCont, iBreak);
sqlite3VdbeResolveLabel(v, iCont);
- sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart); VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); VdbeCoverage(v);
sqlite3VdbeResolveLabel(v, iBreak);
- sqlite3VdbeAddOp2(v, OP_Close, unionTab, 0);
+ sqlite3VdbeAddOp2(v, OP_Close, tab2, 0);
+ sqlite3VdbeAddOp2(v, OP_Close, tab1, 0);
+ break;
}
- break;
}
- default: assert( p->op==TK_INTERSECT ); {
- int tab1, tab2;
- int iCont, iBreak, iStart;
- Expr *pLimit;
- int addr;
- SelectDest intersectdest;
- int r1;
-
- /* INTERSECT is different from the others since it requires
- ** two temporary tables. Hence it has its own case. Begin
- ** by allocating the tables we will need.
- */
- tab1 = pParse->nTab++;
- tab2 = pParse->nTab++;
- assert( p->pOrderBy==0 );
-
- addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab1, 0);
- assert( p->addrOpenEphm[0] == -1 );
- p->addrOpenEphm[0] = addr;
- findRightmost(p)->selFlags |= SF_UsesEphemeral;
- assert( p->pEList );
-
- /* Code the SELECTs to our left into temporary table "tab1".
- */
- sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1);
- rc = sqlite3Select(pParse, pPrior, &intersectdest);
- if( rc ){
- goto multi_select_end;
- }
-
- /* Code the current SELECT into temporary table "tab2"
- */
- addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab2, 0);
- assert( p->addrOpenEphm[1] == -1 );
- p->addrOpenEphm[1] = addr;
- p->pPrior = 0;
- pLimit = p->pLimit;
- p->pLimit = 0;
- intersectdest.iSDParm = tab2;
- ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
- selectOpName(p->op)));
- ExplainQueryPlanSetId(pParse, p);
- rc = sqlite3Select(pParse, p, &intersectdest);
- testcase( rc!=SQLITE_OK );
- pDelete = p->pPrior;
- p->pPrior = pPrior;
- if( p->nSelectRow>pPrior->nSelectRow ) p->nSelectRow = pPrior->nSelectRow;
- sqlite3ExprDelete(db, p->pLimit);
- p->pLimit = pLimit;
-
- /* Generate code to take the intersection of the two temporary
- ** tables.
- */
- assert( p->pEList );
- iBreak = sqlite3VdbeMakeLabel(v);
- iCont = sqlite3VdbeMakeLabel(v);
- computeLimitRegisters(pParse, p, iBreak);
- sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak); VdbeCoverage(v);
- r1 = sqlite3GetTempReg(pParse);
- iStart = sqlite3VdbeAddOp2(v, OP_RowData, tab1, r1);
- sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0); VdbeCoverage(v);
- sqlite3ReleaseTempReg(pParse, r1);
- selectInnerLoop(pParse, p, tab1,
- 0, 0, &dest, iCont, iBreak);
- sqlite3VdbeResolveLabel(v, iCont);
- sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); VdbeCoverage(v);
- sqlite3VdbeResolveLabel(v, iBreak);
- sqlite3VdbeAddOp2(v, OP_Close, tab2, 0);
- sqlite3VdbeAddOp2(v, OP_Close, tab1, 0);
- break;
+
+ #ifndef SQLITE_OMIT_EXPLAIN
+ if( p->pNext==0 ){
+ ExplainQueryPlanPop(pParse);
}
+ #endif
}
-
-#ifndef SQLITE_OMIT_EXPLAIN
- if( p->pNext==0 ){
- ExplainQueryPlanPop(pParse);
- }
-#endif
-
+
/* Compute collating sequences used by
** temporary tables needed to implement the compound select.
** Attach the KeyInfo structure to all temporary tables.
VdbeComment((v, "%s", pItem->pTab->zName));
pItem->addrFillSub = addrTop;
sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn);
- ExplainQueryPlan((pParse, 1, "CO-ROUTINE %p", pSub));
+ ExplainQueryPlan((pParse, 1, "CO-ROUTINE 0x%p", pSub));
ExplainQueryPlanSetId(pParse, pSub);
sqlite3Select(pParse, pSub, &dest);
pItem->pTab->nRowLogEst = pSub->nSelectRow;
pSub->nSelectRow = pPrior->pSelect->nSelectRow;
}else{
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
- ExplainQueryPlan((pParse, 1, "MATERIALIZE %p", pSub));
+ ExplainQueryPlan((pParse, 1, "MATERIALIZE 0x%p", pSub));
ExplainQueryPlanSetId(pParse,pSub);
sqlite3Select(pParse, pSub, &dest);
}
#define SF_MaybeConvert 0x08000 /* Need convertCompoundSelectToSubquery() */
#define SF_Converted 0x10000 /* By convertCompoundSelectToSubquery() */
#define SF_IncludeHidden 0x20000 /* Include hidden columns in output */
-#define SF_ComplexResult 0x40000 /* Result set contains subquery or function */
-
+#define SF_ComplexResult 0x40000 /* Result contains subquery or function */
/*
** The results of a SELECT can be distributed in several ways, as defined
sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
sqlite3StrAccumAppendAll(&str, isSearch ? "SEARCH" : "SCAN");
if( pItem->pSelect ){
- sqlite3XPrintf(&str, " SUBQUERY %p", pItem->pSelect);
+ sqlite3XPrintf(&str, " SUBQUERY 0x%p", pItem->pSelect);
}else{
sqlite3XPrintf(&str, " TABLE %s", pItem->zName);
}
#
do_eqp_test analyze3-1.1.2 {
SELECT sum(y) FROM t1 WHERE x>200 AND x<300
-} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?)}}
+} {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?)}
do_eqp_test analyze3-1.1.3 {
SELECT sum(y) FROM t1 WHERE x>0 AND x<1100
-} {0 0 0 {SCAN TABLE t1}}
+} {SCAN TABLE t1}
# 2017-06-26: Verify that the SQLITE_DBCONFIG_ENABLE_QPSG setting disables
# the use of bound parameters by STAT4
unset -nocomplain u
do_eqp_test analyze3-1.1.3.100 {
SELECT sum(y) FROM t1 WHERE x>$l AND x<$u
-} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?)}}
+} {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?)}
set l 200
set u 300
do_eqp_test analyze3-1.1.3.101 {
SELECT sum(y) FROM t1 WHERE x>$l AND x<$u
-} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?)}}
+} {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?)}
set l 0
set u 1100
do_eqp_test analyze3-1.1.3.102 {
SELECT sum(y) FROM t1 WHERE x>$l AND x<$u
-} {0 0 0 {SCAN TABLE t1}}
+} {SCAN TABLE t1}
db cache flush
sqlite3_db_config db ENABLE_QPSG 1
do_eqp_test analyze3-1.1.3.103 {
SELECT sum(y) FROM t1 WHERE x>$l AND x<$u
-} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?)}}
+} {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?)}
db cache flush
sqlite3_db_config db ENABLE_QPSG 0
do_eqp_test analyze3-1.1.3.104 {
SELECT sum(y) FROM t1 WHERE x>$l AND x<$u
-} {0 0 0 {SCAN TABLE t1}}
+} {SCAN TABLE t1}
do_test analyze3-1.1.4 {
sf_execsql { SELECT sum(y) FROM t1 WHERE x>200 AND x<300 }
} {200 990}
do_eqp_test analyze3-1.2.2 {
SELECT sum(y) FROM t2 WHERE x>1 AND x<2
-} {0 0 0 {SEARCH TABLE t2 USING INDEX i2 (x>? AND x<?)}}
+} {SEARCH TABLE t2 USING INDEX i2 (x>? AND x<?)}
do_eqp_test analyze3-1.2.3 {
SELECT sum(y) FROM t2 WHERE x>0 AND x<99
-} {0 0 0 {SCAN TABLE t2}}
+} {SCAN TABLE t2}
do_test analyze3-1.2.4 {
sf_execsql { SELECT sum(y) FROM t2 WHERE x>12 AND x<20 }
} {99 1000}
do_eqp_test analyze3-1.3.2 {
SELECT sum(y) FROM t3 WHERE x>200 AND x<300
-} {0 0 0 {SEARCH TABLE t3 USING INDEX i3 (x>? AND x<?)}}
+} {SEARCH TABLE t3 USING INDEX i3 (x>? AND x<?)}
do_eqp_test analyze3-1.3.3 {
SELECT sum(y) FROM t3 WHERE x>0 AND x<1100
-} {0 0 0 {SCAN TABLE t3}}
+} {SCAN TABLE t3}
do_test analyze3-1.3.4 {
sf_execsql { SELECT sum(y) FROM t3 WHERE x>200 AND x<300 }
} {}
do_eqp_test analyze3-2.2 {
SELECT count(a) FROM t1 WHERE b LIKE 'a%'
-} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (b>? AND b<?)}}
+} {SEARCH TABLE t1 USING INDEX i1 (b>? AND b<?)}
do_eqp_test analyze3-2.3 {
SELECT count(a) FROM t1 WHERE b LIKE '%a'
-} {0 0 0 {SCAN TABLE t1}}
+} {SCAN TABLE t1}
# Return the first argument if like_match_blobs is true (the default)
# or the second argument if not
do_eqp_test analyze3-6-3 {
SELECT * FROM t1 WHERE a = 5 AND c = 13;
-} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (c=?)}}
+} {SEARCH TABLE t1 USING INDEX i2 (c=?)}
do_eqp_test analyze3-6-2 {
SELECT * FROM t1 WHERE a = 5 AND b > 'w' AND c = 13;
-} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (c=?)}}
+} {SEARCH TABLE t1 USING INDEX i2 (c=?)}
#-----------------------------------------------------------------------------
# 2015-04-20.
reset_db
do_execsql_test 22.0 {
CREATE TABLE t3(a, b, c, d, PRIMARY KEY(a, b)) WITHOUT ROWID;
-}
+ SELECT * FROM t3;
+} {}
do_execsql_test 22.1 {
WITH r(x) AS (
SELECT 1
-- Formerly used index i41. But i41 is not a covering index whereas
-- the PRIMARY KEY is a covering index, and so as of 2017-10-15, the
-- PRIMARY KEY is preferred.
-} {
- 0 0 0 {SEARCH TABLE t4 USING PRIMARY KEY (c=? AND b=? AND a<?)}
-}
+} {SEARCH TABLE t4 USING PRIMARY KEY (c=? AND b=? AND a<?)}
do_eqp_test 23.2 {
SELECT * FROM t4 WHERE
(e=1 AND b='xyz' AND c='zyx' AND a<'JJJ') AND f<300
-} {
- 0 0 0 {SEARCH TABLE t4 USING INDEX i42 (f<?)}
-}
+} {SEARCH TABLE t4 USING INDEX i42 (f<?)}
do_execsql_test 24.0 {
CREATE TABLE t5(c, d, b, e, a, PRIMARY KEY(a, b, c)) WITHOUT ROWID;
# Term (b<?) is estimated at 25%. Better than (a<30) but not as
# good as (a<20).
- do_eqp_test 25.2.1 { SELECT * FROM t6 WHERE a<30 AND b<? } {
- 0 0 0 {SEARCH TABLE t6 USING INDEX bb (b<?)}
- }
- do_eqp_test 25.2.2 { SELECT * FROM t6 WHERE a<20 AND b<? } {
- 0 0 0 {SEARCH TABLE t6 USING INDEX aa (a<?)}
- }
+ do_eqp_test 25.2.1 { SELECT * FROM t6 WHERE a<30 AND b<? } \
+ {SEARCH TABLE t6 USING INDEX bb (b<?)}
+ do_eqp_test 25.2.2 { SELECT * FROM t6 WHERE a<20 AND b<? } \
+ {SEARCH TABLE t6 USING INDEX aa (a<?)}
# Term (b BETWEEN ? AND ?) is estimated at 1/64.
do_eqp_test 25.3.1 {
SELECT * FROM t6 WHERE a BETWEEN 5 AND 10 AND b BETWEEN ? AND ?
- } {
- 0 0 0 {SEARCH TABLE t6 USING INDEX bb (b>? AND b<?)}
- }
+ } {SEARCH TABLE t6 USING INDEX bb (b>? AND b<?)}
# Term (b BETWEEN ? AND 60) is estimated to return roughly 15 rows -
# 60 from (b<=60) multiplied by 0.25 for the b>=? term. Better than
# (a<20) but not as good as (a<10).
do_eqp_test 25.4.1 {
SELECT * FROM t6 WHERE a < 10 AND (b BETWEEN ? AND 60)
- } {
- 0 0 0 {SEARCH TABLE t6 USING INDEX aa (a<?)}
- }
+ } {SEARCH TABLE t6 USING INDEX aa (a<?)}
+
do_eqp_test 25.4.2 {
SELECT * FROM t6 WHERE a < 20 AND (b BETWEEN ? AND 60)
- } {
- 0 0 0 {SEARCH TABLE t6 USING INDEX bb (b>? AND b<?)}
- }
+ } {SEARCH TABLE t6 USING INDEX bb (b>? AND b<?)}
}
#-------------------------------------------------------------------------
#
do_eqp_test 26.1.4 {
SELECT * FROM t1 WHERE x = 10000 AND y < 50 AND z = 444;
-} {
- 0 0 0 {SEARCH TABLE t1 USING INDEX t1z (z=?)}
-}
+} {SEARCH TABLE t1 USING INDEX t1z (z=?)}
# This test - 26.2.* - tests that another manifestation of the same problem
do_eqp_test 26.2.2 {
SELECT * FROM t1 WHERE x='B' AND y>25 AND z=?;
-} {
- 0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x=? AND y>?)}
-}
+} {SEARCH TABLE t1 USING INDEX i1 (x=? AND y>?)}
finish_test
do_eqp_test 1.2 {
SELECT * FROM t2, t1 WHERE t1.a=1 OR t1.b=2;
} {
- 0 0 1 {SEARCH TABLE t1 USING INDEX i1 (a=?)}
- 0 0 1 {SEARCH TABLE t1 USING INDEX i2 (b=?)}
- 0 1 0 {SCAN TABLE t2}
+ QUERY PLAN
+ |--SEARCH TABLE t1 USING INDEX i1 (a=?)
+ |--SEARCH TABLE t1 USING INDEX i2 (b=?)
+ `--SCAN TABLE t2
}
do_eqp_test 1.3 {
SELECT * FROM t2 CROSS JOIN t1 WHERE t1.a=1 OR t1.b=2;
} {
- 0 0 0 {SCAN TABLE t2}
- 0 1 1 {SEARCH TABLE t1 USING INDEX i1 (a=?)}
- 0 1 1 {SEARCH TABLE t1 USING INDEX i2 (b=?)}
+ QUERY PLAN
+ |--SCAN TABLE t2
+ |--SEARCH TABLE t1 USING INDEX i1 (a=?)
+ `--SEARCH TABLE t1 USING INDEX i2 (b=?)
}
do_eqp_test 1.3 {
SELECT a FROM t1 ORDER BY a
} {
- 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1}
+ QUERY PLAN
+ `--SCAN TABLE t1 USING COVERING INDEX i1
}
do_eqp_test 1.4 {
SELECT a FROM t1 ORDER BY +a
} {
- 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1}
- 0 0 0 {USE TEMP B-TREE FOR ORDER BY}
+ QUERY PLAN
+ |--SCAN TABLE t1 USING COVERING INDEX i1
+ `--USE TEMP B-TREE FOR ORDER BY
}
do_eqp_test 1.5 {
SELECT a FROM t1 WHERE a=4
} {
- 0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?)}
+ QUERY PLAN
+ `--SEARCH TABLE t1 USING COVERING INDEX i1 (a=?)
}
do_eqp_test 1.6 {
SELECT DISTINCT count(*) FROM t3 GROUP BY a;
} {
- 0 0 0 {SCAN TABLE t3}
- 0 0 0 {USE TEMP B-TREE FOR GROUP BY}
- 0 0 0 {USE TEMP B-TREE FOR DISTINCT}
+ QUERY PLAN
+ |--SCAN TABLE t3
+ |--USE TEMP B-TREE FOR GROUP BY
+ `--USE TEMP B-TREE FOR DISTINCT
}
do_eqp_test 1.7 {
SELECT * FROM t3 JOIN (SELECT 1)
} {
- 0 0 1 {SCAN SUBQUERY 1}
- 0 1 0 {SCAN TABLE t3}
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ |--SCAN SUBQUERY xxxxxx
+ `--SCAN TABLE t3
}
do_eqp_test 1.8 {
SELECT * FROM t3 JOIN (SELECT 1 UNION SELECT 2)
} {
- 1 0 0 {COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (UNION)}
- 0 0 1 {SCAN SUBQUERY 1}
- 0 1 0 {SCAN TABLE t3}
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | `--COMPOUND QUERY
+ | |--LEFT-MOST SUBQUERY
+ | `--UNION USING TEMP B-TREE
+ |--SCAN SUBQUERY xxxxxx
+ `--SCAN TABLE t3
}
do_eqp_test 1.9 {
SELECT * FROM t3 JOIN (SELECT 1 EXCEPT SELECT a FROM t3 LIMIT 17)
} {
- 3 0 0 {SCAN TABLE t3}
- 1 0 0 {COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (EXCEPT)}
- 0 0 1 {SCAN SUBQUERY 1}
- 0 1 0 {SCAN TABLE t3}
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | `--COMPOUND QUERY
+ | |--LEFT-MOST SUBQUERY
+ | `--EXCEPT USING TEMP B-TREE
+ | `--SCAN TABLE t3
+ |--SCAN SUBQUERY xxxxxx
+ `--SCAN TABLE t3
}
do_eqp_test 1.10 {
SELECT * FROM t3 JOIN (SELECT 1 INTERSECT SELECT a FROM t3 LIMIT 17)
} {
- 3 0 0 {SCAN TABLE t3}
- 1 0 0 {COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (INTERSECT)}
- 0 0 1 {SCAN SUBQUERY 1}
- 0 1 0 {SCAN TABLE t3}
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | `--COMPOUND QUERY
+ | |--LEFT-MOST SUBQUERY
+ | `--INTERSECT USING TEMP B-TREE
+ | `--SCAN TABLE t3
+ |--SCAN SUBQUERY xxxxxx
+ `--SCAN TABLE t3
}
do_eqp_test 1.11 {
SELECT * FROM t3 JOIN (SELECT 1 UNION ALL SELECT a FROM t3 LIMIT 17)
} {
- 3 0 0 {SCAN TABLE t3}
- 1 0 0 {COMPOUND SUBQUERIES 2 AND 3 (UNION ALL)}
- 0 0 1 {SCAN SUBQUERY 1}
- 0 1 0 {SCAN TABLE t3}
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | `--COMPOUND QUERY
+ | |--LEFT-MOST SUBQUERY
+ | `--UNION ALL
+ | `--SCAN TABLE t3
+ |--SCAN SUBQUERY xxxxxx
+ `--SCAN TABLE t3
}
#-------------------------------------------------------------------------
}
det 2.2.1 "SELECT DISTINCT min(x), max(x) FROM t1 GROUP BY x ORDER BY 1" {
- 0 0 0 {SCAN TABLE t1}
- 0 0 0 {USE TEMP B-TREE FOR GROUP BY}
- 0 0 0 {USE TEMP B-TREE FOR DISTINCT}
- 0 0 0 {USE TEMP B-TREE FOR ORDER BY}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ |--USE TEMP B-TREE FOR GROUP BY
+ |--USE TEMP B-TREE FOR DISTINCT
+ `--USE TEMP B-TREE FOR ORDER BY
}
det 2.2.2 "SELECT DISTINCT min(x), max(x) FROM t2 GROUP BY x ORDER BY 1" {
- 0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1}
- 0 0 0 {USE TEMP B-TREE FOR DISTINCT}
- 0 0 0 {USE TEMP B-TREE FOR ORDER BY}
+ QUERY PLAN
+ |--SCAN TABLE t2 USING COVERING INDEX t2i1
+ |--USE TEMP B-TREE FOR DISTINCT
+ `--USE TEMP B-TREE FOR ORDER BY
}
det 2.2.3 "SELECT DISTINCT * FROM t1" {
- 0 0 0 {SCAN TABLE t1}
- 0 0 0 {USE TEMP B-TREE FOR DISTINCT}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ `--USE TEMP B-TREE FOR DISTINCT
}
det 2.2.4 "SELECT DISTINCT * FROM t1, t2" {
- 0 0 0 {SCAN TABLE t1}
- 0 1 1 {SCAN TABLE t2}
- 0 0 0 {USE TEMP B-TREE FOR DISTINCT}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ |--SCAN TABLE t2
+ `--USE TEMP B-TREE FOR DISTINCT
}
det 2.2.5 "SELECT DISTINCT * FROM t1, t2 ORDER BY t1.x" {
- 0 0 0 {SCAN TABLE t1}
- 0 1 1 {SCAN TABLE t2}
- 0 0 0 {USE TEMP B-TREE FOR DISTINCT}
- 0 0 0 {USE TEMP B-TREE FOR ORDER BY}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ |--SCAN TABLE t2
+ |--USE TEMP B-TREE FOR DISTINCT
+ `--USE TEMP B-TREE FOR ORDER BY
}
det 2.2.6 "SELECT DISTINCT t2.x FROM t1, t2 ORDER BY t2.x" {
- 0 0 1 {SCAN TABLE t2 USING COVERING INDEX t2i1}
- 0 1 0 {SCAN TABLE t1}
+ QUERY PLAN
+ |--SCAN TABLE t2 USING COVERING INDEX t2i1
+ `--SCAN TABLE t1
}
det 2.3.1 "SELECT max(x) FROM t2" {
- 0 0 0 {SEARCH TABLE t2 USING COVERING INDEX t2i1}
+ QUERY PLAN
+ `--SEARCH TABLE t2 USING COVERING INDEX t2i1
}
det 2.3.2 "SELECT min(x) FROM t2" {
- 0 0 0 {SEARCH TABLE t2 USING COVERING INDEX t2i1}
+ QUERY PLAN
+ `--SEARCH TABLE t2 USING COVERING INDEX t2i1
}
det 2.3.3 "SELECT min(x), max(x) FROM t2" {
- 0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1}
+ QUERY PLAN
+ `--SCAN TABLE t2 USING COVERING INDEX t2i1
}
det 2.4.1 "SELECT * FROM t1 WHERE rowid=?" {
- 0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?)}
+ QUERY PLAN
+ `--SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?)
}
do_eqp_test 3.1.1 {
SELECT (SELECT x FROM t1 AS sub) FROM t1;
} {
- 0 0 0 {SCAN TABLE t1}
- 0 0 0 {EXECUTE SCALAR SUBQUERY 1}
- 1 0 0 {SCAN TABLE t1 AS sub}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ `--SCALAR SUBQUERY
+ `--SCAN TABLE t1 AS sub
}
do_eqp_test 3.1.2 {
SELECT * FROM t1 WHERE (SELECT x FROM t1 AS sub);
} {
- 0 0 0 {SCAN TABLE t1}
- 0 0 0 {EXECUTE SCALAR SUBQUERY 1}
- 1 0 0 {SCAN TABLE t1 AS sub}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ `--SCALAR SUBQUERY
+ `--SCAN TABLE t1 AS sub
}
do_eqp_test 3.1.3 {
SELECT * FROM t1 WHERE (SELECT x FROM t1 AS sub ORDER BY y);
} {
- 0 0 0 {SCAN TABLE t1}
- 0 0 0 {EXECUTE SCALAR SUBQUERY 1}
- 1 0 0 {SCAN TABLE t1 AS sub}
- 1 0 0 {USE TEMP B-TREE FOR ORDER BY}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ `--SCALAR SUBQUERY
+ |--SCAN TABLE t1 AS sub
+ `--USE TEMP B-TREE FOR ORDER BY
}
do_eqp_test 3.1.4 {
SELECT * FROM t1 WHERE (SELECT x FROM t2 ORDER BY x);
} {
- 0 0 0 {SCAN TABLE t1}
- 0 0 0 {EXECUTE SCALAR SUBQUERY 1}
- 1 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ `--SCALAR SUBQUERY
+ `--SCAN TABLE t2 USING COVERING INDEX t2i1
}
det 3.2.1 {
SELECT * FROM (SELECT * FROM t1 ORDER BY x LIMIT 10) ORDER BY y LIMIT 5
} {
- 1 0 0 {SCAN TABLE t1}
- 1 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 0 0 0 {SCAN SUBQUERY 1}
- 0 0 0 {USE TEMP B-TREE FOR ORDER BY}
+ QUERY PLAN
+ |--CO-ROUTINE xxxxxx
+ | |--SCAN TABLE t1
+ | `--USE TEMP B-TREE FOR ORDER BY
+ |--SCAN SUBQUERY xxxxxx
+ `--USE TEMP B-TREE FOR ORDER BY
}
det 3.2.2 {
SELECT * FROM
(SELECT * FROM t2 ORDER BY x LIMIT 10) AS x2
ORDER BY x2.y LIMIT 5
} {
- 1 0 0 {SCAN TABLE t1}
- 1 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 2 0 0 {SCAN TABLE t2 USING INDEX t2i1}
- 0 0 0 {SCAN SUBQUERY 1 AS x1}
- 0 1 1 {SCAN SUBQUERY 2 AS x2}
- 0 0 0 {USE TEMP B-TREE FOR ORDER BY}
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | |--SCAN TABLE t1
+ | `--USE TEMP B-TREE FOR ORDER BY
+ |--MATERIALIZE xxxxxx
+ | `--SCAN TABLE t2 USING INDEX t2i1
+ |--SCAN SUBQUERY xxxxxx AS x1
+ |--SCAN SUBQUERY xxxxxx AS x2
+ `--USE TEMP B-TREE FOR ORDER BY
}
det 3.3.1 {
SELECT * FROM t1 WHERE y IN (SELECT y FROM t2)
} {
- 0 0 0 {SCAN TABLE t1}
- 0 0 0 {EXECUTE LIST SUBQUERY 1}
- 1 0 0 {SCAN TABLE t2}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ `--LIST SUBQUERY
+ `--SCAN TABLE t2
}
det 3.3.2 {
SELECT * FROM t1 WHERE y IN (SELECT y FROM t2 WHERE t1.x!=t2.x)
} {
- 0 0 0 {SCAN TABLE t1}
- 0 0 0 {EXECUTE CORRELATED LIST SUBQUERY 1}
- 1 0 0 {SCAN TABLE t2}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ `--CORRELATED LIST SUBQUERY
+ `--SCAN TABLE t2
}
det 3.3.3 {
SELECT * FROM t1 WHERE EXISTS (SELECT y FROM t2 WHERE t1.x!=t2.x)
} {
- 0 0 0 {SCAN TABLE t1}
- 0 0 0 {EXECUTE CORRELATED SCALAR SUBQUERY 1}
- 1 0 0 {SCAN TABLE t2}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ `--CORRELATED SCALAR SUBQUERY
+ `--SCAN TABLE t2
}
#-------------------------------------------------------------------------
do_eqp_test 4.1.1 {
SELECT * FROM t1 UNION ALL SELECT * FROM t2
} {
- 1 0 0 {SCAN TABLE t1}
- 2 0 0 {SCAN TABLE t2}
- 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION ALL)}
+ QUERY PLAN
+ `--COMPOUND QUERY
+ |--LEFT-MOST SUBQUERY
+ | `--SCAN TABLE t1
+ `--UNION ALL
+ `--SCAN TABLE t2
}
do_eqp_test 4.1.2 {
SELECT * FROM t1 UNION ALL SELECT * FROM t2 ORDER BY 2
} {
- 1 0 0 {SCAN TABLE t1}
- 1 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 2 0 0 {SCAN TABLE t2}
- 2 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION ALL)}
+ QUERY PLAN
+ `--MERGE (UNION ALL)
+ |--LEFT
+ | |--SCAN TABLE t1
+ | `--USE TEMP B-TREE FOR ORDER BY
+ `--RIGHT
+ |--SCAN TABLE t2
+ `--USE TEMP B-TREE FOR ORDER BY
}
do_eqp_test 4.1.3 {
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY 2
} {
- 1 0 0 {SCAN TABLE t1}
- 1 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 2 0 0 {SCAN TABLE t2}
- 2 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION)}
+ QUERY PLAN
+ `--MERGE (UNION)
+ |--LEFT
+ | |--SCAN TABLE t1
+ | `--USE TEMP B-TREE FOR ORDER BY
+ `--RIGHT
+ |--SCAN TABLE t2
+ `--USE TEMP B-TREE FOR ORDER BY
}
do_eqp_test 4.1.4 {
SELECT * FROM t1 INTERSECT SELECT * FROM t2 ORDER BY 2
} {
- 1 0 0 {SCAN TABLE t1}
- 1 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 2 0 0 {SCAN TABLE t2}
- 2 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (INTERSECT)}
+ QUERY PLAN
+ `--MERGE (INTERSECT)
+ |--LEFT
+ | |--SCAN TABLE t1
+ | `--USE TEMP B-TREE FOR ORDER BY
+ `--RIGHT
+ |--SCAN TABLE t2
+ `--USE TEMP B-TREE FOR ORDER BY
}
do_eqp_test 4.1.5 {
SELECT * FROM t1 EXCEPT SELECT * FROM t2 ORDER BY 2
} {
- 1 0 0 {SCAN TABLE t1}
- 1 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 2 0 0 {SCAN TABLE t2}
- 2 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (EXCEPT)}
+ QUERY PLAN
+ `--MERGE (EXCEPT)
+ |--LEFT
+ | |--SCAN TABLE t1
+ | `--USE TEMP B-TREE FOR ORDER BY
+ `--RIGHT
+ |--SCAN TABLE t2
+ `--USE TEMP B-TREE FOR ORDER BY
}
do_eqp_test 4.2.2 {
SELECT * FROM t1 UNION ALL SELECT * FROM t2 ORDER BY 1
} {
- 1 0 0 {SCAN TABLE t1}
- 1 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 2 0 0 {SCAN TABLE t2 USING INDEX t2i1}
- 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION ALL)}
+ QUERY PLAN
+ `--MERGE (UNION ALL)
+ |--LEFT
+ | |--SCAN TABLE t1
+ | `--USE TEMP B-TREE FOR ORDER BY
+ `--RIGHT
+ `--SCAN TABLE t2 USING INDEX t2i1
}
do_eqp_test 4.2.3 {
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY 1
} {
- 1 0 0 {SCAN TABLE t1}
- 1 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 2 0 0 {SCAN TABLE t2 USING INDEX t2i1}
- 2 0 0 {USE TEMP B-TREE FOR RIGHT PART OF ORDER BY}
- 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION)}
+ QUERY PLAN
+ `--MERGE (UNION)
+ |--LEFT
+ | |--SCAN TABLE t1
+ | `--USE TEMP B-TREE FOR ORDER BY
+ `--RIGHT
+ |--SCAN TABLE t2 USING INDEX t2i1
+ `--USE TEMP B-TREE FOR RIGHT PART OF ORDER BY
}
do_eqp_test 4.2.4 {
SELECT * FROM t1 INTERSECT SELECT * FROM t2 ORDER BY 1
} {
- 1 0 0 {SCAN TABLE t1}
- 1 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 2 0 0 {SCAN TABLE t2 USING INDEX t2i1}
- 2 0 0 {USE TEMP B-TREE FOR RIGHT PART OF ORDER BY}
- 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (INTERSECT)}
+ QUERY PLAN
+ `--MERGE (INTERSECT)
+ |--LEFT
+ | |--SCAN TABLE t1
+ | `--USE TEMP B-TREE FOR ORDER BY
+ `--RIGHT
+ |--SCAN TABLE t2 USING INDEX t2i1
+ `--USE TEMP B-TREE FOR RIGHT PART OF ORDER BY
}
do_eqp_test 4.2.5 {
SELECT * FROM t1 EXCEPT SELECT * FROM t2 ORDER BY 1
} {
- 1 0 0 {SCAN TABLE t1}
- 1 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 2 0 0 {SCAN TABLE t2 USING INDEX t2i1}
- 2 0 0 {USE TEMP B-TREE FOR RIGHT PART OF ORDER BY}
- 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (EXCEPT)}
+ QUERY PLAN
+ `--MERGE (EXCEPT)
+ |--LEFT
+ | |--SCAN TABLE t1
+ | `--USE TEMP B-TREE FOR ORDER BY
+ `--RIGHT
+ |--SCAN TABLE t2 USING INDEX t2i1
+ `--USE TEMP B-TREE FOR RIGHT PART OF ORDER BY
}
do_eqp_test 4.3.1 {
SELECT x FROM t1 UNION SELECT x FROM t2
} {
- 1 0 0 {SCAN TABLE t1}
- 2 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1}
- 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 USING TEMP B-TREE (UNION)}
+ QUERY PLAN
+ `--COMPOUND QUERY
+ |--LEFT-MOST SUBQUERY
+ | `--SCAN TABLE t1
+ `--UNION USING TEMP B-TREE
+ `--SCAN TABLE t2 USING COVERING INDEX t2i1
}
do_eqp_test 4.3.2 {
SELECT x FROM t1 UNION SELECT x FROM t2 UNION SELECT x FROM t1
} {
- 2 0 0 {SCAN TABLE t1}
- 3 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1}
- 1 0 0 {COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (UNION)}
- 4 0 0 {SCAN TABLE t1}
- 0 0 0 {COMPOUND SUBQUERIES 1 AND 4 USING TEMP B-TREE (UNION)}
+ QUERY PLAN
+ `--COMPOUND QUERY
+ |--LEFT-MOST SUBQUERY
+ | `--SCAN TABLE t1
+ |--UNION USING TEMP B-TREE
+ | `--SCAN TABLE t2 USING COVERING INDEX t2i1
+ `--UNION USING TEMP B-TREE
+ `--SCAN TABLE t1
}
do_eqp_test 4.3.3 {
SELECT x FROM t1 UNION SELECT x FROM t2 UNION SELECT x FROM t1 ORDER BY 1
} {
- 2 0 0 {SCAN TABLE t1}
- 2 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 3 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1}
- 1 0 0 {COMPOUND SUBQUERIES 2 AND 3 (UNION)}
- 4 0 0 {SCAN TABLE t1}
- 4 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 0 0 0 {COMPOUND SUBQUERIES 1 AND 4 (UNION)}
-}
-
+ QUERY PLAN
+ `--MERGE (UNION)
+ |--LEFT
+ | `--MERGE (UNION)
+ | |--LEFT
+ | | |--SCAN TABLE t1
+ | | `--USE TEMP B-TREE FOR ORDER BY
+ | `--RIGHT
+ | `--SCAN TABLE t2 USING COVERING INDEX t2i1
+ `--RIGHT
+ |--SCAN TABLE t1
+ `--USE TEMP B-TREE FOR ORDER BY
+}
+
+if 0 {
#-------------------------------------------------------------------------
# This next block of tests verifies that the examples on the
# lang_explain.html page are correct.
#
drop_all_tables
-# EVIDENCE-OF: R-47779-47605 sqlite> EXPLAIN QUERY PLAN SELECT a, b
+# XVIDENCE-OF: R-47779-47605 sqlite> EXPLAIN QUERY PLAN SELECT a, b
# FROM t1 WHERE a=1;
# 0|0|0|SCAN TABLE t1
#
0 0 0 {SCAN TABLE t1}
}
-# EVIDENCE-OF: R-55852-17599 sqlite> CREATE INDEX i1 ON t1(a);
+# XVIDENCE-OF: R-55852-17599 sqlite> CREATE INDEX i1 ON t1(a);
# sqlite> EXPLAIN QUERY PLAN SELECT a, b FROM t1 WHERE a=1;
# 0|0|0|SEARCH TABLE t1 USING INDEX i1
#
0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}
}
-# EVIDENCE-OF: R-21179-11011 sqlite> CREATE INDEX i2 ON t1(a, b);
+# XVIDENCE-OF: R-21179-11011 sqlite> CREATE INDEX i2 ON t1(a, b);
# sqlite> EXPLAIN QUERY PLAN SELECT a, b FROM t1 WHERE a=1;
# 0|0|0|SEARCH TABLE t1 USING COVERING INDEX i2 (a=?)
#
0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=?)}
}
-# EVIDENCE-OF: R-09991-48941 sqlite> EXPLAIN QUERY PLAN
+# XVIDENCE-OF: R-09991-48941 sqlite> EXPLAIN QUERY PLAN
# SELECT t1.*, t2.* FROM t1, t2 WHERE t1.a=1 AND t1.b>2;
# 0|0|0|SEARCH TABLE t1 USING COVERING INDEX i2 (a=? AND b>?)
# 0|1|1|SCAN TABLE t2
0 1 1 {SCAN TABLE t2}
}
-# EVIDENCE-OF: R-33626-61085 sqlite> EXPLAIN QUERY PLAN
+# XVIDENCE-OF: R-33626-61085 sqlite> EXPLAIN QUERY PLAN
# SELECT t1.*, t2.* FROM t2, t1 WHERE t1.a=1 AND t1.b>2;
# 0|0|1|SEARCH TABLE t1 USING COVERING INDEX i2 (a=? AND b>?)
# 0|1|0|SCAN TABLE t2
0 1 0 {SCAN TABLE t2}
}
-# EVIDENCE-OF: R-04002-25654 sqlite> CREATE INDEX i3 ON t1(b);
+# XVIDENCE-OF: R-04002-25654 sqlite> CREATE INDEX i3 ON t1(b);
# sqlite> EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=1 OR b=2;
# 0|0|0|SEARCH TABLE t1 USING COVERING INDEX i2 (a=?)
# 0|0|0|SEARCH TABLE t1 USING INDEX i3 (b=?)
0 0 0 {SEARCH TABLE t1 USING INDEX i3 (b=?)}
}
-# EVIDENCE-OF: R-24577-38891 sqlite> EXPLAIN QUERY PLAN
+# XVIDENCE-OF: R-24577-38891 sqlite> EXPLAIN QUERY PLAN
# SELECT c, d FROM t2 ORDER BY c;
# 0|0|0|SCAN TABLE t2
# 0|0|0|USE TEMP B-TREE FOR ORDER BY
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
}
-# EVIDENCE-OF: R-58157-12355 sqlite> CREATE INDEX i4 ON t2(c);
+# XVIDENCE-OF: R-58157-12355 sqlite> CREATE INDEX i4 ON t2(c);
# sqlite> EXPLAIN QUERY PLAN SELECT c, d FROM t2 ORDER BY c;
# 0|0|0|SCAN TABLE t2 USING INDEX i4
#
0 0 0 {SCAN TABLE t2 USING INDEX i4}
}
-# EVIDENCE-OF: R-13931-10421 sqlite> EXPLAIN QUERY PLAN SELECT
+# XVIDENCE-OF: R-13931-10421 sqlite> EXPLAIN QUERY PLAN SELECT
# (SELECT b FROM t1 WHERE a=0), (SELECT a FROM t1 WHERE b=t2.c) FROM t2;
# 0|0|0|SCAN TABLE t2
# 0|0|0|EXECUTE SCALAR SUBQUERY 1
2 0 0 {SEARCH TABLE t1 USING INDEX i3 (b=?)}
}
-# EVIDENCE-OF: R-50892-45943 sqlite> EXPLAIN QUERY PLAN
+# XVIDENCE-OF: R-50892-45943 sqlite> EXPLAIN QUERY PLAN
# SELECT count(*) FROM (SELECT max(b) AS x FROM t1 GROUP BY a) GROUP BY x;
# 1|0|0|SCAN TABLE t1 USING COVERING INDEX i2
# 0|0|0|SCAN SUBQUERY 1
0 0 0 {USE TEMP B-TREE FOR GROUP BY}
}
-# EVIDENCE-OF: R-46219-33846 sqlite> EXPLAIN QUERY PLAN
+# XVIDENCE-OF: R-46219-33846 sqlite> EXPLAIN QUERY PLAN
# SELECT * FROM (SELECT * FROM t2 WHERE c=1), t1;
# 0|0|0|SEARCH TABLE t2 USING INDEX i4 (c=?)
# 0|1|1|SCAN TABLE t1
0 1 1 {SCAN TABLE t1 USING COVERING INDEX i2}
}
-# EVIDENCE-OF: R-37879-39987 sqlite> EXPLAIN QUERY PLAN
+# XVIDENCE-OF: R-37879-39987 sqlite> EXPLAIN QUERY PLAN
# SELECT a FROM t1 UNION SELECT c FROM t2;
# 1|0|0|SCAN TABLE t1
# 2|0|0|SCAN TABLE t2
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 USING TEMP B-TREE (UNION)}
}
-# EVIDENCE-OF: R-44864-63011 sqlite> EXPLAIN QUERY PLAN
+# XVIDENCE-OF: R-44864-63011 sqlite> EXPLAIN QUERY PLAN
# SELECT a FROM t1 EXCEPT SELECT d FROM t2 ORDER BY 1;
# 1|0|0|SCAN TABLE t1 USING COVERING INDEX i2
# 2|0|0|SCAN TABLE t2 2|0|0|USE TEMP B-TREE FOR ORDER BY
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (EXCEPT)}
}
-
if {![nonzero_reserved_bytes]} {
#-------------------------------------------------------------------------
# The following tests - eqp-6.* - test that the example C code on
0 0 0 COMPOUND SUBQUERIES 1 AND 2 (EXCEPT)
}]
}
+}
#-------------------------------------------------------------------------
# The following tests - eqp-7.* - test that queries that use the OP_Count
}
det 7.1 "SELECT count(*) FROM t1" {
- 0 0 0 {SCAN TABLE t1}
+ QUERY PLAN
+ `--SCAN TABLE t1
}
det 7.2 "SELECT count(*) FROM t2" {
- 0 0 0 {SCAN TABLE t2 USING COVERING INDEX i1}
+ QUERY PLAN
+ `--SCAN TABLE t2 USING COVERING INDEX i1
}
do_execsql_test 7.3 {
sqlite3 db test.db
det 7.4 "SELECT count(*) FROM t1" {
- 0 0 0 {SCAN TABLE t1}
+ QUERY PLAN
+ `--SCAN TABLE t1
}
det 7.5 "SELECT count(*) FROM t2" {
- 0 0 0 {SCAN TABLE t2 USING COVERING INDEX i1}
+ QUERY PLAN
+ `--SCAN TABLE t2 USING COVERING INDEX i1
}
#-------------------------------------------------------------------------
}
det 8.1.1 "SELECT * FROM t2" {
- 0 0 0 {SCAN TABLE t2}
+ QUERY PLAN
+ `--SCAN TABLE t2
}
det 8.1.2 "SELECT * FROM t2 WHERE rowid=?" {
- 0 0 0 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)}
+ QUERY PLAN
+ `--SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)
}
det 8.1.3 "SELECT count(*) FROM t2" {
- 0 0 0 {SCAN TABLE t2}
+ QUERY PLAN
+ `--SCAN TABLE t2
}
det 8.2.1 "SELECT * FROM t1" {
- 0 0 0 {SCAN TABLE t1}
+ QUERY PLAN
+ `--SCAN TABLE t1
}
det 8.2.2 "SELECT * FROM t1 WHERE b=?" {
- 0 0 0 {SEARCH TABLE t1 USING PRIMARY KEY (b=?)}
+ QUERY PLAN
+ `--SEARCH TABLE t1 USING PRIMARY KEY (b=?)
}
det 8.2.3 "SELECT * FROM t1 WHERE b=? AND c=?" {
- 0 0 0 {SEARCH TABLE t1 USING PRIMARY KEY (b=? AND c=?)}
+ QUERY PLAN
+ `--SEARCH TABLE t1 USING PRIMARY KEY (b=? AND c=?)
}
det 8.2.4 "SELECT count(*) FROM t1" {
- 0 0 0 {SCAN TABLE t1}
+ QUERY PLAN
+ `--SCAN TABLE t1
}
uplevel do_test [list $testname] [list "execsql_timed {$sql}"]\
[list [list {*}$result]]
}
+
+# Run an EXPLAIN QUERY PLAN $sql in database "db". Then rewrite the output
+# as an ASCII-art graph and return a string that is that graph.
+#
+# Hexadecimal literals in the output text are converted into "xxxxxx" since those
+# literals are pointer values that might very from one run of the test to the
+# next, yet we want the output to be consistent.
+#
+proc query_plan_graph {sql} {
+ db eval "EXPLAIN QUERY PLAN $sql" {
+ set dx($id) $detail
+ lappend cx($parent) $id
+ }
+ set a "\n QUERY PLAN\n"
+ append a [append_graph " " dx cx 0]
+ return [regsub -all { 0x[A-F0-9]+\y} $a { xxxxxx}]
+}
+
+# Helper routine for [query_plan_graph SQL]:
+#
+# Output rows of the graph that are children of $level.
+#
+# prefix: Prepend to every output line
+#
+# dxname: Name of an array variable that stores text describe
+# The description for $id is $dx($id)
+#
+# cxname: Name of an array variable holding children of item.
+# Children of $id are $cx($id)
+#
+# level: Render all lines that are children of $level
+#
+proc append_graph {prefix dxname cxname level} {
+ upvar $dxname dx $cxname cx
+ set a ""
+ set x $cx($level)
+ set n [llength $x]
+ for {set i 0} {$i<$n} {incr i} {
+ set id [lindex $x $i]
+ if {$i==$n-1} {
+ set p1 "`--"
+ set p2 " "
+ } else {
+ set p1 "|--"
+ set p2 "| "
+ }
+ append a $prefix$p1$dx($id)\n
+ if {[info exists cx($id)]} {
+ append a [append_graph "$prefix$p2" dx cx $id]
+ }
+ }
+ return $a
+}
+
+# Do an EXPLAIN QUERY PLAN test on input $sql with expected results $res
+#
+# If $res begins with a "\s+QUERY PLAN\n" then it is assumed to be the
+# complete graph which must match the output of [query_plan_graph $sql]
+# exactly.
+#
+# If $res does not begin with "\s+QUERY PLAN\n" then take it is a string
+# that must be found somewhere in the query plan output.
+#
proc do_eqp_test {name sql res} {
- uplevel do_execsql_test $name [list "EXPLAIN QUERY PLAN $sql"] [list $res]
+ if {[regexp {^\s+QUERY PLAN\n} $res]} {
+ uplevel do_test $name [list [list query_plan_graph $sql]] [list $res]
+ } else {
+ if {[string index $res 0]!="/"} {
+ set res "/*$res*/"
+ }
+ uplevel do_execsql_test $name [list "EXPLAIN QUERY PLAN $sql"] [list $res]
+ }
}
+
#-------------------------------------------------------------------------
# Usage: do_select_tests PREFIX ?SWITCHES? TESTLIST
#