]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Import the "PRAGMA vdbe_eqp" enhancement and the enhanced EXPLAIN formatting skip-scan
authordrh <drh@noemail.net>
Wed, 13 Nov 2013 19:01:41 +0000 (19:01 +0000)
committerdrh <drh@noemail.net>
Wed, 13 Nov 2013 19:01:41 +0000 (19:01 +0000)
the shell from trunk.  Fix a bug in skip-scan and add a test case to prevent
a regression.

FossilOrigin-Name: f668616a29686f3ce532731c534b168e536adbb5

1  2 
manifest
manifest.uuid
src/shell.c
src/where.c
test/skipscan1.test

diff --cc manifest
index 390483589cba5adebbcaf51fa2283b35feb01f4b,78bd21a26796e58ade67a1aebe8e722533774679..414ce8241483f69fb9e3f00ed6527b526055fdc9
+++ b/manifest
@@@ -1,5 -1,5 +1,5 @@@
- C Add\sVDBE\scomments\sto\sthe\sbeginning\sand\send\sof\sskip-scan\sloops.
- D 2013-11-13T17:24:39.000
 -C In\sthe\sshell\stool,\sif\san\s"EXPLAIN"\scommand\sis\sexecuted\sin\s".explain\son"\smode,\sattempt\sto\sautomatically\sindent\sthe\sbodies\sof\sloops\sin\sthe\soutput\sVDBE\sprogram.
 -D 2013-11-13T18:35:01.894
++C Import\sthe\s"PRAGMA\svdbe_eqp"\senhancement\sand\sthe\senhanced\sEXPLAIN\sformatting\nthe\sshell\sfrom\strunk.\s\sFix\sa\sbug\sin\sskip-scan\sand\sadd\sa\stest\scase\sto\sprevent\na\sregression.
++D 2013-11-13T19:01:41.390
  F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
  F Makefile.in 8a07bebafbfda0eb67728f4bd15a36201662d1a1
  F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@@ -220,7 -220,7 +220,7 @@@ F src/random.c 0b2dbc37fdfbfa6bd455b091
  F src/resolve.c fc4673cc49b116e51e7f12de074c0acf8f2388f9
  F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0
  F src/select.c 7317406831ecced390edba972818f3c5f82238c0
- F src/shell.c 03d8d9b4052430343ff30d646334621f980f1202
 -F src/shell.c 3b23017da75118da0a7e3518c6ce78a7b747fb05
++F src/shell.c 6ccc22b717afe4f6d7d3c8b6baa02e3b99a5fdf0
  F src/sqlite.h.in 4dedcab5b32358bf7a596badffe7363be1f1a82d
  F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e
  F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc
@@@ -293,8 -293,8 +293,8 @@@ F src/vtab.c 5a423b042eb1402ef77697d03d
  F src/wal.c 7dc3966ef98b74422267e7e6e46e07ff6c6eb1b4
  F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
  F src/walker.c e9e593d5bb798c3e67fc3893dfe7055c9e7d8d74
- F src/where.c 1d19a1d49a608441fa697ae32627399073be8dcf
 -F src/where.c 537f4d7e869f41b7aef851ad0b050983b4d6508d
 -F src/whereInt.h 63c8345d01d12ded6250b72e7c16855f0a358041
++F src/where.c 2eb88f96a73d77bd61d6afd50c33ec5d63c15dd5
 +F src/whereInt.h 96a75c61f1d2b9d4a8e4bb17d89deb0cf7cba358
  F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
  F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
  F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6
@@@ -805,7 -805,6 +805,7 @@@ F test/shell5.test 46c8c18d62732415c4fe
  F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3
  F test/shrink.test 8c70f62b6e8eb4d54533de6d65bd06b1b9a17868
  F test/sidedelete.test f0ad71abe6233e3b153100f3b8d679b19a488329
- F test/skipscan1.test bc65ecb228eba396c404aa038f49798340f0677f
++F test/skipscan1.test 6bb4891c2cc5efd5690a9da9e7508e53d4a68e10
  F test/soak.test 0b5b6375c9f4110c828070b826b3b4b0bb65cd5f
  F test/softheap1.test 40562fe6cac6d9827b7b42b86d45aedf12c15e24
  F test/sort.test 0e4456e729e5a92a625907c63dcdedfbe72c5dc5
@@@ -1139,7 -1138,7 +1139,7 @@@ F tool/vdbe-compress.tcl f12c884766bd14
  F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
  F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
  F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
