]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix the query planner so that when it has a choice of full-scan tables to
authordrh <drh@noemail.net>
Thu, 5 Aug 2010 02:52:32 +0000 (02:52 +0000)
committerdrh <drh@noemail.net>
Thu, 5 Aug 2010 02:52:32 +0000 (02:52 +0000)
move to the outer loop, it chooses the one that is likely to give the fewest
output rows.

FossilOrigin-Name: 309bbedf9648c750d7b8aedbc15d4fd68f846824

manifest
manifest.uuid
src/where.c
test/where3.test

index 1e36899f17416b3b8a190432d31fbaae67367067..e0fa80338a2a68dc2008534bd6080b50514cab54 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,8 +1,8 @@
 -----BEGIN PGP SIGNED MESSAGE-----
 Hash: SHA1
 
-C If\sthe\souter\sloop\sof\sa\sjoin\smust\sbe\sa\sfull\stable\sscan,\smake\ssure\sthat\san\nincomplete\sANALYZE\sdoes\snot\strick\sthe\splanner\sinto\suse\sa\stable\sthat\smight\nbe\sindexable\sin\san\sinner\sloop.\s\sTicket\s[13f033c865f878]
-D 2010-08-04T21:17:16
+C Fix\sthe\squery\splanner\sso\sthat\swhen\sit\shas\sa\schoice\sof\sfull-scan\stables\sto\nmove\sto\sthe\souter\sloop,\sit\schooses\sthe\sone\sthat\sis\slikely\sto\sgive\sthe\sfewest\noutput\srows.
+D 2010-08-05T02:52:32
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in ec08dc838fd8110fe24c92e5130bcd91cbb1ff2e
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -233,7 +233,7 @@ F src/vtab.c 82200af3881fa4e1c9cf07cf31d98c09d437e3ab
 F src/wal.c 0925601f3299c2941a67c9cfff41ee710f70ca82
 F src/wal.h 906c85760598b18584921fe08008435aa4eeeeb2
 F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
-F src/where.c a1398b29b8ec129ab656ee136e9116fc2f26e3f2
+F src/where.c a4b2cb14dbd727ff277be3f52851425191a87f66
 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
 F test/all.test 6745008c144bd2956d58864d21f7b304689c1cce
@@ -801,7 +801,7 @@ F test/walslow.test d21625e2e99e11c032ce949e8a94661576548933
 F test/walthread.test a25a393c068a2b42b44333fa3fdaae9072f1617c
 F test/where.test de337a3fe0a459ec7c93db16a519657a90552330
 F test/where2.test 43d4becaf5a5df854e6c21d624a1cb84c6904554
-F test/where3.test acdacc5e1e50ea935b74b3378e4e4b3fa4950092
+F test/where3.test 3bf8006d441b66a57bee02bb420423f84eb8fde3
 F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2
 F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2
 F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b
@@ -844,14 +844,14 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
-P 65b8636ac6e5d3e4502d4f576ddf9350d5df3022
-R f303f8cd6c15f9355d52ea6f34dbc596
+P e7a714b52c45af096af74049826d32c647abfe3f
+R 3b6850bff2212e5e06d5e22c17492cd7
 U drh
-Z 334acc501b61b4dedf959e66e446e709
+Z be2f8a9b75ad13b92899d24ed9ea0e24
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.6 (GNU/Linux)
 
-iD8DBQFMWdjfoxKgR168RlERAtzHAJsHfo8i9JQtGPiUDENko/1YB6PzvwCfchpH
-IJ8P1uaP+Eu9M19yQeVqg/o=
-=L8a0
+iD8DBQFMWidzoxKgR168RlERAgBpAJ9Bgesz+bY3zJhMYLF/PXRObWtInQCdH7Y+
+9WSa5R0wAcOA6sAA258SaiU=
+=4Hzv
 -----END PGP SIGNATURE-----
