]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
When generating individual loops for each ORed term of an OR scan, move any or-optimization
authordan <dan@noemail.net>
Thu, 22 Jun 2017 16:51:16 +0000 (16:51 +0000)
committerdan <dan@noemail.net>
Thu, 22 Jun 2017 16:51:16 +0000 (16:51 +0000)
constant WHERE expressions outside of the loop, as is done for top-level
loops.

FossilOrigin-Name: e4a022be4b069b08cfdfda5295461676b99d28e17bbbedfbcb362dec69de59bd

manifest
manifest.uuid
src/fkey.c
src/tclsqlite.c
src/where.c
test/eqp.test
test/whereF.test

index 24b18868ac9e1578be828733721c40bcbad21729..f564f142d10aad7cc3c715387c40969d76d16704 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Enable\spragma\svirtual\stables\sfor\sthe\sintegrity_check,\squick_check,\sand\nforeign_key_check\spragmas.
-D 2017-06-21T01:36:30.804
+C When\sgenerating\sindividual\sloops\sfor\seach\sORed\sterm\sof\san\sOR\sscan,\smove\sany\nconstant\sWHERE\sexpressions\soutside\sof\sthe\sloop,\sas\sis\sdone\sfor\stop-level\nloops.
+D 2017-06-22T16:51:16.239
 F Makefile.in 1cc758ce3374a32425e4d130c2fe7b026b20de5b8843243de75f087c0a2661fb
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc 8eeb80162074004e906b53d7340a12a14c471a83743aab975947e95ce061efcc
@@ -360,7 +360,7 @@ F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d
 F src/delete.c 3213547e97b676c6fa79948b7a9ede4801ea04a01a2043241deafedf132ecf5d
 F src/expr.c 452c6f3aa656aabf3eefe96bb5f316b2c987fbc12c647964e4ed880f193ca31f
 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
-F src/fkey.c db65492ae549c3b548c9ef1f279ce1684f1c473b116e1c56a90878cd5dcf968d
+F src/fkey.c 5ff2c895fe087756d8085dc1a9bc229b5670e2a65c3929dd87c71e43649af333
 F src/func.c 9d52522cc8ae7f5cdadfe14594262f1618bc1f86083c4cd6da861b4cf5af6174
 F src/global.c 8a6ab6b4d91effb96ffa81b39f0d70c862abca157f8aaa194600a4a8b7923344
 F src/hash.c 63d0ee752a3b92d4695b2b1f5259c4621b2cfebd
@@ -414,7 +414,7 @@ F src/sqliteInt.h 17e9bce594ea0c38c44ad0cbff4aa50cbff4b25f4bac9d38306caf9f1f028a
 F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
 F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1
 F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
-F src/tclsqlite.c c8cf60d0c5411d5e70e7c136470d29dbe760d250f55198b71682c67086524e4a
+F src/tclsqlite.c 1ac29f18b1b3787a30b45dbbdf6fdc4aa4f1a2f8c7c8fe586beba1b177eba97d
 F src/test1.c c99f0442918a7a5d5b68a95d6024c211989e6c782c15ced5a558994baaf76a5e
 F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
 F src/test3.c b8434949dfb8aff8dfa082c8b592109e77844c2135ed3c492113839b6956255b
@@ -486,7 +486,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
 F src/wal.c 40c543f0a2195d1b0dc88ef12142bea690009344
 F src/wal.h 06b2a0b599cc0f53ea97f497cf8c6b758c999f71
 F src/walker.c d46044e7a5842560dfe7122d93ff5145dd4a96f4d0bf5ba5910a7731b8c01e79
-F src/where.c aa213e1b1c29eb8946a9f25108a18666a745ae5bac41b58d0be98730937a7785
+F src/where.c d4f329d9055dbdb475d697f205db1104af886aad4400168fb9b957f6251ea926
 F src/whereInt.h 2a4b634d63ce488b46d4b0da8f2eaa8f9aeab202bc25ef76f007de5e3fba1f20
 F src/wherecode.c 339ee802d9d311acf0cba8b5a9a092e167ef71c3a777d4b3e57de25d193251c7
 F src/whereexpr.c a2fe3811d45af45a5c6667caabc15e01054fe6228c64e86e1f7d2ba5ef5284f9
