]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Test cases and tuning of the new optimizer code. (CVS 2567)
authordrh <drh@noemail.net>
Thu, 28 Jul 2005 20:51:19 +0000 (20:51 +0000)
committerdrh <drh@noemail.net>
Thu, 28 Jul 2005 20:51:19 +0000 (20:51 +0000)
FossilOrigin-Name: 4b02703dec71aa78e5f8d8cab5b950966a4c6abc

manifest
manifest.uuid
src/vdbe.c
src/where.c
test/where2.test [new file with mode: 0644]

index 89e1dd69b3abca96c5784ee74a64a14130d94029..b3cd0fb55db5f7afcc9d264cb0e7b9edbd41cbfd 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C The\snew\soptimizer\snow\spasses\sall\sregression\stests.\s(CVS\s2566)
-D 2005-07-28T16:51:51
+C Test\scases\sand\stuning\sof\sthe\snew\soptimizer\scode.\s(CVS\s2567)
+D 2005-07-28T20:51:19
 F Makefile.in 22ea9c0fe748f591712d8fe3c6d972c6c173a165
 F Makefile.linux-gcc 06be33b2a9ad4f005a5f42b22c4a19dab3cbb5c7
 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@@ -78,14 +78,14 @@ F src/update.c a9d2c5f504212d62da1b094476f1389c0e02f83f
 F src/utf.c bda5eb85039ef16f2d17004c1e18c96e1ab0a80c
 F src/util.c 668d31be592753e5b8ea00e69ea8d3eedb29fa22
 F src/vacuum.c 829d9e1a6d7c094b80e0899686670932eafd768c
-F src/vdbe.c 0145d877d0e086d5d4bc28f52552883105373163
+F src/vdbe.c 69f33e22c7d0a64b23fbb69e6da95a1bb6869032
 F src/vdbe.h 75e466d84d362b0c4498978a9d6b1e6bd32ecf3b
 F src/vdbeInt.h 9be9a6c43d38124bd03cc5cf05715605b1789fd9
 F src/vdbeapi.c 7f392f0792d1258c958083d7de9eae7c3530c9a6
 F src/vdbeaux.c 3732a86566a6be4da4c606e9334baf3fd98667af
 F src/vdbefifo.c b8805850afe13b43f1de78d58088cb5d66f88e1e
 F src/vdbemem.c da8e8d6f29dd1323f782f000d7cd120027c9ff03
-F src/where.c efed9d45672ea5fc550072272d86e28a2649c256
+F src/where.c 6c3de6ee253256d58ef07acb0f07458605477cd4
 F tclinstaller.tcl 046e3624671962dc50f0481d7c25b38ef803eb42
 F test/all.test 7f0988442ab811dfa41793b5b550f5828ce316f3
 F test/alter.test 9d6837a3d946b73df692b7cef2a7644d2e2f6bc6
@@ -225,6 +225,7 @@ F test/vacuum2.test 5d77e98c458bcdbeecc6327de5107179ba1aa095
 F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102
 F test/view.test 3c79232a2ee45918c62a0cf90411525899404a76
 F test/where.test b6ab0f64adc5fbb4259f284b19da6cd9aeadc711
+F test/where2.test 6427619390385bf4fc357b3907b8733012964a92
 F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
 F tool/lemon.c c88936c67f6411608db8fa4254d254f509fa40f6
 F tool/lempar.c f0c30abcae762a7d1eb37cd88b2232ab8dd625fb
@@ -287,7 +288,7 @@ F www/tclsqlite.tcl 425be741b8ae664f55cb1ef2371aab0a75109cf9
 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
 F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b
 F www/whentouse.tcl 528299b8316726dbcc5548e9aa0648c8b1bd055b
-P ee3a08e353f563c36e904479393fcb56f96ee975
-R 0f7ba5ada7ced4acb01d1d193cdec9c1
+P a21212843359fb9fdbd60799ae50ad3566f4399a
+R b3b6fd7089d32b94d5badcc51bbe97b5
 U drh