index 4af956306e86321b907300fab2e40fdaf3b0a5e7..9c848d0255fbdacf0fd41f316246b3e682a2ea73 100644 (file)
@@ -1 +1 @@
-e7a714b52c45af096af74049826d32c647abfe3f
\ No newline at end of file
+309bbedf9648c750d7b8aedbc15d4fd68f846824
\ No newline at end of file
index d32997d063c343dfd2d8a74cb4bee4faabb372ff..ae9bf85fab2513716bb9a14228e2a9ee315fd12a 100644 (file)
@@ -4065,6 +4065,7 @@ WhereInfo *sqlite3WhereBegin(
     Bitmask m;                  /* Bitmask value for j or bestJ */
     int isOptimal;              /* Iterator for optimal/non-optimal search */
     int nUnconstrained;         /* Number tables without INDEXED BY */
+    Bitmask notIndexed;         /* Mask of tables that cannot use an index */
 
     memset(&bestPlan, 0, sizeof(bestPlan));
     bestPlan.rCost = SQLITE_BIG_DBL;
@@ -4107,8 +4108,9 @@ WhereInfo *sqlite3WhereBegin(
     ** costlier approach.
     */
     nUnconstrained = 0;
+    notIndexed = 0;
     for(isOptimal=(iFrom<nTabList-1); isOptimal>=0; isOptimal--){
-      Bitmask mask;  /* Mask of tables not yet ready */
+      Bitmask mask;             /* Mask of tables not yet ready */
       for(j=iFrom, pTabItem=&pTabList->a[j]; j<nTabList; j++, pTabItem++){
         int doNotReorder;    /* True if this table should not be reordered */
         WhereCost sCost;     /* Cost information from best[Virtual]Index() */
@@ -4137,28 +4139,43 @@ WhereInfo *sqlite3WhereBegin(
         }
         assert( isOptimal || (sCost.used&notReady)==0 );
 
+        /* If an INDEXED BY clause is present, then the plan must use that
+        ** index if it uses any index at all */
+        assert( pTabItem->pIndex==0 
+                  || (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0
+                  || sCost.plan.u.pIdx==pTabItem->pIndex );
+
+        if( isOptimal && (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){
+          notIndexed |= m;
+        }
+
         /* Conditions under which this table becomes the best so far:
         **
         **   (1) The table must not depend on other tables that have not
         **       yet run.
         **
         **   (2) A full-table-scan plan cannot supercede another plan unless
-        **       the full-table-scan is an optimal plan.
+        **       it is an "optimal" plan as defined above.
         **
-        **   (3) The plan cost must be lower than prior plans or else the
-        **       cost must be the same and the number of rows must be lower.
-        **
-        **   (4) All tables have an INDEXED BY clause or this table lacks an
+        **   (3) All tables have an INDEXED BY clause or this table lacks an
         **       INDEXED BY clause or this table uses the specific
-        **       index specified by its INDEXED BY clause.
+        **       index specified by its INDEXED BY clause.  This rule ensures
+        **       that a best-so-far is always selected even if an impossible
+        **       combination of INDEXED BY clauses are given.  The error
+        **       will be detected and relayed back to the application later.
+        **       The NEVER() comes about because rule (2) above prevents
+        **       An indexable full-table-scan from reaching rule (3).
+        **
+        **   (4) The plan cost must be lower than prior plans or else the
+        **       cost must be the same and the number of rows must be lower.
         */
         if( (sCost.used&notReady)==0                       /* (1) */
-            && (bestJ<0 || isOptimal                       /* (2) */
+            && (bestJ<0 || (notIndexed&m)!=0               /* (2) */
                 || (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)
-            && (bestJ<0 || sCost.rCost<bestPlan.rCost      /* (3) */
+            && (nUnconstrained==0 || pTabItem->pIndex==0   /* (3) */
+                || NEVER((sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0))
+            && (bestJ<0 || sCost.rCost<bestPlan.rCost      /* (4) */
                 || (sCost.rCost<=bestPlan.rCost && sCost.nRow<bestPlan.nRow))
-            && (nUnconstrained==0 || pTabItem->pIndex==0   /* (4) */
-                || pTabItem->pIndex==sCost.plan.u.pIdx)
         ){
           WHERETRACE(("... best so far with cost=%g and nRow=%g\n",
                       sCost.rCost, sCost.nRow));
index 7296e6a68918a709877cc8416ab43a6ddb153524..9fa4cf9920e4c2e4fb3537663f9fc4d9822a3d97 100644 (file)
@@ -235,4 +235,30 @@ do_test where3-3.1 {
   }
 } {0 1 {TABLE t302} 1 0 {TABLE t301 USING PRIMARY KEY}}
 
+# Verify that when there are multiple tables in a join which must be
+# full table scans that the query planner attempts put the table with
+# the fewest number of output rows as the outer loop.
+#
+do_test where3-4.0 {
+  execsql {
+    CREATE TABLE t400(a INTEGER PRIMARY KEY, b, c);
+    CREATE TABLE t401(p INTEGER PRIMARY KEY, q, r);
+    CREATE TABLE t402(x INTEGER PRIMARY KEY, y, z);
+    EXPLAIN QUERY PLAN
+    SELECT * FROM t400, t401, t402 WHERE t402.z GLOB 'abc*';
+  }
+} {0 2 {TABLE t402} 1 0 {TABLE t400} 2 1 {TABLE t401}}
+do_test where3-4.1 {
+  execsql {
+    EXPLAIN QUERY PLAN
+    SELECT * FROM t400, t401, t402 WHERE t401.r GLOB 'abc*';
+  }
+} {0 1 {TABLE t401} 1 0 {TABLE t400} 2 2 {TABLE t402}}
+do_test where3-4.2 {
+  execsql {
+    EXPLAIN QUERY PLAN
+    SELECT * FROM t400, t401, t402 WHERE t400.c GLOB 'abc*';
+  }
+} {0 0 {TABLE t400} 1 1 {TABLE t401} 2 2 {TABLE t402}}
+
 finish_test