diff --git a/manifest b/manifest index e73b7b1dfc..f718bf6c45 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index 0826b84def..775c716d44 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -decbeb9151885fee473b3fa58c8cf78a2338d2d8 \ No newline at end of file +262bcd17df19f45def6144b5a7e0602ca5b03deb \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 957f9e852d..8e2045bf87 100644 --- a/src/expr.c +++ b/src/expr.c @@ -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; diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 3f71491b4e..8f5481edc5 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -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 */ diff --git a/src/test1.c b/src/test1.c index 3a5bf772fb..009cbf11f5 100644 --- a/src/test1.c +++ b/src/test1.c @@ -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); diff --git a/src/vdbe.c b/src/vdbe.c index 3475130e1b..3da0a0757b 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -30,11 +30,19 @@ ** 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 +/* +** 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; } diff --git a/src/vdbe.h b/src/vdbe.h index 9f69c32279..2c21dbbe14 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -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_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 27 -#define OP_Clear 28 -#define OP_CreateIndex 29 -#define OP_CreateTable 30 -#define OP_Reorganize 31 +#define OP_Destroy 26 +#define OP_Clear 27 +#define OP_CreateIndex 28 +#define OP_CreateTable 29 +#define OP_Reorganize 30 -#define OP_IdxPut 32 -#define OP_IdxDelete 33 -#define OP_IdxRecno 34 -#define OP_IdxGT 35 -#define OP_IdxGE 36 +#define OP_IdxPut 31 +#define OP_IdxDelete 32 +#define OP_IdxRecno 33 +#define OP_IdxGT 34 +#define OP_IdxGE 35 -#define OP_MemLoad 37 -#define OP_MemStore 38 +#define OP_MemLoad 36 +#define OP_MemStore 37 -#define OP_ListWrite 39 -#define OP_ListRewind 40 -#define OP_ListRead 41 -#define OP_ListReset 42 +#define OP_ListWrite 38 +#define OP_ListRewind 39 +#define OP_ListRead 40 +#define OP_ListReset 41 -#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_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 50 -#define OP_FileRead 51 -#define OP_FileColumn 52 +#define OP_FileOpen 49 +#define OP_FileRead 50 +#define OP_FileColumn 51 -#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_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 59 -#define OP_SetFound 60 -#define OP_SetNotFound 61 +#define OP_SetInsert 58 +#define OP_SetFound 59 +#define OP_SetNotFound 60 -#define OP_MakeRecord 62 -#define OP_MakeKey 63 -#define OP_MakeIdxKey 64 -#define OP_IncrKey 65 +#define OP_MakeRecord 61 +#define OP_MakeKey 62 +#define OP_MakeIdxKey 63 +#define OP_IncrKey 64 -#define OP_Goto 66 -#define OP_If 67 -#define OP_Halt 68 +#define OP_Goto 65 +#define OP_If 66 +#define OP_Halt 67 -#define OP_ColumnCount 69 -#define OP_ColumnName 70 -#define OP_Callback 71 -#define OP_NullCallback 72 +#define OP_ColumnCount 68 +#define OP_ColumnName 69 +#define OP_Callback 70 +#define OP_NullCallback 71 -#define OP_Integer 73 -#define OP_String 74 -#define OP_Pop 75 -#define OP_Dup 76 -#define OP_Pull 77 +#define OP_Integer 72 +#define OP_String 73 +#define OP_Pop 74 +#define OP_Dup 75 +#define OP_Pull 76 -#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_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 109 -#define OP_Substr 110 +#define OP_Strlen 108 +#define OP_Substr 109 -#define OP_Limit 111 +#define OP_Limit 110 -#define OP_MAX 111 +#define OP_MAX 110 /* ** Prototypes for the VDBE interface. See comments on the implementation diff --git a/src/where.c b/src/where.c index 8e5b41a600..9efbef043c 100644 --- a/src/where.c +++ b/src/where.c @@ -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. ** - ** "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. + ** 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. + ** + ** 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; jnColumn; k++){ if( pIdx->aiColumn[k]==iColumn ){ - columnMask |= 1<op ){ + case TK_EQ: { + eqMask |= 1<nColumn; k++){ if( pIdx->aiColumn[k]==iColumn ){ - columnMask |= 1<op ){ + case TK_EQ: { + eqMask |= 1<nColumn) ){ - if( pBestIdx==0 || pBestIdx->nColumnnColumn ){ - pBestIdx = pIdx; - } + for(nEq=0; nEqnColumn; nEq++){ + m = (1<<(nEq+1))-1; + if( (m & eqMask)!=m ) break; + } + score = nEq*4; + m = 1<bestScore ){ + pBestIdx = pIdx; + bestScore = score; } } pWInfo->a[i].pIdx = pBestIdx; + pWInfo->a[i].score = bestScore; loopMask |= 1<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; jnColumn; j++){ + int testOp; + int nColumn = pLevel->score/4; + for(j=0; jop==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; jop==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; jop==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; kop==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<10} -} {1} + set sqlite_search_count 0 + execsql {SELECT f1 FROM tbl2 WHERE f2==2000} + set sqlite_search_count +} {29999} finish_test diff --git a/test/where.test b/test/where.test index 5e9776d41d..85096f7c15 100644 --- a/test/where.test +++ b/test/where.test @@ -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=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 121y} +} {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