]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Allow OP_MoveGt and similar to use an array of registers instead of a serialized...
authordanielk1977 <danielk1977@noemail.net>
Fri, 18 Apr 2008 09:01:15 +0000 (09:01 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Fri, 18 Apr 2008 09:01:15 +0000 (09:01 +0000)
FossilOrigin-Name: c448f15aa5ed3dec511426775e893efea324faa1

manifest
manifest.uuid
src/vdbe.c
src/vdbeInt.h
src/vdbeaux.c
src/where.c

index bd77d3142e932627ae4477369a9f6c8a4ba4bd05..5a456e3c1a27bbc96e71c79e008bb5c197ac1de1 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Continuing\sprogress\son\sthe\sjournal_mode\spragma.\s\sIt\sstill\sdoes\snot\swork.\s(CVS\s5027)
-D 2008-04-17T20:59:38
+C Allow\sOP_MoveGt\sand\ssimilar\sto\suse\san\sarray\sof\sregisters\sinstead\sof\sa\sserialized\srecord.\sModify\sone\stype\sof\sindex\srange\sscan\sto\suse\sthis.\s(CVS\s5028)
+D 2008-04-18T09:01:16
 F Makefile.arm-wince-mingw32ce-gcc ac5f7b2cef0cd850d6f755ba6ee4ab961b1fadf7
 F Makefile.in 25b3282a4ac39388632c2fb0e044ff494d490952
 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -174,16 +174,16 @@ F src/update.c 57282dae1ffffaf4aedc3201ed77f8ef09be4f45
 F src/utf.c 8c94fa10efc78c2568d08d436acc59df4df7191b
 F src/util.c 943caa4071488b20ed90588f0704c6825f91836b
 F src/vacuum.c 3524411bfb58aac0d87eadd3e5b7cd532772af30
-F src/vdbe.c e4a3df1221a8ee8025c7132cf8ab6bc88eae4e02
+F src/vdbe.c 6b3a2273255d7527f17a2f4c123bcaa02969ddc0
 F src/vdbe.h bfd84bda447f39cb599302c7ec85067dae20453c
-F src/vdbeInt.h 0b96efdeecb0803e504bf1c16b198f87c91d6019
+F src/vdbeInt.h 05316345da487b0cf540482576f9ae3337d133cd
 F src/vdbeapi.c 0e1b5a808bb0e556f2a975eb7d11fd3153e922bf
-F src/vdbeaux.c 54fc53eecf270e57957bcc596c2fe452527a8274
+F src/vdbeaux.c ca70c67f853c927d4c1172299578d4b22d4eed50
 F src/vdbeblob.c cc713c142c3d4952b380c98ee035f850830ddbdb
 F src/vdbefifo.c a30c237b2a3577e1415fb6e288cbb6b8ed1e5736
 F src/vdbemem.c 237e61216381998ff71c6431e5e7bd03386f6225
 F src/vtab.c f5e78bf73df3b0c1b53861109c1b2e0800b108cc
-F src/where.c 4835f36ba01f663794b96131b81a1ca43ac239fa
+F src/where.c e6850aa2fbe655c15914e9b102a20abf2834ab89
 F tclinstaller.tcl 4356d9d94d2b5ed5e68f9f0c80c4df3048dd7617
 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
 F test/all.test d56a3ca8acdf761204aff0a2e7aa5eb8e11b31e6
@@ -631,7 +631,7 @@ F www/tclsqlite.tcl 8be95ee6dba05eabcd27a9d91331c803f2ce2130
 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
 F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5
-P 0d2e258e1a3276e55903ba2ded987f8d8a18cacd
-R a3d09c936cfdc9f30d64221d6e47db54
-U drh
-Z 6c3e52b390c6e8cd0898e3f15efd7bdb
+P 4a72a7bb9c5793cdaf4ee038482053e042d8db54
+R 63ab958aeb1a64fb1b489284d8bf24a2
+U danielk1977
+Z ffb6897ecd3dcda79af541b97309bd4b
index 745026bfb2231cd4bfa2024bb41cae5cac97e6b4..83af5c671c14bd1f20e7e679748815e463eb15af 100644 (file)
@@ -1 +1 @@
-4a72a7bb9c5793cdaf4ee038482053e042d8db54
\ No newline at end of file
+c448f15aa5ed3dec511426775e893efea324faa1
\ No newline at end of file
index 07bd6b99fcc293f87327079e9a00ac86a116f548..16cd578b7af1cbef8ed4fbdc630a78f0bbeafc4e 100644 (file)
@@ -43,7 +43,7 @@
 ** in this file for details.  If in doubt, do not deviate from existing
 ** commenting and indentation practices when changing or adding code.
 **
-** $Id: vdbe.c,v 1.730 2008/04/15 12:14:22 drh Exp $
+** $Id: vdbe.c,v 1.731 2008/04/18 09:01:16 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -2133,6 +2133,26 @@ op_column_out:
   break;
 }
 
