aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/btree.c4
-rw-r--r--src/btree.h2
-rw-r--r--src/sqliteInt.h34
-rw-r--r--src/vdbe.c43
-rw-r--r--src/wherecode.c9
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 );