diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/btree.c | 4 | ||||
-rw-r--r-- | src/btree.h | 2 | ||||
-rw-r--r-- | src/sqliteInt.h | 34 | ||||
-rw-r--r-- | src/vdbe.c | 43 | ||||
-rw-r--r-- | src/wherecode.c | 9 |
5 files changed, 58 insertions, 34 deletions
diff --git a/src/btree.c b/src/btree.c index 5488d2010..a61a6edf2 100644 --- a/src/btree.c +++ b/src/btree.c @@ -5041,6 +5041,8 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ ** *pRes>0 The cursor is left pointing at an entry that ** is larger than intKey/pIdxKey. ** +** For index tables, the pIdxKey->eqSeen field is set to 1 if there +** exists an entry in the table that exactly matches pIdxKey. */ int sqlite3BtreeMovetoUnpacked( BtCursor *pCur, /* The cursor to be moved */ @@ -9644,7 +9646,6 @@ int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){ return rc; } -#ifdef SQLITE_DEBUG /* ** Return true if the cursor has a hint specified. This routine is ** only used from within assert() statements @@ -9652,7 +9653,6 @@ int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){ int sqlite3BtreeCursorHasHint(BtCursor *pCsr, unsigned int mask){ return (pCsr->hints & mask)!=0; } -#endif /* ** Return true if the given Btree is read-only. diff --git a/src/btree.h b/src/btree.h index f37ec5e7f..0c15a59c1 100644 --- a/src/btree.h +++ b/src/btree.h @@ -257,9 +257,7 @@ int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*); void sqlite3BtreeIncrblobCursor(BtCursor *); void sqlite3BtreeClearCursor(BtCursor *); int sqlite3BtreeSetVersion(Btree *pBt, int iVersion); -#ifdef SQLITE_DEBUG int sqlite3BtreeCursorHasHint(BtCursor*, unsigned int mask); -#endif int sqlite3BtreeIsReadonly(Btree *pBt); int sqlite3HeaderSizeBtree(void); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index b86de1b2b..2b9ece78e 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1812,9 +1812,8 @@ struct KeyInfo { }; /* -** An instance of the following structure holds information about a -** single index record that has already been parsed out into individual -** values. +** This object holds a record which has been parsed out into individual +** fields, for the purposes of doing a comparison. ** ** A record is an object that contains one or more fields of data. ** Records are used to store the content of a table row and to store @@ -1822,11 +1821,30 @@ struct KeyInfo { ** the OP_MakeRecord opcode of the VDBE and is disassembled by the ** OP_Column opcode. ** -** This structure holds a record that has already been disassembled -** into its constituent fields. -** -** The r1 and r2 member variables are only used by the optimized comparison -** functions vdbeRecordCompareInt() and vdbeRecordCompareString(). +** An instance of this object serves as a "key" for doing a search on +** an index b+tree. The goal of the search is to find the entry that +** is closed to the key described by this object. This object might hold +** just a prefix of the key. The number of fields is given by +** pKeyInfo->nField. +** +** The r1 and r2 fields are the values to return if this key is less than +** or greater than a key in the btree, respectively. These are normally +** -1 and +1 respectively, but might be inverted to +1 and -1 if the b-tree +** is in DESC order. +** +** The key comparison functions actually return default_rc when they find +** an equals comparison. default_rc can be -1, 0, or +1. If there are +** multiple entries in the b-tree with the same key (when only looking +** at the first pKeyInfo->nFields,) then default_rc can be set to -1 to +** cause the search to find the last match, or +1 to cause the search to +** find the first match. +** +** The key comparison functions will set eqSeen to true if they ever +** get and equal results when comparing this structure to a b-tree record. +** When default_rc!=0, the search might end up on the record immediately +** before the first match or immediately after the last match. The +** eqSeen field will indicate whether or not an exact match exists in the +** b-tree. */ struct UnpackedRecord { KeyInfo *pKeyInfo; /* Collation and sort-order information */ diff --git a/src/vdbe.c b/src/vdbe.c index 6a4729a69..bfbb7e019 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -3597,6 +3597,13 @@ case OP_ColumnsUsed: { ** 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. ** +** If the cursor P1 was opened using the OPFLAG_SEEKEQ flag, then this +** opcode will always land on a record that equally equals the key, or +** else jump immediately to P2. When the cursor is OPFLAG_SEEKEQ, this +** opcode must be followed by an IdxLE opcode with the same arguments. +** The IdxLE opcode will be skipped if this opcode succeeds, but the +** IdxLE opcode will be used on subsequent loop iterations. +** ** This opcode leaves the cursor configured to move in forward order, ** from the beginning toward the end. In other words, the cursor is ** configured to use Next, not Prev. @@ -3655,18 +3662,26 @@ case OP_ColumnsUsed: { ** from the end toward the beginning. In other words, the cursor is ** configured to use Prev, not Next. ** +** If the cursor P1 was opened using the OPFLAG_SEEKEQ flag, then this +** opcode will always land on a record that equally equals the key, or +** else jump immediately to P2. When the cursor is OPFLAG_SEEKEQ, this +** opcode must be followed by an IdxGE opcode with the same arguments. +** The IdxGE opcode will be skipped if this opcode succeeds, but the +** IdxGE opcode will be used on subsequent loop iterations. +** ** See also: Found, NotFound, SeekGt, SeekGe, SeekLt */ case OP_SeekLT: /* jump, in3 */ case OP_SeekLE: /* jump, in3 */ case OP_SeekGE: /* jump, in3 */ case OP_SeekGT: { /* jump, in3 */ - int res; - int oc; - VdbeCursor *pC; - UnpackedRecord r; - int nField; - i64 iKey; /* The rowid we are to seek to */ + int res; /* Comparison result */ + int oc; /* Opcode */ + VdbeCursor *pC; /* The cursor to seek */ + UnpackedRecord r; /* The key to seek for */ + int nField; /* Number of columns or fields in the key */ + i64 iKey; /* The rowid we are to seek to */ + int eqOnly = 0; /* Only interested in == results */ assert( pOp->p1>=0 && pOp->p1<p->nCursor ); assert( pOp->p2!=0 ); @@ -3688,18 +3703,15 @@ case OP_SeekGT: { /* jump, in3 */ ** OP_SeekLE opcodes are allowed, and these must be immediately followed ** by an OP_IdxGT or OP_IdxLT opcode, respectively, with the same key. */ -#ifdef SQLITE_DEBUG if( sqlite3BtreeCursorHasHint(pC->pCursor, BTREE_SEEK_EQ) ){ + eqOnly = 1; assert( pOp->opcode==OP_SeekGE || pOp->opcode==OP_SeekLE ); -#if 0 assert( pOp[1].opcode==OP_IdxLT || pOp[1].opcode==OP_IdxGT ); assert( pOp[1].p1==pOp[0].p1 ); assert( pOp[1].p2==pOp[0].p2 ); assert( pOp[1].p3==pOp[0].p3 ); assert( pOp[1].p4.i==pOp[0].p4.i ); -#endif } -#endif if( pC->isTable ){ /* The input value in P3 might be of any type: integer, real, string, @@ -3749,6 +3761,7 @@ case OP_SeekGT: { /* jump, in3 */ if( rc!=SQLITE_OK ){ goto abort_due_to_error; } + if( eqOnly && res ) goto seek_not_found; }else{ nField = pOp->p4.i; assert( pOp->p4type==P4_INT32 ); @@ -3779,8 +3792,9 @@ case OP_SeekGT: { /* jump, in3 */ if( rc!=SQLITE_OK ){ goto abort_due_to_error; } - if( (pOp->p5 & OPFLAG_SEEKEQ)!=0 && r.eqSeen==0 ){ - goto take_the_jump; + if( eqOnly && r.eqSeen==0 ){ + assert( res!=0 ); + goto seek_not_found; } } pC->deferredMoveto = 0; @@ -3809,11 +3823,14 @@ case OP_SeekGT: { /* jump, in3 */ res = sqlite3BtreeEof(pC->pCursor); } } -take_the_jump: +seek_not_found: assert( pOp->p2>0 ); VdbeBranchTaken(res!=0,2); if( res ){ goto jump_to_p2; + }else if( eqOnly ){ + assert( pOp[1].opcode==OP_IdxLT || pOp[1].opcode==OP_IdxGT ); + pOp++; /* Skip the OP_IdxLt or OP_IdxGT that follows */ } break; } diff --git a/src/wherecode.c b/src/wherecode.c index 644c1aa0a..87db0e0a2 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -1026,7 +1026,6 @@ Bitmask sqlite3WhereCodeOneLoopStart( 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 eqOnly; /* True if uses only == */ int start_constraints; /* Start of range is constrained */ int nConstraint; /* Number of constraint terms */ Index *pIdx; /* The index we will be using */ @@ -1096,8 +1095,6 @@ Bitmask sqlite3WhereCodeOneLoopStart( } } assert( pRangeEnd==0 || (pRangeEnd->wtFlags & TERM_VNULL)==0 ); - eqOnly = nEq>0 && (pLoop->wsFlags & WHERE_COLUMN_RANGE)==0 - && (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)==0; /* If we are doing a reverse order scan on an ascending index, or ** a forward order scan on a descending index, interchange the @@ -1170,7 +1167,6 @@ Bitmask sqlite3WhereCodeOneLoopStart( VdbeCoverageIf(v, op==OP_SeekGE); testcase( op==OP_SeekGE ); VdbeCoverageIf(v, op==OP_SeekLE); testcase( op==OP_SeekLE ); VdbeCoverageIf(v, op==OP_SeekLT); testcase( op==OP_SeekLT ); - if( eqOnly ) sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ); /* Load the value for the inequality constraint at the end of the ** range (if any). @@ -1206,11 +1202,6 @@ Bitmask sqlite3WhereCodeOneLoopStart( /* Check if the index cursor is past the end of the range. */ if( nConstraint ){ - if( eqOnly ){ - int bx = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp2(v, OP_Goto, 0, bx+2); - pLevel->p2 = bx+1; - } op = aEndOp[bRev*2 + endEq]; sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint); testcase( op==OP_IdxGT ); VdbeCoverageIf(v, op==OP_IdxGT ); |