@@ -695,7 +695,7 @@ F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea
 F test/enc2.test 83437a79ba1545a55fb549309175c683fb334473
 F test/enc3.test 6807f7a7740a00361ca8d0ccd66bc60c8dc5f2b6
 F test/enc4.test c8f1ce3618508fd0909945beb8b8831feef2c020
-F test/eqp.test 3fe051af50921284189d1970eb653f9fcf5117d2
+F test/eqp.test 3f9ba0b2594837c7beaa3ba824e2137cfe857308f020ec5a0c7a62b444e837b0
 F test/errmsg.test f31592a594b44ee121371d25ddd5d63497bb3401
 F test/eval.test a64c9105d6ff163df7cf09d6ac29cdad5922078c
 F test/exclusive.test 9a57bd66e39144b888ca75c309914fcdefb4e3f9
@@ -1465,7 +1465,7 @@ F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5
 F test/whereC.test cae295158703cb3fc23bf1a108a9ab730efff0f6
 F test/whereD.test 711d4df58d6d4fb9b3f5ce040b818564198be002
 F test/whereE.test b3a055eef928c992b0a33198a7b8dc10eea5ad2f
-F test/whereF.test 5b2ba0dbe8074aa13e416b37c753991f0a2492d7
+F test/whereF.test 97a86ecdfa4c21684fdff501dbd2cb7397689be8676d0dbad1f5a0892c6b56a3
 F test/whereG.test dde4c52a97385a55be6a7cd46be8373f0cf35501
 F test/whereH.test e4b07f7a3c2f5d31195cd33710054c78667573b2
 F test/whereI.test eab5b226bbc344ac70d7dc09b963a064860ae6d7
@@ -1583,7 +1583,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 94e420ecfb4ec047eb7d1d3125ca8487c90d466760b7f7183759ff922bd868d1
-R bb441c04952674b0d2cabd8a1e6870d3
-U drh
-Z cb37bb49734166773486fcb21706d402
+P 118f7bb33a6f78951bbffa957f48015d1bce5aaf9246a99262a90bc8ad52e5a3
+R b4aae75660dcec44ff13144cfab52224
+T *branch * or-optimization
+T *sym-or-optimization *
+T -sym-trunk *
+U dan
+Z 7d390f8e00d309abb459da345f7f0ca9
index 82f0dac969855c081864aa25c7420318c34175a9..03b32f88440a0b14f7261db0322ac47cfec1c9a2 100644 (file)
@@ -1 +1 @@
-118f7bb33a6f78951bbffa957f48015d1bce5aaf9246a99262a90bc8ad52e5a3
\ No newline at end of file
+e4a022be4b069b08cfdfda5295461676b99d28e17bbbedfbcb362dec69de59bd
\ No newline at end of file
index f91a6de547088afc3504ae6cacf5e1549c68dcae..0012768a39b6d0f7633c61fd178b5c60207ff2d1 100644 (file)
@@ -633,10 +633,12 @@ static void fkScanChildren(
   /* Create VDBE to loop through the entries in pSrc that match the WHERE
   ** clause. For each row found, increment either the deferred or immediate
   ** foreign key constraint counter. */
-  pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0);
-  sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr);
-  if( pWInfo ){
-    sqlite3WhereEnd(pWInfo);
+  if( pParse->nErr==0 ){
+    pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0);
+    sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr);
+    if( pWInfo ){
+      sqlite3WhereEnd(pWInfo);
+    }
   }
 
   /* Clean up the WHERE clause constructed above. */
index 754775e8e8b8187e27f08614f0f1d8669ab2ad11..bb7c9e5b82b5ab101b19859b25b73e737fecdb62 100644 (file)
@@ -161,6 +161,7 @@ struct SqliteDb {
   int nStmt;                 /* Number of statements in stmtList */
   IncrblobChannel *pIncrblob;/* Linked list of open incrblob channels */
   int nStep, nSort, nIndex;  /* Statistics for most recent operation */
+  int nVMStep;               /* Another statistic for most recent operation */
   int nTransaction;          /* Number of nested [transaction] methods */
   int openFlags;             /* Flags used to open.  (SQLITE_OPEN_URI) */
 #ifdef SQLITE_TEST
@@ -1588,6 +1589,7 @@ static int dbEvalStep(DbEvalContext *p){
       pDb->nStep = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_FULLSCAN_STEP,1);
       pDb->nSort = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_SORT,1);
       pDb->nIndex = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_AUTOINDEX,1);