- P 5e75ab93881b85801cb4ebf70f2063ff7c51ac19
- R 324d2d53e7ac48858b72f1751f7e7610
 -P 8ce33f4c818e1c785a1c176f6f631b8184e1166b
 -R 5d7c6b4eb6f0cadad72d4cdb7ed00f79
 -U dan
 -Z 87b1c90d92386c54cb5a5e86314bc13e
++P 0c85d93b52311dee7980d977be6ed0dc70b060c1 e7d34ec6814ed4606a6d5d7f68c218ae4d25e666
++R 0f42dd23f60246e0b55a84d51974156a
 +U drh
- Z 7cfb6326eedc7a21a0bd15f40c4f97e2
++Z 2c708550c85df0e56ea327d75809b287
diff --cc manifest.uuid
index ac2ea4072256059044869febf798dd5b36d08f24,e811c26432c61ff052bf20b4de5862f91a2845ab..29ff9c336fbdba4e274729bb15a5119075bf77e8
@@@ -1,1 -1,1 +1,1 @@@
- 0c85d93b52311dee7980d977be6ed0dc70b060c1
 -e7d34ec6814ed4606a6d5d7f68c218ae4d25e666
++f668616a29686f3ce532731c534b168e536adbb5
diff --cc src/shell.c
index c3aee0463398e8195aedb9acc14e9377cb118407,676175081605a583892df2006ef421001c12b817..61df7d6d52e4857b5f846ec0d4cface5441df2b5
@@@ -1141,6 -1148,89 +1148,90 @@@ static int display_stats
    return 0;
  }
  
 -**     * For each "Goto", if the jump destination is a "Yield" instruction
 -**       that occurs earlier in the program than the Goto itself, indent
 -**       all opcodes between the "Yield" and "Goto" by 2 spaces.
