]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add optimizations for the IN operator in WHERE clauses. This is a partial
authordrh <drh@noemail.net>
Sat, 8 Jun 2002 23:25:08 +0000 (23:25 +0000)
committerdrh <drh@noemail.net>
Sat, 8 Jun 2002 23:25:08 +0000 (23:25 +0000)
implementation of enhancement #63.  Still need to add test cases. (CVS 610)

FossilOrigin-Name: 8481e841ebdeabe07bf780246bda1aa053eb60b7

manifest
manifest.uuid
src/hash.h
src/sqliteInt.h
src/vdbe.c
src/vdbe.h
src/where.c

index 6cd4990b822980f001458e4a4d3b97bf82fce4c6..fc60bfd35789b6729c717ec33b63c60fb7e8dbbf 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Bug\sfix:\sdo\snot\ssegfault\sif\sa\sSELECT\swithout\sa\sFROM\sclause\sincludes\nthe\s*\swildcard\sin\sthe\sresult\scolumn\slist.\s(CVS\s609)
-D 2002-06-06T23:42:28
+C Add\soptimizations\sfor\sthe\sIN\soperator\sin\sWHERE\sclauses.\s\sThis\sis\sa\spartial\nimplementation\sof\senhancement\s#63.\s\sStill\sneed\sto\sadd\stest\scases.\s(CVS\s610)
+D 2002-06-08T23:25:09
 F Makefile.in 6291a33b87d2a395aafd7646ee1ed562c6f2c28c
 F Makefile.template 4e11752e0b5c7a043ca50af4296ec562857ba495
 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
@@ -26,7 +26,7 @@ F src/encode.c 346b12b46148506c32038524b95c4631ab46d760
 F src/expr.c 8ce9c22655735ff62b1e33ab11ad9d44c4ab99c6
 F src/func.c 061a520a122da7e4f9dcac15697bb996aac7d5df
 F src/hash.c 6a6236b89c8c060c65dabd300a1c8ce7c10edb72
-F src/hash.h dca065dda89d4575f3176e75e9a3dc0f4b4fb8b9
+F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8
 F src/insert.c 4b0bd94296fea46ef1b2ed8bfd05e12a38ce2c90
 F src/main.c 6e53c49a390fabd5fecce9e3b128c61c85208000
 F src/md5.c 0ae1f3e2cac92d06fc6246d1b4b8f61a2fe66d3b
@@ -41,7 +41,7 @@ F src/select.c 1d5cb1ae0bb3376bedfde7ae22e6e927e4d0b5e2
 F src/shell.c 1d22fe870ee852cfb975fd000dbe3973713d0a15
 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
 F src/sqlite.h.in 0038faa6d642de06b91143ee65a131bd831d020b
-F src/sqliteInt.h 3fd61a32c101b10aea610de8e7d931744657712f
+F src/sqliteInt.h 09f3e26d0368284965efef7d1a9999b0eba801d3
 F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63
 F src/tclsqlite.c 9300c9606a38bc0c75d6c0bc8a6197ab979353d1
 F src/test1.c 09d95048b66ce6dcd2bae90f443589043d7d631e
@@ -52,9 +52,9 @@ F src/tokenize.c 35c63867d03fcaf81fe520f8d8206981d0c7270e
 F src/trigger.c d02f8e3510c7c2ad948a0e8c3bb0cca8adaf80c5
 F src/update.c f68375173bf5338cae3e97012708e10f206aedd9
 F src/util.c 7cf46b5612f5d12601c697374b9c6b38b2332ce8
-F src/vdbe.c 27b71e3c6cc77c071421b24462872f32047e2c20
-F src/vdbe.h b8706429131c14b307a07aab7e47f95a9da53610
-F src/where.c b054f2f23127bd57eb5f973bcd38764b875d73fe
+F src/vdbe.c b315d7ad5086164bb8d8aee8bc9edeafcb68b8ea
+F src/vdbe.h 1742d6f8b40f40879475b4c41cf4f9980ceb0e21
+F src/where.c d5308069f8794ec7e9f5084ffd611fe0922ae9f0
 F test/all.test e4d3821eeba751829b419cd47814bd20af4286d1
 F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578
 F test/btree.test bf326f546a666617367a7033fa2c07451bd4f8e1
@@ -136,7 +136,7 @@ F www/speed.tcl da8afcc1d3ccc5696cfb388a68982bc3d9f7f00f
 F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P a0abef62bfe1b0f8c6249ba520dd2735190783a5
