]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
The new code for taking advantage of inequalities in WHERE clauses
authordrh <drh@noemail.net>
Thu, 8 Nov 2001 00:45:21 +0000 (00:45 +0000)
committerdrh <drh@noemail.net>
Thu, 8 Nov 2001 00:45:21 +0000 (00:45 +0000)
is in place.  It appears to work. (CVS 305)

FossilOrigin-Name: 262bcd17df19f45def6144b5a7e0602ca5b03deb

12 files changed:
manifest
manifest.uuid
src/expr.c
src/sqliteInt.h
src/test1.c
src/vdbe.c
src/vdbe.h
src/where.c
test/index.test
test/rowid.test
test/select2.test
test/where.test

index e73b7b1dfc14c03bcb25e5464f73088da197526e..f718bf6c45682da517a23894b9685e53f9d14851 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C New\sNext\sopcode\sand\sindexing\sstyle\simplemented.\s(CVS\s304)
-D 2001-11-07T16:48:27
+C The\snew\scode\sfor\staking\sadvantage\sof\sinequalities\sin\sWHERE\sclauses\nis\sin\splace.\s\sIt\sappears\sto\swork.\s(CVS\s305)
+D 2001-11-08T00:45:21
 F Makefile.in 6801df952cb1df64aa32e4de85fed24511d28efd
 F Makefile.template 1fdb891f14083ee0b63cf7282f91529634438e7a
 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
@@ -23,7 +23,7 @@ F src/btree.c add522fad1b18c0face24e6f9f7468a6c696c5cc
 F src/btree.h 0250a0a577a98cc64ddf1582d50c08b8d2451650
 F src/build.c 40b7d14435e2cfc5298e8b7bab5e92ed26f42310
 F src/delete.c 5d93a21c1388cfb1359bda01c072f25583a2f4f2
-F src/expr.c 2dd0252ced345c1e64db015b94dc6b5d7a57eef3
+F src/expr.c 53515a7ba787bf4f0b3f73be30eb86aadb6f1b90
 F src/hash.c d0110e6da70a5962e21575fccf8206f7d9d75e00
 F src/hash.h a5f5b3ce2d086a172c5879b0b06a27a82eac9fac
 F src/insert.c 3526be771a01035198bef28d8f370cbcab94f46d
@@ -40,18 +40,18 @@ F src/select.c fa1c7144a9ad7ce3f16373b443bc25e764af4be7
 F src/shell.c 71597951753b56a97fea1c7a30908f31e635c00c
 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
 F src/sqlite.h.in 934de9112747ad8d8e7d5fec44876246b24ca5a3
-F src/sqliteInt.h 555cff59458966ac16f044cc985f2267c75a606e
+F src/sqliteInt.h aa26c7f8a0c5c3210a81177c60ca08bf8f3f7825
 F src/table.c c89698bd5bb4b8d14722d6ee7e9be014c383d24a
 F src/tclsqlite.c 4896e078495bf868742f5394dcf01c5efe5bea02
-F src/test1.c e4b31f62ea71963cbae44338acf477a04fc8fc49
+F src/test1.c 41eabe255970ef947263b94145c9b2766bab8675
 F src/test2.c e9f99aa5ee73872819259d6612c11e55e1644321
 F src/test3.c 4a0d7b882fdae731dbb759f512ad867122452f96
 F src/tokenize.c 830e9ef684334070a26583d94770bb869e2727bf
 F src/update.c b1e315e20b98a013d30fd9ff3b7d9dc4f29b39b3
 F src/util.c ac83973ecc647d3d3c58708f148442365abf9b94
-F src/vdbe.c 66e82eb4d042e34752ed23c3c13f921a895376af
-F src/vdbe.h da7c01076268f4fa1a17b7d6f27e21c3fd9a5d3f
-F src/where.c a6cac72314905902542c5239683d07fed27f6ee1
+F src/vdbe.c b4cdc0017bf0574ededf17d7ff5f1d66a58bf430
+F src/vdbe.h cd4c8647051a0c22c0e133c375f1cd17bb8b1e06
+F src/where.c 13a112b720fffd40612051f9e6d37262c4c818c8
 F test/all.test 2a51e5395ac7c2c539689b123b9782a05e3837fe
 F test/bigrow.test 9458134d67f81559845f934fdd6802fe19a68ad1
 F test/btree.test 47952c7a0c22660566264c68c0664592b7da85ce
@@ -61,7 +61,7 @@ F test/delete.test c904a62129fe102b314a96111a8417f10249e4d8
 F test/expr.test b4171c84b767f7b7e94dbce4824ba8e981a1c72f
 F test/func.test 9012f7fc5369422c890e93549aa61d762e0c8bb3
 F test/in.test 9323681388be301dc73f370b4cd62c5a33f79d1e
-F test/index.test 6076f29d09a4f26a2efa38b03b8cc338b8662f0e
+F test/index.test c58829080a24aed3f897d7a77d9592aea30164fa
 F test/insert.test a5c122aa726f1cef6f07d6767e8fd6f220994c11
 F test/insert2.test d6901ca931e308fea7fca8c95ebe7dc957cc9fc2
 F test/ioerr.test 57d9bffaca18b34f9e976f786eadc2591d6efc6a
@@ -74,9 +74,9 @@ F test/pager.test 59bbc4e3d489529ed33db6e15595789e51056077
 F test/printf.test 3cb415073754cb8ff076f26173143c3cd293a9da
 F test/quick.test 6f023c7a73fc413e6d65b7a1879c79764038dc05
 F test/quote.test 286db944717afa9a9bf829dd85e59185c65d5435
-F test/rowid.test 427bfbbe9684fe7a2f851aa05badaae6d4972ce8
+F test/rowid.test ba56df896cb913b70d06661d960869e310fe426e
 F test/select1.test 13aa0a5545209a73d1073cb9062a1b9075b734ae
-F test/select2.test f91c903e2bab0e9d45274855a981eebf846d5e32
+F test/select2.test 070c7ac1a66e294004a2a1bfdc9cd2f4d29c1e6e
 F test/select3.test 5e1fe8e5a4e63fb2827ab3b89527e0fd4ae35259
 F test/select4.test 29a2ffb187f3d8b6ca42a0a6b619e9cabe12e228
 F test/select5.test c2a6c4a003316ee42cbbd689eebef8fdce0db2ac
@@ -91,7 +91,7 @@ F test/trans.test 855337b8a178c73c433fcf8ee88e4b2f5efff0d9
 F test/unique.test ef1f67607a7109e9c0842cd8557550fb121d7ec6
 F test/update.test 8cf76467d46b1650539763c95d5208340c61d561
 F test/vacuum.test 8acf8669f3b627e54149b25165b034aa06c2432e
-F test/where.test 43d5ac94da3f3722375307f948884dc79b326a91
+F test/where.test 20b19475fe894b86b06d2979592260dd16beeb17
 F tool/lemon.c bfd036ab9309c7f34e1357d9a065ad137814e741
 F tool/lempar.c 9b604e6a8b3d55c0b9cbcb130a7302fb8bafe2b9
 F tool/memleak.awk 296dfbce7a9ca499b95ce04e30334e64a50052e0
