From 294352578e980d079285806c360e6994d207b226 Mon Sep 17 00:00:00 2001 From: drh Date: Sun, 28 Dec 2008 18:35:08 +0000 Subject: [PATCH] Optimize WHERE clauses that constain AND, BETWEEN, and LIKE terms as operands of an OR. (CVS 6068) FossilOrigin-Name: 67cf24b30e087796cfb0fccf47328e72ade5ecdc --- manifest | 14 ++++++------- manifest.uuid | 2 +- src/where.c | 52 ++++++++++++++++++++++++++++++++++++++---------- test/where7.test | 13 +++++++++++- 4 files changed, 61 insertions(+), 20 deletions(-) diff --git a/manifest b/manifest index 2877586851..748a7cf804 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Simplify\sthe\sVM\scode\sthat\simplements\sWHERE\sclaues.\s(CVS\s6067) -D 2008-12-28T16:55:25 +C Optimize\sWHERE\sclauses\sthat\sconstain\sAND,\sBETWEEN,\sand\sLIKE\sterms\sas\soperands\nof\san\sOR.\s(CVS\s6068) +D 2008-12-28T18:35:09 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in 77635d0909c2067cee03889a1e04ce910d8fb809 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -207,7 +207,7 @@ F src/vdbeblob.c b0dcebfafedcf9c0addc7901ad98f6f986c08935 F src/vdbemem.c f9c859ac17e2e05a0f249868ce4f191f69edd31d F src/vtab.c e39e011d7443a8d574b1b9cde207a35522e6df43 F src/walker.c 488c2660e13224ff70c0c82761118efb547f8f0d -F src/where.c f41330e71f3dde12cc6631ae9f75c9869b92c32b +F src/where.c 4050b918a379e23d5b645d888b91dba2e7c469a9 F tclinstaller.tcl 4356d9d94d2b5ed5e68f9f0c80c4df3048dd7617 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/alias.test 597662c5d777a122f9a3df0047ea5c5bd383a911 @@ -655,7 +655,7 @@ F test/where3.test 97d3936e6a443b968f1a61cdcc0f673252000e94 F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2 F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2 F test/where6.test 42c4373595f4409d9c6a9987b4a60000ad664faf -F test/where7.test b04da5cee08a573c120c95781d7413a7e25ac8d5 +F test/where7.test c27e4865d69b35dc21b4cbff097cf02e9cdc9950 F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31 F test/zeroblob.test 792124852ec61458a2eb527b5091791215e0be95 F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b @@ -686,7 +686,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e -P 08352f9ea9d2a1759320efc46e418079000855cb -R b27114cf38ee45f9d895c0015e5cf61b +P fa95f843e179a38f663978d675607c4c3037928d +R 7f2475a8de47b6500bdb0400363621d0 U drh -Z 5701aac5e9c50a29a7e1c4535e66ab21 +Z 2bf13b04bd3fbd90db56a109bb7f3eb1 diff --git a/manifest.uuid b/manifest.uuid index dcd41005a3..13953d7e4c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fa95f843e179a38f663978d675607c4c3037928d \ No newline at end of file +67cf24b30e087796cfb0fccf47328e72ade5ecdc \ No newline at end of file diff --git a/src/where.c b/src/where.c index d0a5c531e8..795b2e0550 100644 --- a/src/where.c +++ b/src/where.c @@ -16,7 +16,7 @@ ** so is applicable. Because this module is responsible for selecting ** indices, you might also think of this module as the "query optimizer". ** -** $Id: where.c,v 1.345 2008/12/28 16:55:25 drh Exp $ +** $Id: where.c,v 1.346 2008/12/28 18:35:09 drh Exp $ */ #include "sqliteInt.h" @@ -129,6 +129,7 @@ struct WhereTerm { struct WhereClause { Parse *pParse; /* The parser context */ WhereMaskSet *pMaskSet; /* Mapping of table cursor numbers to bitmasks */ + u8 op; /* Split operator. TK_AND or TK_OR */ int nTerm; /* Number of terms */ int nSlot; /* Number of entries in a[] */ WhereTerm *a; /* Each a[] describes a term of the WHERE cluase */ @@ -149,9 +150,7 @@ struct WhereOrInfo { ** a dynamically allocated instance of the following structure. */ struct WhereAndInfo { - WhereClause wc; /* The OR subexpression broken out */ - Index *pIdx; /* Index to use */ - double cost; /* Cost of evaluating this OR subexpression */ + WhereClause wc; /* The subexpression broken out */ }; /* @@ -369,6 +368,7 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u8 wtFlags){ ** all terms of the WHERE clause. */ static void whereSplit(WhereClause *pWC, Expr *pExpr, int op){ + pWC->op = (u8)op; if( pExpr==0 ) return; if( pExpr->op!=op ){ whereClauseInsert(pWC, pExpr, 0); @@ -835,8 +835,30 @@ static void exprAnalyzeOrTerm( indexable = chngToIN = ~(Bitmask)0; for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0 && indexable; i--, pOrTerm++){ if( (pOrTerm->eOperator & WO_SINGLE)==0 ){ + WhereAndInfo *pAndInfo; + assert( pOrTerm->eOperator==0 ); + assert( (pOrTerm->wtFlags & (TERM_ANDINFO|TERM_ORINFO))==0 ); chngToIN = 0; - indexable = 0; /***** FIX ME. Some AND clauses are indexable. */ + pAndInfo = sqlite3DbMallocRaw(db, sizeof(*pAndInfo)); + if( pAndInfo ){ + WhereClause *pAndWC; + WhereTerm *pAndTerm; + int j; + Bitmask b = 0; + pOrTerm->u.pAndInfo = pAndInfo; + pOrTerm->wtFlags |= TERM_ANDINFO; + pOrTerm->eOperator = WO_AND; + pAndWC = &pAndInfo->wc; + whereClauseInit(pAndWC, pWC->pParse, pMaskSet); + whereSplit(pAndWC, pOrTerm->pExpr, TK_AND); + exprAnalyzeAll(pSrc, pAndWC); + for(j=0, pAndTerm=pAndWC->a; jnTerm; j++, pAndTerm++){ + if( pAndTerm->pExpr && allowedOp(pAndTerm->pExpr->op) ){ + b |= getMask(pMaskSet, pAndTerm->leftCursor); + } + } + indexable &= b; + } }else if( pOrTerm->wtFlags & TERM_COPIED ){ /* Skip this term for now. We revisit it when we process the ** corresponding TERM_VIRTUAL term */ @@ -1082,7 +1104,7 @@ static void exprAnalyze( ** skipped. Or, if the children are satisfied by an index, the original ** BETWEEN term is skipped. */ - else if( pExpr->op==TK_BETWEEN ){ + else if( pExpr->op==TK_BETWEEN && pWC->op==TK_AND ){ ExprList *pList = pExpr->pList; int i; static const u8 ops[] = {TK_GE, TK_LE}; @@ -1108,6 +1130,7 @@ static void exprAnalyze( ** an OR operator. */ else if( pExpr->op==TK_OR ){ + assert( pWC->op==TK_AND ); exprAnalyzeOrTerm(pSrc, pWC, idxTerm); } #endif /* SQLITE_OMIT_OR_OPTIMIZATION */ @@ -1123,7 +1146,8 @@ static void exprAnalyze( ** The last character of the prefix "abc" is incremented to form the ** termination condition "abd". */ - if( isLikeOrGlob(pParse, pExpr, &nPattern, &isComplete, &noCase) ){ + if( isLikeOrGlob(pParse, pExpr, &nPattern, &isComplete, &noCase) + && pWC->op==TK_AND ){ Expr *pLeft, *pRight; Expr *pStr1, *pStr2; Expr *pNewExpr1, *pNewExpr2; @@ -1832,9 +1856,15 @@ static void bestIndex( double nRow = 0; for(j=0, pOrTerm=pOrWC->a; jnTerm; j++, pOrTerm++){ WhereCost sTermCost; - if( pOrTerm->leftCursor!=iCur ) continue; - tempWC.a = pOrTerm; - bestIndex(pParse, &tempWC, pSrc, notReady, 0, &sTermCost); + if( pOrTerm->eOperator==WO_AND ){ + WhereClause *pAndWC = &pOrTerm->u.pAndInfo->wc; + bestIndex(pParse, pAndWC, pSrc, notReady, 0, &sTermCost); + }else if( pOrTerm->leftCursor==iCur ){ + tempWC.a = pOrTerm; + bestIndex(pParse, &tempWC, pSrc, notReady, 0, &sTermCost); + }else{ + continue; + } if( sTermCost.plan.wsFlags==0 ){ rTotal = pCost->rCost; break; @@ -2670,7 +2700,7 @@ static Bitmask codeOneLoopStart( oneTab.a[0] = *pTabItem; for(j=0, pOrTerm=pOrWc->a; jnTerm; j++, pOrTerm++){ WhereInfo *pSubWInfo; - if( pOrTerm->leftCursor!=iCur ) continue; + if( pOrTerm->leftCursor!=iCur && pOrTerm->eOperator!=WO_AND ) continue; pSubWInfo = sqlite3WhereBegin(pParse, &oneTab, pOrTerm->pExpr, 0, WHERE_FILL_ROWSET | WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE, regOrRowset); diff --git a/test/where7.test b/test/where7.test index e7ca4f73d6..a7686220e8 100644 --- a/test/where7.test +++ b/test/where7.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the multi-index OR clause optimizer. # -# $Id: where7.test,v 1.1 2008/12/23 23:56:22 drh Exp $ +# $Id: where7.test,v 1.2 2008/12/28 18:35:09 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -89,6 +89,17 @@ do_test where7-1.10 { SELECT a FROM t1 WHERE (b=3 OR c>=10 OR c=4 OR b>10) } } {2 4 5 scan 0} +do_test where7-1.11 { + count_steps { + SELECT a FROM t1 WHERE (d=5 AND b=3) OR c==100; + } +} {2 5 scan 0} +do_test where7-1.12 { +breakpoint + count_steps { + SELECT a FROM t1 WHERE (b BETWEEN 2 AND 4) OR c=100 + } +} {1 2 3 5 scan 0} finish_test