diff --git a/manifest b/manifest index 178efcf35c..019544c892 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Work\saround\san\soptimization\sissue\swith\sthe\sMSVC\scompiler\sfor\sARM. -D 2012-10-02T22:54:27.390 +C Further\sattempts\sto\soptimize\sout\sunnecessary\sORDER\sBY\sclauses. +D 2012-10-03T00:25:54.662 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 5f4f26109f9d80829122e0e09f9cda008fa065fb F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -249,7 +249,7 @@ F src/vtab.c d8020c0a0e8ccc490ca449d7e665311b6e9f3ba9 F src/wal.c e1fe8f92a0ea0fef8faa87ec43a127a478589d22 F src/wal.h 29c197540b19044e6cd73487017e5e47a1d3dac6 F src/walker.c 3d75ba73de15e0f8cd0737643badbeb0e002f07b -F src/where.c 69398e95e9c1012ae07ce9ea00f21b0e7bab8df1 +F src/where.c 76de1934899015c71d044093de51b308d608e1e5 F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggnested.test 0be144b453e0622a085fae8665c32f5676708e00 @@ -512,7 +512,7 @@ F test/fuzz2.test 207d0f9d06db3eaf47a6b7bfc835b8e2fc397167 F test/fuzz3.test aec64345184d1662bd30e6a17851ff659d596dc5 F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b F test/fuzz_malloc.test 328f70aaca63adf29b4c6f06505ed0cf57ca7c26 -F test/fuzzer1.test 69cf1036b92fd3b8e1fd65bef4d7ee3f085c28fb +F test/fuzzer1.test a2e93bb1e19513dd6bf9c63d3d7c4673c983ca19 F test/fuzzerfault.test ff2282c81797b6a355f0748d8b54c7287c5d2b25 F test/hook.test 5f3749de6462a6b87b4209b74adf7df5ac2df639 F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4 @@ -635,7 +635,7 @@ F test/notnull.test cc7c78340328e6112a13c3e311a9ab3127114347 F test/null.test a8b09b8ed87852742343b33441a9240022108993 F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394 F test/orderby1.test 4875a2a0a87d81920f3600a3405dc42f233b8c82 -F test/orderby2.test d8fa5991d8948ae1f335c2f91d751e955bfee815 +F test/orderby2.test b799f7c96b5b00daa0aa914c22309423a4b56bc8 F test/oserror.test 50417780d0e0d7cd23cf12a8277bb44024765df3 F test/pager1.test 2163c6ef119f497a71a84137c957c63763e640ab F test/pager2.test 745b911dde3d1f24ae0870bd433dfa83d7c658c1 @@ -1018,7 +1018,10 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/win/sqlite.vsix 67d8a99aceb56384a81b3f30d6c71743146d2cc9 -P abcf6a5d054559ee5a093ba39180c47b4958d9cd -R e2af9a84c8984f66075b1b5542126d99 -U mistachkin -Z 2e5e5eca8b24072e2977c29980c9c33a +P 7d301fdfeec540e4a58f43bff04d219e9f769dc4 +R 062d037eebc135368ed6a1f30b2d19fc +T *branch * qp-enhancements +T *sym-qp-enhancements * +T -sym-trunk * +U drh +Z f0dc62fc9e20f7c7c36c5a741064bb38 diff --git a/manifest.uuid b/manifest.uuid index 6ff004a2db..33ca9fe4da 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7d301fdfeec540e4a58f43bff04d219e9f769dc4 \ No newline at end of file +6744d9a37faffed59b4d5cb96c8671ec46a87ea7 \ No newline at end of file diff --git a/src/where.c b/src/where.c index 16b0c1c710..613215a3f7 100644 --- a/src/where.c +++ b/src/where.c @@ -258,7 +258,7 @@ struct WhereCost { #define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR or x>=EXPR constraint */ #define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and xrCostrCost ) return 1; + if( pProbe->rCost>pBaseline->rCost ) return 0; + if( pProbe->plan.nOBSat>pBaseline->plan.nOBSat ) return 1; + if( pProbe->plan.nRowplan.nRow ) return 1; + return 0; +} + /* ** Initialize a preallocated WhereClause structure. */ @@ -1762,6 +1773,7 @@ static void bestOrClauseIndex(WhereBestIdx *p){ p->cost.rCost = rTotal; p->cost.used = used; p->cost.plan.nRow = nRow; + p->cost.plan.nOBSat = p->i ? p->aLevel[p->i-1].plan.nOBSat : 0; p->cost.plan.wsFlags = flags; p->cost.plan.u.pTerm = pTerm; } @@ -2304,7 +2316,10 @@ static void bestVirtualIndex(WhereBestIdx *p){ } p->cost.plan.u.pVtabIdx = pIdxInfo; if( pIdxInfo->orderByConsumed ){ - p->cost.plan.wsFlags |= WHERE_ORDERBY; + p->cost.plan.wsFlags |= WHERE_ORDERED; + p->cost.plan.nOBSat = nOrderBy; + }else{ + p->cost.plan.nOBSat = p->i ? p->aLevel[p->i-1].plan.nOBSat : 0; } p->cost.plan.nEq = 0; pIdxInfo->nOrderBy = nOrderBy; @@ -2730,8 +2745,10 @@ static int isOrderedColumn(WhereBestIdx *p, int iTab, int iCol, int *pbRev){ if( (pLevel->plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){ return 1; } - if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){ - pIdx = pLevel->plan.u.pIdx; + if( (pLevel->plan.wsFlags & WHERE_ORDERED)==0 ){ + return 0; + } + if( (pIdx = pLevel->plan.u.pIdx)!=0 ){ if( iCol<0 ){ sortOrder = 0; testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 ); @@ -2833,10 +2850,14 @@ static int isSortingIndex( nPriorSat = p->aLevel[p->i-1].plan.nOBSat; if( OptimizationDisabled(db, SQLITE_OrderByIdxJoin) ) return nPriorSat; } - if( p->i==0 || (p->aLevel[p->i-1].plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){ + if( nEqCol==0 ){ + if( p->i && (p->aLevel[p->i-1].plan.wsFlags & WHERE_ORDERED)==0 ){ + return nPriorSat; + } + nEqOneRow = 0; + }else if( p->i==0 || (p->aLevel[p->i-1].plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){ nEqOneRow = nEqCol; }else{ - if( nEqCol==0 ) return nPriorSat; sortOrder = bOuterRev; nEqOneRow = -1; } @@ -3043,18 +3064,16 @@ static void bestBtreeIndex(WhereBestIdx *p){ */ for(; pProbe; pIdx=pProbe=pProbe->pNext){ const tRowcnt * const aiRowEst = pProbe->aiRowEst; - double cost; /* Cost of using pProbe */ - double nRow; /* Estimated number of rows in result set */ + WhereCost pc; /* Cost of using pProbe */ double log10N = (double)1; /* base-10 logarithm of nRow (inexact) */ int bRev = 2; /* 0=forward scan. 1=reverse. 2=undecided */ - int wsFlags = 0; - Bitmask used = 0; + memset(&pc, 0, sizeof(pc)); /* The following variables are populated based on the properties of ** index being evaluated. They are then used to determine the expected ** cost and number of rows returned. ** - ** nEq: + ** pc.plan.nEq: ** Number of equality terms that can be implemented using the index. ** In other words, the number of initial fields in the index that ** are used in == or IN or NOT NULL constraints of the WHERE clause. @@ -3120,7 +3139,6 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** SELECT a, b FROM tbl WHERE a = 1; ** SELECT a, b, c FROM tbl WHERE a = 1; */ - int nEq; /* Number of == or IN terms matching index */ int nOrdered; /* Number of ordered terms matching index */ int bInEst = 0; /* True if "x IN (SELECT...)" seen */ int nInMul = 1; /* Number of distinct equalities to lookup */ @@ -3129,7 +3147,7 @@ static void bestBtreeIndex(WhereBestIdx *p){ int bSort; /* True if external sort required */ int bDist; /* True if index cannot help with DISTINCT */ int bLookup = 0; /* True if not a covering index */ - int nOBSat = 0; /* Number of ORDER BY terms satisfied */ + int nPriorSat; /* ORDER BY terms satisfied by outer loops */ int nOrderBy; /* Number of ORDER BY terms */ WhereTerm *pTerm; /* A single term of the WHERE clause */ #ifdef SQLITE_ENABLE_STAT3 @@ -3137,19 +3155,26 @@ static void bestBtreeIndex(WhereBestIdx *p){ #endif nOrderBy = p->pOrderBy ? p->pOrderBy->nExpr : 0; - bSort = nOrderBy>0 && (p->i==0 || p->aLevel[p->i-1].plan.nOBSati==0 && p->pDistinct!=0; + if( p->i ){ + nPriorSat = pc.plan.nOBSat = p->aLevel[p->i-1].plan.nOBSat; + bSort = nPriorSat0; + bDist = p->pDistinct!=0; + } - /* Determine the values of nEq and nInMul */ - for(nEq=nOrdered=0; nEqnColumn; nEq++){ - int j = pProbe->aiColumn[nEq]; + /* Determine the values of pc.plan.nEq and nInMul */ + for(pc.plan.nEq=nOrdered=0; pc.plan.nEqnColumn; pc.plan.nEq++){ + int j = pProbe->aiColumn[pc.plan.nEq]; pTerm = findTerm(pWC, iCur, j, p->notReady, eqTermMask, pIdx); if( pTerm==0 ) break; - wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ); + pc.plan.wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ); testcase( pTerm->pWC!=pWC ); if( pTerm->eOperator & WO_IN ){ Expr *pExpr = pTerm->pExpr; - wsFlags |= WHERE_COLUMN_IN; + pc.plan.wsFlags |= WHERE_COLUMN_IN; if( ExprHasProperty(pExpr, EP_xIsSelect) ){ /* "x IN (SELECT ...)": Assume the SELECT returns 25 rows */ nInMul *= 25; @@ -3159,15 +3184,15 @@ static void bestBtreeIndex(WhereBestIdx *p){ nInMul *= pExpr->x.pList->nExpr; } }else if( pTerm->eOperator & WO_ISNULL ){ - wsFlags |= WHERE_COLUMN_NULL; - if( nEq==nOrdered ) nOrdered++; - }else if( bSort && nEq==nOrdered && isOrderedTerm(p, pTerm, &bRev) ){ + pc.plan.wsFlags |= WHERE_COLUMN_NULL; + if( pc.plan.nEq==nOrdered ) nOrdered++; + }else if( bSort && pc.plan.nEq==nOrdered && isOrderedTerm(p, pTerm, &bRev) ){ nOrdered++; } #ifdef SQLITE_ENABLE_STAT3 - if( nEq==0 && pProbe->aSample ) pFirstTerm = pTerm; + if( pc.plan.nEq==0 && pProbe->aSample ) pFirstTerm = pTerm; #endif - used |= pTerm->prereqRight; + pc.used |= pTerm->prereqRight; } /* If the index being considered is UNIQUE, and there is an equality @@ -3176,75 +3201,80 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** indicate this to the caller. ** ** Otherwise, if the search may find more than one row, test to see if - ** there is a range constraint on indexed column (nEq+1) that can be + ** there is a range constraint on indexed column (pc.plan.nEq+1) that can be ** optimized using the index. */ - if( nEq==pProbe->nColumn && pProbe->onError!=OE_None ){ - testcase( wsFlags & WHERE_COLUMN_IN ); - testcase( wsFlags & WHERE_COLUMN_NULL ); - if( (wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){ - wsFlags |= WHERE_UNIQUE; + if( pc.plan.nEq==pProbe->nColumn && pProbe->onError!=OE_None ){ + testcase( pc.plan.wsFlags & WHERE_COLUMN_IN ); + testcase( pc.plan.wsFlags & WHERE_COLUMN_NULL ); + if( (pc.plan.wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){ + pc.plan.wsFlags |= WHERE_UNIQUE; if( p->i==0 || (p->aLevel[p->i-1].plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){ - wsFlags |= WHERE_ALL_UNIQUE; + pc.plan.wsFlags |= WHERE_ALL_UNIQUE; } } }else if( pProbe->bUnordered==0 ){ - int j = (nEq==pProbe->nColumn ? -1 : pProbe->aiColumn[nEq]); + int j; + j = (pc.plan.nEq==pProbe->nColumn ? -1 : pProbe->aiColumn[pc.plan.nEq]); if( findTerm(pWC, iCur, j, p->notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){ WhereTerm *pTop, *pBtm; pTop = findTerm(pWC, iCur, j, p->notReady, WO_LT|WO_LE, pIdx); pBtm = findTerm(pWC, iCur, j, p->notReady, WO_GT|WO_GE, pIdx); - whereRangeScanEst(pParse, pProbe, nEq, pBtm, pTop, &rangeDiv); + whereRangeScanEst(pParse, pProbe, pc.plan.nEq, pBtm, pTop, &rangeDiv); if( pTop ){ nBound = 1; - wsFlags |= WHERE_TOP_LIMIT; - used |= pTop->prereqRight; + pc.plan.wsFlags |= WHERE_TOP_LIMIT; + pc.used |= pTop->prereqRight; testcase( pTop->pWC!=pWC ); } if( pBtm ){ nBound++; - wsFlags |= WHERE_BTM_LIMIT; - used |= pBtm->prereqRight; + pc.plan.wsFlags |= WHERE_BTM_LIMIT; + pc.used |= pBtm->prereqRight; testcase( pBtm->pWC!=pWC ); } - wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE); + pc.plan.wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE); } } /* If there is an ORDER BY clause and the index being considered will ** naturally scan rows in the required order, set the appropriate flags - ** in wsFlags. Otherwise, if there is an ORDER BY clause but the index - ** will scan rows in a different order, set the bSort variable. */ + ** in pc.plan.wsFlags. Otherwise, if there is an ORDER BY clause but + ** the index will scan rows in a different order, set the bSort + ** variable. */ assert( bRev>=0 && bRev<=2 ); if( bSort ){ testcase( bRev==0 ); testcase( bRev==1 ); testcase( bRev==2 ); - nOBSat = isSortingIndex(p, pProbe, iCur, nOrdered, - wsFlags, bRev&1, &bRev); - if( nOrderBy==nOBSat ){ - bSort = 0; - wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_ORDERBY; + pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, nOrdered, + pc.plan.wsFlags, bRev&1, &bRev); + if( nPriorSatpDistinct, nEq) - && (wsFlags & WHERE_COLUMN_IN)==0 + && isDistinctIndex(pParse, pWC, pProbe, iCur, p->pDistinct, pc.plan.nEq) + && (pc.plan.wsFlags & WHERE_COLUMN_IN)==0 ){ bDist = 0; - wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_DISTINCT; + pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_DISTINCT; } /* If currently calculating the cost of using an index (not the IPK ** index), determine if all required column data may be obtained without ** using the main table (i.e. if the index is a covering ** index for this query). If it is, set the WHERE_IDX_ONLY flag in - ** wsFlags. Otherwise, set the bLookup variable to true. */ + ** pc.plan.wsFlags. Otherwise, set the bLookup variable to true. */ if( pIdx ){ Bitmask m = pSrc->colUsed; int j; @@ -3255,7 +3285,7 @@ static void bestBtreeIndex(WhereBestIdx *p){ } } if( m==0 ){ - wsFlags |= WHERE_IDX_ONLY; + pc.plan.wsFlags |= WHERE_IDX_ONLY; }else{ bLookup = 1; } @@ -3265,10 +3295,10 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** Estimate the number of rows of output. For an "x IN (SELECT...)" ** constraint, do not let the estimate exceed half the rows in the table. */ - nRow = (double)(aiRowEst[nEq] * nInMul); - if( bInEst && nRow*2>aiRowEst[0] ){ - nRow = aiRowEst[0]/2; - nInMul = (int)(nRow / aiRowEst[nEq]); + pc.plan.nRow = (double)(aiRowEst[pc.plan.nEq] * nInMul); + if( bInEst && pc.plan.nRow*2>aiRowEst[0] ){ + pc.plan.nRow = aiRowEst[0]/2; + nInMul = (int)(pc.plan.nRow / aiRowEst[pc.plan.nEq]); } #ifdef SQLITE_ENABLE_STAT3 @@ -3278,15 +3308,18 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** to get a better estimate on the number of rows based on ** VALUE and how common that value is according to the histogram. */ - if( nRow>(double)1 && nEq==1 && pFirstTerm!=0 && aiRowEst[1]>1 ){ + if( pc.plan.nRow>(double)1 && pc.plan.nEq==1 + && pFirstTerm!=0 && aiRowEst[1]>1 ){ assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 ); if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){ testcase( pFirstTerm->eOperator==WO_EQ ); testcase( pFirstTerm->eOperator==WO_ISNULL ); - whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, &nRow); + whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, + &pc.plan.nRow); }else if( bInEst==0 ){ assert( pFirstTerm->eOperator==WO_IN ); - whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, &nRow); + whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, + &pc.plan.nRow); } } #endif /* SQLITE_ENABLE_STAT3 */ @@ -3294,8 +3327,8 @@ static void bestBtreeIndex(WhereBestIdx *p){ /* Adjust the number of output rows and downward to reflect rows ** that are excluded by range constraints. */ - nRow = nRow/rangeDiv; - if( nRow<1 ) nRow = 1; + pc.plan.nRow = pc.plan.nRow/rangeDiv; + if( pc.plan.nRow<1 ) pc.plan.nRow = 1; /* Experiments run on real SQLite databases show that the time needed ** to do a binary search to locate a row in a table or index is roughly @@ -3310,7 +3343,7 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** So this computation assumes table records are about twice as big ** as index records */ - if( (wsFlags&~WHERE_REVERSE)==WHERE_IDX_ONLY + if( (pc.plan.wsFlags&~(WHERE_REVERSE|WHERE_ORDERED))==WHERE_IDX_ONLY && (pWC->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 && sqlite3GlobalConfig.bUseCis && OptimizationEnabled(pParse->db, SQLITE_CoverIdxScan) @@ -3319,9 +3352,9 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** A full-scan of the index might be a little faster than a full-scan ** of the table, so give this case a cost slightly less than a table ** scan. */ - cost = aiRowEst[0]*3 + pProbe->nColumn; - wsFlags |= WHERE_COVER_SCAN|WHERE_COLUMN_RANGE; - }else if( (wsFlags & WHERE_NOT_FULLSCAN)==0 ){ + pc.rCost = aiRowEst[0]*3 + pProbe->nColumn; + pc.plan.wsFlags |= WHERE_COVER_SCAN|WHERE_COLUMN_RANGE; + }else if( (pc.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){ /* The cost of a full table scan is a number of move operations equal ** to the number of rows in the table. ** @@ -3331,11 +3364,11 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** decision and one which we expect to revisit in the future. But ** it seems to be working well enough at the moment. */ - cost = aiRowEst[0]*4; - wsFlags &= ~WHERE_IDX_ONLY; + pc.rCost = aiRowEst[0]*4; + pc.plan.wsFlags &= ~WHERE_IDX_ONLY; }else{ log10N = estLog(aiRowEst[0]); - cost = nRow; + pc.rCost = pc.plan.nRow; if( pIdx ){ if( bLookup ){ /* For an index lookup followed by a table lookup: @@ -3343,20 +3376,20 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** + nRow steps through the index ** + nRow table searches to lookup the table entry using the rowid */ - cost += (nInMul + nRow)*log10N; + pc.rCost += (nInMul + pc.plan.nRow)*log10N; }else{ /* For a covering index: ** nInMul index searches to find the initial entry ** + nRow steps through the index */ - cost += nInMul*log10N; + pc.rCost += nInMul*log10N; } }else{ /* For a rowid primary key lookup: ** nInMult table searches to find the initial entry for each range ** + nRow steps through the table */ - cost += nInMul*log10N; + pc.rCost += nInMul*log10N; } } @@ -3367,10 +3400,12 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** difference and select C of 3.0. */ if( bSort ){ - cost += nRow*estLog(nRow*(nOrderBy - nOBSat)/nOrderBy)*3; + double m = estLog(pc.plan.nRow*(nOrderBy - pc.plan.nOBSat)/nOrderBy); + m *= (double)(pc.plan.nOBSat ? 2 : 3); + pc.rCost += pc.plan.nRow*m; } if( bDist ){ - cost += nRow*estLog(nRow)*3; + pc.rCost += pc.plan.nRow*estLog(pc.plan.nRow)*3; } /**** Cost of using this index has now been computed ****/ @@ -3391,25 +3426,25 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** might be selected even when there exists an optimal index that has ** no such dependency. */ - if( nRow>2 && cost<=p->cost.rCost ){ + if( pc.plan.nRow>2 && pc.rCost<=p->cost.rCost ){ int k; /* Loop counter */ - int nSkipEq = nEq; /* Number of == constraints to skip */ + int nSkipEq = pc.plan.nEq; /* Number of == constraints to skip */ int nSkipRange = nBound; /* Number of < constraints to skip */ Bitmask thisTab; /* Bitmap for pSrc */ thisTab = getMask(pWC->pMaskSet, iCur); - for(pTerm=pWC->a, k=pWC->nTerm; nRow>2 && k; k--, pTerm++){ + for(pTerm=pWC->a, k=pWC->nTerm; pc.plan.nRow>2 && k; k--, pTerm++){ if( pTerm->wtFlags & TERM_VIRTUAL ) continue; if( (pTerm->prereqAll & p->notValid)!=thisTab ) continue; if( pTerm->eOperator & (WO_EQ|WO_IN|WO_ISNULL) ){ if( nSkipEq ){ - /* Ignore the first nEq equality matches since the index + /* Ignore the first pc.plan.nEq equality matches since the index ** has already accounted for these */ nSkipEq--; }else{ /* Assume each additional equality match reduces the result ** set size by a factor of 10 */ - nRow /= 10; + pc.plan.nRow /= 10; } }else if( pTerm->eOperator & (WO_LT|WO_LE|WO_GT|WO_GE) ){ if( nSkipRange ){ @@ -3423,14 +3458,14 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** more selective intentionally because of the subjective ** observation that indexed range constraints really are more ** selective in practice, on average. */ - nRow /= 3; + pc.plan.nRow /= 3; } }else if( pTerm->eOperator!=WO_NOOP ){ /* Any other expression lowers the output row count by half */ - nRow /= 2; + pc.plan.nRow /= 2; } } - if( nRow<2 ) nRow = 2; + if( pc.plan.nRow<2 ) pc.plan.nRow = 2; } @@ -3440,22 +3475,17 @@ static void bestBtreeIndex(WhereBestIdx *p){ " notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f\n" " used=0x%llx nOrdered=%d nOBSat=%d\n", pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk"), - nEq, nInMul, (int)rangeDiv, bSort, bLookup, wsFlags, - p->notReady, log10N, nRow, cost, used, nOrdered, nOBSat + pc.plan.nEq, nInMul, (int)rangeDiv, bSort, bLookup, pc.plan.wsFlags, + p->notReady, log10N, pc.plan.nRow, pc.rCost, pc.used, nOrdered, + pc.plan.nOBSat )); /* If this index is the best we have seen so far, then record this - ** index and its cost in the pCost structure. + ** index and its cost in the p->cost structure. */ - if( (!pIdx || wsFlags) - && (costcost.rCost || (cost<=p->cost.rCost && nRowcost.plan.nRow)) - ){ - p->cost.rCost = cost; - p->cost.used = used; - p->cost.plan.nRow = nRow; - p->cost.plan.wsFlags = (wsFlags&wsFlagMask); - p->cost.plan.nEq = nEq; - p->cost.plan.nOBSat = nOBSat; + if( (!pIdx || pc.plan.wsFlags) && compareCost(&pc, &p->cost) ){ + p->cost = pc; + p->cost.plan.wsFlags &= wsFlagMask; p->cost.plan.u.pIdx = pIdx; } @@ -3477,17 +3507,15 @@ static void bestBtreeIndex(WhereBestIdx *p){ p->cost.plan.wsFlags |= WHERE_REVERSE; } - assert( p->pOrderBy || (p->cost.plan.wsFlags&WHERE_ORDERBY)==0 ); + assert( p->pOrderBy || (p->cost.plan.wsFlags&WHERE_ORDERED)==0 ); assert( p->cost.plan.u.pIdx==0 || (p->cost.plan.wsFlags&WHERE_ROWID_EQ)==0 ); assert( pSrc->pIndex==0 || p->cost.plan.u.pIdx==0 || p->cost.plan.u.pIdx==pSrc->pIndex ); - WHERETRACE(("best index is: %s\n", - ((p->cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ? "none" : - p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk") - )); + WHERETRACE(("best index is: %s\n", + p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk")); bestOrClauseIndex(p); bestAutomaticIndex(p); @@ -4215,7 +4243,7 @@ static Bitmask codeOneLoopStart( ** this requires some special handling. */ if( (wctrlFlags&WHERE_ORDERBY_MIN)!=0 - && (pLevel->plan.wsFlags&WHERE_ORDERBY) + && (pLevel->plan.wsFlags&WHERE_ORDERED) && (pIdx->nColumn>nEq) ){ /* assert( pOrderBy->nExpr==1 ); */ @@ -5078,8 +5106,8 @@ WhereInfo *sqlite3WhereBegin( ** The NEVER() comes about because rule (2) above prevents ** An indexable full-table-scan from reaching rule (3). ** - ** (4) The plan cost must be lower than prior plans or else the - ** cost must be the same and the number of rows must be lower. + ** (4) The plan cost must be lower than prior plans, where "cost" + ** is defined by the compareCost() function above. */ if( (sWBI.cost.used&sWBI.notValid)==0 /* (1) */ && (bestJ<0 || (notIndexed&m)!=0 /* (2) */ @@ -5087,15 +5115,13 @@ WhereInfo *sqlite3WhereBegin( || (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0) && (nUnconstrained==0 || sWBI.pSrc->pIndex==0 /* (3) */ || NEVER((sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)) - && (bestJ<0 || sWBI.cost.rCostpTab->zName, sWBI.cost.rCost, sWBI.cost.plan.nRow, - sWBI.cost.plan.nOBSat)); + sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags)); bestPlan = sWBI.cost; bestJ = j; } @@ -5105,13 +5131,10 @@ WhereInfo *sqlite3WhereBegin( assert( bestJ>=0 ); assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) ); WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n" - " cost=%.1f, nRow=%.1f, nOBSat=%d wsFlags=0x%08x\n", + " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n", bestJ, pTabList->a[bestJ].pTab->zName, pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow, bestPlan.plan.nOBSat, bestPlan.plan.wsFlags)); - if( (bestPlan.plan.wsFlags & WHERE_ORDERBY)!=0 ){ - pWInfo->nOBSat = pOrderBy->nExpr; - } if( (bestPlan.plan.wsFlags & WHERE_DISTINCT)!=0 ){ assert( pWInfo->eDistinct==0 ); pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; @@ -5160,11 +5183,18 @@ WhereInfo *sqlite3WhereBegin( if( pParse->nErr || db->mallocFailed ){ goto whereBeginError; } + if( nTabList ){ + pLevel--; + pWInfo->nOBSat = pLevel->plan.nOBSat; + }else{ + pWInfo->nOBSat = 0; + } /* If the total query only selects a single row, then the ORDER BY ** clause is irrelevant. */ if( (andFlags & WHERE_UNIQUE)!=0 && pOrderBy ){ + assert( nTabList==0 || (pLevel->plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ); pWInfo->nOBSat = pOrderBy->nExpr; } diff --git a/test/fuzzer1.test b/test/fuzzer1.test index dc8b445ee9..827d6a05f9 100644 --- a/test/fuzzer1.test +++ b/test/fuzzer1.test @@ -1864,5 +1864,3 @@ do_execsql_test 10.3 { } {1 21 41 61 81} finish_test - - diff --git a/test/orderby2.test b/test/orderby2.test index 7d543229c0..a2bb4e13a9 100644 --- a/test/orderby2.test +++ b/test/orderby2.test @@ -92,6 +92,6 @@ do_test 2.1 { ORDER BY +a ASC, +c ASC, +e DESC, +g ASC; } } {1,3,7,10 1,3,7,14 1,3,6,11 1,4,8,12 1,4,8,12 1,4,8,13 1,4,5,9 2,3,7,10 2,3,7,14 2,3,6,11} - + finish_test