Allow OP_MoveGt and similar to use an array of registers instead of a serialized record. Modify one type of index range scan to use this. (CVS 5028)
FossilOrigin-Name: c448f15aa5ed3dec511426775e893efea324faa1
This commit is contained in:
parent
fdc40e9156
commit
751de567c1
20
manifest
20
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
|
||||
|
@ -1 +1 @@
|
||||
4a72a7bb9c5793cdaf4ee038482053e042d8db54
|
||||
c448f15aa5ed3dec511426775e893efea324faa1
|
82
src/vdbe.c
82
src/vdbe.c
@ -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;
|
||||
|
@ -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*);
|
||||
|
@ -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;
|
||||
}
|
||||
|
222
src/where.c
222
src/where.c
@ -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);
|
||||
}
|
||||
|
||||
/* 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( 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( 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);
|
||||
}
|
||||
}else if( bRev ){
|
||||
testOp = OP_Noop;
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, iIdxCur, brk);
|
||||
if( pLevel->flags & WHERE_BTM_LIMIT ){
|
||||
pRangeStart = findTerm(&wc, iCur, k, notReady, (WO_GT|WO_GE), pIdx);
|
||||
}
|
||||
|
||||
/* 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.
|
||||
/* 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).
|
||||
*/
|
||||
start = sqlite3VdbeCurrentAddr(v);
|
||||
if( testOp!=OP_Noop ){
|
||||
sqlite3VdbeAddOp3(v, testOp, iIdxCur, nxt, pLevel->iMem);
|
||||
if( (topEq && !bRev) || (!btmEq && bRev) ){
|
||||
sqlite3VdbeChangeP5(v, 1);
|
||||
}
|
||||
if( bRev==((pIdx->aSortOrder[nEq]==SQLITE_SO_ASC)?1:0) ){
|
||||
SWAP(WhereTerm *, pRangeEnd, pRangeStart);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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);
|
||||
|
||||
/* Load the value for the inequality constraint at the end of the
|
||||
** range (if any).
|
||||
*/
|
||||
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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user