-Z 28da42b8f7133daabbc46de020ba7352
+Z 603aecf655d7fb47a89068df78f35d0c
index 9e111afca2b325fc6d97df9d9bbd92c3ff050ea8..ad2b4e32c8ae1d79287124f66ff51053428f9cdf 100644 (file)
@@ -1 +1 @@
-a21212843359fb9fdbd60799ae50ad3566f4399a
\ No newline at end of file
+4b02703dec71aa78e5f8d8cab5b950966a4c6abc
\ No newline at end of file
index 4215ae9950d7ad6bddcb4875de710af803b0ea82..b6cdcdc9ad48201a670d6958f53774804009f053 100644 (file)
@@ -43,7 +43,7 @@
 ** in this file for details.  If in doubt, do not deviate from existing
 ** commenting and indentation practices when changing or adding code.
 **
-** $Id: vdbe.c,v 1.477 2005/07/23 03:18:40 drh Exp $
+** $Id: vdbe.c,v 1.478 2005/07/28 20:51:19 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -454,7 +454,6 @@ int sqlite3VdbeExec(
   int rc = SQLITE_OK;        /* Value to return */
   sqlite3 *db = p->db;       /* The database */
   Mem *pTos;                 /* Top entry in the operand stack */
-  char zBuf[100];            /* Space to sprintf() an integer */
 #ifdef VDBE_PROFILE
   unsigned long long start;  /* CPU clock count at start of opcode */
   int origPc;                /* Program counter at start of opcode */
@@ -2539,11 +2538,7 @@ case OP_OpenWrite: {       /* no-push */
     p2 = pTos->i;
     assert( (pTos->flags & MEM_Dyn)==0 );
     pTos--;
-    if( p2<2 ){
-      sqlite3SetString(&p->zErrMsg, "root page number less than 2", (char*)0);
-      rc = SQLITE_INTERNAL;
-      break;
-    }
+    assert( p2>=2 );
   }
   assert( i>=0 );
   pCur = allocateCursor(p, i);
@@ -4605,9 +4600,7 @@ case OP_Expire: {        /* no-push */
 /* An other opcode is illegal...
 */
 default: {
-  sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",pOp->opcode);
-  sqlite3SetString(&p->zErrMsg, "unknown opcode ", zBuf, (char*)0);
-  rc = SQLITE_INTERNAL;
+  assert( 0 );
   break;
 }
 
@@ -4644,10 +4637,7 @@ default: {
     if( pTos>=p->aStack ){
       sqlite3VdbeMemSanity(pTos, db->enc);
     }
-    if( pc<-1 || pc>=p->nOp ){
-      sqlite3SetString(&p->zErrMsg, "jump destination out of range", (char*)0);
-      rc = SQLITE_INTERNAL;
-    }
+    assert( pc>=-1 && pc<p->nOp );
 #ifdef SQLITE_DEBUG
     /* Code for tracing the vdbe stack. */
     if( p->trace && pTos>=p->aStack ){
index 58bf1d475d03d465a4a95a967ba9fa3af5be853f..88f96f4235cb3560bd79c450ec52e26db1740612 100644 (file)
@@ -16,7 +16,7 @@
 ** so is applicable.  Because this module is responsible for selecting
 ** indices, you might also think of this module as the "query optimizer".
 **
-** $Id: where.c,v 1.154 2005/07/28 16:51:51 drh Exp $
+** $Id: where.c,v 1.155 2005/07/28 20:51:19 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -149,9 +149,7 @@ struct ExprMaskSet {
 ** terms in the where clause.
 */
 #define WO_IN     1
-#define WO_LIST   2
-#define WO_SELECT 4
-#define WO_EQ     8
+#define WO_EQ     2
 #define WO_LT     (WO_EQ<<(TK_LT-TK_EQ))
 #define WO_LE     (WO_EQ<<(TK_LE-TK_EQ))
 #define WO_GT     (WO_EQ<<(TK_GT-TK_EQ))
@@ -470,13 +468,6 @@ static void exprAnalyze(
       pTerm->leftCursor = pLeft->iTable;
       pTerm->leftColumn = pLeft->iColumn;
       pTerm->operator = operatorMask(pExpr->op);
-      if( pTerm->operator==WO_IN ){
-        if( pExpr->pSelect ){
-          pTerm->operator |= WO_SELECT;
-        }else if( pExpr->pList ){
-          pTerm->operator |= WO_LIST;
-        }
-      }
     }
     if( pRight && pRight->op==TK_COLUMN ){
       WhereTerm *pNew;
@@ -649,7 +640,7 @@ static double estLog(double N){
 /*
 ** Find the best index for accessing a particular table.  Return a pointer
 ** to the index, flags that describe how the index should be used, the
-** number of equality constraints and the "cost" for this index.
+** number of equality constraints, and the "cost" for this index.
 **
 ** The lowest cost index wins.  The cost is an estimate of the amount of
 ** CPU and disk I/O need to process the request using the selected index.
@@ -692,6 +683,7 @@ static double bestIndex(
   */
   pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0);
   if( pTerm ){
+    Expr *pExpr;
     *ppIndex = 0;
     bestFlags = WHERE_ROWID_EQ;
     if( pTerm->operator & WO_EQ ){
@@ -702,10 +694,10 @@ static double bestIndex(
       if( pOrderBy ) *pFlags |= WHERE_ORDERBY;
       TRACE(("... best is rowid\n"));
       return 0.0;
-    }else if( pTerm->operator & WO_LIST ){
+    }else if( (pExpr = pTerm->pExpr)->pList!=0 ){
       /* Rowid IN (LIST): cost is NlogN where N is the number of list
       ** elements.  */
-      lowestCost = pTerm->pExpr->pList->nExpr;
+      lowestCost = pExpr->pList->nExpr;
       lowestCost *= estLog(lowestCost);
     }else{
       /* Rowid IN (SELECT): cost is NlogN where N is the number of rows
@@ -777,11 +769,12 @@ static double bestIndex(
       if( pTerm==0 ) break;
       flags |= WHERE_COLUMN_EQ;
       if( pTerm->operator & WO_IN ){
+        Expr *pExpr = pTerm->pExpr;
         flags |= WHERE_COLUMN_IN;
-        if( pTerm->operator & WO_SELECT ){
+        if( pExpr->pSelect!=0 ){
           inMultiplier *= 100.0;
-        }else if( pTerm->operator & WO_LIST ){
-          inMultiplier *= pTerm->pExpr->pList->nExpr + 1.0;
+        }else if( pExpr->pList!=0 ){
+          inMultiplier *= pExpr->pList->nExpr + 1.0;
         }
       }
     }
@@ -795,7 +788,7 @@ static double bestIndex(
       int j = pProbe->aiColumn[nEq];
       pTerm = findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE|WO_GT|WO_GE, pProbe);
       if( pTerm ){
-        flags = WHERE_COLUMN_RANGE;
+        flags |= WHERE_COLUMN_RANGE;
         if( findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE, pProbe) ){
           flags |= WHERE_TOP_LIMIT;
           cost *= 0.333;
@@ -851,9 +844,7 @@ static double bestIndex(
     if( cost < lowestCost ){
       bestIdx = pProbe;
       lowestCost = cost;
-      if( flags==0 ){
-        flags = WHERE_COLUMN_RANGE;
-      }
+      assert( flags!=0 );
       bestFlags = flags;
       bestNEq = nEq;
     }
@@ -963,6 +954,8 @@ static void codeEqualityTerm(
       aIn[0] = OP_Next;
       aIn[1] = iTab;
       aIn[2] = sqlite3VdbeAddOp(v, OP_Column, iTab, 0);
+    }else{
+      pLevel->nIn = 0;
     }
 #endif
   }
@@ -1370,18 +1363,8 @@ WhereInfo *sqlite3WhereBegin(
       WhereTerm *pStart, *pEnd;
 
       assert( omitTable==0 );
-      if( pLevel->flags & WHERE_BTM_LIMIT ){
-        pStart = findTerm(&wc, iCur, -1, notReady, WO_GT|WO_GE, 0);
-        assert( pStart!=0 );
-      }else{
-        pStart = 0;
-      }
-      if( pLevel->flags & WHERE_TOP_LIMIT ){
-        pEnd = findTerm(&wc, iCur, -1, notReady, WO_LT|WO_LE, 0);
-        assert( pEnd!=0 );
-      }else{
-        pEnd = 0;
-      }
+      pStart = findTerm(&wc, iCur, -1, notReady, WO_GT|WO_GE, 0);
+      pEnd = findTerm(&wc, iCur, -1, notReady, WO_LT|WO_LE, 0);
       if( bRev ){
         pTerm = pStart;
         pStart = pEnd;
@@ -1603,18 +1586,11 @@ WhereInfo *sqlite3WhereBegin(
       /* Case 5:  There is no usable index.  We must do a complete
       **          scan of the entire table.
       */
-      int opRewind;
-
       assert( omitTable==0 );
-      if( bRev ){
-        opRewind = OP_Last;
-        pLevel->op = OP_Prev;
-      }else{
-        opRewind = OP_Rewind;
-        pLevel->op = OP_Next;
-      }
+      assert( bRev==0 );
+      pLevel->op = OP_Next;
       pLevel->p1 = iCur;
-      pLevel->p2 = 1 + sqlite3VdbeAddOp(v, opRewind, iCur, brk);
+      pLevel->p2 = 1 + sqlite3VdbeAddOp(v, OP_Rewind, iCur, brk);
     }
     notReady &= ~getMask(&maskSet, iCur);
 
diff --git a/test/where2.test b/test/where2.test
new file mode 100644 (file)
index 0000000..ba5dcd2
--- /dev/null
@@ -0,0 +1,207 @@
+# 2005 July 28
+#
+# 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 regression tests for SQLite library.  The
+# focus of this file is testing the use of indices in WHERE clauses
+# based on recent changes to the optimizer.
+#
+# $Id: where2.test,v 1.1 2005/07/28 20:51:19 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Build some test data
+#
+do_test where2-1.0 {
+  execsql {
+    BEGIN;
+    CREATE TABLE t1(w int, x int, y int, z int);
+  }
+  for {set i 1} {$i<=100} {incr i} {
+    set w $i
+    set x [expr {int(log($i)/log(2))}]
+    set y [expr {$i*$i + 2*$i + 1}]
+    set z [expr {$x+$y}]
+    execsql {INSERT INTO t1 VALUES($::w,$::x,$::y,$::z)}
+  }
+  execsql {
+    CREATE UNIQUE INDEX i1w ON t1(w);
+    CREATE INDEX i1xy ON t1(x,y);
+    CREATE INDEX i1zyx ON t1(z,y,x);
+    COMMIT;
+  }
+} {}
+
+# Do an SQL statement.  Append the search count to the end of the result.
+#
+proc count sql {
+  set ::sqlite_search_count 0
+  return [concat [execsql $sql] $::sqlite_search_count]
+}
+
+# This procedure executes the SQL.  Then it checks to see if the OP_Sort
+# opcode was executed.  If an OP_Sort did occur, then "sort" is appended
+# to the result.  If no OP_Sort happened, then "nosort" is appended.
+#
+# This procedure is used to check to make sure sorting is or is not
+# occurring as expected.
+#
+proc cksort {sql} {
+  set ::sqlite_sort_count 0
+  set data [execsql $sql]
+  if {$::sqlite_sort_count} {set x sort} {set x nosort}
+  lappend data $x
+  return $data
+}
+
+# This procedure executes the SQL.  Then it appends to the result the
+# "sort" or "nosort" keyword (as in the cksort procedure above) then
+# it appends the ::sqlite_query_plan variable.
+#
+proc queryplan {sql} {
+  set ::sqlite_sort_count 0
+  set data [execsql $sql]
+  if {$::sqlite_sort_count} {set x sort} {set x nosort}
+  lappend data $x
+  return [concat $data $::sqlite_query_plan]
+}
+
+
+# Prefer a UNIQUE index over another index.
+#
+do_test where2-1.1 {
+  queryplan {
+    SELECT * FROM t1 WHERE w=85 AND x=6 AND y=7396
+  }
+} {85 6 7396 7402 nosort t1 i1w}
+
+# Always prefer a rowid== constraint over any other index.
+#
+do_test where2-1.3 {
+  queryplan {
+    SELECT * FROM t1 WHERE w=85 AND x=6 AND y=7396 AND rowid=85
+  }
+} {85 6 7396 7402 nosort t1 *}
+
+# When constrained by a UNIQUE index, the ORDER BY clause is always ignored.
+#
+do_test where2-2.1 {
+  queryplan {
+    SELECT * FROM t1 WHERE w=85 ORDER BY random(5);
+  }
+} {85 6 7396 7402 nosort t1 i1w}
+do_test where2-2.2 {
+  queryplan {
+    SELECT * FROM t1 WHERE x=6 AND y=7396 ORDER BY random(5);
+  }
+} {85 6 7396 7402 sort t1 i1xy}
+do_test where2-2.3 {
+  queryplan {
+    SELECT * FROM t1 WHERE rowid=85 AND x=6 AND y=7396 ORDER BY random(5);
+  }
+} {85 6 7396 7402 nosort t1 *}
+
+
+# Efficient handling of forward and reverse table scans.
+#
+do_test where2-3.1 {
+  queryplan {
+    SELECT * FROM t1 ORDER BY rowid LIMIT 2
+  }
+} {1 0 4 4 2 1 9 10 nosort t1 *}
+do_test where2-3.2 {
+  queryplan {
+    SELECT * FROM t1 ORDER BY rowid DESC LIMIT 2
+  }
+} {100 6 10201 10207 99 6 10000 10006 nosort t1 *}
+
+# The IN operator can be used by indices at multiple layers
+#
+do_test where2-4.1 {
+  queryplan {
+    SELECT * FROM t1 WHERE z IN (10207,10006) AND y IN (10000,10201)
+                     AND x>0 AND x<10
+    ORDER BY w
+  }
+} {99 6 10000 10006 100 6 10201 10207 sort t1 i1zyx}
+do_test where2-4.2 {
+  queryplan {
+    SELECT * FROM t1 WHERE z IN (10207,10006) AND y=10000
+                     AND x>0 AND x<10
+    ORDER BY w
+  }
+} {99 6 10000 10006 sort t1 i1zyx}
+do_test where2-4.3 {
+  queryplan {
+    SELECT * FROM t1 WHERE z=10006 AND y IN (10000,10201)
+                     AND x>0 AND x<10
+    ORDER BY w
+  }
+} {99 6 10000 10006 sort t1 i1zyx}
+do_test where2-4.4 {
+  queryplan {
+    SELECT * FROM t1 WHERE z IN (SELECT 10207 UNION SELECT 10006)
+                     AND y IN (10000,10201)
+                     AND x>0 AND x<10
+    ORDER BY w
+  }
+} {99 6 10000 10006 100 6 10201 10207 sort t1 i1zyx}
+do_test where2-4.5 {
+  queryplan {
+    SELECT * FROM t1 WHERE z IN (SELECT 10207 UNION SELECT 10006)
+                     AND y IN (SELECT 10000 UNION SELECT 10201)
+                     AND x>0 AND x<10
+    ORDER BY w
+  }
+} {99 6 10000 10006 100 6 10201 10207 sort t1 i1zyx}
+do_test where2-4.6 {
+  queryplan {
+    SELECT * FROM t1
+     WHERE x IN (1,2,3,4,5,6,7,8)
+       AND y IN (10000,10001,10002,10003,10004,10005)
+     ORDER BY 2
+  }
+} {99 6 10000 10006 sort t1 i1xy}
+
+# Duplicate entires on the RHS of an IN operator do not cause duplicate
+# output rows.
+#
+do_test where2-4.6 {
+  queryplan {
+    SELECT * FROM t1 WHERE z IN (10207,10006,10006,10207)
+    ORDER BY w
+  }
+} {99 6 10000 10006 100 6 10201 10207 sort t1 i1zyx}
+do_test where2-4.7 {
+  queryplan {
+    SELECT * FROM t1 WHERE z IN (
+       SELECT 10207 UNION ALL SELECT 10006
+       UNION ALL SELECT 10006 UNION ALL SELECT 10207)
+    ORDER BY w
+  }
+} {99 6 10000 10006 100 6 10201 10207 sort t1 i1zyx}
+
+# The use of an IN operator disables the index as a sorter.
+#
+do_test where2-5.1 {
+  queryplan {
+    SELECT * FROM t1 WHERE w=99 ORDER BY w
+  }
+} {99 6 10000 10006 nosort t1 i1w}
+do_test where2-5.2 {
+  queryplan {
+    SELECT * FROM t1 WHERE w IN (99) ORDER BY w
+  }
+} {99 6 10000 10006 sort t1 i1w}
+
+
+integrity_check {where2-99.0}
+
+finish_test