/*
** Generate code that will evaluate all == and IN constraints for an
-** index.
+** index scan.
**
** For example, consider table t1(a,b,c,d,e,f) with index i1(a,b,c).
** Suppose the WHERE clause is this: a==5 AND b IN (1,2,3) AND c>5 AND c<10
** The only thing it does is allocate the pLevel->iMem memory cell and
** compute the affinity string.
**
-** This routine always allocates at least one memory cell and returns
-** the index of that memory cell. The code that
-** calls this routine will use that memory cell to store the termination
+** The nExtraReg parameter is 0 or 1. It is 0 if all WHERE clause constraints
+** are == or IN and are covered by the nEq. nExtraReg is 1 if there is
+** an inequality constraint (such as the "c>=5 AND c<10" in the example) that
+** occurs after the nEq quality constraints.
+**
+** This routine allocates a range of nEq+nExtraReg memory cells and returns
+** the index of the first memory cell in that range. The code that
+** calls this routine will use that memory range to store keys for
+** start and termination conditions of the loop.
** key value of the loop. If one or more IN operators appear, then
** this routine allocates an additional nEq memory cells for internal
** use.
int nExtraReg, /* Number of extra registers to allocate */
char **pzAff /* OUT: Set to point to affinity string */
){
- int nEq; /* The number of == or IN constraints to code */
+ u16 nEq; /* The number of == or IN constraints to code */
+ u16 nSkip; /* Number of left-most columns to skip */
Vdbe *v = pParse->pVdbe; /* The vm under construction */
Index *pIdx; /* The index being used for this loop */
WhereTerm *pTerm; /* A single constraint term */
pLoop = pLevel->pWLoop;
assert( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 );
nEq = pLoop->u.btree.nEq;
+ nSkip = pLoop->u.btree.nSkip;
pIdx = pLoop->u.btree.pIndex;
assert( pIdx!=0 );
pParse->db->mallocFailed = 1;
}
+ if( nSkip ){
+ int iIdxCur = pLevel->iIdxCur;
+ sqlite3VdbeAddOp2(v, (bRev?OP_Last:OP_Rewind), iIdxCur, pLevel->addrNxt);
+ pLevel->addrSkip = sqlite3VdbeCurrentAddr(v);
+ pLevel->opSkip = bRev ? OP_SeekLt : OP_SeekGt;
+ pLevel->p3Skip = regBase;
+ pLevel->p4Skip = nSkip;
+ for(j=0; j<nSkip; j++){
+ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, j, regBase+j);
+ assert( pIdx->aiColumn[j]>=0 );
+ VdbeComment((v, "%s", pIdx->pTable->aCol[pIdx->aiColumn[j]].zName));
+ }
+ }
+
/* Evaluate the equality constraints
*/
assert( zAff==0 || (int)strlen(zAff)>=nEq );
- for(j=0; j<nEq; j++){
+ for(j=nSkip; j<nEq; j++){
int r1;
pTerm = pLoop->aLTerm[j];
assert( pTerm!=0 );
- /* The following true for indices with redundant columns.
+ /* The following testcase is true for indices with redundant columns.
** Ex: CREATE INDEX i1 ON t1(a,b,a); SELECT * FROM t1 WHERE a=0 AND b=0; */
testcase( (pTerm->wtFlags & TERM_CODED)!=0 );
testcase( pTerm->wtFlags & TERM_VIRTUAL );
*/
static char *explainIndexRange(sqlite3 *db, WhereLoop *pLoop, Table *pTab){
Index *pIndex = pLoop->u.btree.pIndex;
- int nEq = pLoop->u.btree.nEq;
+ u16 nEq = pLoop->u.btree.nEq;
+ u16 nSkip = pLoop->u.btree.nSkip;
int i, j;
Column *aCol = pTab->aCol;
i16 *aiColumn = pIndex->aiColumn;
sqlite3StrAccumAppend(&txt, " (", 2);
for(i=0; i<nEq; i++){
char *z = (i==pIndex->nKeyCol ) ? "rowid" : aCol[aiColumn[i]].zName;
- explainAppendTerm(&txt, i, z, "=");
+ if( i>=nSkip ){
+ explainAppendTerm(&txt, i, z, "=");
+ }else{
+ if( i ) sqlite3StrAccumAppend(&txt, " AND ", 5);
+ sqlite3StrAccumAppend(&txt, "ANY(", 4);
+ sqlite3StrAccumAppend(&txt, z, -1);
+ sqlite3StrAccumAppend(&txt, ")", 1);
+ }
}
j = i;
OP_IdxGE, /* 1: (end_constraints && !bRev) */
OP_IdxLT /* 2: (end_constraints && bRev) */
};
- int nEq = pLoop->u.btree.nEq; /* Number of == or IN terms */
- int isMinQuery = 0; /* If this is an optimized SELECT min(x).. */
+ u16 nEq = pLoop->u.btree.nEq; /* Number of == or IN terms */
+ u16 nSkip = pLoop->u.btree.nSkip; /* Number of left index terms to skip */
+ 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 */
pIdx = pLoop->u.btree.pIndex;
iIdxCur = pLevel->iIdxCur;
+ assert( nEq>=nSkip );
/* If this loop satisfies a sort order (pOrderBy) request that
** was passed to this function to implement a "SELECT min(x) ..."
&& (pWInfo->bOBSat!=0)
&& (pIdx->nKeyCol>nEq)
){
- /* assert( pOrderBy->nExpr==1 ); */
- /* assert( pOrderBy->a[0].pExpr->iColumn==pIdx->aiColumn[nEq] ); */
+ assert( nSkip==0 );
isMinQuery = 1;
nExtraReg = 1;
}
sqlite3ExplainBegin(v);
for(i=0; i<p->nLTerm; i++){
WhereTerm *pTerm = p->aLTerm[i];
+ if( pTerm==0 ) continue;
sqlite3ExplainPrintf(v, " (%d) #%-2d ", i+1, (int)(pTerm-pWC->a));
sqlite3ExplainPush(v);
whereExplainTerm(v, pTerm);
WhereScan scan; /* Iterator for WHERE terms */
Bitmask saved_prereq; /* Original value of pNew->prereq */
u16 saved_nLTerm; /* Original value of pNew->nLTerm */
- int saved_nEq; /* Original value of pNew->u.btree.nEq */
+ u16 saved_nEq; /* Original value of pNew->u.btree.nEq */
+ u16 saved_nSkip; /* Original value of pNew->u.btree.nSkip */
u32 saved_wsFlags; /* Original value of pNew->wsFlags */
LogEst saved_nOut; /* Original value of pNew->nOut */
int iCol; /* Index of the column in the table */
pTerm = whereScanInit(&scan, pBuilder->pWC, pSrc->iCursor, iCol,
opMask, pProbe);
saved_nEq = pNew->u.btree.nEq;
+ saved_nSkip = pNew->u.btree.nSkip;
saved_nLTerm = pNew->nLTerm;
saved_wsFlags = pNew->wsFlags;
saved_prereq = pNew->prereq;
saved_nOut = pNew->nOut;
pNew->rSetup = 0;
rLogSize = estLog(sqlite3LogEst(pProbe->aiRowEst[0]));
+ if( pTerm==0
+ && saved_nEq==saved_nSkip
+ && saved_nEq+1<pProbe->nKeyCol
+ && pProbe->aiRowEst[saved_nEq+1]>50
+ ){
+ pNew->u.btree.nEq++;
+ pNew->u.btree.nSkip++;
+ pNew->aLTerm[pNew->nLTerm++] = 0;
+ pNew->wsFlags |= WHERE_SKIP_SCAN;
+ whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nInMul);
+ }
for(; rc==SQLITE_OK && pTerm!=0; pTerm = whereScanNext(&scan)){
int nIn = 0;
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
}
pNew->prereq = saved_prereq;
pNew->u.btree.nEq = saved_nEq;
+ pNew->u.btree.nSkip = saved_nSkip;
pNew->wsFlags = saved_wsFlags;
pNew->nOut = saved_nOut;
pNew->nLTerm = saved_nLTerm;
if( pTerm->prereqRight & pNew->maskSelf ) continue;
if( termCanDriveIndex(pTerm, pSrc, 0) ){
pNew->u.btree.nEq = 1;
+ pNew->u.btree.nSkip = 0;
pNew->u.btree.pIndex = 0;
pNew->nLTerm = 1;
pNew->aLTerm[0] = pTerm;
continue; /* Partial index inappropriate for this query */
}
pNew->u.btree.nEq = 0;
+ pNew->u.btree.nSkip = 0;
pNew->nLTerm = 0;
pNew->iSortIdx = 0;
pNew->rSetup = 0;
/* Skip over == and IS NULL terms */
if( j<pLoop->u.btree.nEq
+ && pLoop->u.btree.nSkip==0
&& ((i = pLoop->aLTerm[j]->eOperator) & (WO_EQ|WO_ISNULL))!=0
){
if( i & WO_ISNULL ){
pWC = &pWInfo->sWC;
pLoop = pBuilder->pNew;
pLoop->wsFlags = 0;
+ pLoop->u.btree.nSkip = 0;
pTerm = findTerm(pWC, iCur, -1, 0, WO_EQ, 0);
if( pTerm ){
pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_IPK|WHERE_ONEROW;
VdbeNoopComment((v, "End WHERE-core"));
sqlite3ExprCacheClear(pParse);
for(i=pWInfo->nLevel-1; i>=0; i--){
+ int addr;
pLevel = &pWInfo->a[i];
pLoop = pLevel->pWLoop;
sqlite3VdbeResolveLabel(v, pLevel->addrCont);
sqlite3DbFree(db, pLevel->u.in.aInLoop);
}
sqlite3VdbeResolveLabel(v, pLevel->addrBrk);
+ if( pLevel->addrSkip ){
+ addr = sqlite3VdbeAddOp4Int(v, pLevel->opSkip, pLevel->iIdxCur, 0,
+ pLevel->p3Skip, pLevel->p4Skip);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrSkip);
+ sqlite3VdbeJumpHere(v, pLevel->addrSkip-1);
+ sqlite3VdbeJumpHere(v, addr);
+ }
if( pLevel->iLeftJoin ){
- int addr;
addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin);
assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0
|| (pLoop->wsFlags & WHERE_INDEXED)!=0 );