+/* Opcode: Affinity P1 P2 * P4 *
+**
+** Apply affinities to a range of P2 registers starting with P1.
+**
+** P4 is a string that is P2 characters long. The nth character of the
+** string indicates the column affinity that should be used for the nth
+** memory cell in the range.
+*/
+case OP_Affinity: {
+  char *zAffinity = pOp->p4.z;
+  Mem *pData0 = &p->aMem[pOp->p1];
+  Mem *pLast = &pData0[pOp->p2-1];
+  Mem *pRec;
+
+  for(pRec=pData0; pRec<=pLast; pRec++){
+    applyAffinity(pRec, zAffinity[pRec-pData0], encoding);
+  }
+  break;
+}
+
 /* Opcode: MakeRecord P1 P2 P3 P4 *
 **
 ** Convert P2 registers beginning with P1 into a single entry
@@ -2142,7 +2162,7 @@ op_column_out:
 ** Refer to source code comments for the details of the record
 ** format.
 **
-** P4 may be a string that is P1 characters long.  The nth character of the
+** P4 may be a string that is P2 characters long.  The nth character of the
 ** string indicates the column affinity that should be used for the nth
 ** field of the index key.
 **
@@ -2807,13 +2827,17 @@ case OP_Close: {
 **
 ** See also: Found, NotFound, Distinct, MoveGt, MoveGe, MoveLe
 */
-/* Opcode: MoveLe P1 P2 P3 * *
+/* Opcode: MoveLe P1 P2 P3 P4 *
 **
-** Use the value in register P3 as a key.  Reposition
-** cursor P1 so that it points to the largest entry that is less than
-** or equal to the key.
-** If there are no records less than or eqal to the key
-** then jump to P2.
+** P4 is always an integer value. If it is zero, then use the value in
+** register P3 as a key. Reposition cursor P1 so that it points to the
+** largest entry that is less than or equal to the key. If there are no 
+** records less than or eqal to the key then jump to P2.
+**
+** If the integer value in operand P4 is non-zero, then P3 is the first
+** of a contiguous array of P4 memory cells that form an unpacked index
+** key. In this case the unpacked key is used instead of the value of
+** register P3 in the procedure described above.
 **
 ** See also: Found, NotFound, Distinct, MoveGt, MoveGe, MoveLt
 */
@@ -2848,9 +2872,20 @@ case OP_MoveGt: {       /* jump, in3 */
       pC->lastRowid = iKey;
       pC->rowidIsValid = res==0;
     }else{
-      assert( pIn3->flags & MEM_Blob );
-      ExpandBlob(pIn3);
-      rc = sqlite3BtreeMoveto(pC->pCursor, pIn3->z, 0, pIn3->n, 0, &res);
+      int nField = ((pOp->p4type==P4_INT32)?pOp->p4.i:0);
+      assert( pIn3->flags&MEM_Blob || nField>0 );
+      if( nField==0 ){
+        ExpandBlob(pIn3);
+        rc = sqlite3BtreeMoveto(pC->pCursor, pIn3->z, 0, pIn3->n, 0, &res);
+      }else{
+        UnpackedRecord r;
+        r.pKeyInfo = pC->pKeyInfo;
+        r.nField = nField;
+        r.needFree = 0;
+        r.needDestroy = 0;
+        r.aMem = &p->aMem[pOp->p3];
+        rc = sqlite3BtreeMoveto(pC->pCursor, 0, &r, 0, 0, &res);
+      }
       if( rc!=SQLITE_OK ){
         goto abort_due_to_error;
       }
@@ -3023,7 +3058,7 @@ case OP_IsUnique: {        /* jump, in3 */
         break;
       }
     }