-R 5d70f1605b7b9ff2e858e45ab5b5947d
+P d939294994e5f6c7862b66573301e111e56a2681
+R 3ccd9ba147258740a1a4d6ac8604b91f
 U drh
-Z 6eb753fe6a2122abae187660dd07b07e
+Z 0bb147d1fd50388db4609fcd1f1c4973
index 40dddeeb0a7b2c01db935b350392f1ea179281bc..f32749e1d564dac991ecf2bc2bb804655e8cd336 100644 (file)
@@ -1 +1 @@
-d939294994e5f6c7862b66573301e111e56a2681
\ No newline at end of file
+8481e841ebdeabe07bf780246bda1aa053eb60b7
\ No newline at end of file
index e51396e356fd33c2be7a20ae1eb7e4d71a2452ce..bccfbebf6dc23458c9076152c02dad4b759c09dc 100644 (file)
@@ -12,7 +12,7 @@
 ** This is the header file for the generic hash-table implemenation
 ** used in SQLite.
 **
-** $Id: hash.h,v 1.4 2002/02/23 23:45:45 drh Exp $
+** $Id: hash.h,v 1.5 2002/06/08 23:25:09 drh Exp $
 */
 #ifndef _SQLITE_HASH_H_
 #define _SQLITE_HASH_H_
@@ -99,6 +99,7 @@ void sqliteHashClear(Hash*);
 #define sqliteHashNext(E)   ((E)->next)
 #define sqliteHashData(E)   ((E)->data)
 #define sqliteHashKey(E)    ((E)->pKey)
+#define sqliteHashKeysize(E) ((E)->nKey)
 
 /*
 ** Number of entries in a hash table
index 4bb4cc638af24f715c1dd8492581c4a49103229c..c94c755f4ac2baf8df79de8828c0df5b27141132 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.120 2002/06/06 18:54:41 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.121 2002/06/08 23:25:09 drh Exp $
 */
 #include "sqlite.h"
 #include "hash.h"
@@ -411,7 +411,8 @@ struct Token {
 struct Expr {
   int op;                /* Operation performed by this node */
   Expr *pLeft, *pRight;  /* Left and right subnodes */
-  ExprList *pList;       /* A list of expressions used as a function argument */
+  ExprList *pList;       /* A list of expressions used as function arguments
+                         ** or in "<expr> IN (<expr-list)" */
   Token token;           /* An operand token */
   Token span;            /* Complete text of the expression */
   int iTable, iColumn;   /* When op==TK_COLUMN, then this expr node means the
@@ -419,7 +420,8 @@ struct Expr {
                          ** op==TK_FUNCTION, iColumn holds the function id */
   int iAgg;              /* When op==TK_COLUMN and pParse->useAgg==TRUE, pull
                          ** result from the iAgg-th element of the aggregator */
-  Select *pSelect;       /* When the expression is a sub-select */
+  Select *pSelect;       /* When the expression is a sub-select.  Also the
+                         ** right side of "<expr> IN (<select>)" */
 };
 
 /*
@@ -508,6 +510,7 @@ struct WhereLevel {
   int op, p1, p2;      /* Opcode used to terminate the loop */
   int iLeftJoin;       /* Memory cell used to implement LEFT OUTER JOIN */
   int top;             /* First instruction of interior of the loop */
+  int inOp, inP1, inP2;/* Opcode used to implement an IN operator */
 };
 
 /*
index 032b39a7696b1e56dda27879c397b276600b2228..16b247caaa2c7ceb454ce7ea1ee599f8fdccd65d 100644 (file)
@@ -30,7 +30,7 @@
 ** But other routines are also provided to help in building up
 ** a program instruction by instruction.
 **
-** $Id: vdbe.c,v 1.153 2002/06/06 23:16:06 drh Exp $
+** $Id: vdbe.c,v 1.154 2002/06/08 23:25:09 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -198,6 +198,7 @@ struct AggElem {
 typedef struct Set Set;
 struct Set {
   Hash hash;             /* A set is just a hash table */
