From: drh Date: Mon, 3 Jun 2013 20:46:35 +0000 (+0000) Subject: Pull in recent trunk changes. Fix the ORDER BY optimizer so that it is better X-Git-Tag: version-3.8.0~130^2~37 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=45c154ac90fa300d509e8844b345c3e7645e6f17;p=thirdparty%2Fsqlite.git Pull in recent trunk changes. Fix the ORDER BY optimizer so that it is better able to deal with COLLATE clauses. Clean up ambiguities in the descidx1.test script. FossilOrigin-Name: 6bc71dfcf0ef757c5c2b426dd8fddc1e5ae0f598 --- 45c154ac90fa300d509e8844b345c3e7645e6f17 diff --cc manifest index 0a83808515,4c2a451fd1..dba927beb0 --- a/manifest +++ b/manifest @@@ -1,5 -1,5 +1,5 @@@ - C Do\snot\suse\san\sindex\sfullscan\sfor\san\sUPDATE\sor\sDELETE\sor\sif\sdisabled\sby\nsqlite3_test_control()\sor\ssqlite3_config(). - D 2013-06-03T19:17:40.684 -C Fix\sa\stypo\sin\sa\scollating\sfunction\sinside\sthe\se_reindex.test\sscript. -D 2013-06-03T20:39:15.752 ++C Pull\sin\srecent\strunk\schanges.\s\sFix\sthe\sORDER\sBY\soptimizer\sso\sthat\sit\sis\sbetter\nable\sto\sdeal\swith\sCOLLATE\sclauses.\s\sClean\sup\sambiguities\sin\sthe\sdescidx1.test\nscript. ++D 2013-06-03T20:46:35.011 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 5e41da95d92656a5004b03d3576e8b226858a28e F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@@ -224,8 -224,8 +224,8 @@@ F src/sqliteInt.h 3ddccdf8ef912119da269 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c bedc37ec1a6bb9399944024d63f4c769971955a9 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e - F src/tclsqlite.c 2ecec9937e69bc17560ad886da35195daa7261b8 + F src/tclsqlite.c b8835978e853a89bf58de88acc943a5ca94d752e -F src/test1.c 6d2a340eea1d866bf7059894491652a69a7ee802 +F src/test1.c 5f9837aee1a0708ab2dbf67ce0b9c18e658cd36b F src/test2.c 7355101c085304b90024f2261e056cdff13c6c35 F src/test3.c 1c0e5d6f080b8e33c1ce8b3078e7013fdbcd560c F src/test4.c 9b32d22f5f150abe23c1830e2057c4037c45b3df @@@ -289,7 -289,7 +289,7 @@@ F src/vtab.c b05e5f1f4902461ba9f5fc49bb F src/wal.c 436bfceb141b9423c45119e68e444358ee0ed35d F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 F src/walker.c 4fa43583d0a84b48f93b1e88f11adf2065be4e73 - F src/where.c 6226d9912f494a92b51b06c58b5c0eed9b7eb84e -F src/where.c 5c4cbc1e5205d8d534c483fa4495c81513b45dea ++F src/where.c 0082ef59948fc8c28b35226f44e1718cb1b0300a F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6 @@@ -414,7 -414,7 +414,7 @@@ F test/default.test 6faf23ccb3001149243 F test/delete.test a065b05d2ebf60fd16639c579a4adfb7c381c701 F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa F test/delete3.test 555e84a00a99230b7d049d477a324a631126a6ab --F test/descidx1.test 533dcbda614b0463b0ea029527fd27e5a9ab2d66 ++F test/descidx1.test 6d03b44c8538fe0eb4924e19fba10cdd8f3c9240 F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d F test/descidx3.test 09ddbe3f5295f482d2f8b687cf6db8bad7acd9a2 F test/diskfull.test 106391384780753ea6896b7b4f005d10e9866b6e @@@ -1093,7 -1093,7 +1093,7 @@@ F tool/vdbe-compress.tcl f12c884766bd14 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac - P dcbbcb2e6c85780276cb3a392549f70e21e94408 - R 5e83ed1a81fa16225d238fe289659759 -P 3bd5ad095b23102dd3379cb62997cbf23cc67b7a -R 0bab77d0f95310ae1c21cfea10915144 ++P fabb21854e662b1d8e5631e79f828d5322ceb595 4d74fccf02134a998a84097b021ba9d501e34ff0 ++R b746dc544342dfc07463de0423275bdc U drh - Z 8c74536a41fa8c42d526bd323c460f6c -Z 1023ee14390bd42e471d5323a67fa234 ++Z 5182eac11ae0820ee1af6bccc6719c26 diff --cc manifest.uuid index c94f0e1371,e3db3db774..1e5db89b77 --- a/manifest.uuid +++ b/manifest.uuid @@@ -1,1 -1,1 +1,1 @@@ - fabb21854e662b1d8e5631e79f828d5322ceb595 -4d74fccf02134a998a84097b021ba9d501e34ff0 ++6bc71dfcf0ef757c5c2b426dd8fddc1e5ae0f598 diff --cc src/where.c index ee604f0713,e614f4a6d8..57dc7e8aef --- a/src/where.c +++ b/src/where.c @@@ -3784,591 -4938,21 +3784,591 @@@ static void whereLoopDelete(sqlite3 *db */ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){ if( ALWAYS(pWInfo) ){ - int i; - for(i=0; inLevel; 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->nTermnTerm + && (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; iinExpr; 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 && pTermprereqRight & 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( xa[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; inConstraint; 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; imxTerm; i++) pNew->aTerm[i] = 0; + pNew->u.vtab.omitMask = 0; + for(i=0; inConstraint; 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<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; } } } diff --cc test/descidx1.test index c7fab340a5,c7fab340a5..a223664ff3 --- a/test/descidx1.test +++ b/test/descidx1.test @@@ -197,12 -197,12 +197,12 @@@ ifcapable bloblit } {1.0 2.2 2.0 2.1 2.3 3.0 4.0 5.0 6.0} do_test descidx1-4.3 { execsql { -- SELECT d FROM t2 WHERE a>=2; ++ SELECT d FROM t2 WHERE a>=2 ORDER BY a; } } {2.2 2.0 2.1 2.3 3.0 4.0 5.0 6.0} do_test descidx1-4.4 { execsql { -- SELECT d FROM t2 WHERE a>2; ++ SELECT d FROM t2 WHERE a>2 ORDER BY a; } } {3.0 4.0 5.0 6.0} do_test descidx1-4.5 {