+ /*
+ ** Parameter azArray points to a zero-terminated array of strings. zStr
+ ** points to a single nul-terminated string. Return non-zero if zStr
+ ** is equal, according to strcmp(), to any of the strings in the array.
+ ** Otherwise, return zero.
+ */
+ static int str_in_array(const char *zStr, const char **azArray){
+   int i;
+   for(i=0; azArray[i]; i++){
+     if( 0==strcmp(zStr, azArray[i]) ) return 1;
+   }
+   return 0;
+ }
+ /*
+ ** If compiled statement pSql appears to be an EXPLAIN statement, allocate
+ ** and populate the callback_data.aiIndent[] array with the number of
+ ** spaces each opcode should be indented before it is output. 
+ **
+ ** The indenting rules are:
+ **
+ **     * For each "Next", "Prev", "VNext" or "VPrev" instruction, indent
+ **       all opcodes that occur between the p2 jump destination and the opcode
+ **       itself by 2 spaces.
+ **
 -  const char *azYield[] = { "Yield", 0 };
++**     * For each "Goto", if the jump destination is a "Yield", "SeekGt",
++**       or "SeekLt" instruction that occurs earlier in the program than
++**       the Goto itself, indent all opcodes between the earlier instruction
++**       and "Goto" by 2 spaces.
+ */
+ static void explain_data_prepare(struct callback_data *p, sqlite3_stmt *pSql){
+   const char *zSql;               /* The text of the SQL statement */
+   const char *z;                  /* Used to check if this is an EXPLAIN */
+   int *abYield = 0;               /* True if op is an OP_Yield */
+   int nAlloc = 0;                 /* Allocated size of p->aiIndent[], abYield */
+   int iOp;
+   const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", 0 };
++  const char *azYield[] = { "Yield", "SeekLt", "SeekGt", 0 };
+   const char *azGoto[] = { "Goto", 0 };
+   /* Try to figure out if this is really an EXPLAIN statement. If this
+   ** cannot be verified, return early.  */
+   zSql = sqlite3_sql(pSql);
+   if( zSql==0 ) return;
+   for(z=zSql; *z==' ' || *z=='\t' || *z=='\n' || *z=='\f' || *z=='\r'; z++);
+   if( sqlite3_strnicmp(z, "explain", 7) ) return;
+   for(iOp=0; SQLITE_ROW==sqlite3_step(pSql); iOp++){
+     int i;
+     const char *zOp = (const char*)sqlite3_column_text(pSql, 1);
+     int p2 = sqlite3_column_int(pSql, 3);
+     /* Grow the p->aiIndent array as required */
+     if( iOp>=nAlloc ){
+       nAlloc += 100;
+       p->aiIndent = (int*)sqlite3_realloc(p->aiIndent, nAlloc*sizeof(int));
+       abYield = (int*)sqlite3_realloc(abYield, nAlloc*sizeof(int));
+     }
+     abYield[iOp] = str_in_array(zOp, azYield);
+     p->aiIndent[iOp] = 0;
+     p->nIndent = iOp+1;
+     if( str_in_array(zOp, azNext) ){
+       for(i=p2; i<iOp; i++) p->aiIndent[i] += 2;
+     }
+     if( str_in_array(zOp, azGoto) && p2<p->nIndent && abYield[p2] ){
+       for(i=p2+1; i<iOp; i++) p->aiIndent[i] += 2;
+     }
+   }
+   sqlite3_free(abYield);
+   sqlite3_reset(pSql);
+ }
+ /*
+ ** Free the array allocated by explain_data_prepare().
+ */
+ static void explain_data_delete(struct callback_data *p){
+   sqlite3_free(p->aiIndent);
+   p->aiIndent = 0;
+   p->nIndent = 0;
+ }
  /*
  ** Execute a statement or set of statements.  Print 
  ** any result rows/columns depending on the current mode 
diff --cc src/where.c
index 2fd7c600c810dc074d6c240e22f13281d104593b,f5030b6a689de851c237e9bd29e13f2d7ae74271..c4f25675c2b9f7b8bf09f124588229ac1e8edbad
@@@ -3852,6 -3822,6 +3855,7 @@@ static void whereLoopOutputAdjust(Where
      if( (pTerm->prereqAll & notAllowed)!=0 ) continue;
      for(j=pLoop->nLTerm-1; j>=0; j--){
        pX = pLoop->aLTerm[j];
++      if( pX==0 ) continue;
        if( pX==pTerm ) break;
        if( pX->iParent>=0 && (&pWC->a[pX->iParent])==pTerm ) break;
      }
index eba7383eaa4134f3328a729911e713e565252f11,0000000000000000000000000000000000000000..622e48fcfa061267d5628235518d5e12c42fbe15
mode 100644,000000..100644
--- /dev/null
@@@ -1,175 -1,0 +1,190 @@@
 +# 2013-11-13
 +#
 +# 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.
 +#
 +#***********************************************************************
 +#
 +# This file implements tests of the "skip-scan" query strategy.
 +#
 +
 +set testdir [file dirname $argv0]
 +source $testdir/tester.tcl
 +
 +do_execsql_test skipscan1-1.1 {
 +  CREATE TABLE t1(a TEXT, b INT, c INT, d INT);
 +  CREATE INDEX t1abc ON t1(a,b,c);
 +  INSERT INTO t1 VALUES('abc',123,4,5);
 +  INSERT INTO t1 VALUES('abc',234,5,6);
 +  INSERT INTO t1 VALUES('abc',234,6,7);
 +  INSERT INTO t1 VALUES('abc',345,7,8);
 +  INSERT INTO t1 VALUES('def',567,8,9);
 +  INSERT INTO t1 VALUES('def',345,9,10);
 +  INSERT INTO t1 VALUES('bcd',100,6,11);
 +
 +  /* Fake the sqlite_stat1 table so that the query planner believes
 +  ** the table contains thousands of rows and that the first few
 +  ** columns are not selective. */
 +  ANALYZE;
 +  DELETE FROM sqlite_stat1;
 +  INSERT INTO sqlite_stat1 VALUES('t1','t1abc','10000 5000 2000 10');
 +  ANALYZE sqlite_master;
 +} {}
 +
 +# Simple queries that leave the first one or two columns of the
 +# index unconstrainted.
 +#
 +do_execsql_test skipscan1-1.2 {
 +  SELECT a,b,c,d,'|' FROM t1 WHERE b=345 ORDER BY a;
 +} {abc 345 7 8 | def 345 9 10 |}
 +do_execsql_test skipscan1-1.2eqp {
 +  EXPLAIN QUERY PLAN
 +  SELECT a,b,c,d,'|' FROM t1 WHERE b=345 ORDER BY a;
 +} {/* USING INDEX t1abc (ANY(a) AND b=?)*/}
 +do_execsql_test skipscan1-1.2sort {
 +  EXPLAIN QUERY PLAN
 +  SELECT a,b,c,d,'|' FROM t1 WHERE b=345 ORDER BY a;
 +} {~/*ORDER BY*/}
 +
 +do_execsql_test skipscan1-1.3 {
 +  SELECT a,b,c,d,'|' FROM t1 WHERE b=345 ORDER BY a DESC;
 +} {def 345 9 10 | abc 345 7 8 |}
 +do_execsql_test skipscan1-1.3eqp {
 +  EXPLAIN QUERY PLAN
 +  SELECT a,b,c,d,'|' FROM t1 WHERE b=345 ORDER BY a;
 +} {/* USING INDEX t1abc (ANY(a) AND b=?)*/}
 +do_execsql_test skipscan1-1.3sort {
 +  EXPLAIN QUERY PLAN
 +  SELECT a,b,c,d,'|' FROM t1 WHERE b=345 ORDER BY a;
 +} {~/*ORDER BY*/}
 +
 +do_execsql_test skipscan1-1.4 {
 +  SELECT a,b,c,d,'|' FROM t1 WHERE c=6 ORDER BY a, b, c;
 +} {abc 234 6 7 | bcd 100 6 11 |}
 +do_execsql_test skipscan1-1.4eqp {
 +  EXPLAIN QUERY PLAN
 +  SELECT a,b,c,d,'|' FROM t1 WHERE c=6 ORDER BY a, b, c;
 +} {/* USING INDEX t1abc (ANY(a) AND ANY(b) AND c=?)*/}
 +do_execsql_test skipscan1-1.4sort {
 +  EXPLAIN QUERY PLAN
 +  SELECT a,b,c,d,'|' FROM t1 WHERE c=6 ORDER BY a, b, c;
 +} {~/*ORDER BY*/}
 +
 +do_execsql_test skipscan1-1.5 {
 +  SELECT a,b,c,d,'|' FROM t1 WHERE c IN (6,7) ORDER BY a, b, c;
 +} {abc 234 6 7 | abc 345 7 8 | bcd 100 6 11 |}
 +do_execsql_test skipscan1-1.5eqp {
 +  EXPLAIN QUERY PLAN
 +  SELECT a,b,c,d,'|' FROM t1 WHERE c IN (6,7) ORDER BY a, b, c;
 +} {/* USING INDEX t1abc (ANY(a) AND ANY(b) AND c=?)*/}
 +do_execsql_test skipscan1-1.5sort {
 +  EXPLAIN QUERY PLAN
 +  SELECT a,b,c,d,'|' FROM t1 WHERE c IN (6,7) ORDER BY a, b, c;
 +} {~/*ORDER BY*/}
 +
 +do_execsql_test skipscan1-1.6 {
 +  SELECT a,b,c,d,'|' FROM t1 WHERE c BETWEEN 6 AND 7 ORDER BY a, b, c;
 +} {abc 234 6 7 | abc 345 7 8 | bcd 100 6 11 |}
 +do_execsql_test skipscan1-1.6eqp {
 +  EXPLAIN QUERY PLAN
 +  SELECT a,b,c,d,'|' FROM t1 WHERE c BETWEEN 6 AND 7 ORDER BY a, b, c;
 +} {/* USING INDEX t1abc (ANY(a) AND ANY(b) AND c>? AND c<?)*/}
 +do_execsql_test skipscan1-1.6sort {
 +  EXPLAIN QUERY PLAN
 +  SELECT a,b,c,d,'|' FROM t1 WHERE c BETWEEN 6 AND 7 ORDER BY a, b, c;
 +} {~/*ORDER BY*/}
 +
