Rework the rowid lookup logic in the query optimizer to fix various
boundary value problems. Ticket #3536. Renamed the OP_MoveXX opcodes to OP_SeekXX. (CVS 6024) FossilOrigin-Name: e545490a2828f7f703172dda6d2b9f3342dc134a
This commit is contained in:
17
manifest
17
manifest
@@ -1,5 +1,5 @@
|
||||
C Previous\schange\sto\sthe\sOP_ForceInt\sopcode\sdid\snot\swork\scorrectly\swhen\sthe\ninput\sis\sa\snegative\sfloating\spoint\svalue.\s\sThis\schange\sis\sthe\sfix.\nTicket\s#3536.\s(CVS\s6023)
|
||||
D 2008-12-11T20:03:22
|
||||
C Rework\sthe\srowid\slookup\slogic\sin\sthe\squery\soptimizer\sto\sfix\svarious\r\nboundary\svalue\sproblems.\s\sTicket\s#3536.\s\sRenamed\sthe\sOP_MoveXX\sopcodes\r\nto\sOP_SeekXX.\s(CVS\s6024)
|
||||
D 2008-12-12T17:56:16
|
||||
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
|
||||
F Makefile.in f7e4c81c347b04f7b0f1c1b081a168645d7b8af7
|
||||
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
|
||||
@@ -194,7 +194,7 @@ F src/update.c 080889d241e4dcd1c545c8051eb6de86f4939295
|
||||
F src/utf.c 1da9c832dba0fa8f865b5b902d93f420a1ee4245
|
||||
F src/util.c ea62608f66f33a7e8322de83024ae37c415c0c7f
|
||||
F src/vacuum.c 383d6297bddc011ab04a9eed110db6eaf523e8e9
|
||||
F src/vdbe.c 0f9f19520c0b9ac474115b242d07ce5a2e8573d2
|
||||
F src/vdbe.c ee98938f76a3d9e513aca9696391c6e19fa2cafe
|
||||
F src/vdbe.h 03516f28bf5aca00a53c4dccd6c313f96adb94f6
|
||||
F src/vdbeInt.h e6e80a99ce634983b7cc2498843b4d2e5540900a
|
||||
F src/vdbeapi.c 85c33cfbfa56249cbe627831610afafba754477d
|
||||
@@ -203,7 +203,7 @@ F src/vdbeblob.c b0dcebfafedcf9c0addc7901ad98f6f986c08935
|
||||
F src/vdbemem.c f9c859ac17e2e05a0f249868ce4f191f69edd31d
|
||||
F src/vtab.c e39e011d7443a8d574b1b9cde207a35522e6df43
|
||||
F src/walker.c 488c2660e13224ff70c0c82761118efb547f8f0d
|
||||
F src/where.c 7c6ab881411110fd8cf2735205532e15475d3349
|
||||
F src/where.c 0ef44949222bc9ddcdecf4e44febb9f6dfae4411
|
||||
F tclinstaller.tcl 4356d9d94d2b5ed5e68f9f0c80c4df3048dd7617
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
F test/alias.test 597662c5d777a122f9a3df0047ea5c5bd383a911
|
||||
@@ -235,6 +235,7 @@ F test/bind.test 1134441f1ea47abd1c740090435a6ecbe9ceb263
|
||||
F test/bindxfer.test d4f573750e06c34ef2309acb95ad57da1d3c983f
|
||||
F test/bitvec.test ecea9aa315f36991e56e326701279b7775cb2bef
|
||||
F test/blob.test 2a38d867bdf08f9ce081776acec1ac8d4bca66be
|
||||
F test/boundary1.test 7481c191f25082a2a25c9b01ee8d04139177cfc5
|
||||
F test/busy.test 76b4887f8b9160ba903c1ac22e8ff406ad6ae2f0
|
||||
F test/cache.test 3ff445c445742a7b6b9ba6e1d62a25263f9424b9
|
||||
F test/capi2.test 36f87803c811b5986dd31eb5492cb704552776b4
|
||||
@@ -469,7 +470,7 @@ F test/pragma2.test 5364893491b9231dd170e3459bfc2e2342658b47
|
||||
F test/printf.test 262a5acd3158f788e9bdf7f18d718f3af32ff6ef
|
||||
F test/progress.test 5b075c3c790c7b2a61419bc199db87aaf48b8301 x
|
||||
F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
|
||||
F test/quick.test 70695607082b3e1ca54023b2c856991604b07703
|
||||
F test/quick.test 5ef986df012628262ad4183cd8c28f0361911387
|
||||
F test/quote.test 215897dbe8de1a6f701265836d6601cc6ed103e6
|
||||
F test/rdonly.test bd054831f8a3078e765a0657e247182486f0cb47
|
||||
F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
|
||||
@@ -664,7 +665,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
|
||||
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
||||
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
||||
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
||||
P 6a049c6595550c123e77199cf7f3898bfcf40c86
|
||||
R 30f4db4022dbc33bbe241ed05b40d603
|
||||
P f6c50f357c302907a5398c551a5c596f7d305031
|
||||
R 5363a9d2ee8fa1a51a6491d7cbd42e82
|
||||
U drh
|
||||
Z 34043079a7476d69b5d19a3b388c9c6e
|
||||
Z 19a314fdfac85425d684fa034d87f9b6
|
||||
|
||||
@@ -1 +1 @@
|
||||
f6c50f357c302907a5398c551a5c596f7d305031
|
||||
e545490a2828f7f703172dda6d2b9f3342dc134a
|
||||
188
src/vdbe.c
188
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.801 2008/12/11 20:03:22 drh Exp $
|
||||
** $Id: vdbe.c,v 1.802 2008/12/12 17:56:16 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
@@ -51,7 +51,7 @@
|
||||
|
||||
/*
|
||||
** The following global variable is incremented every time a cursor
|
||||
** moves, either by the OP_MoveXX, OP_Next, or OP_Prev opcodes. The test
|
||||
** moves, either by the OP_SeekXX, OP_Next, or OP_Prev opcodes. The test
|
||||
** procedures use this information to make sure that indices are
|
||||
** working correctly. This variable has no function other than to
|
||||
** help verify the correct operation of the library.
|
||||
@@ -1437,49 +1437,6 @@ case OP_AddImm: { /* in1 */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: ForceInt P1 P2 P3 * *
|
||||
**
|
||||
** Convert value in register P1 into an integer. If the value
|
||||
** in P1 is not numeric (meaning that is is a NULL or a string that
|
||||
** does not look like an integer) then jump to P2.
|
||||
**
|
||||
** If the value in P1 is numeric then convert it into the smallest
|
||||
** integer that is greater than or equal to its current value if P3==0,
|
||||
** or to the smallest integer that is strictly greater than its current
|
||||
** value if P3==1.
|
||||
**
|
||||
** If the conversion described in the previous paragraph causes the
|
||||
** value to exceed the largest possible integer value
|
||||
** (9223372036854775807), then jump to P2.
|
||||
*/
|
||||
case OP_ForceInt: { /* jump, in1 */
|
||||
i64 v; /* The value to store in P1 */
|
||||
int incrV = 0; /* True to cause v to increment */
|
||||
applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding);
|
||||
if( (pIn1->flags & (MEM_Int|MEM_Real))==0 ){
|
||||
pc = pOp->p2 - 1;
|
||||
break;
|
||||
}
|
||||
incrV = pOp->p3 ?1:0;
|
||||
if( pIn1->flags & MEM_Int ){
|
||||
v = pIn1->u.i;
|
||||
}else{
|
||||
assert( pIn1->flags & MEM_Real );
|
||||
v = (sqlite3_int64)pIn1->r;
|
||||
incrV = pIn1->r>(double)v ?1:0;
|
||||
}
|
||||
if( incrV ){
|
||||
if( v==LARGEST_INT64 ){
|
||||
pc = pOp->p2 - 1;
|
||||
break;
|
||||
}
|
||||
v++;
|
||||
}
|
||||
pIn1->u.i = v;
|
||||
MemSetTypeFlag(pIn1, MEM_Int);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: MustBeInt P1 P2 * * *
|
||||
**
|
||||
** Force the value in register P1 to be an integer. If the value
|
||||
@@ -2881,10 +2838,10 @@ case OP_Close: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: MoveGe P1 P2 P3 P4 *
|
||||
/* Opcode: SeekGe P1 P2 P3 P4 *
|
||||
**
|
||||
** If cursor P1 refers to an SQL table (B-Tree that uses integer keys),
|
||||
** use the integer value in register P3 as a key. If cursor P1 refers
|
||||
** use the value in register P3 as the key. If cursor P1 refers
|
||||
** to an SQL index, then P3 is the first in an array of P4 registers
|
||||
** that are used as an unpacked index key.
|
||||
**
|
||||
@@ -2892,19 +2849,12 @@ case OP_Close: {
|
||||
** is greater than or equal to the key value. If there are no records
|
||||
** greater than or equal to the key and P2 is not zero, then jump to P2.
|
||||
**
|
||||
** A special feature of this opcode (and different from the
|
||||
** related OP_MoveGt, OP_MoveLt, and OP_MoveLe) is that if P2 is
|
||||
** zero and P1 is an SQL table (a b-tree with integer keys) then
|
||||
** the seek is deferred until it is actually needed. It might be
|
||||
** the case that the cursor is never accessed. By deferring the
|
||||
** seek, we avoid unnecessary seeks.
|
||||
**
|
||||
** See also: Found, NotFound, Distinct, MoveLt, MoveGt, MoveLe
|
||||
** See also: Found, NotFound, Distinct, SeekLt, SeekGt, SeekLe
|
||||
*/
|
||||
/* Opcode: MoveGt P1 P2 P3 P4 *
|
||||
/* Opcode: SeekGt P1 P2 P3 P4 *
|
||||
**
|
||||
** If cursor P1 refers to an SQL table (B-Tree that uses integer keys),
|
||||
** use the integer value in register P3 as a key. If cursor P1 refers
|
||||
** use the value in register P3 as a key. If cursor P1 refers
|
||||
** to an SQL index, then P3 is the first in an array of P4 registers
|
||||
** that are used as an unpacked index key.
|
||||
**
|
||||
@@ -2912,12 +2862,12 @@ case OP_Close: {
|
||||
** is greater than the key value. If there are no records greater than
|
||||
** the key and P2 is not zero, then jump to P2.
|
||||
**
|
||||
** See also: Found, NotFound, Distinct, MoveLt, MoveGe, MoveLe
|
||||
** See also: Found, NotFound, Distinct, SeekLt, SeekGe, SeekLe
|
||||
*/
|
||||
/* Opcode: MoveLt P1 P2 P3 P4 *
|
||||
/* Opcode: SeekLt P1 P2 P3 P4 *
|
||||
**
|
||||
** If cursor P1 refers to an SQL table (B-Tree that uses integer keys),
|
||||
** use the integer value in register P3 as a key. If cursor P1 refers
|
||||
** use the value in register P3 as a key. If cursor P1 refers
|
||||
** to an SQL index, then P3 is the first in an array of P4 registers
|
||||
** that are used as an unpacked index key.
|
||||
**
|
||||
@@ -2925,12 +2875,12 @@ case OP_Close: {
|
||||
** is less than the key value. If there are no records less than
|
||||
** the key and P2 is not zero, then jump to P2.
|
||||
**
|
||||
** See also: Found, NotFound, Distinct, MoveGt, MoveGe, MoveLe
|
||||
** See also: Found, NotFound, Distinct, SeekGt, SeekGe, SeekLe
|
||||
*/
|
||||
/* Opcode: MoveLe P1 P2 P3 P4 *
|
||||
/* Opcode: SeekLe P1 P2 P3 P4 *
|
||||
**
|
||||
** If cursor P1 refers to an SQL table (B-Tree that uses integer keys),
|
||||
** use the integer value in register P3 as a key. If cursor P1 refers
|
||||
** use the value in register P3 as a key. If cursor P1 refers
|
||||
** to an SQL index, then P3 is the first in an array of P4 registers
|
||||
** that are used as an unpacked index key.
|
||||
**
|
||||
@@ -2938,16 +2888,17 @@ case OP_Close: {
|
||||
** is less than or equal to the key value. If there are no records
|
||||
** less than or equal to the key and P2 is not zero, then jump to P2.
|
||||
**
|
||||
** See also: Found, NotFound, Distinct, MoveGt, MoveGe, MoveLt
|
||||
** See also: Found, NotFound, Distinct, SeekGt, SeekGe, SeekLt
|
||||
*/
|
||||
case OP_MoveLt: /* jump, in3 */
|
||||
case OP_MoveLe: /* jump, in3 */
|
||||
case OP_MoveGe: /* jump, in3 */
|
||||
case OP_MoveGt: { /* jump, in3 */
|
||||
case OP_SeekLt: /* jump, in3 */
|
||||
case OP_SeekLe: /* jump, in3 */
|
||||
case OP_SeekGe: /* jump, in3 */
|
||||
case OP_SeekGt: { /* jump, in3 */
|
||||
int i = pOp->p1;
|
||||
VdbeCursor *pC;
|
||||
|
||||
assert( i>=0 && i<p->nCursor );
|
||||
assert( pOp->p2!=0 );
|
||||
pC = p->apCsr[i];
|
||||
assert( pC!=0 );
|
||||
if( pC->pCursor!=0 ){
|
||||
@@ -2955,20 +2906,64 @@ case OP_MoveGt: { /* jump, in3 */
|
||||
oc = pOp->opcode;
|
||||
pC->nullRow = 0;
|
||||
if( pC->isTable ){
|
||||
i64 iKey = sqlite3VdbeIntValue(pIn3);
|
||||
if( pOp->p2==0 ){
|
||||
assert( pOp->opcode==OP_MoveGe );
|
||||
pC->movetoTarget = iKey;
|
||||
pC->rowidIsValid = 0;
|
||||
pC->deferredMoveto = 1;
|
||||
break;
|
||||
}
|
||||
i64 iKey; /* The rowid we are to seek to */
|
||||
|
||||
/* The input value in P3 might be of any type: integer, real, string,
|
||||
** blob, or NULL. But it needs to be an integer before we can do
|
||||
** the seek, so covert it. */
|
||||
applyNumericAffinity(pIn3);
|
||||
iKey = sqlite3VdbeIntValue(pIn3);
|
||||
pC->rowidIsValid = 0;
|
||||
|
||||
/* If the P3 value could not be converted into an integer without
|
||||
** loss of information, then special processing is required... */
|
||||
if( (pIn3->flags & MEM_Int)==0 ){
|
||||
if( (pIn3->flags & MEM_Real)==0 ){
|
||||
/* If the P3 value cannot be converted into any kind of a number,
|
||||
** then the seek is not possible, so jump to P2 */
|
||||
pc = pOp->p2 - 1;
|
||||
break;
|
||||
}
|
||||
/* If we reach this point, then the P3 value must be a floating
|
||||
** point number. */
|
||||
assert( (pIn3->flags & MEM_Real)!=0 );
|
||||
|
||||
if( iKey==SMALLEST_INT64 && (pIn3->r<(double)iKey || pIn3->r>0) ){
|
||||
/* The P3 value is to large in magnitude to be expressed as an
|
||||
** integer. */
|
||||
res = 1;
|
||||
if( pIn3->r<0 ){
|
||||
if( oc==OP_SeekGt || oc==OP_SeekGe ){
|
||||
rc = sqlite3BtreeFirst(pC->pCursor, &res);
|
||||
if( rc!=SQLITE_OK ) goto abort_due_to_error;
|
||||
}
|
||||
}else{
|
||||
if( oc==OP_SeekLt || oc==OP_SeekLe ){
|
||||
rc = sqlite3BtreeLast(pC->pCursor, &res);
|
||||
if( rc!=SQLITE_OK ) goto abort_due_to_error;
|
||||
}
|
||||
}
|
||||
if( res ){
|
||||
pc = pOp->p2 - 1;
|
||||
}
|
||||
break;
|
||||
}else if( oc==OP_SeekLt || oc==OP_SeekGe ){
|
||||
/* Use the ceiling() function to convert real->int */
|
||||
if( pIn3->r > (double)iKey ) iKey++;
|
||||
}else{
|
||||
/* Use the floor() function to convert real->int */
|
||||
assert( oc==OP_SeekLe || oc==OP_SeekGt );
|
||||
if( pIn3->r < (double)iKey ) iKey--;
|
||||
}
|
||||
}
|
||||
rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, 0, (u64)iKey, 0, &res);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto abort_due_to_error;
|
||||
}
|
||||
pC->lastRowid = iKey;
|
||||
pC->rowidIsValid = res==0 ?1:0;
|
||||
if( res==0 ){
|
||||
pC->rowidIsValid = 1;
|
||||
pC->lastRowid = iKey;
|
||||
}
|
||||
}else{
|
||||
UnpackedRecord r;
|
||||
int nField = pOp->p4.i;
|
||||
@@ -2976,7 +2971,7 @@ case OP_MoveGt: { /* jump, in3 */
|
||||
assert( nField>0 );
|
||||
r.pKeyInfo = pC->pKeyInfo;
|
||||
r.nField = (u16)nField;
|
||||
if( oc==OP_MoveGt || oc==OP_MoveLe ){
|
||||
if( oc==OP_SeekGt || oc==OP_SeekLe ){
|
||||
r.flags = UNPACKED_INCRKEY;
|
||||
}else{
|
||||
r.flags = 0;
|
||||
@@ -2993,8 +2988,8 @@ case OP_MoveGt: { /* jump, in3 */
|
||||
#ifdef SQLITE_TEST
|
||||
sqlite3_search_count++;
|
||||
#endif
|
||||
if( oc==OP_MoveGe || oc==OP_MoveGt ){
|
||||
if( res<0 ){
|
||||
if( oc==OP_SeekGe || oc==OP_SeekGt ){
|
||||
if( res<0 || (res==0 && oc==OP_SeekGt) ){
|
||||
rc = sqlite3BtreeNext(pC->pCursor, &res);
|
||||
if( rc!=SQLITE_OK ) goto abort_due_to_error;
|
||||
pC->rowidIsValid = 0;
|
||||
@@ -3002,8 +2997,8 @@ case OP_MoveGt: { /* jump, in3 */
|
||||
res = 0;
|
||||
}
|
||||
}else{
|
||||
assert( oc==OP_MoveLt || oc==OP_MoveLe );
|
||||
if( res>=0 ){
|
||||
assert( oc==OP_SeekLt || oc==OP_SeekLe );
|
||||
if( res>0 || (res==0 && oc==OP_SeekLt) ){
|
||||
rc = sqlite3BtreePrevious(pC->pCursor, &res);
|
||||
if( rc!=SQLITE_OK ) goto abort_due_to_error;
|
||||
pC->rowidIsValid = 0;
|
||||
@@ -3028,6 +3023,33 @@ case OP_MoveGt: { /* jump, in3 */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: Seek P1 P2 * * *
|
||||
**
|
||||
** P1 is an open table cursor and P2 is a rowid integer. Arrange
|
||||
** for P1 to move so that it points to the rowid given by P2.
|
||||
**
|
||||
** This is actually a deferred seek. Nothing actually happens until
|
||||
** the cursor is used to read a record. That way, if no reads
|
||||
** occur, no unnecessary I/O happens.
|
||||
*/
|
||||
case OP_Seek: { /* in2 */
|
||||
int i = pOp->p1;
|
||||
VdbeCursor *pC;
|
||||
|
||||
assert( i>=0 && i<p->nCursor );
|
||||
pC = p->apCsr[i];
|
||||
assert( pC!=0 );
|
||||
if( pC->pCursor!=0 ){
|
||||
assert( pC->isTable );
|
||||
pC->nullRow = 0;
|
||||
pC->movetoTarget = sqlite3VdbeIntValue(pIn2);
|
||||
pC->rowidIsValid = 0;
|
||||
pC->deferredMoveto = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* Opcode: Found P1 P2 P3 * *
|
||||
**
|
||||
** Register P3 holds a blob constructed by MakeRecord. P1 is an index.
|
||||
|
||||
57
src/where.c
57
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.336 2008/12/10 19:26:24 drh Exp $
|
||||
** $Id: where.c,v 1.337 2008/12/12 17:56:16 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -2491,27 +2491,30 @@ WhereInfo *sqlite3WhereBegin(
|
||||
pEnd = pTerm;
|
||||
}
|
||||
if( pStart ){
|
||||
Expr *pX;
|
||||
int r1;
|
||||
Expr *pX; /* The expression that defines the start bound */
|
||||
int r1, rTemp; /* Registers for holding the start boundary */
|
||||
|
||||
/* The following constant maps TK_xx codes into corresponding
|
||||
** seek opcodes. It depends on a particular ordering of TK_xx
|
||||
*/
|
||||
const u8 aMoveOp[] = {
|
||||
/* TK_GT */ OP_SeekGt,
|
||||
/* TK_LE */ OP_SeekLe,
|
||||
/* TK_LT */ OP_SeekLt,
|
||||
/* TK_GE */ OP_SeekGe
|
||||
};
|
||||
assert( TK_LE==TK_GT+1 ); /* Make sure the ordering.. */
|
||||
assert( TK_LT==TK_GT+2 ); /* ... of the TK_xx values... */
|
||||
assert( TK_GE==TK_GT+3 ); /* ... is correcct. */
|
||||
|
||||
pX = pStart->pExpr;
|
||||
assert( pX!=0 );
|
||||
assert( pStart->leftCursor==iCur );
|
||||
|
||||
/* The ForceInt instruction may modify the register that it operates
|
||||
** on. For example it may replace a real value with an integer one,
|
||||
** or if p3 is true it may increment the register value. For this
|
||||
** reason we need to make sure that register r1 is really a newly
|
||||
** allocated temporary register, and not part of the column-cache.
|
||||
** For this reason we cannot use sqlite3ExprCodeTemp() here.
|
||||
*/
|
||||
r1 = sqlite3GetTempReg(pParse);
|
||||
sqlite3ExprCode(pParse, pX->pRight, r1);
|
||||
|
||||
sqlite3VdbeAddOp3(v, OP_ForceInt, r1, addrBrk,
|
||||
pX->op==TK_LE || pX->op==TK_GT);
|
||||
sqlite3VdbeAddOp3(v, bRev ? OP_MoveLt : OP_MoveGe, iCur, addrBrk, r1);
|
||||
r1 = sqlite3ExprCodeTemp(pParse, pX->pRight, &rTemp);
|
||||
sqlite3VdbeAddOp3(v, aMoveOp[pX->op-TK_GT], iCur, addrBrk, r1);
|
||||
VdbeComment((v, "pk"));
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
sqlite3ExprCacheAffinityChange(pParse, r1, 1);
|
||||
sqlite3ReleaseTempReg(pParse, rTemp);
|
||||
disableTerm(pLevel, pStart);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, addrBrk);
|
||||
@@ -2579,10 +2582,10 @@ WhereInfo *sqlite3WhereBegin(
|
||||
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) */
|
||||
OP_SeekGt, /* 4: (start_constraints && !startEq && !bRev) */
|
||||
OP_SeekLt, /* 5: (start_constraints && !startEq && bRev) */
|
||||
OP_SeekGe, /* 6: (start_constraints && startEq && !bRev) */
|
||||
OP_SeekLe /* 7: (start_constraints && startEq && bRev) */
|
||||
};
|
||||
int aEndOp[] = {
|
||||
OP_Noop, /* 0: (!end_constraints) */
|
||||
@@ -2675,10 +2678,10 @@ WhereInfo *sqlite3WhereBegin(
|
||||
assert( op!=0 );
|
||||
testcase( op==OP_Rewind );
|
||||
testcase( op==OP_Last );
|
||||
testcase( op==OP_MoveGt );
|
||||
testcase( op==OP_MoveGe );
|
||||
testcase( op==OP_MoveLe );
|
||||
testcase( op==OP_MoveLt );
|
||||
testcase( op==OP_SeekGt );
|
||||
testcase( op==OP_SeekGe );
|
||||
testcase( op==OP_SeekLe );
|
||||
testcase( op==OP_SeekLt );
|
||||
sqlite3VdbeAddOp4(v, op, iIdxCur, addrNxt, regBase,
|
||||
SQLITE_INT_TO_PTR(nConstraint), P4_INT32);
|
||||
|
||||
@@ -2720,7 +2723,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
/* Seek the table cursor, if required */
|
||||
if( !omitTable ){
|
||||
sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, r1);
|
||||
sqlite3VdbeAddOp3(v, OP_MoveGe, iCur, 0, r1); /* Deferred seek */
|
||||
sqlite3VdbeAddOp2(v, OP_Seek, iCur, r1); /* Deferred seek */
|
||||
}
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
|
||||
|
||||
7642
test/boundary1.test
Normal file
7642
test/boundary1.test
Normal file
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@
|
||||
#***********************************************************************
|
||||
# This file runs all tests.
|
||||
#
|
||||
# $Id: quick.test,v 1.88 2008/10/13 14:16:11 drh Exp $
|
||||
# $Id: quick.test,v 1.89 2008/12/12 17:56:16 drh Exp $
|
||||
|
||||
proc lshift {lvar} {
|
||||
upvar $lvar l
|
||||
@@ -47,6 +47,7 @@ set EXCLUDE {
|
||||
async2.test
|
||||
async3.test
|
||||
corrupt.test
|
||||
corruptC.test
|
||||
crash.test
|
||||
crash2.test
|
||||
crash3.test
|
||||
|
||||
Reference in New Issue
Block a user