]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Query planner optimization to detect empty tables in a join early and bail out
authordrh <drh@noemail.net>
Wed, 15 Feb 2017 22:36:15 +0000 (22:36 +0000)
committerdrh <drh@noemail.net>
Wed, 15 Feb 2017 22:36:15 +0000 (22:36 +0000)
without doing excess work.

FossilOrigin-Name: 58797e9bafa95709e0f706a15f42f93b409e2db5

manifest
manifest.uuid
src/wherecode.c
test/emptytable.test [new file with mode: 0644]

index 74218673906bd7ef1c6e76d10587b7be86905536..b98e432695c1bba907829c82706aa2d2d3d0b8c4 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Minor\senhancement\sto\smutex\stracing\son\sWin32.
-D 2017-02-15T18:30:57.375
+C Query\splanner\soptimization\sto\sdetect\sempty\stables\sin\sa\sjoin\searly\sand\sbail\sout\nwithout\sdoing\sexcess\swork.
+D 2017-02-15T22:36:15.061
 F Makefile.in edb6bcdd37748d2b1c3422ff727c748df7ffe918
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc 067a6766f800cc8d72845ab61f8de4ffe8f3fc99
@@ -477,7 +477,7 @@ F src/wal.h 06b2a0b599cc0f53ea97f497cf8c6b758c999f71
 F src/walker.c 91a6df7435827e41cff6bb7df50ea00934ee78b0
 F src/where.c 6397fab50fdbf9bde76c574ce07b3b776eb28b34
 F src/whereInt.h 2bcc3d176e6091cb8f50a30b65c006e88a73614d
-F src/wherecode.c 99a8ced164c75edf41b3a865a75381c9adb38b28
+F src/wherecode.c 677e95413c472c0b413023b6b69a47f40fce1b04
 F src/whereexpr.c 130cdd1a43af71b19755270fb1224874cf55158c
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
@@ -676,6 +676,7 @@ F test/e_wal.test ae9a593207a77d711443ee69ffe081fda9243625
 F test/e_walauto.test 248af31e73c98df23476a22bdb815524c9dc3ba8
 F test/e_walckpt.test 28c371a6bb5e5fe7f31679c1df1763a19d19e8a0
 F test/e_walhook.test 4c0613a0c76e7a9d5c4c211e1b4cbcc1143914df
+F test/emptytable.test a38110becbdfa6325cd65cb588dca658cd885f62
 F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea
 F test/enc2.test 83437a79ba1545a55fb549309175c683fb334473
 F test/enc3.test 6807f7a7740a00361ca8d0ccd66bc60c8dc5f2b6
@@ -1555,7 +1556,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 810d29320b853b3a01aa50d8f2a0bceacf79e0aa
-R 91fee55f54fe2e54254ba44f0240778d
-U mistachkin
-Z 72fbc76e1b27f40edc76050b0de83496
+P 830b9235673be55f0c932fb157de03725e648c25
+R 32e948726be0c8ec843ec4b81e44685b
+U drh
+Z 345aeb62ab5f52d0700dc3107ec5c535
index 54948777cb460ba994857416d3a4acbbabb6dd10..8b9eddf3bbdfafa7ecda6d73bffb63950b6caa1c 100644 (file)
@@ -1 +1 @@
-830b9235673be55f0c932fb157de03725e648c25
\ No newline at end of file
+58797e9bafa95709e0f706a15f42f93b409e2db5
\ No newline at end of file
index 58e040628ae8752a98dafdd2472e776dee3977f3..4fd5e16fac9944c4d6903d106a691c47e70c0009 100644 (file)
@@ -1062,6 +1062,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
   Vdbe *v;                        /* The prepared stmt under constructions */
   struct SrcList_item *pTabItem;  /* FROM clause term being coded */
   int addrBrk;                    /* Jump here to break out of the loop */
+  int addrHalt;                   /* addrBrk for the outermost loop */
   int addrCont;                   /* Jump here to continue with next cycle */
   int iRowidReg = 0;        /* Rowid is stored in this register, if not zero */
   int iReleaseReg = 0;      /* Temp register to free before returning */
@@ -1103,6 +1104,11 @@ Bitmask sqlite3WhereCodeOneLoopStart(
     VdbeComment((v, "init LEFT JOIN no-match flag"));
   }
 
+  /* Compute a safe address to jump to if we discover that the table for
+  ** this loop is empty and can never contribute content. */
+  for(j=iLevel; j>0 && pWInfo->a[j].iLeftJoin==0; j--){}
+  addrHalt = pWInfo->a[j].addrBrk;
+
   /* Special case of a FROM clause subquery implemented as a co-routine */
   if( pTabItem->fg.viaCoroutine ){
     int regYield = pTabItem->regReturn;
@@ -1287,7 +1293,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
       sqlite3ExprCacheAffinityChange(pParse, r1, 1);
       sqlite3ReleaseTempReg(pParse, rTemp);
     }else{
-      sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, addrBrk);
+      sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, addrHalt);
       VdbeCoverageIf(v, bRev==0);
       VdbeCoverageIf(v, bRev!=0);
     }
@@ -1933,7 +1939,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
       codeCursorHint(pTabItem, pWInfo, pLevel, 0);
       pLevel->op = aStep[bRev];
       pLevel->p1 = iCur;
-      pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk);
+      pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrHalt);
       VdbeCoverageIf(v, bRev==0);
       VdbeCoverageIf(v, bRev!=0);
       pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
diff --git a/test/emptytable.test b/test/emptytable.test
new file mode 100644 (file)
index 0000000..79cd16e
--- /dev/null
@@ -0,0 +1,50 @@
+# 2017-02-15
+#
+# 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.
+#
+#***********************************************************************
+#
+# Test cases to show that a join involving an empty table is very fast.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Build some test data
+#
+do_execsql_test emptytable-100 {
+  CREATE TABLE t1(a);
+  WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100)
+    INSERT INTO t1(a) SELECT x FROM c;
+  CREATE TABLE empty(x);
+  SELECT count(*) FROM t1;
+} {100}
+
+# Interrupt queries after 1M cycles to prevent burning excess CPU
+proc stopDb {args} {
+  db interrupt
+}
+db progress 1000000 {stopDb}
+
+# Prior to the query planner optimization on 2017-02-15, this query would
+# take a ridiculous amount of time.  If that optimization stops working,
+# the result here will be in interrupt for running too long.
+#
+do_catchsql_test emptytable-110 {
+  SELECT count(*) FROM t1, t1, t1, t1, t1, t1, empty;
+} {0 0}
+
+do_catchsql_test emptytable-120 {
+  SELECT count(*) FROM t1, t1 LEFT JOIN empty;
+} {0 10000}
+do_catchsql_test emptytable-121 {
+  SELECT count(*) FROM t1, t1 LEFT JOIN t1, empty;
+} {0 0}
+
+
+finish_test