+  HashElem *prev;        /* Previously accessed hash elemen */
 };
 
 /*
@@ -1066,19 +1067,19 @@ static char *zOpName[] = { 0,
   "FileOpen",          "FileRead",          "FileColumn",        "AggReset",
   "AggFocus",          "AggNext",           "AggSet",            "AggGet",
   "AggFunc",           "AggInit",           "AggPush",           "AggPop",
-  "SetInsert",         "SetFound",          "SetNotFound",       "MakeRecord",
-  "MakeKey",           "MakeIdxKey",        "IncrKey",           "Goto",
-  "If",                "IfNot",             "Halt",              "ColumnCount",
-  "ColumnName",        "Callback",          "NullCallback",      "Integer",
-  "String",            "Pop",               "Dup",               "Pull",
-  "Push",              "MustBeInt",         "Add",               "AddImm",
-  "Subtract",          "Multiply",          "Divide",            "Remainder",
-  "BitAnd",            "BitOr",             "BitNot",            "ShiftLeft",
-  "ShiftRight",        "AbsValue",          "Eq",                "Ne",
-  "Lt",                "Le",                "Gt",                "Ge",
-  "IsNull",            "NotNull",           "Negative",          "And",
-  "Or",                "Not",               "Concat",            "Noop",
-  "Function",          "Limit",           
+  "SetInsert",         "SetFound",          "SetNotFound",       "SetFirst",
+  "SetNext",           "MakeRecord",        "MakeKey",           "MakeIdxKey",
+  "IncrKey",           "Goto",              "If",                "IfNot",
+  "Halt",              "ColumnCount",       "ColumnName",        "Callback",
+  "NullCallback",      "Integer",           "String",            "Pop",
+  "Dup",               "Pull",              "Push",              "MustBeInt",
+  "Add",               "AddImm",            "Subtract",          "Multiply",
+  "Divide",            "Remainder",         "BitAnd",            "BitOr",
+  "BitNot",            "ShiftLeft",         "ShiftRight",        "AbsValue",
+  "Eq",                "Ne",                "Lt",                "Le",
+  "Gt",                "Ge",                "IsNull",            "NotNull",
+  "Negative",          "And",               "Or",                "Not",
+  "Concat",            "Noop",              "Function",          "Limit",
 };
 
 /*
@@ -1957,7 +1958,7 @@ case OP_AddImm: {
 /* Opcode: MustBeInt  * P2 *
 ** 
 ** Force the top of the stack to be an integer.  If the top of the
-** stack is not an integer and cannot be comverted into an integer
+** stack is not an integer and cannot be converted into an integer
 ** with out data loss, then jump immediately to P2, or if P2==0
 ** raise an SQLITE_MISMATCH exception.
 */