+      pDb->nVMStep = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_VM_STEP,1);
       dbReleaseColumnNames(p);
       p->pPreStmt = 0;
 
@@ -2855,7 +2857,7 @@ static int SQLITE_TCLAPI DbObjCmd(
   }
 
   /*
-  **     $db status (step|sort|autoindex)
+  **     $db status (step|sort|autoindex|vmstep)
   **
   ** Display SQLITE_STMTSTATUS_FULLSCAN_STEP or
   ** SQLITE_STMTSTATUS_SORT for the most recent eval.
@@ -2874,9 +2876,11 @@ static int SQLITE_TCLAPI DbObjCmd(
       v = pDb->nSort;
     }else if( strcmp(zOp, "autoindex")==0 ){
       v = pDb->nIndex;
+    }else if( strcmp(zOp, "vmstep")==0 ){
+      v = pDb->nVMStep;
     }else{
       Tcl_AppendResult(interp,
-            "bad argument: should be autoindex, step, or sort",
+            "bad argument: should be autoindex, step, sort or vmstep",
             (char*)0);
       return TCL_ERROR;
     }
index 99b0df4e5decbd509a61fbc113be62330b6ce437..1cd5d7033025351d0cb494e5dc7ff8db116c4b37 100644 (file)
@@ -4294,6 +4294,31 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){
   return 0;
 }
 
+/*
+** Helper function for exprIsDeterministic().
+*/
+static int exprNodeIsDeterministic(Walker *pWalker, Expr *pExpr){
+  if( pExpr->op==TK_FUNCTION && ExprHasProperty(pExpr, EP_ConstFunc)==0 ){
+    pWalker->eCode = 0;
+    return WRC_Abort;
+  }
+  return WRC_Continue;
+}
+
+/*
+** Return true if the expression contains no non-deterministic SQL 
+** functions. Do not consider non-deterministic SQL functions that are 
+** part of sub-select statements.
+*/
+static int exprIsDeterministic(Expr *p){
+  Walker w;
+  memset(&w, 0, sizeof(w));
+  w.eCode = 1;
+  w.xExprCallback = exprNodeIsDeterministic;
+  sqlite3WalkExpr(&w, p);
+  return w.eCode;
+}
+
 /*
 ** Generate the beginning of the loop used for WHERE clause processing.
 ** The return value is a pointer to an opaque structure that contains
@@ -4492,17 +4517,6 @@ WhereInfo *sqlite3WhereBegin(
   sqlite3WhereClauseInit(&pWInfo->sWC, pWInfo);
   sqlite3WhereSplit(&pWInfo->sWC, pWhere, TK_AND);
     
-  /* Special case: a WHERE clause that is constant.  Evaluate the
-  ** expression and either jump over all of the code or fall thru.
-  */
-  for(ii=0; ii<sWLB.pWC->nTerm; ii++){
-    if( nTabList==0 || sqlite3ExprIsConstantNotJoin(sWLB.pWC->a[ii].pExpr) ){
-      sqlite3ExprIfFalse(pParse, sWLB.pWC->a[ii].pExpr, pWInfo->iBreak,
-                         SQLITE_JUMPIFNULL);
-      sWLB.pWC->a[ii].wtFlags |= TERM_CODED;
-    }
-  }
-
   /* Special case: No FROM clause
   */
   if( nTabList==0 ){
@@ -4541,6 +4555,25 @@ WhereInfo *sqlite3WhereBegin(
   sqlite3WhereExprAnalyze(pTabList, &pWInfo->sWC);
   if( db->mallocFailed ) goto whereBeginError;
 
+  /* Special case: WHERE terms that do not refer to any tables in the join
+  ** (constant expressions). Evaluate each such term, and jump over all the
+  ** generated code if the result is not true.  
+  **
+  ** Do not do this if the expression contains non-deterministic functions
+  ** that are not within a sub-select. This is not strictly required, but
+  ** preserves SQLite's legacy behaviour in the following two cases:
+  **
+  **   FROM ... WHERE random()>0;           -- eval random() once per row
+  **   FROM ... WHERE (SELECT random())>0;  -- eval random() once overall
+  */
+  for(ii=0; ii<sWLB.pWC->nTerm; ii++){
+    WhereTerm *pT = &sWLB.pWC->a[ii];
+    if( pT->prereqAll==0 && (nTabList==0 || exprIsDeterministic(pT->pExpr)) ){
+      sqlite3ExprIfFalse(pParse, pT->pExpr, pWInfo->iBreak, SQLITE_JUMPIFNULL);
+      pT->wtFlags |= TERM_CODED;
+    }
+  }
+
   if( wctrlFlags & WHERE_WANT_DISTINCT ){
     if( isDistinctRedundant(pParse, pTabList, &pWInfo->sWC, pResultSet) ){
       /* The DISTINCT marking is pointless.  Ignore it. */
index c955a80c213873eed19c5a9be7f04f04c145a76c..30fcdf287fe329f94857f60a68a24fa6094f4345 100644 (file)
@@ -188,24 +188,24 @@ do_eqp_test 3.1.1 {
 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}
+  0 0 0 {SCAN TABLE t1}
 }
 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}
+  0 0 0 {SCAN TABLE t1}
 }
 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}