++do_execsql_test skipscan1-1.7 {
++  SELECT a,b,c,d,'|' FROM t1 WHERE b IN (234, 345) AND c BETWEEN 6 AND 7
++   ORDER BY a, b;
++} {abc 234 6 7 | abc 345 7 8 |}
++do_execsql_test skipscan1-1.7eqp {
++  EXPLAIN QUERY PLAN
++  SELECT a,b,c,d,'|' FROM t1 WHERE b IN (234, 345) AND c BETWEEN 6 AND 7
++   ORDER BY a, b;
++} {/* USING INDEX t1abc (ANY(a) AND b=? AND c>? AND c<?)*/}
++do_execsql_test skipscan1-1.7sort {
++  EXPLAIN QUERY PLAN
++  SELECT a,b,c,d,'|' FROM t1 WHERE b IN (234, 345) AND c BETWEEN 6 AND 7
++   ORDER BY a, b;
++} {~/*ORDER BY*/}
++
 +
 +# Joins
 +#
 +do_execsql_test skipscan1-1.51 {
 +  CREATE TABLE t1j(x TEXT, y INTEGER);
 +  INSERT INTO t1j VALUES('one',1),('six',6),('ninty-nine',99);
 +  INSERT INTO sqlite_stat1 VALUES('t1j',null,'3');
 +  ANALYZE sqlite_master;
 +  SELECT x, a, b, c, d, '|' FROM t1j, t1 WHERE c=y ORDER BY +a;
 +} {six abc 234 6 7 | six bcd 100 6 11 |}
 +do_execsql_test skipscan1-1.51eqp {
 +  EXPLAIN QUERY PLAN
 +  SELECT x, a, b, c, d, '|' FROM t1j, t1 WHERE c=y ORDER BY +a;
 +} {/* INDEX t1abc (ANY(a) AND ANY(b) AND c=?)*/}
 +
 +do_execsql_test skipscan1-1.52 {
 +  SELECT x, a, b, c, d, '|' FROM t1j LEFT JOIN t1 ON c=y ORDER BY +y, +a;
 +} {one {} {} {} {} | six abc 234 6 7 | six bcd 100 6 11 | ninty-nine {} {} {} {} |}
 +do_execsql_test skipscan1-1.52eqp {
 +  EXPLAIN QUERY PLAN
 +  SELECT x, a, b, c, d, '|' FROM t1j LEFT JOIN t1 ON c=y ORDER BY +y, +a;
 +} {/* INDEX t1abc (ANY(a) AND ANY(b) AND c=?)*/}
 +
 +do_execsql_test skipscan1-2.1 {
 +  CREATE TABLE t2(a TEXT, b INT, c INT, d INT,
 +                  PRIMARY KEY(a,b,c));
 +  INSERT INTO t2 SELECT * FROM t1;
 +
 +  /* Fake the sqlite_stat1 table so that the query planner believes
 +  ** the table contains thousands of rows and that the first few
 +  ** columns are not selective. */
 +  ANALYZE;
 +  UPDATE sqlite_stat1 SET stat='10000 5000 2000 10' WHERE idx NOT NULL;
 +  ANALYZE sqlite_master;
 +} {}
 +
 +do_execsql_test skipscan1-2.2 {
 +  SELECT a,b,c,d,'|' FROM t2 WHERE b=345 ORDER BY a;
 +} {abc 345 7 8 | def 345 9 10 |}
 +do_execsql_test skipscan1-2.2eqp {
 +  EXPLAIN QUERY PLAN
 +  SELECT a,b,c,d,'|' FROM t2 WHERE b=345 ORDER BY a;
 +} {/* USING INDEX sqlite_autoindex_t2_1 (ANY(a) AND b=?)*/}
 +do_execsql_test skipscan1-2.2sort {
 +  EXPLAIN QUERY PLAN
 +  SELECT a,b,c,d,'|' FROM t2 WHERE b=345 ORDER BY a;
 +} {~/*ORDER BY*/}
 +
 +
 +do_execsql_test skipscan1-3.1 {
 +  CREATE TABLE t3(a TEXT, b INT, c INT, d INT,
 +                  PRIMARY KEY(a,b,c)) WITHOUT ROWID;
 +  INSERT INTO t3 SELECT * FROM t1;
 +
 +  /* Fake the sqlite_stat1 table so that the query planner believes
 +  ** the table contains thousands of rows and that the first few
 +  ** columns are not selective. */
 +  ANALYZE;
 +  UPDATE sqlite_stat1 SET stat='10000 5000 2000 10' WHERE idx NOT NULL;
 +  ANALYZE sqlite_master;
 +} {}
 +
 +do_execsql_test skipscan1-3.2 {
 +  SELECT a,b,c,d,'|' FROM t3 WHERE b=345 ORDER BY a;
 +} {abc 345 7 8 | def 345 9 10 |}
 +do_execsql_test skipscan1-3.2eqp {
 +  EXPLAIN QUERY PLAN
 +  SELECT a,b,c,d,'|' FROM t3 WHERE b=345 ORDER BY a;
 +} {/* INDEX sqlite_autoindex_t3_1 (ANY(a) AND b=?)*/}
 +do_execsql_test skipscan1-3.2sort {
 +  EXPLAIN QUERY PLAN
 +  SELECT a,b,c,d,'|' FROM t3 WHERE b=345 ORDER BY a;
 +} {~/*ORDER BY*/}
 +
 +finish_test