@@ -3018,7 +3019,7 @@ case OP_MoveTo: {
 ** This operation is similar to NotFound except that this operation
 ** does not pop the key from the stack.
 **
-** See also: Found, NotFound, MoveTo
+** See also: Found, NotFound, MoveTo, IsUnique, NotExists
 */
 /* Opcode: Found P1 P2 *
 **
@@ -3027,7 +3028,7 @@ case OP_MoveTo: {
 ** does not exist, then fall thru.  The cursor is left pointing
 ** to the record if it exists.  The key is popped from the stack.
 **
-** See also: Distinct, NotFound, MoveTo
+** See also: Distinct, NotFound, MoveTo, IsUnique, NotExists
 */
 /* Opcode: NotFound P1 P2 *
 **
@@ -3084,7 +3085,7 @@ case OP_Found: {
 ** number for that entry is pushed onto the stack and control
 ** falls through to the next instruction.
 **
-** See also: Distinct, NotFound, NotExists
+** See also: Distinct, NotFound, NotExists, Found
 */
 case OP_IsUnique: {
   int i = pOp->p1;
@@ -3167,7 +3168,7 @@ case OP_IsUnique: {
 ** operation assumes the key is an integer and NotFound assumes it
 ** is a string.
 **
-** See also: Distinct, Found, MoveTo, NotExists
+** See also: Distinct, Found, MoveTo, NotFound, IsUnique
 */
 case OP_NotExists: {
   int i = pOp->p1;
@@ -4775,6 +4776,46 @@ case OP_SetNotFound: {
   break;
 }
 
+/* Opcode: SetFirst P1 P2 *
+**
+** Read the first element from set P1 and push it onto the stack.  If the
+** set is empty, push nothing and jump immediately to P2.  This opcode is
+** used in combination with OP_SetNext to loop over all elements of a set.
+*/
+/* Opcode: SetNext P1 P2 *
+**
+** Read the next element from set P1 and push it onto the stack.  If there
+** are no more elements in the set, do not do the push and fall through.
+** Otherwise, jump to P2 after pushing the next set element.
+*/
+case OP_SetFirst: 
+case OP_SetNext: {
+  Set *pSet;
+  int tos;
+  VERIFY( if( pOp->p1<0 || pOp->p1>=p->nSet ) goto bad_instruction; )
+  pSet = &p->aSet[pOp->p1];
+  if( pOp->opcode==OP_SetFirst ){
+    pSet->prev = sqliteHashFirst(&pSet->hash);
+    if( pSet->prev==0 ){
+      pc = pOp->p2 - 1;
+      break;
+    }
+  }else{
+    VERIFY( if( pSet->prev==0 ) goto bad_instruction; )
+    pSet->prev = sqliteHashNext(pSet->prev);
+    if( pSet->prev==0 ){
+      break;
+    }else{
+      pc = pOp->p2 - 1;
+    }
+  }
+  tos = ++p->tos;
+  VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; )
+  zStack[tos] = sqliteHashKey(pSet->prev);
+  aStack[tos].n = sqliteHashKeysize(pSet->prev);
+  aStack[tos].flags = STK_Str | STK_Static;
+  break;
+}
 
 /* An other opcode is illegal...
 */
index 42e9a07b0d96c65589deb04fc2d9812048f9c610..e8ffe6d3604bab188958bff822007be5ac56d10a 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.53 2002/05/26 20:54:34 drh Exp $
+** $Id: vdbe.h,v 1.54 2002/06/08 23:25:09 drh Exp $
 */
 #ifndef _SQLITE_VDBE_H_
 #define _SQLITE_VDBE_H_
@@ -149,62 +149,64 @@ typedef struct VdbeOp VdbeOp;
 #define OP_SetInsert          69
 #define OP_SetFound           70
 #define OP_SetNotFound        71
-
-#define OP_MakeRecord         72
-#define OP_MakeKey            73
-#define OP_MakeIdxKey         74
-#define OP_IncrKey            75
-
-#define OP_Goto               76
-#define OP_If                 77
-#define OP_IfNot              78
-#define OP_Halt               79
-
-#define OP_ColumnCount        80
-#define OP_ColumnName         81
-#define OP_Callback           82
-#define OP_NullCallback       83
-
-#define OP_Integer            84
-#define OP_String             85
-#define OP_Pop                86
-#define OP_Dup                87
-#define OP_Pull               88
-#define OP_Push               89
-#define OP_MustBeInt          90
-
-#define OP_Add                91
-#define OP_AddImm             92
-#define OP_Subtract           93
-#define OP_Multiply           94
-#define OP_Divide             95
-#define OP_Remainder          96
-#define OP_BitAnd             97
-#define OP_BitOr              98
-#define OP_BitNot             99
-#define OP_ShiftLeft         100
-#define OP_ShiftRight        101
-#define OP_AbsValue          102
-#define OP_Eq                103
-#define OP_Ne                104
-#define OP_Lt                105
-#define OP_Le                106
-#define OP_Gt                107
-#define OP_Ge                108
-#define OP_IsNull            109
-#define OP_NotNull           110
-#define OP_Negative          111
-#define OP_And               112
-#define OP_Or                113
-#define OP_Not               114
-#define OP_Concat            115
-#define OP_Noop              116
-#define OP_Function          117
-
-#define OP_Limit             118
-
-
-#define OP_MAX               118
+#define OP_SetFirst           72
+#define OP_SetNext            73
+
+#define OP_MakeRecord         74
+#define OP_MakeKey            75
+#define OP_MakeIdxKey         76
+#define OP_IncrKey            77
+
+#define OP_Goto               78
+#define OP_If                 79
+#define OP_IfNot              80
+#define OP_Halt               81
+
+#define OP_ColumnCount        82
+#define OP_ColumnName         83
+#define OP_Callback           84
+#define OP_NullCallback       85
+
+#define OP_Integer            86
+#define OP_String             87
+#define OP_Pop                88
+#define OP_Dup                89
+#define OP_Pull               90
+#define OP_Push               91
+#define OP_MustBeInt          92
+
+#define OP_Add                93
+#define OP_AddImm             94
+#define OP_Subtract           95
+#define OP_Multiply           96
+#define OP_Divide             97
+#define OP_Remainder          98
+#define OP_BitAnd             99
+#define OP_BitOr             100
+#define OP_BitNot            101
+#define OP_ShiftLeft         102
+#define OP_ShiftRight        103
+#define OP_AbsValue          104
+#define OP_Eq                105
+#define OP_Ne                106
+#define OP_Lt                107
+#define OP_Le                108
+#define OP_Gt                109
+#define OP_Ge                110
+#define OP_IsNull            111
+#define OP_NotNull           112
+#define OP_Negative          113
+#define OP_And               114
+#define OP_Or                115
+#define OP_Not               116
+#define OP_Concat            117
+#define OP_Noop              118
+#define OP_Function          119
+
+#define OP_Limit             120
+
+
+#define OP_MAX               120
 
 /*
 ** Prototypes for the VDBE interface.  See comments on the implementation
index 0f14df9deefb9d5cf373d490555db443f432b567..b89ff24116f77c26e360a67af05d42f38ad734b6 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.48 2002/05/26 20:54:34 drh Exp $
+** $Id: where.c,v 1.49 2002/06/08 23:25:10 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -112,6 +112,7 @@ static int allowedOp(int op){
     case TK_GT:
     case TK_GE:
     case TK_EQ:
+    case TK_IN:
       return 1;
     default:
       return 0;
@@ -136,7 +137,7 @@ static void exprAnalyze(int base, ExprInfo *pInfo){
   pInfo->idxLeft = -1;
   pInfo->idxRight = -1;
   if( allowedOp(pExpr->op) && (pInfo->prereqRight & pInfo->prereqLeft)==0 ){
-    if( pExpr->pRight->op==TK_COLUMN ){
+    if( pExpr->pRight && pExpr->pRight->op==TK_COLUMN ){
       pInfo->idxRight = pExpr->pRight->iTable - base;
       pInfo->indexable = 1;
     }
@@ -288,6 +289,7 @@ WhereInfo *sqliteWhereBegin(
       if( aExpr[j].idxLeft==idx && aExpr[j].p->pLeft->iColumn<0
             && (aExpr[j].prereqRight & loopMask)==aExpr[j].prereqRight ){
         switch( aExpr[j].p->op ){
+          case TK_IN:
           case TK_EQ: iDirectEq[i] = j; break;
           case TK_LE:
           case TK_LT: iDirectLt[i] = j; break;
@@ -329,6 +331,9 @@ WhereInfo *sqliteWhereBegin(
     ** 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>...");
+    **
+    ** The IN operator as in "<expr> IN (...)" is treated the same as
+    ** an equality comparison.
     */
     for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
       int eqMask = 0;  /* Index columns covered by an x=... constraint */
@@ -346,6 +351,7 @@ WhereInfo *sqliteWhereBegin(
           for(k=0; k<pIdx->nColumn; k++){
             if( pIdx->aiColumn[k]==iColumn ){
               switch( aExpr[j].p->op ){
+                case TK_IN:
                 case TK_EQ: {
                   eqMask |= 1<<k;
                   break;
@@ -467,6 +473,7 @@ WhereInfo *sqliteWhereBegin(
     }
 
     pIdx = pLevel->pIdx;
+    pLevel->inOp = OP_Noop;
     if( i<ARRAYSIZE(iDirectEq) && iDirectEq[i]>=0 ){
       /* Case 1:  We can directly reference a single row using an
       **          equality comparison against the ROWID field.
@@ -475,30 +482,31 @@ WhereInfo *sqliteWhereBegin(
       assert( k<nExpr );
       assert( aExpr[k].p!=0 );
       assert( aExpr[k].idxLeft==idx || aExpr[k].idxRight==idx );
+      brk = pLevel->brk = sqliteVdbeMakeLabel(v);
       if( aExpr[k].idxLeft==idx ){
-        sqliteExprCode(pParse, aExpr[k].p->pRight);
+        Expr *pX = aExpr[k].p;
+        if( pX->op!=TK_IN ){
+          sqliteExprCode(pParse, aExpr[k].p->pRight);
+        }else if( pX->pList ){
+          sqliteVdbeAddOp(v, OP_SetFirst, pX->iTable, brk);
+          pLevel->inOp = OP_SetNext;
+          pLevel->inP1 = pX->iTable;
+          pLevel->inP2 = sqliteVdbeCurrentAddr(v);
+        }else{
+          assert( pX->pSelect );
+          sqliteVdbeAddOp(v, OP_Rewind, pX->iTable, brk);
+          sqliteVdbeAddOp(v, OP_KeyAsData, pX->iTable, 1);
+          pLevel->inP2 = sqliteVdbeAddOp(v, OP_FullKey, pX->iTable, 0);
+          pLevel->inOp = OP_Next;
+          pLevel->inP1 = pX->iTable;
+        }
       }else{
         sqliteExprCode(pParse, aExpr[k].p->pLeft);
       }
       aExpr[k].p = 0;
-      brk = pLevel->brk = sqliteVdbeMakeLabel(v);
-      cont = pLevel->cont = brk;
+      cont = pLevel->cont = sqliteVdbeMakeLabel(v);
       sqliteVdbeAddOp(v, OP_MustBeInt, 0, brk);
-      if( i==pTabList->nSrc-1 && pushKey ){
-        /* Note: The OP_Dup below will cause the recno to be left on the
-        ** stack if the record does not exists and the OP_NotExists jump is
-        ** taken.  This violates a general rule of the VDBE that you should
-        ** never leave values on the stack in order to avoid a stack overflow.
-        ** But in this case, the OP_Dup will never happen inside of a loop,
-        ** because the pushKey flag is only true for UPDATE and DELETE, not
-        ** for SELECT, and nested loops only occur on a SELECT.
-        ** So it is safe to leave the recno on the stack.
-        */
-        haveKey = 1;
-        sqliteVdbeAddOp(v, OP_Dup, 0, 0);
-      }else{
-        haveKey = 0;
-      }
+      haveKey = 0;
       sqliteVdbeAddOp(v, OP_NotExists, base+idx, brk);
       pLevel->op = OP_Noop;
     }else if( pIdx!=0 && pLevel->score%4==0 ){
@@ -507,17 +515,37 @@ WhereInfo *sqliteWhereBegin(
       int start;
       int testOp;
       int nColumn = pLevel->score/4;
+      brk = pLevel->brk = sqliteVdbeMakeLabel(v);
       for(j=0; j<nColumn; j++){
         for(k=0; k<nExpr; k++){
-          if( aExpr[k].p==0 ) continue;
+          Expr *pX = aExpr[k].p;
+          if( pX==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]
+             && pX->pLeft->iColumn==pIdx->aiColumn[j]
           ){
-            sqliteExprCode(pParse, aExpr[k].p->pRight);
-            aExpr[k].p = 0;
-            break;
+            if( pX->op==TK_EQ ){
+              sqliteExprCode(pParse, pX->pRight);
+              aExpr[k].p = 0;
+              break;
+            }
+            if( pX->op==TK_IN && nColumn==1 ){
+              if( pX->pList ){
+                sqliteVdbeAddOp(v, OP_SetFirst, pX->iTable, brk);
+                pLevel->inOp = OP_SetNext;
+                pLevel->inP1 = pX->iTable;
+                pLevel->inP2 = sqliteVdbeCurrentAddr(v);
+              }else{
+                assert( pX->pSelect );
+                sqliteVdbeAddOp(v, OP_Rewind, pX->iTable, brk);
+                sqliteVdbeAddOp(v, OP_KeyAsData, pX->iTable, 1);
+                pLevel->inP2 = sqliteVdbeAddOp(v, OP_FullKey, pX->iTable, 0);
+                pLevel->inOp = OP_Next;
+                pLevel->inP1 = pX->iTable;
+              }
+              aExpr[k].p = 0;
+              break;
+            }
           }
           if( aExpr[k].idxRight==idx 
              && aExpr[k].p->op==TK_EQ
@@ -531,7 +559,6 @@ WhereInfo *sqliteWhereBegin(
         }
       }
       pLevel->iMem = pParse->nMem++;
-      brk = pLevel->brk = sqliteVdbeMakeLabel(v);
       cont = pLevel->cont = sqliteVdbeMakeLabel(v);
       sqliteVdbeAddOp(v, OP_MakeKey, nColumn, 0);
       if( nColumn==pIdx->nColumn ){
@@ -834,6 +861,9 @@ void sqliteWhereEnd(WhereInfo *pWInfo){
       sqliteVdbeAddOp(v, pLevel->op, pLevel->p1, pLevel->p2);
     }
     sqliteVdbeResolveLabel(v, pLevel->brk);
+    if( pLevel->inOp!=OP_Noop ){
+      sqliteVdbeAddOp(v, pLevel->inOp, pLevel->inP1, pLevel->inP2);
+    }
     if( pLevel->iLeftJoin ){
       int addr;
       addr = sqliteVdbeAddOp(v, OP_MemLoad, pLevel->iLeftJoin, 0);