+  0 0 0 {SCAN TABLE t1}
 }
 
 det 3.2.1 {
index b9580bb196d200054c3e5d0802ec3a000b3e76f3..3b938aa203bc63b096043897497226f0408aa3f8 100644 (file)
@@ -119,4 +119,61 @@ do_execsql_test 4.0 {
   EXPLAIN QUERY PLAN SELECT rowid FROM t4 WHERE a=? AND b=?;
 } {/a=. AND b=./}
 
+#-------------------------------------------------------------------------
+# Test the following case:
+#
+#   ... FROM t1, t2 WHERE (
+#     t2.rowid = +t1.rowid OR (t2.f2 = t1.f1 AND t1.f1!=-1)
+#   )
+#
+# where there is an index on t2(f2). The planner should use "t1" as the
+# outer loop. The inner loop, on "t2", is an OR optimization. One pass
+# for:
+#
+#     t2.rowid = $1
+#
+# and another for:
+#
+#     t2.f2=$1 AND $1!=-1
+#
+# the test is to ensure that on the second pass, the ($1!=-1) condition
+# is tested before any seek operations are performed - i.e. outside of
+# the loop through the f2=$1 range of the t2(f2) index.
+#
+reset_db
+do_execsql_test 5.0 {
+  CREATE TABLE t1(f1);
+  CREATE TABLE t2(f2);
+  CREATE INDEX t2f ON t2(f2);
+
+  INSERT INTO t1 VALUES(-1);
+  INSERT INTO t1 VALUES(-1);
+  INSERT INTO t1 VALUES(-1);
+  INSERT INTO t1 VALUES(-1);
+
+  WITH w(i) AS (
+    SELECT 1 UNION ALL SELECT i+1 FROM w WHERE i<1000
+  )
+  INSERT INTO t2 SELECT -1 FROM w;
+}
+
+do_execsql_test 5.1 {
+  SELECT count(*) FROM t1, t2 WHERE t2.rowid = +t1.rowid
+} {4}
+do_test 5.2 { expr [db status vmstep]<200 } 1
+
+do_execsql_test 5.3 {
+  SELECT count(*) FROM t1, t2 WHERE (
+    t2.rowid = +t1.rowid OR t2.f2 = t1.f1
+  )
+} {4000}
+do_test 5.4 { expr [db status vmstep]>1000 } 1
+
+do_execsql_test 5.5 {
+  SELECT count(*) FROM t1, t2 WHERE (
+    t2.rowid = +t1.rowid OR (t2.f2 = t1.f1 AND t1.f1!=-1)
+  )
+} {4}
+do_test 5.6 { expr [db status vmstep]<200 } 1
+
 finish_test