aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordrh <drh@noemail.net>2016-01-30 02:10:38 +0000
committerdrh <drh@noemail.net>2016-01-30 02:10:38 +0000
commit8ea0056d5c1da937377e28e38c8bbccf1a8053e2 (patch)
treeb37706b2f1493deb1b20826ed8eaa23a3602088a /src
parent65875f37bc26aa5ebda3f13ebf0571c2618d90db (diff)
parentb17020265b4f9ff369845e0fe1adc92c5858b975 (diff)
downloadsqlite-8ea0056d5c1da937377e28e38c8bbccf1a8053e2.tar.gz
sqlite-8ea0056d5c1da937377e28e38c8bbccf1a8053e2.zip
Make use of covering indexes in the OR optimization.
FossilOrigin-Name: 9de3d7123007636aa97da1c70bc34344b0391078
Diffstat (limited to 'src')
-rw-r--r--src/select.c5
-rw-r--r--src/vdbe.c25
-rw-r--r--src/vdbeInt.h6
-rw-r--r--src/vdbeaux.c25
-rw-r--r--src/wherecode.c51
5 files changed, 100 insertions, 12 deletions
diff --git a/src/select.c b/src/select.c
index ea4298e67..891b12354 100644
--- a/src/select.c
+++ b/src/select.c
@@ -2870,10 +2870,11 @@ static int multiSelectOrderBy(
** to the right and the left are evaluated, they use the correct
** collation.
*/
- aPermute = sqlite3DbMallocRaw(db, sizeof(int)*nOrderBy);
+ aPermute = sqlite3DbMallocRaw(db, sizeof(int)*(nOrderBy + 1));
if( aPermute ){
struct ExprList_item *pItem;
- for(i=0, pItem=pOrderBy->a; i<nOrderBy; i++, pItem++){
+ aPermute[0] = nOrderBy;
+ for(i=1, pItem=pOrderBy->a; i<=nOrderBy; i++, pItem++){
assert( pItem->u.x.iOrderByCol>0 );
assert( pItem->u.x.iOrderByCol<=p->pEList->nExpr );
aPermute[i] = pItem->u.x.iOrderByCol - 1;
diff --git a/src/vdbe.c b/src/vdbe.c
index cfff895c3..b7c0258ce 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -2067,11 +2067,14 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
** The permutation is only valid until the next OP_Compare that has
** the OPFLAG_PERMUTE bit set in P5. Typically the OP_Permutation should
** occur immediately prior to the OP_Compare.
+**
+** The first integer in the P4 integer array is the length of the array
+** and does not become part of the permutation.
*/
case OP_Permutation: {
assert( pOp->p4type==P4_INTARRAY );
assert( pOp->p4.ai );
- aPermute = pOp->p4.ai;
+ aPermute = pOp->p4.ai + 1;
break;
}
@@ -2376,12 +2379,16 @@ case OP_Column: {
u32 t; /* A type code from the record header */
Mem *pReg; /* PseudoTable input register */
+ pC = p->apCsr[pOp->p1];
p2 = pOp->p2;
+
+ /* If the cursor cache is stale, bring it up-to-date */
+ rc = sqlite3VdbeCursorMoveto(&pC, &p2);
+
assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
pDest = &aMem[pOp->p3];
memAboutToChange(p, pDest);
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( p2<pC->nField );
aOffset = pC->aOffset;
@@ -2390,8 +2397,6 @@ case OP_Column: {
assert( pC->eCurType!=CURTYPE_SORTER );
pCrsr = pC->uc.pCursor;
- /* If the cursor cache is stale, bring it up-to-date */
- rc = sqlite3VdbeCursorMoveto(pC);
if( rc ) goto abort_due_to_error;
if( pC->cacheStatus!=p->cacheCtr ){
if( pC->nullRow ){
@@ -3846,7 +3851,7 @@ seek_not_found:
break;
}
-/* Opcode: Seek P1 P2 * * *
+/* Opcode: Seek P1 P2 P3 P4 *
** Synopsis: intkey=r[P2]
**
** P1 is an open table cursor and P2 is a rowid integer. Arrange
@@ -3855,6 +3860,13 @@ seek_not_found:
** 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.
+**
+** P4 may contain an array of integers (type P4_INTARRAY) containing
+** one entry for each column in the table P1 is open on. If so, then
+** parameter P3 is a cursor open on a database index. If array entry
+** a[i] is non-zero, then reading column (a[i]-1) from cursor P3 is
+** equivalent to performing the deferred seek and then reading column i
+** from P1.
*/
case OP_Seek: { /* in2 */
VdbeCursor *pC;
@@ -3869,6 +3881,9 @@ case OP_Seek: { /* in2 */
pIn2 = &aMem[pOp->p2];
pC->movetoTarget = sqlite3VdbeIntValue(pIn2);
pC->deferredMoveto = 1;
+ assert( pOp->p4type==P4_INTARRAY || pOp->p4.ai==0 );
+ pC->aAltMap = pOp->p4.ai;
+ pC->pAltCursor = p->apCsr[pOp->p3];
break;
}
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index b231cf908..85c1a5451 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -74,6 +74,7 @@ typedef struct AuxData AuxData;
** * A virtual table
** * A one-row "pseudotable" stored in a single register
*/
+typedef struct VdbeCursor VdbeCursor;
struct VdbeCursor {
u8 eCurType; /* One of the CURTYPE_* values above */
i8 iDb; /* Index of cursor database in db->aDb[] (or -1) */
@@ -100,6 +101,8 @@ struct VdbeCursor {
int seekResult; /* Result of previous sqlite3BtreeMoveto() */
i64 seqCount; /* Sequence counter */
i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */
+ VdbeCursor *pAltCursor; /* Associated index cursor from which to read */
+ int *aAltMap; /* Mapping from table to index column numbers */
#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
u64 maskUsed; /* Mask of columns used by this cursor */
#endif
@@ -124,7 +127,6 @@ struct VdbeCursor {
** static element declared in the structure. nField total array slots for
** aType[] and nField+1 array slots for aOffset[] */
};
-typedef struct VdbeCursor VdbeCursor;
/*
** When a sub-program is executed (OP_Program), a structure of this type
@@ -423,7 +425,7 @@ struct Vdbe {
void sqlite3VdbeError(Vdbe*, const char *, ...);
void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*);
void sqliteVdbePopStack(Vdbe*,int);
-int sqlite3VdbeCursorMoveto(VdbeCursor*);
+int sqlite3VdbeCursorMoveto(VdbeCursor**, int*);
int sqlite3VdbeCursorRestore(VdbeCursor*);
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
void sqlite3VdbePrintOp(FILE*, int, Op*);
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 3bbc5aad5..aaba2d3d0 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -1288,7 +1288,21 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
}
#endif
case P4_INTARRAY: {
- sqlite3_snprintf(nTemp, zTemp, "intarray");
+ int i, j;
+ int *ai = pOp->p4.ai;
+ int n = ai[0]; /* The first element of an INTARRAY is always the
+ ** count of the number of elements to follow */
+ zTemp[0] = '[';
+ for(i=j=1; i<n && j<nTemp-7; i++){
+ if( j>1 ) zTemp[j++] = ',';
+ sqlite3_snprintf(nTemp-j, zTemp+j, "%d", ai[i]);
+ j += sqlite3Strlen30(zTemp+j);
+ }
+ if( i<n ){
+ memcpy(zTemp+j, ",...]", 6);
+ }else{
+ memcpy(zTemp+j, "]", 2);
+ }
break;
}
case P4_SUBPROGRAM: {
@@ -3008,9 +3022,16 @@ int sqlite3VdbeCursorRestore(VdbeCursor *p){
** If the cursor is already pointing to the correct row and that row has
** not been deleted out from under the cursor, then this routine is a no-op.
*/
-int sqlite3VdbeCursorMoveto(VdbeCursor *p){
+int sqlite3VdbeCursorMoveto(VdbeCursor **pp, int *piCol){
+ VdbeCursor *p = *pp;
if( p->eCurType==CURTYPE_BTREE ){
if( p->deferredMoveto ){
+ int iMap;
+ if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 ){
+ *pp = p->pAltCursor;
+ *piCol = iMap - 1;
+ return SQLITE_OK;
+ }
return handleDeferredMoveto(p);
}
if( sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){
diff --git a/src/wherecode.c b/src/wherecode.c
index 4fd7399ef..fea397c54 100644
--- a/src/wherecode.c
+++ b/src/wherecode.c
@@ -747,6 +747,55 @@ static void codeCursorHint(
#endif /* SQLITE_ENABLE_CURSOR_HINTS */
/*
+** Cursor iCur is open on an intkey b-tree (a table). Register iRowid contains
+** a rowid value just read from cursor iIdxCur, open on index pIdx. This
+** function generates code to do a deferred seek of cursor iCur to the
+** rowid stored in register iRowid.
+**
+** Normally, this is just:
+**
+** OP_Seek $iCur $iRowid
+**
+** However, if the scan currently being coded is a branch of an OR-loop and
+** the statement currently being coded is a SELECT, then P3 of the OP_Seek
+** is set to iIdxCur and P4 is set to point to an array of integers
+** containing one entry for each column of the table cursor iCur is open
+** on. For each table column, if the column is the i'th column of the
+** index, then the corresponding array entry is set to (i+1). If the column
+** does not appear in the index at all, the array entry is set to 0.
+*/
+static void codeDeferredSeek(
+ WhereInfo *pWInfo, /* Where clause context */
+ Index *pIdx, /* Index scan is using */
+ int iCur, /* Cursor for IPK b-tree */
+ int iRowid, /* Register containing rowid to seek to */
+ int iIdxCur /* Index cursor */
+){
+ Parse *pParse = pWInfo->pParse; /* Parse context */
+ Vdbe *v = pParse->pVdbe; /* Vdbe to generate code within */
+
+ assert( iIdxCur>0 );
+ assert( pIdx->aiColumn[pIdx->nColumn-1]==-1 );
+
+ sqlite3VdbeAddOp3(v, OP_Seek, iCur, iRowid, iIdxCur);
+ if( (pWInfo->wctrlFlags & WHERE_FORCE_TABLE)
+ && sqlite3ParseToplevel(pParse)->writeMask==0
+ ){
+ int i;
+ Table *pTab = pIdx->pTable;
+ int *ai = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*(pTab->nCol+1));
+ if( ai ){
+ ai[0] = pTab->nCol;
+ for(i=0; i<pIdx->nColumn-1; i++){
+ assert( pIdx->aiColumn[i]<pTab->nCol );
+ if( pIdx->aiColumn[i]>=0 ) ai[pIdx->aiColumn[i]+1] = i+1;
+ }
+ sqlite3VdbeChangeP4(v, -1, (char*)ai, P4_INTARRAY);
+ }
+ }
+}
+
+/*
** Generate code for the start of the iLevel-th loop in the WHERE clause
** implementation described by pWInfo.
*/
@@ -1232,7 +1281,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, iRowidReg);
VdbeCoverage(v);
}else{
- sqlite3VdbeAddOp2(v, OP_Seek, iCur, iRowidReg); /* Deferred seek */
+ codeDeferredSeek(pWInfo, pIdx, iCur, iRowidReg, iIdxCur);
}
}else if( iCur!=iIdxCur ){
Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);