-    rc = sqlite3VdbeIdxKeyCompare(pCx, len, (u8*)zKey, &res); 
+    rc = sqlite3VdbeIdxKeyCompare(pCx, 0, len, (u8*)zKey, &res); 
     if( rc!=SQLITE_OK ) goto abort_due_to_error;
     if( res>0 ){
       pc = pOp->p2 - 1;
@@ -3817,10 +3852,9 @@ case OP_IdxRowid: {              /* out2-prerelease */
 ** If the P1 index entry is less than the register P3 value
 ** then jump to P2.  Otherwise fall through to the next instruction.
 **
-** If P5 is non-zero then the
-** index taken from register P3 is temporarily increased by
-** an epsilon prior to the comparison.  This makes the opcode work
-** like IdxLE.
+** If P5 is non-zero then the index taken from register P3 is temporarily 
+** increased by an epsilon prior to the comparison.  This makes the opcode 
+** work like IdxLE.
 */
 case OP_IdxLT:          /* jump, in3 */
 case OP_IdxGE: {        /* jump, in3 */
@@ -3832,12 +3866,22 @@ case OP_IdxGE: {        /* jump, in3 */
   if( (pC = p->apCsr[i])->pCursor!=0 ){
     int res;
  
-    assert( pIn3->flags & MEM_Blob );  /* Created using OP_MakeRecord */
     assert( pC->deferredMoveto==0 );
-    ExpandBlob(pIn3);
     assert( pOp->p5==0 || pOp->p5==1 );
     *pC->pIncrKey = pOp->p5;
-    rc = sqlite3VdbeIdxKeyCompare(pC, pIn3->n, (u8*)pIn3->z, &res);
+    if( pOp->p4type!=P4_INT32 || pOp->p4.i==0 ){
+      assert( pIn3->flags & MEM_Blob );  /* Created using OP_MakeRecord */
+      ExpandBlob(pIn3);
+      rc = sqlite3VdbeIdxKeyCompare(pC, 0, pIn3->n, (u8*)pIn3->z, &res);
+    }else{
+      UnpackedRecord r;
+      r.pKeyInfo = pC->pKeyInfo;
+      r.nField = pOp->p4.i;
+      r.needFree = 0;
+      r.needDestroy = 0;
+      r.aMem = &p->aMem[pOp->p3];
+      rc = sqlite3VdbeIdxKeyCompare(pC, &r, 0, 0, &res);
+    }
     *pC->pIncrKey = 0;
     if( rc!=SQLITE_OK ){
       break;
index 7f99daae93b81976a65f7fbcfe51da246bd4dbcb..8260d999f54a1cb260f9ae3ef850208efa211491 100644 (file)
@@ -387,7 +387,7 @@ int sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*);
 void sqlite3VdbeDeleteAuxData(VdbeFunc*, int);
 
 int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *);
-int sqlite3VdbeIdxKeyCompare(Cursor*,int,const unsigned char*,int*);
+int sqlite3VdbeIdxKeyCompare(Cursor*,UnpackedRecord *,int,const unsigned char*,int*);
 int sqlite3VdbeIdxRowid(BtCursor *, i64 *);
 int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);
 int sqlite3VdbeIdxRowidLen(const u8*);
index 17528676656088779a23aa0ad217a05521a719d5..903c12f02ecde0fe461cb936f1a9821f6e9d11c8 100644 (file)
@@ -2401,6 +2401,7 @@ int sqlite3VdbeIdxRowid(BtCursor *pCur, i64 *rowid){
 */
 int sqlite3VdbeIdxKeyCompare(
   Cursor *pC,                 /* The cursor to compare against */
+  UnpackedRecord *pUnpacked,
   int nKey, const u8 *pKey,   /* The key to compare */
   int *res                    /* Write the comparison result here */
 ){
@@ -2425,13 +2426,19 @@ int sqlite3VdbeIdxKeyCompare(
     return rc;
   }
   lenRowid = sqlite3VdbeIdxRowidLen((u8*)m.z);
-  pRec = sqlite3VdbeRecordUnpack(pC->pKeyInfo, nKey, pKey,
+  if( !pUnpacked ){
+    pRec = sqlite3VdbeRecordUnpack(pC->pKeyInfo, nKey, pKey,
                                 zSpace, sizeof(zSpace));
+  }else{
+    pRec = pUnpacked;
+  }
   if( pRec==0 ){
     return SQLITE_NOMEM;
   }
   *res = sqlite3VdbeRecordCompare(m.n-lenRowid, m.z, pRec);
-  sqlite3VdbeDeleteUnpackedRecord(pRec);
+  if( !pUnpacked ){
+    sqlite3VdbeDeleteUnpackedRecord(pRec);
+  }
   sqlite3VdbeMemRelease(&m);
   return SQLITE_OK;
 }
index 3b7df014e409f8f00a0d28d0628d75d7a6627701..0ccc601663cb3108b271d02595825b01fe8ee62e 100644 (file)
@@ -16,7 +16,7 @@
 ** so is applicable.  Because this module is responsible for selecting
 ** indices, you might also think of this module as the "query optimizer".
 **
-** $Id: where.c,v 1.299 2008/04/17 19:14:02 drh Exp $
+** $Id: where.c,v 1.300 2008/04/18 09:01:16 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 
@@ -2475,36 +2475,40 @@ WhereInfo *sqlite3WhereBegin(
       **         constraints but an index is selected anyway, in order
       **         to force the output order to conform to an ORDER BY.
       */
-      int start;
+      int aStartOp[] = {
+        0,
+        0,
+        OP_Rewind,           /* 2: (!start_constraints && startEq && !bRev) */
+        OP_Last,             /* 3: (!start_constraints && startEq && bRev)  */
+        OP_MoveGt,           /* 4: (start_constraints  && !startEq && !bRev) */
+        OP_MoveLt,           /* 5: (start_constraints  && !startEq && bRev) */
+        OP_MoveGe,           /* 6: (start_constraints  &&  startEq && !bRev) */
+        OP_MoveLe            /* 7: (start_constraints  &&  startEq && bRev) */
+      };
+      int aEndOp[] = {
+        OP_Noop,             /* 0: () */
+        OP_IdxGE,            /* 1: (end_constraints && !bRev) */
+        OP_IdxLT             /* 2: (end_constraints && bRev) */
+      };
       int nEq = pLevel->nEq;
-      int topEq=0;        /* True if top limit uses ==. False is strictly < */
-      int btmEq=0;        /* True if btm limit uses ==. False if strictly > */
-      int topOp, btmOp;   /* Operators for the top and bottom search bounds */
-      int testOp;
-      int topLimit = (pLevel->flags & WHERE_TOP_LIMIT)!=0;
-      int btmLimit = (pLevel->flags & WHERE_BTM_LIMIT)!=0;
-      int isMinQuery = 0;      /* If this is an optimized SELECT min(x) ... */
-      int regBase;        /* Base register holding constraint values */
-      int r1;             /* Temp register */
+      int isMinQuery = 0;          /* If this is an optimized SELECT min(x).. */
+      int regBase;                 /* Base register holding constraint values */
+      int r1;                      /* Temp register */
+      WhereTerm *pRangeStart = 0;  /* Inequality constraint at range start */
+      WhereTerm *pRangeEnd = 0;    /* Inequality constraint at range end */
+      int startEq;                 /* True if range start uses ==, >= or <= */
+      int endEq;                   /* True if range end uses ==, >= or <= */
+      int start_constraints;       /* Start of range is constrained */
+      int k = pIdx->aiColumn[nEq]; /* Column for inequality constraints */
+      char *ptr;
+      int op;
 
       /* Generate code to evaluate all constraint terms using == or IN
-      ** and level the values of those terms on the stack.
+      ** and store the values of those terms in an array of registers
+      ** starting at regBase.
       */
       regBase = codeAllEqualityTerms(pParse, pLevel, &wc, notReady, 2);
-
-      /* Figure out what comparison operators to use for top and bottom 
-      ** search bounds. For an ascending index, the bottom bound is a > or >=
-      ** operator and the top bound is a < or <= operator.  For a descending
-      ** index the operators are reversed.
-      */
-      if( pIdx->aSortOrder[nEq]==SQLITE_SO_ASC ){
-        topOp = WO_LT|WO_LE;
-        btmOp = WO_GT|WO_GE;
-      }else{
-        topOp = WO_GT|WO_GE;
-        btmOp = WO_LT|WO_LE;
-        SWAP(int, topLimit, btmLimit);
-      }
+      nxt = pLevel->nxt;
 
       /* If this loop satisfies a sort order (pOrderBy) request that 
       ** was passed to this function to implement a "SELECT min(x) ..." 
@@ -2522,121 +2526,95 @@ WhereInfo *sqlite3WhereBegin(
         isMinQuery = 1;
       }
 
-      /* Generate the termination key.  This is the key value that
-      ** will end the search.  There is no termination key if there
-      ** are no equality terms and no "X<..." term.
-      **
-      ** 2002-Dec-04: On a reverse-order scan, the so-called "termination"
-      ** key computed here really ends up being the start key.
+      /* Find the inequality constraint terms for the start and end 
+      ** of the range. 
       */
-      nxt = pLevel->nxt;
-      if( topLimit ){
-        Expr *pX;
-        int k = pIdx->aiColumn[nEq];
-        pTerm = findTerm(&wc, iCur, k, notReady, topOp, pIdx);
-        assert( pTerm!=0 );
-        pX = pTerm->pExpr;
-        assert( (pTerm->flags & TERM_CODED)==0 );
-        sqlite3ExprCode(pParse, pX->pRight, regBase+nEq);
-        sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, nxt);
-        topEq = pTerm->eOperator & (WO_LE|WO_GE);
-        disableTerm(pLevel, pTerm);
-        testOp = OP_IdxGE;
-      }else{
-        testOp = nEq>0 ? OP_IdxGE : OP_Noop;
-        topEq = 1;
+      if( pLevel->flags & WHERE_TOP_LIMIT ){
+        pRangeEnd = findTerm(&wc, iCur, k, notReady, (WO_LT|WO_LE), pIdx);
       }
-      if( testOp!=OP_Noop || (isMinQuery&&bRev) ){
-        int nCol = nEq + topLimit;
-        if( isMinQuery && bRev && !topLimit ){
-          sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nCol);
-          nCol++;
-          topEq = 0;
-        }
-        buildIndexProbe(pParse, nCol, pIdx, regBase, pLevel->iMem);
-        if( bRev ){
-          int op = topEq ? OP_MoveLe : OP_MoveLt;
-          sqlite3VdbeAddOp3(v, op, iIdxCur, nxt, pLevel->iMem);
-        }
-      }else if( bRev ){
-        sqlite3VdbeAddOp2(v, OP_Last, iIdxCur, brk);
+      if( pLevel->flags & WHERE_BTM_LIMIT ){
+        pRangeStart = findTerm(&wc, iCur, k, notReady, (WO_GT|WO_GE), pIdx);
       }
-   
-      /* Generate the start key.  This is the key that defines the lower
-      ** bound on the search.  There is no start key if there are no
-      ** equality terms and if there is no "X>..." term.  In
-      ** that case, generate a "Rewind" instruction in place of the
-      ** start key search.
-      **
-      ** 2002-Dec-04: In the case of a reverse-order search, the so-called
-      ** "start" key really ends up being used as the termination key.
+
+      /* If we are doing a reverse order scan on an ascending index, or
+      ** a forward order scan on a descending index, interchange the 
+      ** start and end terms (pRangeStart and pRangeEnd).
       */
-      if( btmLimit ){
-        Expr *pX;
-        int k = pIdx->aiColumn[nEq];
-        pTerm = findTerm(&wc, iCur, k, notReady, btmOp, pIdx);
-        assert( pTerm!=0 );
-        pX = pTerm->pExpr;
-        assert( (pTerm->flags & TERM_CODED)==0 );
-        sqlite3ExprCode(pParse, pX->pRight, regBase+nEq);
-        sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, nxt);
-        btmEq = pTerm->eOperator & (WO_LE|WO_GE);
-        disableTerm(pLevel, pTerm);
-      }else{
-        btmEq = 1;
+      if( bRev==((pIdx->aSortOrder[nEq]==SQLITE_SO_ASC)?1:0) ){
+        SWAP(WhereTerm *, pRangeEnd, pRangeStart);
       }
-      if( nEq>0 || btmLimit || (isMinQuery&&!bRev) ){
-        int nCol = nEq + btmLimit;
-        if( isMinQuery && !bRev && !btmLimit ){
-          sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nCol);
-          nCol++;
-          btmEq = 0;
-        }
-        if( bRev ){
-          r1 = pLevel->iMem;
-          testOp = OP_IdxLT;
-        }else{
-          r1 = sqlite3GetTempReg(pParse);
-        }
-        buildIndexProbe(pParse, nCol, pIdx, regBase, r1);
-        if( !bRev ){
-          int op = btmEq ? OP_MoveGe : OP_MoveGt;
-          sqlite3VdbeAddOp3(v, op, iIdxCur, nxt, r1);
-          sqlite3ReleaseTempReg(pParse, r1);
+
+      startEq = ((!pRangeStart || pRangeStart->eOperator & (WO_LE|WO_GE))?1:0);
+      endEq = ((!pRangeEnd || pRangeEnd->eOperator & (WO_LE|WO_GE))?1:0);
+      start_constraints = ((pRangeStart || nEq>0)?1:0);
+
+      /* Seek the index cursor to the start of the range. */
+      ptr = (char *)(sqlite3_intptr_t)nEq;
+      if( pRangeStart ){
+        int dcc = pParse->disableColCache;
+        if( pRangeEnd ){
+          pParse->disableColCache = 1;
         }
-      }else if( bRev ){
-        testOp = OP_Noop;
-      }else{
-        sqlite3VdbeAddOp2(v, OP_Rewind, iIdxCur, brk);
+        sqlite3ExprCode(pParse, pRangeStart->pExpr->pRight, regBase+nEq);
+        pParse->disableColCache = dcc;
+        sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, nxt);
+        ptr++;
+      }else if( isMinQuery ){
+        sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
+        ptr++;
+        startEq = 0;
+        start_constraints = 1;
       }
+      sqlite3VdbeAddOp2(v, OP_Affinity, regBase, (int)ptr);
+      sqlite3IndexAffinityStr(v, pIdx);
+      op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev];
+      sqlite3VdbeAddOp4(v, op, iIdxCur, nxt, regBase, ptr, P4_INT32);
 
-      /* 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.
+      /* Load the value for the inequality constraint at the end of the
+      ** range (if any).
       */
-      start = sqlite3VdbeCurrentAddr(v);
-      if( testOp!=OP_Noop ){
-        sqlite3VdbeAddOp3(v, testOp, iIdxCur, nxt, pLevel->iMem);
-        if( (topEq && !bRev) || (!btmEq && bRev) ){
-          sqlite3VdbeChangeP5(v, 1);
-        }
+      ptr = (char *)(sqlite3_intptr_t)nEq;
+      if( pRangeEnd ){
+        sqlite3ExprCode(pParse, pRangeEnd->pExpr->pRight, regBase+nEq);
+        sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, nxt);
+        ptr++;
       }
+      sqlite3VdbeAddOp2(v, OP_Affinity, regBase, (int)ptr);
+      sqlite3IndexAffinityStr(v, pIdx);
+
+      /* Top of the loop body */
+      pLevel->p2 = sqlite3VdbeCurrentAddr(v);
+
+      /* Check if the index cursor is past the end of the range. */
+      op = aEndOp[((pRangeEnd || nEq)?1:0) * (1 + bRev)];
+      sqlite3VdbeAddOp4(v, op, iIdxCur, nxt, regBase, ptr, P4_INT32);
+      sqlite3VdbeChangeP5(v, endEq!=bRev);
+
+      /* If there are inequality constraints (there may not be if the
+      ** index is only being used to optimize ORDER BY), check that the
+      ** value of the table column the inequality contrains is not NULL.
+      ** If it is, jump to the next iteration of the loop.
+      */
       r1 = sqlite3GetTempReg(pParse);
-      if( topLimit | btmLimit ){
+      if( pLevel->flags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT) ){
         sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, nEq, r1);
         sqlite3VdbeAddOp2(v, OP_IsNull, r1, cont);
       }
+
+      /* Seek the table cursor, if required */
       if( !omitTable ){
         sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, r1);
         sqlite3VdbeAddOp3(v, OP_MoveGe, iCur, 0, r1);  /* Deferred seek */
       }
       sqlite3ReleaseTempReg(pParse, r1);
 
-      /* Record the instruction used to terminate the loop.
+      /* Record the instruction used to terminate the loop. Disable 
+      ** WHERE clause terms made redundant by the index range scan.
       */
       pLevel->op = bRev ? OP_Prev : OP_Next;
       pLevel->p1 = iIdxCur;
-      pLevel->p2 = start;
+      disableTerm(pLevel, pRangeStart);
+      disableTerm(pLevel, pRangeEnd);
     }else if( pLevel->flags & WHERE_COLUMN_EQ ){
       /* Case 4:  There is an index and all terms of the WHERE clause that
       **          refer to the index using the "==" or "IN" operators.