@@ -115,7 +115,7 @@ F www/speed.tcl 212a91d555384e01873160d6a189f1490c791bc2
 F www/sqlite.tcl 6a21242a272e9c0939a04419a51c3d50cae33e3e
 F www/tclsqlite.tcl 13d50723f583888fc80ae1a38247c0ab415066fa
 F www/vdbe.tcl bb7d620995f0a987293e9d4fb6185a3b077e9b44
-P e6ca23fa4569bc33065bf57ce7ce6132cd6a9de0
-R 2ff273a13d7d9aa297cec267ea881bcc
+P decbeb9151885fee473b3fa58c8cf78a2338d2d8
+R d92c3b8d059b1ac938a54da5297c6da3
 U drh
-Z 17c5246b4b0cf0179995e37754c3ee96
+Z 73b06b3dcf3434a2ed8ea2fd74f12aeb
index 0826b84defbb06df62233c9fe4431b7002872791..775c716d44c06a2f3ef9faab27e2922b20844a16 100644 (file)
@@ -1 +1 @@
-decbeb9151885fee473b3fa58c8cf78a2338d2d8
\ No newline at end of file
+262bcd17df19f45def6144b5a7e0602ca5b03deb
\ No newline at end of file
index 957f9e852d310b50aba5839b0f3170ed4300dd4a..8e2045bf87c161714c67466d22b748813f105bce 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains routines used for analyzing expressions and
 ** for generating VDBE code that evaluates expressions in SQLite.
 **
-** $Id: expr.c,v 1.32 2001/10/22 02:58:10 drh Exp $
+** $Id: expr.c,v 1.33 2001/11/08 00:45:21 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -340,7 +340,6 @@ int sqliteFuncId(Token *pToken){
      { "max",    3, FN_Max    },
      { "sum",    3, FN_Sum    },
      { "avg",    3, FN_Avg    },
-     { "fcnt",   4, FN_Fcnt   },  /* Used for testing only */
      { "length", 6, FN_Length },
      { "substr", 6, FN_Substr },
      { "abs",    3, FN_Abs    },
@@ -419,18 +418,6 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){
           too_many_args = n>3;
           break;
         }
