From 8871ef5afd1217110189bcaa55400ab30bdac46b Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 7 Oct 2011 13:33:10 +0000 Subject: [PATCH 1/4] Begin an effort to enhance the query planner to do a better job with OR terms in the WHERE clause. This change allows ANDs outside of the OR to be factored into the OR terms if that is helpful in finding better indices. FossilOrigin-Name: 876bd21aaac444c7e056730e35696a74e9a1af0a --- manifest | 15 ++++---- manifest.uuid | 2 +- src/where.c | 96 ++++++++++++++++++++++++++++++++++----------------- 3 files changed, 74 insertions(+), 39 deletions(-) diff --git a/manifest b/manifest index 791d69d826..3749bfc8ff 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Enhance\sthe\ssqlite3_data_count()\sroutine\sso\sthat\sit\scan\sbe\sused\sto\sdetermine\nif\sSQLITE_DONE\shas\sbeen\sseen\son\sthe\sprepared\sstatement. -D 2011-10-07T12:59:23.182 +C Begin\san\seffort\sto\senhance\sthe\squery\splanner\sto\sdo\sa\sbetter\sjob\swith\sOR\sterms\nin\sthe\sWHERE\sclause.\s\sThis\schange\sallows\sANDs\soutside\sof\sthe\sOR\sto\sbe\sfactored\ninto\sthe\sOR\sterms\sif\sthat\sis\shelpful\sin\sfinding\sbetter\sindices. +D 2011-10-07T13:33:10.760 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in a162fe39e249b8ed4a65ee947c30152786cfe897 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -251,7 +251,7 @@ F src/vtab.c 901791a47318c0562cd0c676a2c6ff1bc530e582 F src/wal.c 3154756177d6219e233d84291d5b05f4e06ff5e9 F src/wal.h 66b40bd91bc29a5be1c88ddd1f5ade8f3f48728a F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f -F src/where.c aafcb21a74e41f9aae76ea604e1e787ff8574125 +F src/where.c 80c53e8e21fe9c5c5830e33d806fc168577427ed F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 @@ -967,7 +967,10 @@ F tool/symbols.sh caaf6ccc7300fd43353318b44524853e222557d5 F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 -P baa80c7bc31900decae0d8e6090b30fcde377492 -R 1223a474d1e5cea6170a2af45d3f0303 +P 9913996e7b0f94ba1c51200b61433193002f3638 +R 7d8737bed3e72adf3b068f98f3f5d762 +T *branch * or-opt +T *sym-or-opt * +T -sym-trunk * U drh -Z 7e8fd93d23ec7d2fecbdff0e025997fa +Z 6d6f0e795135cadc2210cb2fc14d2593 diff --git a/manifest.uuid b/manifest.uuid index 9fe70085bf..7e8c9eb4d6 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9913996e7b0f94ba1c51200b61433193002f3638 \ No newline at end of file +876bd21aaac444c7e056730e35696a74e9a1af0a \ No newline at end of file diff --git a/src/where.c b/src/where.c index 220411de2f..fa81094af9 100644 --- a/src/where.c +++ b/src/where.c @@ -127,11 +127,20 @@ struct WhereTerm { /* ** An instance of the following structure holds all information about a ** WHERE clause. Mostly this is a container for one or more WhereTerms. +** +** Explanation of pOuter: For a WHERE clause of the form +** +** a AND ((b AND c) OR (d AND e)) AND f +** +** There are separate WhereClause objects for the whole clause and for +** the subclauses "(b AND c)" and "(d AND e)". The pOuter field of the +** subclauses points to the WhereClause object for the whole clause. */ struct WhereClause { Parse *pParse; /* The parser context */ WhereMaskSet *pMaskSet; /* Mapping of table cursor numbers to bitmasks */ Bitmask vmask; /* Bitmask identifying virtual table cursors */ + WhereClause *pOuter; /* Outer conjunction */ u8 op; /* Split operator. TK_AND or TK_OR */ int nTerm; /* Number of terms */ int nSlot; /* Number of entries in a[] */ @@ -265,6 +274,7 @@ static void whereClauseInit( ){ pWC->pParse = pParse; pWC->pMaskSet = pMaskSet; + pWC->pOuter = 0; pWC->nTerm = 0; pWC->nSlot = ArraySize(pWC->aStatic); pWC->a = pWC->aStatic; @@ -584,36 +594,38 @@ static WhereTerm *findTerm( int k; assert( iCur>=0 ); op &= WO_ALL; - for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){ - if( pTerm->leftCursor==iCur - && (pTerm->prereqRight & notReady)==0 - && pTerm->u.leftColumn==iColumn - && (pTerm->eOperator & op)!=0 - ){ - if( pIdx && pTerm->eOperator!=WO_ISNULL ){ - Expr *pX = pTerm->pExpr; - CollSeq *pColl; - char idxaff; - int j; - Parse *pParse = pWC->pParse; - - idxaff = pIdx->pTable->aCol[iColumn].affinity; - if( !sqlite3IndexAffinityOk(pX, idxaff) ) continue; - - /* Figure out the collation sequence required from an index for - ** it to be useful for optimising expression pX. Store this - ** value in variable pColl. - */ - assert(pX->pLeft); - pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); - assert(pColl || pParse->nErr); - - for(j=0; pIdx->aiColumn[j]!=iColumn; j++){ - if( NEVER(j>=pIdx->nColumn) ) return 0; + for(; pWC; pWC=pWC->pOuter){ + for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){ + if( pTerm->leftCursor==iCur + && (pTerm->prereqRight & notReady)==0 + && pTerm->u.leftColumn==iColumn + && (pTerm->eOperator & op)!=0 + ){ + if( pIdx && pTerm->eOperator!=WO_ISNULL ){ + Expr *pX = pTerm->pExpr; + CollSeq *pColl; + char idxaff; + int j; + Parse *pParse = pWC->pParse; + + idxaff = pIdx->pTable->aCol[iColumn].affinity; + if( !sqlite3IndexAffinityOk(pX, idxaff) ) continue; + + /* Figure out the collation sequence required from an index for + ** it to be useful for optimising expression pX. Store this + ** value in variable pColl. + */ + assert(pX->pLeft); + pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); + assert(pColl || pParse->nErr); + + for(j=0; pIdx->aiColumn[j]!=iColumn; j++){ + if( NEVER(j>=pIdx->nColumn) ) return 0; + } + if( pColl && sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ) continue; } - if( pColl && sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ) continue; + return pTerm; } - return pTerm; } } return 0; @@ -907,6 +919,7 @@ static void exprAnalyzeOrTerm( whereClauseInit(pAndWC, pWC->pParse, pMaskSet); whereSplit(pAndWC, pOrTerm->pExpr, TK_AND); exprAnalyzeAll(pSrc, pAndWC); + pAndWC->pOuter = pWC; testcase( db->mallocFailed ); if( !db->mallocFailed ){ for(j=0, pAndTerm=pAndWC->a; jnTerm; j++, pAndTerm++){ @@ -1833,6 +1846,7 @@ static void bestOrClauseIndex( WhereClause tempWC; tempWC.pParse = pWC->pParse; tempWC.pMaskSet = pWC->pMaskSet; + tempWC.pOuter = pWC; tempWC.op = TK_AND; tempWC.a = pOrTerm; tempWC.nTerm = 1; @@ -3769,7 +3783,8 @@ static Bitmask codeOneLoopStart( WhereInfo *pWInfo, /* Complete information about the WHERE clause */ int iLevel, /* Which level of pWInfo->a[] should be coded */ u16 wctrlFlags, /* One of the WHERE_* flags defined in sqliteInt.h */ - Bitmask notReady /* Which tables are currently available */ + Bitmask notReady, /* Which tables are currently available */ + Expr *pWhere /* Complete WHERE clause */ ){ int j, k; /* Loop counters */ int iCur; /* The VDBE cursor for the table */ @@ -4251,7 +4266,8 @@ static Bitmask codeOneLoopStart( int iLoopBody = sqlite3VdbeMakeLabel(v); /* Start of loop body */ int iRetInit; /* Address of regReturn init */ int untestedTerms = 0; /* Some terms not completely tested */ - int ii; + int ii; /* Loop counter */ + Expr *pAndExpr = 0; /* An ".. AND (...)" expression */ pTerm = pLevel->plan.u.pTerm; assert( pTerm!=0 ); @@ -4301,12 +4317,27 @@ static Bitmask codeOneLoopStart( } iRetInit = sqlite3VdbeAddOp2(v, OP_Integer, 0, regReturn); + /* If the original WHERE clause is z of the form: (x1 OR x2 OR ...) AND y + ** Then for every term xN, evaluate as the subexpression: xN AND z + ** That way, terms in y that are factored into the disjunction will + ** be picked up by the recursive calls to sqlite3WhereBegin() below. + */ + if( pWC->nTerm>1 ){ + pAndExpr = sqlite3ExprAlloc(pParse->db, TK_AND, 0, 0); + pAndExpr->pRight = pWhere; + } + for(ii=0; iinTerm; ii++){ WhereTerm *pOrTerm = &pOrWc->a[ii]; if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){ WhereInfo *pSubWInfo; /* Info for single OR-term scan */ + Expr *pOrExpr = pOrTerm->pExpr; + if( pAndExpr ){ + pAndExpr->pLeft = pOrExpr; + pOrExpr = pAndExpr; + } /* Loop through table entries that match term pOrTerm. */ - pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrTerm->pExpr, 0, 0, + pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0, WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE | WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY); if( pSubWInfo ){ @@ -4335,6 +4366,7 @@ static Bitmask codeOneLoopStart( } } } + sqlite3DbFree(pParse->db, pAndExpr); sqlite3VdbeChangeP1(v, iRetInit, sqlite3VdbeCurrentAddr(v)); sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrBrk); sqlite3VdbeResolveLabel(v, iLoopBody); @@ -4989,7 +5021,7 @@ WhereInfo *sqlite3WhereBegin( for(i=0; ia[i]; explainOneScan(pParse, pTabList, pLevel, i, pLevel->iFrom, wctrlFlags); - notReady = codeOneLoopStart(pWInfo, i, wctrlFlags, notReady); + notReady = codeOneLoopStart(pWInfo, i, wctrlFlags, notReady, pWhere); pWInfo->iContinue = pLevel->addrCont; } From 9ef61f4f4ef46f4d95ebcff1e4f726224ae25e2e Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 7 Oct 2011 14:40:59 +0000 Subject: [PATCH 2/4] Prevent infinite recursion of in the query planner for some pathological test cases by disabling OR-clause processing upon first recursion. FossilOrigin-Name: 9fca05eac503d712886a05d03794f76c61fb39ed --- manifest | 17 +++++++---------- manifest.uuid | 2 +- src/sqliteInt.h | 8 ++++---- src/where.c | 24 +++++++++++++++--------- 4 files changed, 27 insertions(+), 24 deletions(-) diff --git a/manifest b/manifest index 3749bfc8ff..d40e0138a7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Begin\san\seffort\sto\senhance\sthe\squery\splanner\sto\sdo\sa\sbetter\sjob\swith\sOR\sterms\nin\sthe\sWHERE\sclause.\s\sThis\schange\sallows\sANDs\soutside\sof\sthe\sOR\sto\sbe\sfactored\ninto\sthe\sOR\sterms\sif\sthat\sis\shelpful\sin\sfinding\sbetter\sindices. -D 2011-10-07T13:33:10.760 +C Prevent\sinfinite\srecursion\sof\sin\sthe\squery\splanner\sfor\ssome\spathological\ntest\scases\sby\sdisabling\sOR-clause\sprocessing\supon\sfirst\srecursion. +D 2011-10-07T14:40:59.269 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in a162fe39e249b8ed4a65ee947c30152786cfe897 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -183,7 +183,7 @@ F src/select.c d9b7d20b0365f80761846f00ef3638d4b33eeaf2 F src/shell.c e8fe1251aee84baa2fb232ce83d938de25aa650f F src/sqlite.h.in 1865923bdb9deb8dde42da5862aca0071adb6061 F src/sqlite3ext.h 1a1a4f784aa9c3b00edd287940197de52487cd93 -F src/sqliteInt.h 28cca77ebdaf6025ae5df52717dff429c7c6d4ef +F src/sqliteInt.h 2f66bf068131f0e499dd5e0abea3f68cd6b27b2d F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 4568e72dfd36b6a5911f93457364deb072e0b03a F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -251,7 +251,7 @@ F src/vtab.c 901791a47318c0562cd0c676a2c6ff1bc530e582 F src/wal.c 3154756177d6219e233d84291d5b05f4e06ff5e9 F src/wal.h 66b40bd91bc29a5be1c88ddd1f5ade8f3f48728a F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f -F src/where.c 80c53e8e21fe9c5c5830e33d806fc168577427ed +F src/where.c 2e82da485ae1e037d2e372fd361c926cd4885934 F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 @@ -967,10 +967,7 @@ F tool/symbols.sh caaf6ccc7300fd43353318b44524853e222557d5 F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 -P 9913996e7b0f94ba1c51200b61433193002f3638 -R 7d8737bed3e72adf3b068f98f3f5d762 -T *branch * or-opt -T *sym-or-opt * -T -sym-trunk * +P 876bd21aaac444c7e056730e35696a74e9a1af0a +R 2e052ba91d5f74c653875a92f40f0614 U drh -Z 6d6f0e795135cadc2210cb2fc14d2593 +Z ec0c9bad465d9d136336e81b2a3d87e4 diff --git a/manifest.uuid b/manifest.uuid index 7e8c9eb4d6..a9d34afed4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -876bd21aaac444c7e056730e35696a74e9a1af0a \ No newline at end of file +9fca05eac503d712886a05d03794f76c61fb39ed \ No newline at end of file diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 35ab54a5f7..2a54e4593a 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1979,10 +1979,10 @@ struct WhereLevel { #define WHERE_ORDERBY_MAX 0x0002 /* ORDER BY processing for max() func */ #define WHERE_ONEPASS_DESIRED 0x0004 /* Want to do one-pass UPDATE/DELETE */ #define WHERE_DUPLICATES_OK 0x0008 /* Ok to return a row more than once */ -#define WHERE_OMIT_OPEN 0x0010 /* Table cursors are already open */ -#define WHERE_OMIT_CLOSE 0x0020 /* Omit close of table & index cursors */ -#define WHERE_FORCE_TABLE 0x0040 /* Do not use an index-only search */ -#define WHERE_ONETABLE_ONLY 0x0080 /* Only code the 1st table in pTabList */ +#define WHERE_OMIT_OPEN_CLOSE 0x0010 /* Table cursors are already open */ +#define WHERE_FORCE_TABLE 0x0020 /* Do not use an index-only search */ +#define WHERE_ONETABLE_ONLY 0x0040 /* Only code the 1st table in pTabList */ +#define WHERE_AND_ONLY 0x0080 /* Don't use indices for OR terms */ /* ** The WHERE clause processing routine has two halves. The diff --git a/src/where.c b/src/where.c index fa81094af9..9d5adc1832 100644 --- a/src/where.c +++ b/src/where.c @@ -142,6 +142,7 @@ struct WhereClause { Bitmask vmask; /* Bitmask identifying virtual table cursors */ WhereClause *pOuter; /* Outer conjunction */ u8 op; /* Split operator. TK_AND or TK_OR */ + u16 wctrlFlags; /* Might include WHERE_AND_ONLY */ int nTerm; /* Number of terms */ int nSlot; /* Number of entries in a[] */ WhereTerm *a; /* Each a[] describes a term of the WHERE cluase */ @@ -270,7 +271,8 @@ struct WhereCost { static void whereClauseInit( WhereClause *pWC, /* The WhereClause to be initialized */ Parse *pParse, /* The parsing context */ - WhereMaskSet *pMaskSet /* Mapping from table cursor numbers to bitmasks */ + WhereMaskSet *pMaskSet, /* Mapping from table cursor numbers to bitmasks */ + u16 wctrlFlags /* Might include WHERE_AND_ONLY */ ){ pWC->pParse = pParse; pWC->pMaskSet = pMaskSet; @@ -279,6 +281,7 @@ static void whereClauseInit( pWC->nSlot = ArraySize(pWC->aStatic); pWC->a = pWC->aStatic; pWC->vmask = 0; + pWC->wctrlFlags = wctrlFlags; } /* Forward reference */ @@ -889,7 +892,7 @@ static void exprAnalyzeOrTerm( if( pOrInfo==0 ) return; pTerm->wtFlags |= TERM_ORINFO; pOrWc = &pOrInfo->wc; - whereClauseInit(pOrWc, pWC->pParse, pMaskSet); + whereClauseInit(pOrWc, pWC->pParse, pMaskSet, pWC->wctrlFlags); whereSplit(pOrWc, pExpr, TK_OR); exprAnalyzeAll(pSrc, pOrWc); if( db->mallocFailed ) return; @@ -916,7 +919,7 @@ static void exprAnalyzeOrTerm( pOrTerm->wtFlags |= TERM_ANDINFO; pOrTerm->eOperator = WO_AND; pAndWC = &pAndInfo->wc; - whereClauseInit(pAndWC, pWC->pParse, pMaskSet); + whereClauseInit(pAndWC, pWC->pParse, pMaskSet, pWC->wctrlFlags); whereSplit(pAndWC, pOrTerm->pExpr, TK_AND); exprAnalyzeAll(pSrc, pAndWC); pAndWC->pOuter = pWC; @@ -1814,11 +1817,14 @@ static void bestOrClauseIndex( WhereTerm * const pWCEnd = &pWC->a[pWC->nTerm]; /* End of pWC->a[] */ WhereTerm *pTerm; /* A single term of the WHERE clause */ - /* No OR-clause optimization allowed if the INDEXED BY or NOT INDEXED clauses - ** are used */ + /* The OR-clause optimization is disallowed if the INDEXED BY or + ** NOT INDEXED clauses are used or if the WHERE_AND_ONLY bit is set. */ if( pSrc->notIndexed || pSrc->pIndex!=0 ){ return; } + if( pWC->wctrlFlags & WHERE_AND_ONLY ){ + return; + } /* Search the WHERE clause terms for a usable WO_OR term. */ for(pTerm=pWC->a; pTermplan.wsFlags & WHERE_IDX_ONLY)==0 - && (wctrlFlags & WHERE_OMIT_OPEN)==0 ){ + && (wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 ){ int op = pWInfo->okOnePass ? OP_OpenWrite : OP_OpenRead; sqlite3OpenTable(pParse, pTabItem->iCursor, iDb, pTab, op); testcase( pTab->nCol==BMS-1 ); @@ -5156,7 +5162,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ assert( pTab!=0 ); if( (pTab->tabFlags & TF_Ephemeral)==0 && pTab->pSelect==0 - && (pWInfo->wctrlFlags & WHERE_OMIT_CLOSE)==0 + && (pWInfo->wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 ){ int ws = pLevel->plan.wsFlags; if( !pWInfo->okOnePass && (ws & WHERE_IDX_ONLY)==0 ){ From 85ae206e69c69179e35c389617f0960ead17826e Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 7 Oct 2011 16:08:28 +0000 Subject: [PATCH 3/4] More test cases for the OR optimization. FossilOrigin-Name: 4997d8b81cd3ea7c708911bfece00020d11224f9 --- manifest | 14 ++++---- manifest.uuid | 2 +- test/where7.test | 4 +-- test/where9.test | 84 +++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 88 insertions(+), 16 deletions(-) diff --git a/manifest b/manifest index d40e0138a7..67d3608db6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Prevent\sinfinite\srecursion\sof\sin\sthe\squery\splanner\sfor\ssome\spathological\ntest\scases\sby\sdisabling\sOR-clause\sprocessing\supon\sfirst\srecursion. -D 2011-10-07T14:40:59.269 +C More\stest\scases\sfor\sthe\sOR\soptimization. +D 2011-10-07T16:08:28.818 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in a162fe39e249b8ed4a65ee947c30152786cfe897 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -916,10 +916,10 @@ F test/where3.test 8e1175c7ef710c70502858fc4fb08d784b3620b9 F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2 F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2 F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b -F test/where7.test aa4cfcd6f66e2a4ef87b6717327325bf4d547502 +F test/where7.test 814d7373413398e074f134cff5f8872e2c08bd3b F test/where8.test a6c740fd286d7883e274e17b6230a9d672a7ab1f F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739 -F test/where9.test 24f19ad14bb1b831564ced5273e681e495662848 +F test/where9.test bed66dcfc69a54a99661c0c9906189cb5e58f4e2 F test/whereA.test 24c234263c8fe358f079d5e57d884fb569d2da0a F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5 F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31 @@ -967,7 +967,7 @@ F tool/symbols.sh caaf6ccc7300fd43353318b44524853e222557d5 F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 -P 876bd21aaac444c7e056730e35696a74e9a1af0a -R 2e052ba91d5f74c653875a92f40f0614 +P 9fca05eac503d712886a05d03794f76c61fb39ed +R bd6384414b9b60e15876987ee9f999e9 U drh -Z ec0c9bad465d9d136336e81b2a3d87e4 +Z 427731fab44d769232d079e056d051e3 diff --git a/manifest.uuid b/manifest.uuid index a9d34afed4..937b15f843 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9fca05eac503d712886a05d03794f76c61fb39ed \ No newline at end of file +4997d8b81cd3ea7c708911bfece00020d11224f9 \ No newline at end of file diff --git a/test/where7.test b/test/where7.test index e34778c763..ffb7173d21 100644 --- a/test/where7.test +++ b/test/where7.test @@ -10,8 +10,6 @@ #*********************************************************************** # 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.9 2009/06/07 23:45:11 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -23341,7 +23339,7 @@ do_execsql_test where7-3.1 { OR t301.c8 = 1407424651264000) ORDER BY t302.c5 LIMIT 200; } { - 0 0 1 {SEARCH TABLE t301 USING COVERING INDEX t301_c4 (c4=?) (~10 rows)} + 0 0 1 {SEARCH TABLE t301 USING COVERING INDEX t301_c4 (c4=?) (~5 rows)} 0 0 1 {SEARCH TABLE t301 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} 0 1 0 {SEARCH TABLE t302 USING INDEX t302_c8_c3 (c8=? AND c3>?) (~2 rows)} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} diff --git a/test/where9.test b/test/where9.test index 9a180116a8..b4a2d8d130 100644 --- a/test/where9.test +++ b/test/where9.test @@ -11,7 +11,6 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the multi-index OR clause optimizer. # -# $Id: where9.test,v 1.9 2009/06/05 17:09:12 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -365,7 +364,7 @@ ifcapable explain { } { 0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} 0 1 1 {SEARCH TABLE t2 USING INDEX t2d (d=?) (~2 rows)} - 0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?) (~10 rows)} + 0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?) (~5 rows)} } do_execsql_test where9-3.2 { EXPLAIN QUERY PLAN @@ -375,7 +374,7 @@ ifcapable explain { } { 0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} 0 1 1 {SEARCH TABLE t2 USING INDEX t2d (d=?) (~2 rows)} - 0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?) (~10 rows)} + 0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?) (~5 rows)} } } @@ -454,8 +453,8 @@ ifcapable explain { do_execsql_test where9-5.1 { EXPLAIN QUERY PLAN SELECT a FROM t1 WHERE b>1000 AND (c=31031 OR d IS NULL) } { - 0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c=?) (~10 rows)} - 0 0 0 {SEARCH TABLE t1 USING INDEX t1d (d=?) (~10 rows)} + 0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c=?) (~2 rows)} + 0 0 0 {SEARCH TABLE t1 USING INDEX t1d (d=?) (~2 rows)} } # In contrast, b=1000 is preferred over any OR-clause. @@ -783,4 +782,79 @@ do_test where9-6.8.2 { } } {1 {cannot use index: t1b}} +############################################################################ +# Test cases where terms inside an OR series are combined with AND terms +# external to the OR clause. In other words, cases where +# +# x AND (y OR z) +# +# is able to use indices on x,y and x,z, or indices y,x and z,x. +# +do_test where9-7.0 { + execsql { + CREATE TABLE t5(a, b, c, d, e, f, g, x, y); + INSERT INTO t5 + SELECT a, b, c, e, d, f, g, + CASE WHEN (a&1)!=0 THEN 'y' ELSE 'n' END, + CASE WHEN (a&2)!=0 THEN 'y' ELSE 'n' END + FROM t1; + CREATE INDEX t5xb ON t5(x, b); + CREATE INDEX t5xc ON t5(x, c); + CREATE INDEX t5xd ON t5(x, d); + CREATE INDEX t5xe ON t5(x, e); + CREATE INDEX t5xf ON t5(x, f); + CREATE INDEX t5xg ON t5(x, g); + CREATE INDEX t5yb ON t5(y, b); + CREATE INDEX t5yc ON t5(y, c); + CREATE INDEX t5yd ON t5(y, d); + CREATE INDEX t5ye ON t5(y, e); + CREATE INDEX t5yf ON t5(y, f); + CREATE INDEX t5yg ON t5(y, g); + CREATE TABLE t6(a, b, c, e, d, f, g, x, y); + INSERT INTO t6 SELECT * FROM t5; + ANALYZE t5; + } +} {} +do_test where9-7.1.1 { + count_steps { + SELECT a FROM t5 WHERE x='y' AND (b=913 OR c=27027) ORDER BY a; + } +} {79 81 83 scan 0 sort 1} +do_test where9-7.1.2 { + execsql { + SELECT a FROM t6 WHERE x='y' AND (b=913 OR c=27027) ORDER BY a; + } +} {79 81 83} +do_test where9-7.1.3 { + count_steps { + SELECT a FROM t5 WHERE x='n' AND (b=913 OR c=27027) ORDER BY a; + } +} {80 scan 0 sort 1} +do_test where9-7.1.4 { + execsql { + SELECT a FROM t6 WHERE x='n' AND (b=913 OR c=27027) ORDER BY a; + } +} {80} +do_test where9-7.2.1 { + count_steps { + SELECT a FROM t5 WHERE (x='y' OR y='y') AND b=913 ORDER BY a; + } +} {83 scan 0 sort 1} +do_test where9-7.2.2 { + execsql { + SELECT a FROM t6 WHERE (x='y' OR y='y') AND b=913 ORDER BY a; + } +} {83} +do_test where9-7.3.1 { + count_steps { + SELECT a FROM t5 WHERE (x='y' OR y='y') AND c=27027 ORDER BY a; + } +} {79 81 scan 0 sort 1} +do_test where9-7.3.2 { + execsql { + SELECT a FROM t6 WHERE (x='y' OR y='y') AND c=27027 ORDER BY a; + } +} {79 81} + + finish_test From 0a61df670ec218e98a7e1ac4768c2f3b7ebb1cda Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 7 Oct 2011 17:45:58 +0000 Subject: [PATCH 4/4] Add testcase() macros to ensure good test coverage. FossilOrigin-Name: 5c132592820e9dc2355e26ea14e155c797c335b3 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/where.c | 3 +++ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 67d3608db6..dd2d1469f9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C More\stest\scases\sfor\sthe\sOR\soptimization. -D 2011-10-07T16:08:28.818 +C Add\stestcase()\smacros\sto\sensure\sgood\stest\scoverage. +D 2011-10-07T17:45:58.914 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in a162fe39e249b8ed4a65ee947c30152786cfe897 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -251,7 +251,7 @@ F src/vtab.c 901791a47318c0562cd0c676a2c6ff1bc530e582 F src/wal.c 3154756177d6219e233d84291d5b05f4e06ff5e9 F src/wal.h 66b40bd91bc29a5be1c88ddd1f5ade8f3f48728a F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f -F src/where.c 2e82da485ae1e037d2e372fd361c926cd4885934 +F src/where.c a6d127dd05daf0f9ffa654edfab4f1236dc759b7 F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 @@ -967,7 +967,7 @@ F tool/symbols.sh caaf6ccc7300fd43353318b44524853e222557d5 F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 -P 9fca05eac503d712886a05d03794f76c61fb39ed -R bd6384414b9b60e15876987ee9f999e9 +P 4997d8b81cd3ea7c708911bfece00020d11224f9 +R b935363a5df75d52ae6d528d229f42f6 U drh -Z 427731fab44d769232d079e056d051e3 +Z e878c83927724d1c2481f6ca56873708 diff --git a/manifest.uuid b/manifest.uuid index 937b15f843..661a2a46ad 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4997d8b81cd3ea7c708911bfece00020d11224f9 \ No newline at end of file +5c132592820e9dc2355e26ea14e155c797c335b3 \ No newline at end of file diff --git a/src/where.c b/src/where.c index 9d5adc1832..da7ffbdf86 100644 --- a/src/where.c +++ b/src/where.c @@ -3032,6 +3032,7 @@ static void bestBtreeIndex( pTerm = findTerm(pWC, iCur, j, notReady, eqTermMask, pIdx); if( pTerm==0 ) break; wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ); + testcase( pTerm->pWC!=pWC ); if( pTerm->eOperator & WO_IN ){ Expr *pExpr = pTerm->pExpr; wsFlags |= WHERE_COLUMN_IN; @@ -3063,11 +3064,13 @@ static void bestBtreeIndex( nBound = 1; wsFlags |= WHERE_TOP_LIMIT; used |= pTop->prereqRight; + testcase( pTop->pWC!=pWC ); } if( pBtm ){ nBound++; wsFlags |= WHERE_BTM_LIMIT; used |= pBtm->prereqRight; + testcase( pBtm->pWC!=pWC ); } wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE); }