From: drh Date: Thu, 4 Oct 2012 12:10:25 +0000 (+0000) Subject: Yet another refactoring of ORDER BY logic in the query planner. This X-Git-Tag: version-3.7.15~84^2~4 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=4f68d6c8c0784cc1969b395e471ab7b2f0447c49;p=thirdparty%2Fsqlite.git Yet another refactoring of ORDER BY logic in the query planner. This particular check-in works mostly, but still has a few minor issues. FossilOrigin-Name: 8f4487450be1a2b0371f8251a967cbe341b2dea1 --- diff --git a/manifest b/manifest index f15d875432..311e37d60d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\san\sout-of-order\smemset()\sthat\soccurs\sbefore\sall\svariable\sdeclarations\nare\sfinished.\s\sAlso\sfix\sa\sline\sthat\sexceeds\sthe\s80-character\sline\slength\nlimit. -D 2012-10-03T18:09:32.822 +C Yet\sanother\srefactoring\sof\sORDER\sBY\slogic\sin\sthe\squery\splanner.\s\sThis\nparticular\scheck-in\sworks\smostly,\sbut\sstill\shas\sa\sfew\sminor\sissues. +D 2012-10-04T12:10:25.997 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 fabdb473752fb465b0cae66d446b7af678033d0c +F src/where.c f2468071088a73703cd0fa9c9a2a3014b6a5501b F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggnested.test 0be144b453e0622a085fae8665c32f5676708e00 @@ -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 956e4d7f8958e7065ff2d61cd71519d6f4113d4a -R 9f4b1a764df6137e6f14304dffc8ad1d +P ba2f492f957ab5556cd540e21a76ebb75efea725 +R 9e8b25b7e134451a912caf8748b81c9a +T *branch * qp-enhancements +T *sym-qp-enhancements * +T -sym-trunk * U drh -Z a3c3e4f99944b9339f658e73f3668f5f +Z 0d6c8df6b2d44ceaf575eaf63391c9ce diff --git a/manifest.uuid b/manifest.uuid index d51ad9182c..c7968b7212 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ba2f492f957ab5556cd540e21a76ebb75efea725 \ No newline at end of file +8f4487450be1a2b0371f8251a967cbe341b2dea1 \ No newline at end of file diff --git a/src/where.c b/src/where.c index 0df9bb08d5..bce9b4041a 100644 --- a/src/where.c +++ b/src/where.c @@ -1442,22 +1442,6 @@ static void exprAnalyze( pTerm->prereqRight |= extraRight; } -/* -** Return TRUE if the given index is UNIQUE and all columns past the -** first nSkip columns are NOT NULL. -*/ -static int indexIsUniqueNotNull(Index *pIdx, int nSkip){ - Table *pTab = pIdx->pTable; - int i; - if( pIdx->onError==OE_None ) return 0; - for(i=nSkip; inColumn; i++){ - int j = pIdx->aiColumn[i]; - assert( j>=0 && jnCol ); - if( pTab->aCol[j].notNull==0 ) return 0; - } - return 1; -} - /* ** This function searches the expression list passed as the second argument ** for an expression of type TK_COLUMN that refers to the same column and @@ -2781,27 +2765,49 @@ static int isOrderedColumn(WhereBestIdx *p, int iTab, int iCol, int *pbRev){ } /* -** pTerm is an == constraint. Check to see if the other side of -** the == is a constant or a value that is guaranteed to be ordered -** by outer loops. Return 1 if pTerm is ordered, and 0 if not. +** Check to see if there is an == or IS NULL constraint in the WHERE clause +** that restricts base.iColumn to be well-ordered. If base.iColumn must +** be a constant or must be NULL, that qualifies as well-ordered. If +** base.iColumn must equal the value of a column in an outer loop that is +** ordered, that also qualifies as being well ordered. +** +** In the second case (when base.iColumn == an ordered value in an outer +** loop) set or verify the sort order. If *pbRev is initially 2, then set +** it approprately. If *pbRev is 0 or 1, make sure it matches the sort +** order of the outer loop constraint. */ -static int isOrderedTerm(WhereBestIdx *p, WhereTerm *pTerm, int *pbRev){ - Expr *pExpr = pTerm->pExpr; - assert( pExpr->op==TK_EQ ); - assert( pExpr->pLeft!=0 && pExpr->pLeft->op==TK_COLUMN ); - assert( pExpr->pRight!=0 ); - if( pTerm->prereqRight==0 ){ - return 1; /* RHS of the == is a constant */ - } - if( pExpr->pRight->op==TK_COLUMN - && isOrderedColumn(p, pExpr->pRight->iTable, pExpr->pRight->iColumn, pbRev) - ){ - return 1; - } +static int existsEqualityColumnConstraint( + WhereBestIdx *p, /* Best index search context */ + Index *pIdx, /* Constraint must be compatible with this index */ + int base, /* Cursor number for the table to be sorted */ + int iColumn, /* Index of a column on the "base" table */ + int *pbRev, /* Set to 1 for reverse-order constraint */ + int *notNull /* Set to 0 if an IS NULL constraint is seen */ +){ + WhereTerm *pTerm; + int rc; - /* If we cannot prove that the constraint is ordered, assume it is not */ - return 0; +WHERETRACE(("EQ Constraint on %d.%d: pbRev-in=%d", base, iColumn, *pbRev)); + pTerm = findTerm(p->pWC, base, iColumn, p->notReady, WO_EQ|WO_ISNULL, pIdx); + if( pTerm==0 ){ + rc = 0; + }else if( pTerm->prereqRight==0 ){ + rc = 1; + }else if( pTerm->eOperator & WO_ISNULL ){ + *notNull = 0; + rc = 1; + }else{ + Expr *pRight = pTerm->pExpr->pRight; + if( pRight->op==TK_COLUMN ){ + rc = isOrderedColumn(p, pRight->iTable, pRight->iColumn, pbRev); + }else{ + rc = 0; + } + } +WHERETRACE((" rc=%d pbRev-out=%d\n", rc, *pbRev)); + return rc; } + /* ** This routine decides if pIdx can be used to satisfy the ORDER BY @@ -2827,22 +2833,20 @@ static int isSortingIndex( WhereBestIdx *p, /* Best index search context */ Index *pIdx, /* The index we are testing */ int base, /* Cursor number for the table to be sorted */ - int nEqCol, /* Number of index columns with ordered == constraints */ - int wsFlags, /* Index usages flags */ - int bOuterRev, /* True if outer loops scan in reverse order */ int *pbRev /* Set to 1 for reverse-order scan of pIdx */ ){ int i; /* Number of pIdx terms used */ int j; /* Number of ORDER BY terms satisfied */ - int sortOrder = 0; /* XOR of index and ORDER BY sort direction */ + int sortOrder = 2; /* 0: forward. 1: backward. 2: unknown */ int nTerm; /* Number of ORDER BY terms */ struct ExprList_item *pTerm; /* A term of the ORDER BY clause */ + Table *pTab = pIdx->pTable; /* Table that owns index pIdx */ ExprList *pOrderBy; /* The ORDER BY clause */ Parse *pParse = p->pParse; /* Parser context */ sqlite3 *db = pParse->db; /* Database connection */ int nPriorSat; /* ORDER BY terms satisfied by outer loops */ int seenRowid = 0; /* True if an ORDER BY rowid term is seen */ - int nEqOneRow; /* Idx columns that ref unique values */ + int uniqueNotNull = 1; /* pIdx is UNIQUE with all terms are NOT NULL */ if( p->i==0 ){ nPriorSat = 0; @@ -2850,22 +2854,11 @@ static int isSortingIndex( nPriorSat = p->aLevel[p->i-1].plan.nOBSat; if( OptimizationDisabled(db, SQLITE_OrderByIdxJoin) ) return nPriorSat; } - 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{ - sortOrder = bOuterRev; - nEqOneRow = -1; - } pOrderBy = p->pOrderBy; assert( pOrderBy!=0 ); - if( wsFlags & WHERE_COLUMN_IN ) return nPriorSat; if( pIdx->bUnordered ) return nPriorSat; nTerm = pOrderBy->nExpr; + uniqueNotNull = pIdx->onError==OE_None; assert( nTerm>0 ); /* Argument pIdx must either point to a 'real' named index structure, @@ -2881,15 +2874,15 @@ static int isSortingIndex( ** of the index is also allowed to match against the ORDER BY ** clause. */ - for(i=0,j=nPriorSat,pTerm=&pOrderBy->a[j]; ja[j]; jnColumn; i++){ Expr *pExpr; /* The expression of the ORDER BY pTerm */ CollSeq *pColl; /* The collating sequence of pExpr */ int termSortOrder; /* Sort order for this term */ int iColumn; /* The i-th column of the index. -1 for rowid */ int iSortOrder; /* 1 for DESC, 0 for ASC on the i-th index term */ + int isEq; /* Subject to an == or IS NULL constraint */ const char *zColl; /* Name of the collating sequence for i-th index term */ - assert( i<=pIdx->nColumn ); pExpr = pTerm->pExpr; if( pExpr->op!=TK_COLUMN || pExpr->iTable!=base ){ /* Can not use an index sort on anything that is not a column in the @@ -2912,16 +2905,20 @@ static int isSortingIndex( iSortOrder = 0; zColl = pColl->zName; } + assert( pTerm->sortOrder==0 || pTerm->sortOrder==1 ); + assert( iSortOrder==0 || iSortOrder==1 ); + termSortOrder = pTerm->sortOrder; + isEq = existsEqualityColumnConstraint(p, pIdx, base, iColumn, + &termSortOrder, &uniqueNotNull); + termSortOrder = iSortOrder ^ pTerm->sortOrder; if( pExpr->iColumn!=iColumn || sqlite3StrICmp(pColl->zName, zColl) ){ /* Term j of the ORDER BY clause does not match column i of the index */ - if( inColumn ){ - /* Index column i is the rowid. All other terms match. */ - break; }else{ /* If an index column fails to match and is not constrained by == ** then the index cannot satisfy the ORDER BY constraint. @@ -2929,12 +2926,8 @@ static int isSortingIndex( return nPriorSat; } } - assert( pIdx->aSortOrder!=0 || iColumn==-1 ); - assert( pTerm->sortOrder==0 || pTerm->sortOrder==1 ); - assert( iSortOrder==0 || iSortOrder==1 ); - termSortOrder = iSortOrder ^ pTerm->sortOrder; - if( i>nEqOneRow ){ - if( termSortOrder!=sortOrder ){ + if( sortOrder<2 ){ + if( sortOrder!=termSortOrder ){ /* Indices can only be used if all ORDER BY terms past the ** equality constraints have the correct DESC or ASC. */ break; @@ -2947,20 +2940,17 @@ static int isSortingIndex( if( iColumn<0 ){ seenRowid = 1; break; + }else if( pTab->aCol[iColumn].notNull==0 ){ + uniqueNotNull = 0; } } - *pbRev = sortOrder; + *pbRev = sortOrder & 1; /* If there was an "ORDER BY rowid" term that matched, or it is only ** possible for a single row from this table to match, then skip over ** any additional ORDER BY terms dealing with this table. */ - if( seenRowid || - ( (wsFlags & WHERE_COLUMN_NULL)==0 - && i>=pIdx->nColumn - && indexIsUniqueNotNull(pIdx, nEqCol) - ) - ){ + if( seenRowid || (uniqueNotNull && i>=pIdx->nColumn) ){ /* Advance j over additional ORDER BY terms associated with base */ WhereMaskSet *pMS = p->pWC->pMaskSet; Bitmask m = ~getMask(pMS, base); @@ -3097,10 +3087,6 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** the sub-select is assumed to return 25 rows for the purposes of ** determining nInMul. ** - ** nOrdered: - ** The number of equality terms that are constrainted by outer loop - ** variables that are well-ordered. - ** ** bInEst: ** Set to true if there was at least one "x IN (SELECT ...)" term used ** in determining the value of nInMul. Note that the RHS of the @@ -3138,7 +3124,6 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** SELECT a, b FROM tbl WHERE a = 1; ** SELECT a, b, c FROM tbl WHERE a = 1; */ - 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 */ double rangeDiv = (double)1; /* Estimated reduction in search space */ @@ -3166,7 +3151,7 @@ static void bestBtreeIndex(WhereBestIdx *p){ } /* Determine the values of pc.plan.nEq and nInMul */ - for(pc.plan.nEq=nOrdered=0; pc.plan.nEqnColumn; pc.plan.nEq++){ + for(pc.plan.nEq=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; @@ -3185,10 +3170,6 @@ static void bestBtreeIndex(WhereBestIdx *p){ } }else if( pTerm->eOperator & WO_ISNULL ){ 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( pc.plan.nEq==0 && pProbe->aSample ) pFirstTerm = pTerm; @@ -3248,8 +3229,9 @@ static void bestBtreeIndex(WhereBestIdx *p){ testcase( bRev==0 ); testcase( bRev==1 ); testcase( bRev==2 ); - pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, nOrdered, - pc.plan.wsFlags, bRev&1, &bRev); +WHERETRACE(("--> before isSortingIndex: bRev=%d nPriorSat=%d\n", bRev, nPriorSat)); + pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, &bRev); +WHERETRACE(("--> after isSortingIndex: bRev=%d nOBSat=%d\n", bRev, pc.plan.nOBSat)); if( nPriorSatpTab->zName, (pIdx ? pIdx->zName : "ipk"), pc.plan.nEq, nInMul, (int)rangeDiv, bSort, bLookup, pc.plan.wsFlags, - p->notReady, log10N, pc.plan.nRow, pc.rCost, pc.used, nOrdered, + p->notReady, log10N, pc.plan.nRow, pc.rCost, pc.used, pc.plan.nOBSat ));