-        /* The "fcnt(*)" function always returns the number of OP_MoveTo
-        ** operations that have occurred so far while processing the
-        ** SQL statement.  This information can be used by test procedures
-        ** to verify that indices are being used properly to minimize
-        ** searching.  All arguments to fcnt() are ignored.  fcnt() has
-        ** no use (other than testing) that we are aware of.
-        */
-        case FN_Fcnt: {
-          n = 0;
-          break;
-        }
-      
         default: break;
       }
       if( no_such_func ){
@@ -635,10 +622,6 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
       int i;
       ExprList *pList = pExpr->pList;
       switch( id ){
-        case FN_Fcnt: {
-          sqliteVdbeAddOp(v, OP_Fcnt, 0, 0);
-          break;
-        }
         case FN_Min: 
         case FN_Max: {
           op = id==FN_Min ? OP_Min : OP_Max;
index 3f71491b4e51e177e46028cc600579f8824e7122..8f5481edc59b7f57813cee4d9600608af6daa50b 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.68 2001/11/07 16:48:27 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.69 2001/11/08 00:45:21 drh Exp $
 */
 #include "sqlite.h"
 #include "hash.h"
@@ -309,6 +309,7 @@ struct WhereLevel {
   int iMem;            /* Memory cell used by this level */
   Index *pIdx;         /* Index used */
   int iCur;            /* Cursor number used for this index */
+  int score;           /* How well this indexed scored */
   int brk;             /* Jump here to break out of the loop */
   int cont;            /* Jump here to continue with the next loop cycle */
   int op, p1, p2;      /* Opcode used to terminate the loop */
index 3a5bf772fb1801d75641ca22f597abf458261f75..009cbf11f55c159738f2712eab8eba4ed18f89e8 100644 (file)
@@ -13,7 +13,7 @@
 ** is not included in the SQLite library.  It is used for automated
 ** testing of the SQLite library.
 **
-** $Id: test1.c,v 1.4 2001/09/24 03:12:40 drh Exp $
+** $Id: test1.c,v 1.5 2001/11/08 00:45:21 drh Exp $
 */
 #include "sqliteInt.h"
 #include "tcl.h"
@@ -303,6 +303,7 @@ static int sqlite_malloc_stat(
 ** Register commands with the TCL interpreter.
 */
 int Sqlitetest1_Init(Tcl_Interp *interp){
+  extern int sqlite_search_count;
   Tcl_CreateCommand(interp, "sqlite_mprintf_int", sqlite_mprintf_int, 0, 0);
   Tcl_CreateCommand(interp, "sqlite_mprintf_str", sqlite_mprintf_str, 0, 0);
   Tcl_CreateCommand(interp, "sqlite_mprintf_double", sqlite_mprintf_double,0,0);
@@ -311,6 +312,8 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
   Tcl_CreateCommand(interp, "sqlite_get_table_printf", test_get_table_printf,
       0, 0);
   Tcl_CreateCommand(interp, "sqlite_close", sqlite_test_close, 0, 0);
+  Tcl_LinkVar(interp, "sqlite_search_count", 
+      (char*)&sqlite_search_count, TCL_LINK_INT);
 #ifdef MEMORY_DEBUG
   Tcl_CreateCommand(interp, "sqlite_malloc_fail", sqlite_malloc_fail, 0, 0);
   Tcl_CreateCommand(interp, "sqlite_malloc_stat", sqlite_malloc_stat, 0, 0);
index 3475130e1bea20d13cd7b64f305b8a5f9c0d30c6..3da0a0757b00ab57e71a01d746a42aa82c6386a4 100644 (file)
 ** But other routines are also provided to help in building up
 ** a program instruction by instruction.
 **
-** $Id: vdbe.c,v 1.95 2001/11/07 16:48:27 drh Exp $
+** $Id: vdbe.c,v 1.96 2001/11/08 00:45:22 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
 
+/*
+** The following global variable is incremented every time a cursor
+** moves, either by the OP_MoveTo or the OP_Next opcode.  The test
+** procedures use this information to make sure that indices are
+** working correctly.
+*/
+int sqlite_search_count = 0;
+
 /*
 ** SQL is translated into a sequence of instructions to be
 ** executed by a virtual machine.  Each instruction is an instance
@@ -206,7 +214,6 @@ struct Vdbe {
   Agg agg;            /* Aggregate information */
   int nSet;           /* Number of sets allocated */
   Set *aSet;          /* An array of sets */
-  int nFetch;         /* Number of OP_Fetch instructions executed */
   int nCallback;      /* Number of callbacks invoked so far */
   int iLimit;         /* Limit on the number of callbacks remaining */
   int iOffset;        /* Offset before beginning to do callbacks */
@@ -837,31 +844,31 @@ static char *zOpName[] = { 0,
   "Transaction",       "Commit",            "Rollback",          "ReadCookie",
   "SetCookie",         "VerifyCookie",      "Open",              "OpenTemp",
   "OpenWrite",         "OpenAux",           "OpenWrAux",         "Close",
-  "MoveTo",            "Fcnt",              "NewRecno",          "Put",
-  "Distinct",          "Found",             "NotFound",          "Delete",
-  "Column",            "KeyAsData",         "Recno",             "FullKey",
-  "Rewind",            "Next",              "Destroy",           "Clear",
-  "CreateIndex",       "CreateTable",       "Reorganize",        "IdxPut",
-  "IdxDelete",         "IdxRecno",          "IdxGT",             "IdxGE",
-  "MemLoad",           "MemStore",          "ListWrite",         "ListRewind",
-  "ListRead",          "ListReset",         "SortPut",           "SortMakeRec",
-  "SortMakeKey",       "Sort",              "SortNext",          "SortCallback",
-  "SortReset",         "FileOpen",          "FileRead",          "FileColumn",
-  "AggReset",          "AggFocus",          "AggIncr",           "AggNext",
-  "AggSet",            "AggGet",            "SetInsert",         "SetFound",
-  "SetNotFound",       "MakeRecord",        "MakeKey",           "MakeIdxKey",
-  "IncrKey",           "Goto",              "If",                "Halt",
-  "ColumnCount",       "ColumnName",        "Callback",          "NullCallback",
-  "Integer",           "String",            "Pop",               "Dup",
-  "Pull",              "Add",               "AddImm",            "Subtract",
-  "Multiply",          "Divide",            "Remainder",         "BitAnd",
-  "BitOr",             "BitNot",            "ShiftLeft",         "ShiftRight",
-  "AbsValue",          "Precision",         "Min",               "Max",
-  "Like",              "Glob",              "Eq",                "Ne",
-  "Lt",                "Le",                "Gt",                "Ge",
-  "IsNull",            "NotNull",           "Negative",          "And",
-  "Or",                "Not",               "Concat",            "Noop",
-  "Strlen",            "Substr",            "Limit",           
+  "MoveTo",            "NewRecno",          "Put",               "Distinct",
+  "Found",             "NotFound",          "Delete",            "Column",
+  "KeyAsData",         "Recno",             "FullKey",           "Rewind",
+  "Next",              "Destroy",           "Clear",             "CreateIndex",
+  "CreateTable",       "Reorganize",        "IdxPut",            "IdxDelete",
+  "IdxRecno",          "IdxGT",             "IdxGE",             "MemLoad",
+  "MemStore",          "ListWrite",         "ListRewind",        "ListRead",
+  "ListReset",         "SortPut",           "SortMakeRec",       "SortMakeKey",
+  "Sort",              "SortNext",          "SortCallback",      "SortReset",
+  "FileOpen",          "FileRead",          "FileColumn",        "AggReset",
+  "AggFocus",          "AggIncr",           "AggNext",           "AggSet",
+  "AggGet",            "SetInsert",         "SetFound",          "SetNotFound",
+  "MakeRecord",        "MakeKey",           "MakeIdxKey",        "IncrKey",
+  "Goto",              "If",                "Halt",              "ColumnCount",
+  "ColumnName",        "Callback",          "NullCallback",      "Integer",
+  "String",            "Pop",               "Dup",               "Pull",
+  "Add",               "AddImm",            "Subtract",          "Multiply",
+  "Divide",            "Remainder",         "BitAnd",            "BitOr",
+  "BitNot",            "ShiftLeft",         "ShiftRight",        "AbsValue",
+  "Precision",         "Min",               "Max",               "Like",
+  "Glob",              "Eq",                "Ne",                "Lt",
+  "Le",                "Gt",                "Ge",                "IsNull",
+  "NotNull",           "Negative",          "And",               "Or",
+  "Not",               "Concat",            "Noop",              "Strlen",
+  "Substr",            "Limit",           
 };
 
 /*
@@ -2524,7 +2531,7 @@ case OP_MoveTo: {
       sqliteBtreeMoveto(pC->pCursor, zStack[tos], aStack[tos].n, &res);
       pC->recnoIsValid = 0;
     }
-    p->nFetch++;
+    sqlite_search_count++;
     if( res<0 ){
       sqliteBtreeNext(pC->pCursor, &res);
       pC->recnoIsValid = 0;
@@ -2537,23 +2544,6 @@ case OP_MoveTo: {
   break;
 }
 
-/* Opcode: Fcnt * * *
-**
-** Push an integer onto the stack which is the total number of
-** MoveTo opcodes that have been executed by this virtual machine.
-**
-** This instruction is used to implement the special fcnt() function
-** in the SQL dialect that SQLite understands.  fcnt() is used for
-** testing purposes.
-*/
-case OP_Fcnt: {
-  int i = ++p->tos;
-  VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; )
-  aStack[i].i = p->nFetch;
-  aStack[i].flags = STK_Int;
-  break;
-}
-
 /* Opcode: Distinct P1 P2 *
 **
 ** Use the top of the stack as a key.  If a record with that key does
@@ -2940,7 +2930,7 @@ case OP_Next: {
     rc = sqliteBtreeNext(pCrsr, &res);
     if( res==0 ){
       pc = pOp->p2 - 1;
-      p->nFetch++;
+      sqlite_search_count++;
     }
     p->aCsr[i].recnoIsValid = 0;
   }
index 9f69c32279e64318fe87865c8168a87d0691cdaf..2c21dbbe1443fa21dbfceb47c93a058e09d2dd95 100644 (file)
@@ -15,7 +15,7 @@
 ** or VDBE.  The VDBE implements an abstract machine that runs a
 ** simple program to access and modify the underlying database.
 **
-** $Id: vdbe.h,v 1.35 2001/11/07 16:48:28 drh Exp $
+** $Id: vdbe.h,v 1.36 2001/11/08 00:45:22 drh Exp $
 */
 #ifndef _SQLITE_VDBE_H_
 #define _SQLITE_VDBE_H_
@@ -83,121 +83,120 @@ typedef struct VdbeOp VdbeOp;
 #define OP_OpenWrAux          11
 #define OP_Close              12
 #define OP_MoveTo             13
-#define OP_Fcnt               14
-#define OP_NewRecno           15
-#define OP_Put                16
-#define OP_Distinct           17
-#define OP_Found              18
-#define OP_NotFound           19
-#define OP_Delete             20
-#define OP_Column             21
-#define OP_KeyAsData          22
-#define OP_Recno              23
-#define OP_FullKey            24
-#define OP_Rewind             25
-#define OP_Next               26
-
-#define OP_Destroy            27
-#define OP_Clear              28
-#define OP_CreateIndex        29
-#define OP_CreateTable        30
-#define OP_Reorganize         31
-
-#define OP_IdxPut             32
-#define OP_IdxDelete          33
-#define OP_IdxRecno           34
-#define OP_IdxGT              35
-#define OP_IdxGE              36
-
-#define OP_MemLoad            37
-#define OP_MemStore           38
-
-#define OP_ListWrite          39
-#define OP_ListRewind         40
-#define OP_ListRead           41
-#define OP_ListReset          42
-
-#define OP_SortPut            43
-#define OP_SortMakeRec        44
-#define OP_SortMakeKey        45
-#define OP_Sort               46
-#define OP_SortNext           47
-#define OP_SortCallback       48
-#define OP_SortReset          49
-
-#define OP_FileOpen           50
-#define OP_FileRead           51
-#define OP_FileColumn         52
-
-#define OP_AggReset           53
-#define OP_AggFocus           54
-#define OP_AggIncr            55
-#define OP_AggNext            56
-#define OP_AggSet             57
-#define OP_AggGet             58
-
-#define OP_SetInsert          59
-#define OP_SetFound           60
-#define OP_SetNotFound        61
-
-#define OP_MakeRecord         62
-#define OP_MakeKey            63
-#define OP_MakeIdxKey         64
-#define OP_IncrKey            65
-
-#define OP_Goto               66
-#define OP_If                 67
-#define OP_Halt               68
-
-#define OP_ColumnCount        69
-#define OP_ColumnName         70
-#define OP_Callback           71
-#define OP_NullCallback       72
-
-#define OP_Integer            73
-#define OP_String             74
-#define OP_Pop                75
-#define OP_Dup                76
-#define OP_Pull               77
-
-#define OP_Add                78
-#define OP_AddImm             79
-#define OP_Subtract           80
-#define OP_Multiply           81
-#define OP_Divide             82
-#define OP_Remainder          83
-#define OP_BitAnd             84
-#define OP_BitOr              85
-#define OP_BitNot             86
-#define OP_ShiftLeft          87
-#define OP_ShiftRight         88
-#define OP_AbsValue           89
-#define OP_Precision          90
-#define OP_Min                91
-#define OP_Max                92
-#define OP_Like               93
-#define OP_Glob               94
-#define OP_Eq                 95
-#define OP_Ne                 96
-#define OP_Lt                 97
-#define OP_Le                 98
-#define OP_Gt                 99
-#define OP_Ge                100
-#define OP_IsNull            101
-#define OP_NotNull           102
-#define OP_Negative          103
-#define OP_And               104
-#define OP_Or                105
-#define OP_Not               106
-#define OP_Concat            107
-#define OP_Noop              108
-
-#define OP_Strlen            109
-#define OP_Substr            110
-
-#define OP_Limit             111
-
-#define OP_MAX               111
+#define OP_NewRecno           14
+#define OP_Put                15
+#define OP_Distinct           16
+#define OP_Found              17
+#define OP_NotFound           18
+#define OP_Delete             19
+#define OP_Column             20
+#define OP_KeyAsData          21
+#define OP_Recno              22
+#define OP_FullKey            23
+#define OP_Rewind             24
+#define OP_Next               25
+
+#define OP_Destroy            26
+#define OP_Clear              27
+#define OP_CreateIndex        28
+#define OP_CreateTable        29
+#define OP_Reorganize         30
+
+#define OP_IdxPut             31
+#define OP_IdxDelete          32
+#define OP_IdxRecno           33
+#define OP_IdxGT              34
+#define OP_IdxGE              35
+
+#define OP_MemLoad            36
+#define OP_MemStore           37
+
+#define OP_ListWrite          38
+#define OP_ListRewind         39
+#define OP_ListRead           40
+#define OP_ListReset          41
+
+#define OP_SortPut            42
+#define OP_SortMakeRec        43
+#define OP_SortMakeKey        44
+#define OP_Sort               45
+#define OP_SortNext           46
+#define OP_SortCallback       47
+#define OP_SortReset          48
+
+#define OP_FileOpen           49
+#define OP_FileRead           50
+#define OP_FileColumn         51
+
+#define OP_AggReset           52
+#define OP_AggFocus           53
+#define OP_AggIncr            54
+#define OP_AggNext            55
+#define OP_AggSet             56
+#define OP_AggGet             57
+
+#define OP_SetInsert          58
+#define OP_SetFound           59
+#define OP_SetNotFound        60
+
+#define OP_MakeRecord         61
+#define OP_MakeKey            62
+#define OP_MakeIdxKey         63
+#define OP_IncrKey            64
+
+#define OP_Goto               65
+#define OP_If                 66
+#define OP_Halt               67
+
+#define OP_ColumnCount        68
+#define OP_ColumnName         69
+#define OP_Callback           70
+#define OP_NullCallback       71
+
+#define OP_Integer            72
+#define OP_String             73
+#define OP_Pop                74
+#define OP_Dup                75
+#define OP_Pull               76
+
+#define OP_Add                77
+#define OP_AddImm             78
+#define OP_Subtract           79
+#define OP_Multiply           80
+#define OP_Divide             81
+#define OP_Remainder          82
+#define OP_BitAnd             83
+#define OP_BitOr              84
+#define OP_BitNot             85
+#define OP_ShiftLeft          86
+#define OP_ShiftRight         87
+#define OP_AbsValue           88
+#define OP_Precision          89
+#define OP_Min                90
+#define OP_Max                91
+#define OP_Like               92
+#define OP_Glob               93
+#define OP_Eq                 94
+#define OP_Ne                 95
+#define OP_Lt                 96
+#define OP_Le                 97
+#define OP_Gt                 98
+#define OP_Ge                 99
+#define OP_IsNull            100
+#define OP_NotNull           101
+#define OP_Negative          102
+#define OP_And               103
+#define OP_Or                104
+#define OP_Not               105
+#define OP_Concat            106
+#define OP_Noop              107
+
+#define OP_Strlen            108
+#define OP_Substr            109
+
+#define OP_Limit             110
+
+#define OP_MAX               110
 
 /*
 ** Prototypes for the VDBE interface.  See comments on the implementation
index 8e5b41a600a68c76e3e90dbd7ab6d9e8fc0628e7..9efbef043c5ec15c216d59d2db0ff47b2fe3cb51 100644 (file)
@@ -13,7 +13,7 @@
 ** the WHERE clause of SQL statements.  Also found here are subroutines
 ** to generate VDBE code to evaluate expressions.
 **
-** $Id: where.c,v 1.25 2001/11/07 16:48:28 drh Exp $
+** $Id: where.c,v 1.26 2001/11/08 00:45:22 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -94,6 +94,24 @@ static int exprTableUsage(int base, Expr *p){
   return mask;
 }
 
+/*
+** Return TRUE if the given operator is one of the operators that is
+** allowed for an indexable WHERE clause.  The allowed operators are
+** "=", "<", ">", "<=", and ">=".
+*/
+static int allowedOp(int op){
+  switch( op ){
+    case TK_LT:
+    case TK_LE:
+    case TK_GT:
+    case TK_GE:
+    case TK_EQ:
+      return 1;
+    default:
+      return 0;
+  }
+}
+
 /*
 ** The input to this routine is an ExprInfo structure with only the
 ** "p" field filled in.  The job of this routine is to analyze the
@@ -111,7 +129,7 @@ static void exprAnalyze(int base, ExprInfo *pInfo){
   pInfo->indexable = 0;
   pInfo->idxLeft = -1;
   pInfo->idxRight = -1;
-  if( pExpr->op==TK_EQ && (pInfo->prereqRight & pInfo->prereqLeft)==0 ){
+  if( allowedOp(pExpr->op) && (pInfo->prereqRight & pInfo->prereqLeft)==0 ){
     if( pExpr->pRight->op==TK_COLUMN ){
       pInfo->idxRight = pExpr->pRight->iTable - base;
       pInfo->indexable = 1;
@@ -213,6 +231,7 @@ WhereInfo *sqliteWhereBegin(
     Table *pTab = pTabList->a[idx].pTab;
     Index *pIdx;
     Index *pBestIdx = 0;
+    int bestScore = 0;
 
     /* Check to see if there is an expression that uses only the
     ** ROWID field of this table.  If so, set aDirect[i] to 1.
@@ -238,17 +257,30 @@ WhereInfo *sqliteWhereBegin(
     }
 
     /* Do a search for usable indices.  Leave pBestIdx pointing to
-    ** the most specific usable index.
+    ** the "best" index.  pBestIdx is left set to NULL if no indices
+    ** are usable.
+    **
+    ** The best index is determined as follows.  For each of the
+    ** left-most terms that is fixed by an equality operator, add
+    ** 4 to the score.  The right-most term of the index may be
+    ** constrained by an inequality.  Add 1 if for an "x<..." constraint
+    ** and add 2 for an "x>..." constraint.  Chose the index that
+    ** gives the best score.
     **
-    ** "Most specific" means that pBestIdx is the usable index that
-    ** has the largest value for nColumn.  A usable index is one for
-    ** which there are subexpressions to compute every column of the
-    ** index.
+    ** This scoring system is designed so that the score can later be
+    ** used to determine how the index is used.  If the score&3 is 0
+    ** then all constraints are equalities.  If score&1 is not 0 then
+    ** there is an inequality used as a termination key.  (ex: "x<...")
+    ** If score&2 is not 0 then there is an inequality used as the
+    ** start key.  (ex: "x>...");
     */
     for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
-      int columnMask = 0;
+      int eqMask = 0;  /* Index columns covered by an x=... constraint */
+      int ltMask = 0;  /* Index columns covered by an x<... constraint */
+      int gtMask = 0;  /* Index columns covered by an x>... constraing */
+      int nEq, m, score;
 
-      if( pIdx->nColumn>32 ) continue;
+      if( pIdx->nColumn>32 ) continue;  /* Ignore indices too many columns */
       for(j=0; j<nExpr; j++){
         if( aExpr[j].idxLeft==idx 
              && (aExpr[j].prereqRight & loopMask)==aExpr[j].prereqRight ){
@@ -256,7 +288,27 @@ WhereInfo *sqliteWhereBegin(
           int k;
           for(k=0; k<pIdx->nColumn; k++){
             if( pIdx->aiColumn[k]==iColumn ){
-              columnMask |= 1<<k;
+              switch( aExpr[j].p->op ){
+                case TK_EQ: {
+                  eqMask |= 1<<k;
+                  break;
+                }
+                case TK_LE:
+                case TK_LT: {
+                  ltMask |= 1<<k;
+                  break;
+                }
+                case TK_GE:
+                case TK_GT: {
+                  gtMask |= 1<<k;
+                  break;
+                }
+                default: {
+                  /* CANT_HAPPEN */
+                  assert( 0 );
+                  break;
+                }
+              }
               break;
             }
           }
@@ -267,19 +319,47 @@ WhereInfo *sqliteWhereBegin(
           int k;
           for(k=0; k<pIdx->nColumn; k++){
             if( pIdx->aiColumn[k]==iColumn ){
-              columnMask |= 1<<k;
+              switch( aExpr[j].p->op ){
+                case TK_EQ: {
+                  eqMask |= 1<<k;
+                  break;
+                }
+                case TK_LE:
+                case TK_LT: {
+                  gtMask |= 1<<k;
+                  break;
+                }
+                case TK_GE:
+                case TK_GT: {
+                  ltMask |= 1<<k;
+                  break;
+                }
+                default: {
+                  /* CANT_HAPPEN */
+                  assert( 0 );
+                  break;
+                }
+              }
               break;
             }
           }
         }
       }
-      if( columnMask + 1 == (1<<pIdx->nColumn) ){
-        if( pBestIdx==0 || pBestIdx->nColumn<pIdx->nColumn ){
-          pBestIdx = pIdx;
-        }
+      for(nEq=0; nEq<pIdx->nColumn; nEq++){
+        m = (1<<(nEq+1))-1;
+        if( (m & eqMask)!=m ) break;
+      }
+      score = nEq*4;
+      m = 1<<nEq;
+      if( m & ltMask ) score++;
+      if( m & gtMask ) score+=2;
+      if( score>bestScore ){
+        pBestIdx = pIdx;
+        bestScore = score;
       }
     }
     pWInfo->a[i].pIdx = pBestIdx;
+    pWInfo->a[i].score = bestScore;
     loopMask |= 1<<idx;
     if( pBestIdx ){
       pWInfo->a[i].iCur = nCur++;
@@ -361,7 +441,7 @@ WhereInfo *sqliteWhereBegin(
       pLevel->op = OP_Noop;
     }else if( pIdx==0 ){
       /* Case 2:  There was no usable index.  We must do a complete
-      ** scan of the table.
+      **          scan of the entire database table.
       */
       int start;
 
@@ -373,14 +453,17 @@ WhereInfo *sqliteWhereBegin(
       pLevel->p1 = base+idx;
       pLevel->p2 = start;
       haveKey = 0;
-    }else{
-      /* Case 3:  We do have a usable index in pIdx.
+    }else if( pLevel->score%4==0 ){
+      /* Case 3:  All index constraints are equality operators.
       */
       int start;
-      for(j=0; j<pIdx->nColumn; j++){
+      int testOp;
+      int nColumn = pLevel->score/4;
+      for(j=0; j<nColumn; j++){
         for(k=0; k<nExpr; k++){
           if( aExpr[k].p==0 ) continue;
           if( aExpr[k].idxLeft==idx 
+             && aExpr[k].p->op==TK_EQ
              && (aExpr[k].prereqRight & loopMask)==aExpr[k].prereqRight 
              && aExpr[k].p->pLeft->iColumn==pIdx->aiColumn[j]
           ){
@@ -389,6 +472,7 @@ WhereInfo *sqliteWhereBegin(
             break;
           }
           if( aExpr[k].idxRight==idx 
+             && aExpr[k].p->op==TK_EQ
              && (aExpr[k].prereqLeft & loopMask)==aExpr[k].prereqLeft
              && aExpr[k].p->pRight->iColumn==pIdx->aiColumn[j]
           ){
@@ -401,11 +485,19 @@ WhereInfo *sqliteWhereBegin(
       pLevel->iMem = pParse->nMem++;
       brk = pLevel->brk = sqliteVdbeMakeLabel(v);
       cont = pLevel->cont = sqliteVdbeMakeLabel(v);
-      sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nColumn, 0);
-      sqliteVdbeAddOp(v, OP_MemStore, pLevel->iMem, 0);
+      sqliteVdbeAddOp(v, OP_MakeKey, nColumn, 0);
+      if( nColumn==pIdx->nColumn ){
+        sqliteVdbeAddOp(v, OP_MemStore, pLevel->iMem, 0);
+        testOp = OP_IdxGT;
+      }else{
+        sqliteVdbeAddOp(v, OP_Dup, 0, 0);
+        sqliteVdbeAddOp(v, OP_IncrKey, 0, 0);
+        sqliteVdbeAddOp(v, OP_MemStore, pLevel->iMem, 1);
+        testOp = OP_IdxGE;
+      }
       sqliteVdbeAddOp(v, OP_MoveTo, pLevel->iCur, brk);
       start = sqliteVdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
-      sqliteVdbeAddOp(v, OP_IdxGT, pLevel->iCur, brk);
+      sqliteVdbeAddOp(v, testOp, pLevel->iCur, brk);
       sqliteVdbeAddOp(v, OP_IdxRecno, pLevel->iCur, 0);
       if( i==pTabList->nId-1 && pushKey ){
         haveKey = 1;
@@ -416,6 +508,159 @@ WhereInfo *sqliteWhereBegin(
       pLevel->op = OP_Next;
       pLevel->p1 = pLevel->iCur;
       pLevel->p2 = start;
+    }else{
+      /* Case 4: The contraints on the right-most index field are
+      **         inequalities.
+      */
+      int score = pLevel->score;
+      int nEqColumn = score/4;
+      int start;
+      int leFlag, geFlag;
+      int testOp;
+
+      /* Evaluate the equality constraints
+      */
+      for(j=0; j<nEqColumn; j++){
+        for(k=0; k<nExpr; k++){
+          if( aExpr[k].p==0 ) continue;
+          if( aExpr[k].idxLeft==idx 
+             && aExpr[k].p->op==TK_EQ
+             && (aExpr[k].prereqRight & loopMask)==aExpr[k].prereqRight 
+             && aExpr[k].p->pLeft->iColumn==pIdx->aiColumn[j]
+          ){
+            sqliteExprCode(pParse, aExpr[k].p->pRight);
+            aExpr[k].p = 0;
+            break;
+          }
+          if( aExpr[k].idxRight==idx 
+             && aExpr[k].p->op==TK_EQ
+             && (aExpr[k].prereqLeft & loopMask)==aExpr[k].prereqLeft
+             && aExpr[k].p->pRight->iColumn==pIdx->aiColumn[j]
+          ){
+            sqliteExprCode(pParse, aExpr[k].p->pLeft);
+            aExpr[k].p = 0;
+            break;
+          }
+        }
+      }
+
+      /* Duplicate the equality contraint values because they will all be
+      ** used twice: once to make the termination key and once to make the
+      ** start key.
+      */
+      for(j=0; j<nEqColumn; j++){
+        sqliteVdbeAddOp(v, OP_Dup, nEqColumn-1, 0);
+      }
+
+      /* Generate the termination key.  This is the key value that
+      ** will end the search.  There is no termination key if there
+      ** are no equality contraints and no "X<..." constraint.
+      */
+      if( (score & 1)!=0 ){
+        for(k=0; k<nExpr; k++){
+          Expr *pExpr = aExpr[k].p;
+          if( pExpr==0 ) continue;
+          if( aExpr[k].idxLeft==idx 
+             && (pExpr->op==TK_LT || pExpr->op==TK_LE)
+             && (aExpr[k].prereqRight & loopMask)==aExpr[k].prereqRight 
+             && pExpr->pLeft->iColumn==pIdx->aiColumn[j]
+          ){
+            sqliteExprCode(pParse, pExpr->pRight);
+            leFlag = pExpr->op==TK_LE;
+            aExpr[k].p = 0;
+            break;
+          }
+          if( aExpr[k].idxRight==idx 
+             && (pExpr->op==TK_GT || pExpr->op==TK_GE)
+             && (aExpr[k].prereqLeft & loopMask)==aExpr[k].prereqLeft
+             && pExpr->pRight->iColumn==pIdx->aiColumn[j]
+          ){
+            sqliteExprCode(pParse, pExpr->pLeft);
+            leFlag = pExpr->op==TK_GE;
+            aExpr[k].p = 0;
+            break;
+          }
+        }
+        testOp = OP_IdxGE;
+      }else{
+        testOp = nEqColumn>0 ? OP_IdxGE : OP_Noop;
+        leFlag = 1;
+      }
+      if( testOp!=OP_Noop ){
+        pLevel->iMem = pParse->nMem++;
+        sqliteVdbeAddOp(v, OP_MakeKey, nEqColumn + (score & 1), 0);
+        if( leFlag ){
+          sqliteVdbeAddOp(v, OP_IncrKey, 0, 0);
+        }
+        sqliteVdbeAddOp(v, OP_MemStore, pLevel->iMem, 1);
+      }
+
+      /* Generate the start key.  This is the key that defines the lower
+      ** bound on the search.  There is no start key if there are not
+      ** equality constraints and if there is no "X>..." constraint.  In
+      ** that case, generate a "Rewind" instruction in place of the
+      ** start key search.
+      */
+      if( (score & 2)!=0 ){
+        for(k=0; k<nExpr; k++){
+          Expr *pExpr = aExpr[k].p;
+          if( pExpr==0 ) continue;
+          if( aExpr[k].idxLeft==idx 
+             && (pExpr->op==TK_GT || pExpr->op==TK_GE)
+             && (aExpr[k].prereqRight & loopMask)==aExpr[k].prereqRight 
+             && pExpr->pLeft->iColumn==pIdx->aiColumn[j]
+          ){
+            sqliteExprCode(pParse, pExpr->pRight);
+            geFlag = pExpr->op==TK_GE;
+            aExpr[k].p = 0;
+            break;
+          }
+          if( aExpr[k].idxRight==idx 
+             && (pExpr->op==TK_LT || pExpr->op==TK_LE)
+             && (aExpr[k].prereqLeft & loopMask)==aExpr[k].prereqLeft
+             && pExpr->pRight->iColumn==pIdx->aiColumn[j]
+          ){
+            sqliteExprCode(pParse, pExpr->pLeft);
+            geFlag = pExpr->op==TK_LE;
+            aExpr[k].p = 0;
+            break;
+          }
+        }
+      }
+      brk = pLevel->brk = sqliteVdbeMakeLabel(v);
+      cont = pLevel->cont = sqliteVdbeMakeLabel(v);
+      if( nEqColumn>0 || (score&2)!=0 ){
+        sqliteVdbeAddOp(v, OP_MakeKey, nEqColumn + ((score&2)!=0), 0);
+        if( !geFlag ){
+          sqliteVdbeAddOp(v, OP_IncrKey, 0, 0);
+        }
+        sqliteVdbeAddOp(v, OP_MoveTo, pLevel->iCur, brk);
+      }else{
+        sqliteVdbeAddOp(v, OP_Rewind, pLevel->iCur, brk);
+      }
+
+      /* Generate the the top of the loop.  If there is a termination
+      ** key we have to test for that key and abort at the top of the
+      ** loop.
+      */
+      start = sqliteVdbeCurrentAddr(v);
+      if( testOp!=OP_Noop ){
+        sqliteVdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
+        sqliteVdbeAddOp(v, testOp, pLevel->iCur, brk);
+      }
+      sqliteVdbeAddOp(v, OP_IdxRecno, pLevel->iCur, 0);
+      if( i==pTabList->nId-1 && pushKey ){
+        haveKey = 1;
+      }else{
+        sqliteVdbeAddOp(v, OP_MoveTo, base+idx, 0);
+        haveKey = 0;
+      }
+
+      /* Record the instruction used to terminate the loop.
+      */
+      pLevel->op = OP_Next;
+      pLevel->p1 = pLevel->iCur;
+      pLevel->p2 = start;
     }
     loopMask |= 1<<idx;
 
index f61d42de255a85e279959d3e4ae53faa72660354..616ebf84b4e3cf2742ccab2461224da46660f392 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this file is testing the CREATE INDEX statement.
 #
-# $Id: index.test,v 1.14 2001/09/27 15:11:55 drh Exp $
+# $Id: index.test,v 1.15 2001/11/08 00:45:22 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -317,7 +317,8 @@ do_test index-11.1 {
   for {set i 1} {$i<=50} {incr i} {
     execsql "INSERT INTO t3 VALUES('x${i}x',$i,0.$i)"
   }
-  execsql {SELECT c, fcnt() FROM t3 WHERE b==10}
-} {0.10 1}
+  set sqlite_search_count 0
+  concat [execsql {SELECT c FROM t3 WHERE b==10}] $sqlite_search_count
+} {0.10 3}
 
 finish_test
index c8ac7838e57d13f28cd57f19467bcbf8664f5578..7e589c36272fe28831a3ea033ae3de87a721a2ba 100644 (file)
@@ -12,7 +12,7 @@
 # focus of this file is testing the magic ROWID column that is
 # found on all tables.
 #
-# $Id: rowid.test,v 1.5 2001/09/18 02:02:23 drh Exp $
+# $Id: rowid.test,v 1.6 2001/11/08 00:45:22 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -238,17 +238,19 @@ do_test rowid-4.4.2 {
 } {256}
 do_test rowid-4.5 {
   execsql {CREATE INDEX idxt2 ON t2(y)}
-  execsql {
-    SELECT t1.x, fcnt() FROM t2, t1 
+  set sqlite_search_count 0
+  concat [execsql {
+    SELECT t1.x FROM t2, t1 
     WHERE t2.y==256 AND t1.rowid==t2.rowid
-  }
-} {4 1}
+  }] $sqlite_search_count
+} {4 3}
 do_test rowid-4.5.1 {
-  execsql {
-    SELECT t1.x, fcnt() FROM t2, t1 
+  set sqlite_search_count 0
+  concat [execsql {
+    SELECT t1.x FROM t2, t1 
     WHERE t1.OID==t2.rowid AND t2.y==81
-  }
-} {3 1}
+  }] $sqlite_search_count
+} {3 3}
 do_test rowid-4.6 {
   execsql {
     SELECT t1.x FROM t1, t2
index ccce1a4c216d1c367f40d2f42f1116f6e09f4b0d..b370cdbb49e9cc5e4c8a6a25469a5601f84baeb9 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this file is testing the SELECT statement.
 #
-# $Id: select2.test,v 1.14 2001/09/16 00:13:28 drh Exp $
+# $Id: select2.test,v 1.15 2001/11/08 00:45:22 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -93,30 +93,23 @@ do_test select2-3.2c {
   execsql {SELECT f1 FROM tbl2 WHERE f2=1000}
 } {500}
 do_test select2-3.2d {
-  execsql {SELECT fcnt() FROM tbl2 WHERE 1000=f2}
-} {1}
+  set sqlite_search_count 0
+  execsql {SELECT * FROM tbl2 WHERE 1000=f2}
+  set sqlite_search_count
+} {3}
 do_test select2-3.2e {
-  execsql {SELECT fcnt() FROM tbl2 WHERE f2=1000}
-} {1}
-
-# omit the time-dependent tests
-#
-do_probtest select2-3.2f {
-  set t1 [lindex [time {execsql {SELECT f1 FROM tbl2 WHERE 1000=f2}} 1] 0]
-  set t2 [lindex [time {execsql {SELECT f1 FROM tbl2 WHERE f2=1000}} 1] 0]
-  expr {$t1*0.7<$t2 && $t2*0.7<$t1}
-} {1}
+  set sqlite_search_count 0
+  execsql {SELECT * FROM tbl2 WHERE f2=1000}
+  set sqlite_search_count
+} {3}
 
 # Make sure queries run faster with an index than without
 #
-do_probtest select2-3.3 {
-  set t1 [lindex [time {execsql {SELECT f1 from tbl2 WHERE f2==2000}} 1] 0]
+do_test select2-3.3 {
   execsql {DROP INDEX idx1}
-  set t2 [lindex [time {execsql {SELECT f1 FROM tbl2 WHERE f2==2000}} 1] 0]
-  expr {$t1*10 < $t2}
-} {1}
-do_probtest select2-3.4 {
-  expr {[execsql {SELECT fcnt() FROM tbl2 WHERE f2==2000}]>10}
-} {1}
+  set sqlite_search_count 0
+  execsql {SELECT f1 FROM tbl2 WHERE f2==2000}
+  set sqlite_search_count
+} {29999}
 
 finish_test
index 5e9776d41dc537dcd0c9bed9263672580e4e326b..85096f7c15008d7c56be883e602eb3026c123cfd 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this file is testing the use of indices in WHERE clases.
 #
-# $Id: where.test,v 1.3 2001/09/16 00:13:28 drh Exp $
+# $Id: where.test,v 1.4 2001/11/08 00:45:22 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -39,110 +39,202 @@ do_test where-1.0 {
   }
 } {}
 
-# Verify that queries use an index.  We are using the special "fcnt(*)"
-# function to verify the results.  fcnt(*) returns the number of Fetch
-# operations that have occurred up to the point where fcnt(*) is invoked.
-# By verifing that fcnt(*) returns a small number we know that an index
-# was used instead of an exhaustive search.
+# 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]
+}
+
+# Verify that queries use an index.  We are using the special variable
+# "sqlite_search_count" which tallys the number of executions of MoveTo
+# and Next operators in the VDBE.  By verifing that the search count is
+# small we can be assured that indices are being used properly.
 #
 do_test where-1.1 {
-  execsql {SELECT x, y, fcnt(*) FROM t1 WHERE w=10}
-} {3 121 1}
+  count {SELECT x, y FROM t1 WHERE w=10}
+} {3 121 3}
 do_test where-1.2 {
-  execsql {SELECT x, y, fcnt(*) FROM t1 WHERE w=11}
-} {3 144 1}
+  count {SELECT x, y FROM t1 WHERE w=11}
+} {3 144 3}
 do_test where-1.3 {
-  execsql {SELECT x, y, fcnt(*) FROM t1 WHERE 11=w}
-} {3 144 1}
+  count {SELECT x, y FROM t1 WHERE 11=w}
+} {3 144 3}
 do_test where-1.4 {
-  execsql {SELECT x, y, fcnt(*) FROM t1 WHERE 11=w AND x>2}
-} {3 144 1}
+  count {SELECT x, y FROM t1 WHERE 11=w AND x>2}
+} {3 144 3}
 do_test where-1.5 {
-  execsql {SELECT x, y, fcnt(*) FROM t1 WHERE y<200 AND w=11 AND x>2}
-} {3 144 1}
+  count {SELECT x, y FROM t1 WHERE y<200 AND w=11 AND x>2}
+} {3 144 3}
 do_test where-1.6 {
-  execsql {SELECT x, y, fcnt(*) FROM t1 WHERE y<200 AND x>2 AND w=11}
-} {3 144 1}
+  count {SELECT x, y FROM t1 WHERE y<200 AND x>2 AND w=11}
+} {3 144 3}
 do_test where-1.7 {
-  execsql {SELECT x, y, fcnt(*) FROM t1 WHERE w=11 AND y<200 AND x>2}
-} {3 144 1}
+  count {SELECT x, y FROM t1 WHERE w=11 AND y<200 AND x>2}
+} {3 144 3}
 do_test where-1.8 {
-  execsql {SELECT x, y, fcnt(*) FROM t1 WHERE w>10 AND y=144 AND x=3}
-} {3 144 1}
+  count {SELECT x, y FROM t1 WHERE w>10 AND y=144 AND x=3}
+} {3 144 3}
 do_test where-1.9 {
-  execsql {SELECT x, y, fcnt(*) FROM t1 WHERE y=144 AND w>10 AND x=3}
-} {3 144 1}
+  count {SELECT x, y FROM t1 WHERE y=144 AND w>10 AND x=3}
+} {3 144 3}
 do_test where-1.10 {
-  execsql {SELECT x, y, fcnt(*) FROM t1 WHERE x=3 AND w>=10 AND y=121}
-} {3 121 1}
+  count {SELECT x, y FROM t1 WHERE x=3 AND w>=10 AND y=121}
+} {3 121 3}
 do_test where-1.11 {
-  execsql {SELECT x, y, fcnt(*) FROM t1 WHERE x=3 AND y=100 AND w<10}
-} {3 100 1}
+  count {SELECT x, y FROM t1 WHERE x=3 AND y=100 AND w<10}
+} {3 100 3}
+
+# New for SQLite version 2.1: Verify that that inequality constraints
+# are used correctly.
+#
+do_test where-1.12 {
+  count {SELECT w FROM t1 WHERE x=3 AND y<100}
+} {8 3}
+do_test where-1.13 {
+  count {SELECT w FROM t1 WHERE x=3 AND 100>y}
+} {8 3}
+do_test where-1.14 {
+  count {SELECT w FROM t1 WHERE 3=x AND y<100}
+} {8 3}
+do_test where-1.15 {
+  count {SELECT w FROM t1 WHERE 3=x AND 100>y}
+} {8 3}
+do_test where-1.16 {
+  count {SELECT w FROM t1 WHERE x=3 AND y<=100}
+} {8 9 5}
+do_test where-1.17 {
+  count {SELECT w FROM t1 WHERE x=3 AND 100>=y}
+} {8 9 5}
+do_test where-1.18 {
+  count {SELECT w FROM t1 WHERE x=3 AND y>225}
+} {15 3}
+do_test where-1.19 {
+  count {SELECT w FROM t1 WHERE x=3 AND 225<y}
+} {15 3}
+do_test where-1.20 {
+  count {SELECT w FROM t1 WHERE x=3 AND y>=225}
+} {14 15 5}
+do_test where-1.21 {
+  count {SELECT w FROM t1 WHERE x=3 AND 225<=y}
+} {14 15 5}
+do_test where-1.22 {
+  count {SELECT w FROM t1 WHERE x=3 AND y>121 AND y<196}
+} {11 12 5}
+do_test where-1.23 {
+  count {SELECT w FROM t1 WHERE x=3 AND y>=121 AND y<=196}
+} {10 11 12 13 9}
+do_test where-1.24 {
+  count {SELECT w FROM t1 WHERE x=3 AND 121<y AND 196>y}
+} {11 12 5}
+do_test where-1.25 {
+  count {SELECT w FROM t1 WHERE x=3 AND 121<=y AND 196>=y}
+} {10 11 12 13 9}
+
+# Need to work on optimizing the BETWEEN operator.  
+#
+# do_test where-1.26 {
+#   count {SELECT w FROM t1 WHERE x=3 AND y BETWEEN 121 AND 196}
+# } {10 11 12 13 9}
+
+do_test where-1.27 {
+  count {SELECT w FROM t1 WHERE x=3 AND y+1==122}
+} {10 17}
+do_test where-1.28 {
+  count {SELECT w FROM t1 WHERE x+1=4 AND y+1==122}
+} {10 99}
+do_test where-1.29 {
+  count {SELECT w FROM t1 WHERE y==121}
+} {10 99}
+
+
+do_test where-1.30 {
+  count {SELECT w FROM t1 WHERE w>97}
+} {98 99 100 6}
+do_test where-1.31 {
+  count {SELECT w FROM t1 WHERE w>=97}
+} {97 98 99 100 8}
+do_test where-1.33 {
+  count {SELECT w FROM t1 WHERE w==97}
+} {97 3}
+do_test where-1.34 {
+  count {SELECT w FROM t1 WHERE w+1==98}
+} {97 99}
+do_test where-1.35 {
+  count {SELECT w FROM t1 WHERE w<3}
+} {1 2 4}
+do_test where-1.36 {
+  count {SELECT w FROM t1 WHERE w<=3}
+} {1 2 3 6}
+do_test where-1.37 {
+  count {SELECT w FROM t1 WHERE w+1<=4}
+} {1 2 3 99}
+
 
 # Do the same kind of thing except use a join as the data source.
 #
 do_test where-2.1 {
-  execsql {
-    SELECT w, p, fcnt(*) FROM t2, t1
+  count {
+    SELECT w, p FROM t2, t1
     WHERE x=q AND y=s AND r=8977
   }
-} {34 67 2}
+} {34 67 6}
 do_test where-2.2 {
-  execsql {
-    SELECT w, p, fcnt(*) FROM t2, t1
+  count {
+    SELECT w, p FROM t2, t1
     WHERE x=q AND s=y AND r=8977
   }
-} {34 67 2}
+} {34 67 6}
 do_test where-2.3 {
-  execsql {
-    SELECT w, p, fcnt(*) FROM t2, t1
+  count {
+    SELECT w, p FROM t2, t1
     WHERE x=q AND s=y AND r=8977 AND w>10
   }
-} {34 67 2}
+} {34 67 6}
 do_test where-2.4 {
-  execsql {
-    SELECT w, p, fcnt(*) FROM t2, t1
+  count {
+    SELECT w, p FROM t2, t1
     WHERE p<80 AND x=q AND s=y AND r=8977 AND w>10
   }
-} {34 67 2}
+} {34 67 6}
 do_test where-2.5 {
-  execsql {
-    SELECT w, p, fcnt(*) FROM t2, t1
+  count {
+    SELECT w, p FROM t2, t1
     WHERE p<80 AND x=q AND 8977=r AND s=y AND w>10
   }
-} {34 67 2}
+} {34 67 6}
 do_test where-2.6 {
-  execsql {
-    SELECT w, p, fcnt(*) FROM t2, t1
+  count {
+    SELECT w, p FROM t2, t1
     WHERE x=q AND p=77 AND s=y AND w>5
   }
-} {24 77 2}
+} {24 77 6}
 do_test where-2.7 {
-  execsql {
-    SELECT w, p, fcnt(*) FROM t1, t2
+  count {
+    SELECT w, p FROM t1, t2
     WHERE x=q AND p>77 AND s=y AND w=5
   }
-} {5 96 2}
+} {5 96 6}
 
 # Lets do a 3-way join.
 #
 do_test where-3.1 {
-  execsql {
-    SELECT A.w, B.p, C.w, fcnt(*) FROM t1 as A, t2 as B, t1 as C
+  count {
+    SELECT A.w, B.p, C.w FROM t1 as A, t2 as B, t1 as C
     WHERE C.w=101-B.p AND B.r=10202-A.y AND A.w=11
   }
-} {11 90 11 3}
+} {11 90 11 9}
 do_test where-3.2 {
-  execsql {
-    SELECT A.w, B.p, C.w, fcnt(*) FROM t1 as A, t2 as B, t1 as C
+  count {
+    SELECT A.w, B.p, C.w FROM t1 as A, t2 as B, t1 as C
     WHERE C.w=101-B.p AND B.r=10202-A.y AND A.w=12
   }
-} {12 89 12 3}
+} {12 89 12 9}
 do_test where-3.3 {
-  execsql {
-    SELECT A.w, B.p, C.w, fcnt(*) FROM t1 as A, t2 as B, t1 as C
+  count {
+    SELECT A.w, B.p, C.w FROM t1 as A, t2 as B, t1 as C
     WHERE A.w=15 AND B.p=C.w AND B.r=10202-A.y
   }
-} {15 86 86 3}
+} {15 86 86 9}
 
 finish_test