*/
static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
if( ALWAYS(pWInfo) ){
- int i;
- for(i=0; i<pWInfo->nLevel; i++){
- sqlite3_index_info *pInfo = pWInfo->a[i].pIdxInfo;
- if( pInfo ){
- /* assert( pInfo->needToFreeIdxStr==0 || db->mallocFailed ); */
- if( pInfo->needToFreeIdxStr ){
- sqlite3_free(pInfo->idxStr);
+ whereClauseClear(pWInfo->pWC);
+ while( pWInfo->pLoops ){
+ WhereLoop *p = pWInfo->pLoops;
+ pWInfo->pLoops = p->pNextLoop;
+ whereLoopDelete(db, p);
+ }
+ sqlite3DbFree(db, pWInfo);
+ }
+}
+
+/*
+** Insert or replace a WhereLoop entry using the template supplied.
+**
+** An existing WhereLoop entry might be overwritten if the new template
+** is better and has fewer dependencies. Or the template will be ignored
+** and no insert will occur if an existing WhereLoop is faster and has
+** fewer dependencies than the template. Otherwise a new WhereLoop is
+** added based on the template.
+**
+** If pBuilder->pBest is not NULL then we only care about the very
+** best template and that template should be stored in pBuilder->pBest.
+** If pBuilder->pBest is NULL then a list of the best templates are stored
+** in pBuilder->pWInfo->pLoops.
+**
+** When accumulating multiple loops (when pBuilder->pBest is NULL) we
+** still might overwrite similar loops with the new template if the
+** template is better. Loops may be overwritten if the following
+** conditions are met:
+**
+** (1) They have the same iTab.
+** (2) They have the same iSortIdx.
+** (3) The template has same or fewer dependencies than the current loop
+** (4) The template has the same or lower cost than the current loop
+** (5) The template uses more terms of the same index but has no additional
+** dependencies
+*/
+static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){
+ WhereLoop **ppPrev, *p, *pNext = 0, *pToFree = 0;
+ WhereTerm **paTerm = 0;
+ sqlite3 *db = pBuilder->db;
+ WhereInfo *pWInfo = pBuilder->pWInfo;
+
+ /* If pBuilder->pBest is defined, then only keep track of the single
+ ** best WhereLoop. pBuilder->pBest->maskSelf==0 indicates that no
+ ** prior WhereLoops have been evaluated and that the current pTemplate
+ ** is therefore the first and hence the best and should be retained.
+ */
+ if( (p = pBuilder->pBest)!=0 ){
+ if( p->maskSelf!=0 ){
+ if( p->rRun+p->rSetup < pTemplate->rRun+pTemplate->rSetup ){
+ goto whereLoopInsert_noop;
+ }
+ if( p->rRun+p->rSetup == pTemplate->rRun+pTemplate->rSetup
+ && p->prereq <= pTemplate->prereq ){
+ goto whereLoopInsert_noop;
+ }
+ }
+ *p = *pTemplate;
+ p->aTerm = 0;
+ p->u.vtab.needFree = 0;
+#if WHERETRACE_ENABLED
+ if( sqlite3WhereTrace & 0x8 ){
+ sqlite3DebugPrintf("ins-best: ");
+ whereLoopPrint(pTemplate, pBuilder->pTabList);
+ }
+#endif
+ return SQLITE_OK;
+ }
+
+ /* Search for an existing WhereLoop to overwrite, or which takes
+ ** priority over pTemplate.
+ */
+ for(ppPrev=&pWInfo->pLoops, p=*ppPrev; p; ppPrev=&p->pNextLoop, p=*ppPrev){
+ if( p->iTab!=pTemplate->iTab || p->iSortIdx!=pTemplate->iSortIdx ) continue;
+ if( p->nTerm<pTemplate->nTerm
+ && (p->wsFlags & WHERE_INDEXED)!=0
+ && (pTemplate->wsFlags & WHERE_INDEXED)!=0
+ && p->u.btree.pIndex==pTemplate->u.btree.pIndex
+ && p->prereq==pTemplate->prereq
+ ){
+ /* Overwrite an existing WhereLoop with an similar one that uses
+ ** more terms of the index */
+ pNext = p->pNextLoop;
+ whereLoopClear(db, p);
+ break;
+ }
+ if( (p->prereq & pTemplate->prereq)==p->prereq
+ && p->rSetup<=pTemplate->rSetup
+ && p->rRun<=pTemplate->rRun
+ ){
+ /* Already holding an equal or better WhereLoop.
+ ** Return without changing or adding anything */
+ goto whereLoopInsert_noop;
+ }
+ if( (p->prereq & pTemplate->prereq)==pTemplate->prereq
+ && p->rSetup>=pTemplate->rSetup
+ && p->rRun>=pTemplate->rRun
+ ){
+ /* Overwrite an existing WhereLoop with a better one */
+ pNext = p->pNextLoop;
+ whereLoopClear(db, p);
+ break;
+ }
+ }
+
+ /* If we reach this point it means that either p[] should be overwritten
+ ** with pTemplate[] if p[] exists, or if p==NULL then allocate a new
+ ** WhereLoop and insert it.
+ */
+#if WHERETRACE_ENABLED
+ if( sqlite3WhereTrace & 0x8 ){
+ if( p!=0 ){
+ sqlite3DebugPrintf("ins-del: ");
+ whereLoopPrint(p, pBuilder->pTabList);
+ }
+ sqlite3DebugPrintf("ins-new: ");
+ whereLoopPrint(pTemplate, pBuilder->pTabList);
+ }
+#endif
+ if( p==0 ){
+ p = pToFree = sqlite3DbMallocRaw(db, sizeof(WhereLoop));
+ if( p==0 ) return SQLITE_NOMEM;
+ }
+ if( pTemplate->nTerm ){
+ paTerm = sqlite3DbMallocRaw(db, pTemplate->nTerm*sizeof(p->aTerm[0]));
+ if( paTerm==0 ){
+ sqlite3DbFree(db, pToFree);
+ return SQLITE_NOMEM;
+ }
+ }
+ *p = *pTemplate;
+ p->pNextLoop = pNext;
+ *ppPrev = p;
+ p->aTerm = paTerm;
+ if( p->nTerm ){
+ memcpy(p->aTerm, pTemplate->aTerm, p->nTerm*sizeof(p->aTerm[0]));
+ }
+ if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){
+ Index *pIndex = p->u.btree.pIndex;
+ if( pIndex && pIndex->tnum==0 ){
+ p->u.btree.pIndex = 0;
+ }
+ }else{
+ pTemplate->u.vtab.needFree = 0;
+ }
+ return SQLITE_OK;
+
+ /* Jump here if the insert is a no-op */
+whereLoopInsert_noop:
+#if WHERETRACE_ENABLED
+ if( sqlite3WhereTrace & 0x8 ){
+ sqlite3DebugPrintf("ins-noop: ");
+ whereLoopPrint(pTemplate, pBuilder->pTabList);
+ }
+#endif
+ return SQLITE_OK;
+}
+
+/*
+** We have so far matched pBuilder->pNew->u.btree.nEq terms of the index pIndex.
+** Try to match one more.
+**
+** If pProbe->tnum==0, that means pIndex is a fake index used for the
+** INTEGER PRIMARY KEY.
+*/
+static int whereLoopAddBtreeIndex(
+ WhereLoopBuilder *pBuilder, /* The WhereLoop factory */
+ struct SrcList_item *pSrc, /* FROM clause term being analyzed */
+ Index *pProbe, /* An index on pSrc */
+ int nInMul /* Number of iterations due to IN */
+){
+ sqlite3 *db; /* Database connection malloc context */
+ WhereLoop *pNew; /* Template WhereLoop under construction */
+ WhereTerm *pTerm; /* A WhereTerm under consideration */
+ int opMask; /* Valid operators for constraints */
+ WhereScan scan; /* Iterator for WHERE terms */
+ WhereLoop savedLoop; /* Saved original content of pNew[] */
+ int iCol; /* Index of the column in the table */
+ int rc = SQLITE_OK; /* Return code */
+ tRowcnt iRowEst; /* Estimated index selectivity */
+ double rLogSize; /* Logarithm of table size */
+ WhereTerm *pTop, *pBtm; /* Top and bottom range constraints */
+
+ db = pBuilder->db;
+ pNew = pBuilder->pNew;
+ if( db->mallocFailed ) return SQLITE_NOMEM;
+
+ assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 );
+ assert( pNew->u.btree.nEq<=pProbe->nColumn );
+ assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 );
+ if( pNew->wsFlags & WHERE_BTM_LIMIT ){
+ opMask = WO_LT|WO_LE;
+ }else if( pProbe->tnum<=0 || (pSrc->jointype & JT_LEFT)!=0 ){
+ opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE;
+ }else{
+ opMask = WO_EQ|WO_IN|WO_ISNULL|WO_GT|WO_GE|WO_LT|WO_LE;
+ }
+ if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
+
+ if( pNew->u.btree.nEq < pProbe->nColumn ){
+ iCol = pProbe->aiColumn[pNew->u.btree.nEq];
+ iRowEst = pProbe->aiRowEst[pNew->u.btree.nEq+1];
+ }else{
+ iCol = -1;
+ iRowEst = 1;
+ }
+ pTerm = whereScanInit(&scan, pBuilder->pWC, pSrc->iCursor, iCol,
+ opMask, pProbe);
+ savedLoop = *pNew;
+ pNew->rSetup = (double)0;
+ rLogSize = estLog(pProbe->aiRowEst[0]);
+ for(; rc==SQLITE_OK && pTerm!=0; pTerm = whereScanNext(&scan)){
+ int nIn = 1;
+ if( pTerm->prereqRight & pNew->maskSelf ) continue;
+ pNew->wsFlags = savedLoop.wsFlags;
+ pNew->u.btree.nEq = savedLoop.u.btree.nEq;
+ pNew->nTerm = savedLoop.nTerm;
+ if( pNew->nTerm>=pBuilder->mxTerm ) break; /* Repeated column in index */
+ pNew->aTerm[pNew->nTerm++] = pTerm;
+ pNew->prereq = (savedLoop.prereq | pTerm->prereqRight) & ~pNew->maskSelf;
+ if( pTerm->eOperator & WO_IN ){
+ Expr *pExpr = pTerm->pExpr;
+ pNew->wsFlags |= WHERE_COLUMN_IN;
+ if( ExprHasProperty(pExpr, EP_xIsSelect) ){
+ /* "x IN (SELECT ...)": Assume the SELECT returns 25 rows */
+ nIn = 25;
+ }else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){
+ /* "x IN (value, value, ...)" */
+ nIn = pExpr->x.pList->nExpr;
+ }
+ pNew->u.btree.nEq++;
+ pNew->nOut = (double)iRowEst * nInMul * nIn;
+ }else if( pTerm->eOperator & (WO_EQ) ){
+ pNew->wsFlags |= WHERE_COLUMN_EQ;
+ if( iCol<0
+ || (pProbe->onError==OE_Abort && nInMul==1
+ && pNew->u.btree.nEq==pProbe->nColumn-1)
+ ){
+ pNew->wsFlags |= WHERE_UNIQUE;
+ }
+ pNew->u.btree.nEq++;
+ pNew->nOut = (double)iRowEst * nInMul;
+ }else if( pTerm->eOperator & (WO_ISNULL) ){
+ pNew->wsFlags |= WHERE_COLUMN_NULL;
+ pNew->u.btree.nEq++;
+ pNew->nOut = (double)iRowEst * nInMul;
+ }else if( pTerm->eOperator & (WO_GT|WO_GE) ){
+ pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT;
+ pBtm = pTerm;
+ pTop = 0;
+ }else if( pTerm->eOperator & (WO_LT|WO_LE) ){
+ pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT;
+ pTop = pTerm;
+ pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ?
+ pNew->aTerm[pNew->nTerm-2] : 0;
+ }
+ pNew->rRun = rLogSize*nIn; /* Cost for nIn binary searches */
+ if( pNew->wsFlags & WHERE_COLUMN_RANGE ){
+ /* Adjust nOut and rRun for STAT3 range values */
+ double rDiv;
+ whereRangeScanEst(pBuilder->pParse, pProbe, pNew->u.btree.nEq,
+ pBtm, pTop, &rDiv);
+ pNew->nOut = savedLoop.nOut/rDiv;
+ }
+#ifdef SQLITE_ENABLE_STAT3
+ if( pNew->u.btree.nEq==1 && pProbe->nSample ){
+ if( (pTerm->eOperator & (WO_EQ|WO_ISNULL))!=0 ){
+ rc = whereEqualScanEst(pBuilder->pParse, pProbe, pTerm->pExpr->pRight,
+ &pNew->nOut);
+ }else if( (pTerm->eOperator & WO_IN)
+ && !ExprHasProperty(pTerm->pExpr, EP_xIsSelect) ){
+ rc = whereInScanEst(pBuilder->pParse, pProbe, pTerm->pExpr->x.pList,
+ &pNew->nOut);
+
+ }
+ }
+#endif
+ if( pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK) ){
+ pNew->rRun += pNew->nOut; /* Unit step cost to reach each row */
+ }else{
+ /* Each row involves a step of the index, then a binary search of
+ ** the main table */
+ pNew->rRun += pNew->nOut*(1 + rLogSize);
+ }
+ /* TBD: Adjust nOut for additional constraints */
+ rc = whereLoopInsert(pBuilder, pNew);
+ if( (pNew->wsFlags & WHERE_TOP_LIMIT)==0
+ && pNew->u.btree.nEq<=pProbe->nColumn
+ && pProbe->zName!=0
+ ){
+ whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nInMul*nIn);
+ }
+ }
+ *pNew = savedLoop;
+ return rc;
+}
+
+/*
+** Return True if it is possible that pIndex might be useful in
+** implementing the ORDER BY clause in pBuilder.
+**
+** Return False if pBuilder does not contain an ORDER BY clause or
+** if there is no way for pIndex to be useful in implementing that
+** ORDER BY clause.
+*/
+static int indexMightHelpWithOrderBy(
+ WhereLoopBuilder *pBuilder,
+ Index *pIndex,
+ int iCursor
+){
+ ExprList *pOB;
+ int iCol;
+ int ii;
+
+ if( (pOB = pBuilder->pOrderBy)==0 ) return 0;
+ iCol = pIndex->aiColumn[0];
+ for(ii=0; ii<pOB->nExpr; ii++){
- Expr *pExpr = pOB->a[ii].pExpr;
++ Expr *pExpr = sqlite3ExprSkipCollate(pOB->a[ii].pExpr);
+ if( pExpr->op!=TK_COLUMN ) return 0;
+ if( pExpr->iTable==iCursor ){
+ if( pExpr->iColumn==iCol ) return 1;
+ return 0;
+ }
+ }
+ return 0;
+}
+
+/*
+** Add all WhereLoop objects a single table of the join were the table
+** is idenfied by pBuilder->pNew->iTab. That table is guaranteed to be
+** a b-tree table, not a virtual table.
+*/
+static int whereLoopAddBtree(
+ WhereLoopBuilder *pBuilder, /* WHERE clause information */
+ Bitmask mExtra /* Extra prerequesites for using this table */
+){
+ Index *pProbe; /* An index we are evaluating */
+ Index sPk; /* A fake index object for the primary key */
+ tRowcnt aiRowEstPk[2]; /* The aiRowEst[] value for the sPk index */
+ int aiColumnPk = -1; /* The aColumn[] value for the sPk index */
+ struct SrcList_item *pSrc; /* The FROM clause btree term to add */
+ WhereLoop *pNew; /* Template WhereLoop object */
+ int rc = SQLITE_OK; /* Return code */
+ int iSortIdx = 1; /* Index number */
+ int b; /* A boolean value */
+ double rSize; /* number of rows in the table */
+ double rLogSize; /* Logarithm of the number of rows in the table */
+
+ pNew = pBuilder->pNew;
+ pSrc = pBuilder->pTabList->a + pNew->iTab;
+ assert( !IsVirtual(pSrc->pTab) );
+
+ if( pSrc->pIndex ){
+ /* An INDEXED BY clause specifies a particular index to use */
+ pProbe = pSrc->pIndex;
+ }else{
+ /* There is no INDEXED BY clause. Create a fake Index object in local
+ ** variable sPk to represent the rowid primary key index. Make this
+ ** fake index the first in a chain of Index objects with all of the real
+ ** indices to follow */
+ Index *pFirst; /* First of real indices on the table */
+ memset(&sPk, 0, sizeof(Index));
+ sPk.nColumn = 1;
+ sPk.aiColumn = &aiColumnPk;
+ sPk.aiRowEst = aiRowEstPk;
+ sPk.onError = OE_Replace;
+ sPk.pTable = pSrc->pTab;
+ aiRowEstPk[0] = pSrc->pTab->nRowEst;
+ aiRowEstPk[1] = 1;
+ pFirst = pSrc->pTab->pIndex;
+ if( pSrc->notIndexed==0 ){
+ /* The real indices of the table are only considered if the
+ ** NOT INDEXED qualifier is omitted from the FROM clause */
+ sPk.pNext = pFirst;
+ }
+ pProbe = &sPk;
+ }
+ rSize = (double)pSrc->pTab->nRowEst;
+ rLogSize = estLog(rSize);
+
+ /* Automatic indexes */
+ if( !pBuilder->pBest
+ && (pBuilder->pParse->db->flags & SQLITE_AutoIndex)!=0
+ && !pSrc->viaCoroutine
+ && !pSrc->notIndexed
+ && !pSrc->isCorrelated
+ ){
+ /* Generate auto-index WhereLoops */
+ WhereClause *pWC = pBuilder->pWC;
+ WhereTerm *pTerm;
+ WhereTerm *pWCEnd = pWC->a + pWC->nTerm;
+ for(pTerm=pWC->a; rc==SQLITE_OK && pTerm<pWCEnd; pTerm++){
+ if( pTerm->prereqRight & pNew->maskSelf ) continue;
+ if( termCanDriveIndex(pTerm, pSrc, 0) ){
+ pNew->u.btree.nEq = 1;
+ pNew->u.btree.pIndex = 0;
+ pNew->nTerm = 1;
+ pNew->aTerm[0] = pTerm;
+ pNew->rSetup = 20*rLogSize*pSrc->pTab->nRowEst;
+ pNew->nOut = (double)10;
+ pNew->rRun = rLogSize + pNew->nOut;
+ pNew->wsFlags = WHERE_TEMP_INDEX;
+ pNew->prereq = mExtra | pTerm->prereqRight;
+ rc = whereLoopInsert(pBuilder, pNew);
+ }
+ }
+ }
+
+ /* Loop over all indices
+ */
+ for(; rc==SQLITE_OK && pProbe; pProbe=pProbe->pNext, iSortIdx++){
+ pNew->u.btree.nEq = 0;
+ pNew->nTerm = 0;
+ pNew->iSortIdx = 0;
+ pNew->rSetup = (double)0;
+ pNew->prereq = mExtra;
+ pNew->u.btree.pIndex = pProbe;
+ b = indexMightHelpWithOrderBy(pBuilder, pProbe, pSrc->iCursor);
+ if( pProbe->tnum<=0 ){
+ /* Integer primary key index */
+ pNew->wsFlags = WHERE_IPK;
+
+ /* Full table scan */
+ pNew->iSortIdx = b ? iSortIdx : 0;
+ pNew->nOut = rSize;
+ pNew->rRun = (rSize + rLogSize)*(3+b); /* 4x penalty for a full-scan */
+ rc = whereLoopInsert(pBuilder, pNew);
+ if( rc ) break;
+ }else{
+ Bitmask m = pSrc->colUsed;
+ int j;
+ for(j=pProbe->nColumn-1; j>=0; j--){
+ int x = pProbe->aiColumn[j];
+ if( x<BMS-1 ){
+ m &= ~(((Bitmask)1)<<x);
}
- sqlite3DbFree(db, pInfo);
}
- if( pWInfo->a[i].plan.wsFlags & WHERE_TEMP_INDEX ){
- Index *pIdx = pWInfo->a[i].plan.u.pIdx;
- if( pIdx ){
- sqlite3DbFree(db, pIdx->zColAff);
- sqlite3DbFree(db, pIdx);
+ pNew->wsFlags = (m==0) ? (WHERE_IDX_ONLY|WHERE_INDEXED) : WHERE_INDEXED;
+
+ /* Full scan via index */
+ if( (m==0 || b)
+ && pProbe->bUnordered==0
+ && (pBuilder->pWC->wctrlFlags & WHERE_ONEPASS_DESIRED)==0
+ && sqlite3GlobalConfig.bUseCis
+ && OptimizationEnabled(pBuilder->pParse->db, SQLITE_CoverIdxScan)
+ ){
+ pNew->iSortIdx = b ? iSortIdx : 0;
+ pNew->nOut = rSize;
+ pNew->rRun = (m==0) ? (rSize + rLogSize)*(1+b) : (rSize*rLogSize);
+ rc = whereLoopInsert(pBuilder, pNew);
+ if( rc ) break;
+ }
+ }
+ rc = whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, 1);
+
+ /* If there was an INDEXED BY clause, then only that one index is
+ ** considered. */
+ if( pSrc->pIndex ) break;
+ }
+ return rc;
+}
+
+/*
+** Add all WhereLoop objects for a table of the join identified by
+** pBuilder->pNew->iTab. That table is guaranteed to be a virtual table.
+*/
+static int whereLoopAddVirtual(
+ WhereLoopBuilder *pBuilder, /* WHERE clause information */
+ Bitmask mExtra /* Extra prerequesites for using this table */
+){
+ Parse *pParse; /* The parsing context */
+ WhereClause *pWC; /* The WHERE clause */
+ struct SrcList_item *pSrc; /* The FROM clause term to search */
+ Table *pTab;
+ sqlite3 *db;
+ sqlite3_index_info *pIdxInfo;
+ struct sqlite3_index_constraint *pIdxCons;
+ struct sqlite3_index_constraint_usage *pUsage;
+ WhereTerm *pTerm;
+ int i, j;
+ int iTerm, mxTerm;
+ int seenIn = 0; /* True if an IN operator is seen */
+ int seenVar = 0; /* True if a non-constant constraint is seen */
+ int iPhase; /* 0: const w/o IN, 1: const, 2: no IN, 2: IN */
+ WhereLoop *pNew;
+ int rc = SQLITE_OK;
+
+ pParse = pBuilder->pParse;
+ db = pParse->db;
+ pWC = pBuilder->pWC;
+ pNew = pBuilder->pNew;
+ pSrc = &pBuilder->pTabList->a[pNew->iTab];
+ pTab = pSrc->pTab;
+ assert( IsVirtual(pTab) );
+ pIdxInfo = allocateIndexInfo(pParse, pWC, pSrc, pBuilder->pOrderBy);
+ if( pIdxInfo==0 ) return SQLITE_NOMEM;
+ pNew->prereq = 0;
+ pNew->rSetup = 0;
+ pNew->wsFlags = WHERE_VIRTUALTABLE;
+ pNew->nTerm = 0;
+ pNew->u.vtab.needFree = 0;
+ pUsage = pIdxInfo->aConstraintUsage;
+
+ for(iPhase=0; iPhase<=3; iPhase++){
+ if( !seenIn && (iPhase&1)!=0 ){
+ iPhase++;
+ if( iPhase>3 ) break;
+ }
+ if( !seenVar && iPhase>1 ) break;
+ pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
+ for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
+ j = pIdxCons->iTermOffset;
+ pTerm = &pWC->a[j];
+ switch( iPhase ){
+ case 0: /* Constants without IN operator */
+ pIdxCons->usable = 0;
+ if( (pTerm->eOperator & WO_IN)!=0 ){
+ seenIn = 1;
+ }else if( pTerm->prereqRight!=0 ){
+ seenVar = 1;
+ }else{
+ pIdxCons->usable = 1;
+ }
+ break;
+ case 1: /* Constants with IN operators */
+ assert( seenIn );
+ pIdxCons->usable = (pTerm->prereqRight==0);
+ break;
+ case 2: /* Variables without IN */
+ assert( seenVar );
+ pIdxCons->usable = (pTerm->eOperator & WO_IN)==0;
+ break;
+ default: /* Variables with IN */
+ assert( seenVar && seenIn );
+ pIdxCons->usable = 1;
+ break;
+ }
+ }
+ memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint);
+ if( pIdxInfo->needToFreeIdxStr ) sqlite3_free(pIdxInfo->idxStr);
+ pIdxInfo->idxStr = 0;
+ pIdxInfo->idxNum = 0;
+ pIdxInfo->needToFreeIdxStr = 0;
+ pIdxInfo->orderByConsumed = 0;
+ /* ((double)2) In case of SQLITE_OMIT_FLOATING_POINT... */
+ pIdxInfo->estimatedCost = SQLITE_BIG_DBL / ((double)2);
+ rc = vtabBestIndex(pParse, pTab, pIdxInfo);
+ if( rc ) goto whereLoopAddVtab_exit;
+ pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
+ pNew->prereq = 0;
+ mxTerm = -1;
+ for(i=0; i<pBuilder->mxTerm; i++) pNew->aTerm[i] = 0;
+ pNew->u.vtab.omitMask = 0;
+ for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
+ if( (iTerm = pUsage[i].argvIndex - 1)>=0 ){
+ if( iTerm>=pBuilder->mxTerm ) break;
+ j = pIdxCons->iTermOffset;
+ if( iTerm>=pIdxInfo->nConstraint
+ || j<0
+ || j>=pWC->nTerm
+ || pNew->aTerm[iTerm]!=0
+ ){
+ rc = SQLITE_ERROR;
+ sqlite3ErrorMsg(pParse, "%s.xBestIndex() malfunction", pTab->zName);
+ goto whereLoopAddVtab_exit;
+ }
+ pTerm = &pWC->a[j];
+ pNew->prereq |= pTerm->prereqRight;
+ pNew->aTerm[iTerm] = pTerm;
+ if( iTerm>mxTerm ) mxTerm = iTerm;
+ if( iTerm<16 && pUsage[i].omit ) pNew->u.vtab.omitMask |= 1<<iTerm;
+ if( (pTerm->eOperator & WO_IN)!=0 ){
+ if( pUsage[i].omit==0 ){
+ /* Do not attempt to use an IN constraint if the virtual table
+ ** says that the equivalent EQ constraint cannot be safely omitted.
+ ** If we do attempt to use such a constraint, some rows might be
+ ** repeated in the output. */
+ break;
+ }
+ /* A virtual table that is constrained by an IN clause may not
+ ** consume the ORDER BY clause because (1) the order of IN terms
+ ** is not necessarily related to the order of output terms and
+ ** (2) Multiple outputs from a single IN value will not merge
+ ** together. */
+ pIdxInfo->orderByConsumed = 0;
}
}
}