From: drh Date: Wed, 16 Dec 2009 22:10:49 +0000 (+0000) Subject: Ensure WHERE clause terms involving tables on the right end of a join X-Git-Tag: version-3.7.2~698 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c01a3c17753d90c24d9debaaf65afb8247ceab47;p=thirdparty%2Fsqlite.git Ensure WHERE clause terms involving tables on the right end of a join are not prematurely evaluated when tables on the left end of the join make use of the OR-clause optimization. Fix for ticket [31338dca7e]. FossilOrigin-Name: 2c2de252666662f5459904fc33a9f2956cbff23c --- diff --git a/manifest b/manifest index bfc6dc5643..69d6f433ab 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,8 @@ -C Modify\ssome\scomments\sin\se_fkey.test.\sNo\schanges\sto\scode\sor\stests. -D 2009-12-16T14:49:16 +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA1 + +C Ensure\sWHERE\sclause\sterms\sinvolving\stables\son\sthe\sright\send\sof\sa\sjoin\nare\snot\sprematurely\sevaluated\swhen\stables\son\sthe\sleft\send\sof\sthe\sjoin\nmake\suse\sof\sthe\sOR-clause\soptimization.\s\s\nFix\sfor\sticket\s[31338dca7e]. +D 2009-12-16T22:10:49 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in c5827ead754ab32b9585487177c93bb00b9497b3 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -166,7 +169,7 @@ F src/select.c 68c58dc49341472e4e5661a47a1a9e5f8a161340 F src/shell.c f4948cb6d30665d755a6b5e0ec313d1094aab828 F src/sqlite.h.in 176e993dce3c9cfe5610aca0ef99f4dfd70896bb F src/sqlite3ext.h 69dfb8116af51b84a029cddb3b35062354270c89 -F src/sqliteInt.h e946a6a3f2df015cdbc7668e9626987e8badbb5f +F src/sqliteInt.h 93d5d01d9ea57b95ba709733dce830ec056deb84 F src/sqliteLimit.h 3afab2291762b5d09ae20c18feb8e9fa935a60a6 F src/status.c e651be6b30d397d86384c6867bc016e4913bcac7 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -219,7 +222,7 @@ F src/vdbemem.c 1e16e3a16e55f4c3452834f0e041726021aa66e0 F src/vdbetrace.c 864cef96919323482ebd9986f2132435115e9cc2 F src/vtab.c 7c7713d66cda699f16bf1cc601d8d4f5070ab935 F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f -F src/where.c 11b5b00c49d53e767a7eb855bc60790edeca6185 +F src/where.c 5d057a1054972396c05b5e28e6636359675a60d4 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 F test/all.test 14165b3e32715b700b5f0cbf8f6e3833dda0be45 @@ -601,6 +604,7 @@ F test/thread_common.tcl b65e6b1d1d90dc885e10ad080896c6c56eef0819 F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b F test/threadtest2.c ace893054fa134af3fc8d6e7cfecddb8e3acefb9 F test/tkt-2ea2425d34.test 1cf13e6f75d149b3209a0cb32927a82d3d79fb28 +F test/tkt-31338dca7e.test 5741cd48de500347a437ba1be58c8335e83c5a5e F test/tkt-3fe897352e.test 8084dad39807eac10b10720c84193bd1a5980973 F test/tkt-4a03edc4c8.test 2865e4edbc075b954daa82f8da7cc973033ec76e F test/tkt-5ee23731f.test 3581260f2a71e51db94e1506ba6b0f7311d002a9 @@ -745,7 +749,7 @@ F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2 F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2 F test/where6.test 42c4373595f4409d9c6a9987b4a60000ad664faf F test/where7.test fdd58ab9dec9f97679e65d4414bf5e07d725d79f -F test/where8.test 434f08974964b10378d67867773a2c3aedaf1d4b +F test/where8.test 2bb8ea44b745fcc93db150fac9ce33d12e499760 F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739 F test/where9.test be19e1a92f80985c1a121b4678bf7d2123eaa623 F test/whereA.test 1d1674254614147c866ab9b59af6582f454a858c @@ -779,7 +783,14 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 0daec3099d439ce8a8779260b12975f262037bc8 -R 45bbd6fcd1582d1baf524d507b053719 -U dan -Z 4241a4f431ff69b9b947be1f424e2dae +P 1ba4f412325fc2ce552ad7feecf93891f7f95059 +R 0f0daa13e962576cf0e0e163e05eaf4e +U drh +Z 0003328655e85cfcc94ce890d5aea52a +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v1.4.6 (GNU/Linux) + +iD8DBQFLKVrtoxKgR168RlERAuSjAJ9orzNKVuBJ+JkBFgDa9NTlSgG6OwCfYqCf +2/3GkgkUHpv1vo5CeiJiOuY= +=yKm6 +-----END PGP SIGNATURE----- diff --git a/manifest.uuid b/manifest.uuid index 81911cb5f8..6ea0ecba5e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1ba4f412325fc2ce552ad7feecf93891f7f95059 \ No newline at end of file +2c2de252666662f5459904fc33a9f2956cbff23c \ No newline at end of file diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 1202971a84..1e15dbd167 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1894,6 +1894,7 @@ struct WhereLevel { #define WHERE_OMIT_OPEN 0x0010 /* Table cursor 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 */ /* ** The WHERE clause processing routine has two halves. The @@ -1906,6 +1907,7 @@ struct WhereInfo { Parse *pParse; /* Parsing and code generating context */ u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */ u8 okOnePass; /* Ok to use one-pass algorithm for UPDATE or DELETE */ + u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */ SrcList *pTabList; /* List of tables in the join */ int iTop; /* The very beginning of the WHERE loop */ int iContinue; /* Jump here to continue with next record */ diff --git a/src/where.c b/src/where.c index 067ec4079b..90215edd8c 100644 --- a/src/where.c +++ b/src/where.c @@ -3265,13 +3265,14 @@ static Bitmask codeOneLoopStart( */ WhereClause *pOrWc; /* The OR-clause broken out into subterms */ WhereTerm *pFinal; /* Final subterm within the OR-clause. */ - SrcList oneTab; /* Shortened table list */ + SrcList *pOrTab; /* Shortened table list or OR-clause generation */ int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */ int regRowset = 0; /* Register for RowSet object */ int regRowid = 0; /* Register holding rowid */ int iLoopBody = sqlite3VdbeMakeLabel(v); /* Start of loop body */ int iRetInit; /* Address of regReturn init */ + int untestedTerms = 0; /* Some terms not completely tested */ int ii; pTerm = pLevel->plan.u.pTerm; @@ -3280,11 +3281,29 @@ static Bitmask codeOneLoopStart( assert( (pTerm->wtFlags & TERM_ORINFO)!=0 ); pOrWc = &pTerm->u.pOrInfo->wc; pFinal = &pOrWc->a[pOrWc->nTerm-1]; + pLevel->op = OP_Return; + pLevel->p1 = regReturn; - /* Set up a SrcList containing just the table being scanned by this loop. */ - oneTab.nSrc = 1; - oneTab.nAlloc = 1; - oneTab.a[0] = *pTabItem; + /* Set up a new SrcList ni pOrTab containing the table being scanned + ** by this loop in the a[0] slot and all notReady tables in a[1..] slots. + ** This becomes the SrcList in the recursive call to sqlite3WhereBegin(). + */ + if( pWInfo->nLevel>1 ){ + int nNotReady; /* The number of notReady tables */ + struct SrcList_item *origSrc; /* Original list of tables */ + nNotReady = pWInfo->nLevel - iLevel - 1; + pOrTab = sqlite3StackAllocRaw(pParse->db, + sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0])); + if( pOrTab==0 ) return notReady; + pOrTab->nSrc = pOrTab->nAlloc = nNotReady + 1; + memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem)); + origSrc = pWInfo->pTabList->a; + for(k=1; k<=nNotReady; k++){ + memcpy(&pOrTab->a[k], &origSrc[pLevel[k].iFrom], sizeof(pOrTab->a[k])); + } + }else{ + pOrTab = pWInfo->pTabList; + } /* Initialize the rowset register to contain NULL. An SQL NULL is ** equivalent to an empty rowset. @@ -3309,8 +3328,9 @@ static Bitmask codeOneLoopStart( if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){ WhereInfo *pSubWInfo; /* Info for single OR-term scan */ /* Loop through table entries that match term pOrTerm. */ - pSubWInfo = sqlite3WhereBegin(pParse, &oneTab, pOrTerm->pExpr, 0, - WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE | WHERE_FORCE_TABLE); + pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrTerm->pExpr, 0, + WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE | + WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY); if( pSubWInfo ){ if( (wctrlFlags & WHERE_DUPLICATES_OK)==0 ){ int iSet = ((ii==pOrWc->nTerm-1)?-1:ii); @@ -3322,19 +3342,24 @@ static Bitmask codeOneLoopStart( } sqlite3VdbeAddOp2(v, OP_Gosub, regReturn, iLoopBody); + /* The pSubWInfo->untestedTerms flag means that this OR term + ** contained one or more AND term from a notReady table. The + ** terms from the notReady table could not be tested and will + ** need to be tested later. + */ + if( pSubWInfo->untestedTerms ) untestedTerms = 1; + /* Finish the loop through table entries that match term pOrTerm. */ sqlite3WhereEnd(pSubWInfo); } } } sqlite3VdbeChangeP1(v, iRetInit, sqlite3VdbeCurrentAddr(v)); - /* sqlite3VdbeAddOp2(v, OP_Null, 0, regRowset); */ sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrBrk); sqlite3VdbeResolveLabel(v, iLoopBody); - pLevel->op = OP_Return; - pLevel->p1 = regReturn; - disableTerm(pLevel, pTerm); + if( pWInfo->nLevel>1 ) sqlite3StackFree(pParse->db, pOrTab); + if( !untestedTerms ) disableTerm(pLevel, pTerm); }else #endif /* SQLITE_OMIT_OR_OPTIMIZATION */ @@ -3362,7 +3387,12 @@ static Bitmask codeOneLoopStart( testcase( pTerm->wtFlags & TERM_VIRTUAL ); testcase( pTerm->wtFlags & TERM_CODED ); if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; - if( (pTerm->prereqAll & notReady)!=0 ) continue; + if( (pTerm->prereqAll & notReady)!=0 ){ + testcase( pWInfo->untestedTerms==0 + && (pWInfo->wctrlFlags & WHERE_ONETABLE_ONLY)!=0 ); + pWInfo->untestedTerms = 1; + continue; + } pE = pTerm->pExpr; assert( pE!=0 ); if( pLevel->iLeftJoin && !ExprHasProperty(pE, EP_FromJoin) ){ @@ -3385,7 +3415,12 @@ static Bitmask codeOneLoopStart( testcase( pTerm->wtFlags & TERM_VIRTUAL ); testcase( pTerm->wtFlags & TERM_CODED ); if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; - if( (pTerm->prereqAll & notReady)!=0 ) continue; + if( (pTerm->prereqAll & notReady)!=0 ){ + testcase( pWInfo->untestedTerms==0 + && (pWInfo->wctrlFlags & WHERE_ONETABLE_ONLY)!=0 ); + pWInfo->untestedTerms = 1; + continue; + } assert( pTerm->pExpr ); sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL); pTerm->wtFlags |= TERM_CODED; @@ -3528,6 +3563,7 @@ WhereInfo *sqlite3WhereBegin( ){ int i; /* Loop counter */ int nByteWInfo; /* Num. bytes allocated for WhereInfo struct */ + int nTabList; /* Number of elements in pTabList */ WhereInfo *pWInfo; /* Will become the return value of this function */ Vdbe *v = pParse->pVdbe; /* The virtual database engine */ Bitmask notReady; /* Cursors that are not yet positioned */ @@ -3547,6 +3583,13 @@ WhereInfo *sqlite3WhereBegin( return 0; } + /* This function normally generates a nested loop for all tables in + ** pTabList. But if the WHERE_ONETABLE_ONLY flag is set, then we should + ** only generate code for the first table in pTabList and assume that + ** any cursors associated with subsequent tables are uninitialized. + */ + nTabList = (wctrlFlags & WHERE_ONETABLE_ONLY) ? 1 : pTabList->nSrc; + /* Allocate and initialize the WhereInfo structure that will become the ** return value. A single allocation is used to store the WhereInfo ** struct, the contents of WhereInfo.a[], the WhereClause structure @@ -3555,7 +3598,7 @@ WhereInfo *sqlite3WhereBegin( ** some architectures. Hence the ROUND8() below. */ db = pParse->db; - nByteWInfo = ROUND8(sizeof(WhereInfo)+(pTabList->nSrc-1)*sizeof(WhereLevel)); + nByteWInfo = ROUND8(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel)); pWInfo = sqlite3DbMallocZero(db, nByteWInfo + sizeof(WhereClause) + @@ -3564,7 +3607,7 @@ WhereInfo *sqlite3WhereBegin( if( db->mallocFailed ){ goto whereBeginError; } - pWInfo->nLevel = pTabList->nSrc; + pWInfo->nLevel = nTabList; pWInfo->pParse = pParse; pWInfo->pTabList = pTabList; pWInfo->iBreak = sqlite3VdbeMakeLabel(v); @@ -3583,7 +3626,7 @@ WhereInfo *sqlite3WhereBegin( /* Special case: a WHERE clause that is constant. Evaluate the ** expression and either jump over all of the code or fall thru. */ - if( pWhere && (pTabList->nSrc==0 || sqlite3ExprIsConstantNotJoin(pWhere)) ){ + if( pWhere && (nTabList==0 || sqlite3ExprIsConstantNotJoin(pWhere)) ){ sqlite3ExprIfFalse(pParse, pWhere, pWInfo->iBreak, SQLITE_JUMPIFNULL); pWhere = 0; } @@ -3603,6 +3646,11 @@ WhereInfo *sqlite3WhereBegin( ** to virtual table cursors are set. This is used to selectively disable ** the OR-to-IN transformation in exprAnalyzeOrTerm(). It is not helpful ** with virtual tables. + ** + ** Note that bitmasks are created for all pTabList->nSrc tables in + ** pTabList, not just the first nTabList tables. nTabList is normally + ** equal to pTabList->nSrc but might be shortened to 1 if the + ** WHERE_ONETABLE_ONLY flag is set. */ assert( pWC->vmask==0 && pMaskSet->n==0 ); for(i=0; inSrc; i++){ @@ -3654,7 +3702,7 @@ WhereInfo *sqlite3WhereBegin( pLevel = pWInfo->a; andFlags = ~0; WHERETRACE(("*** Optimizer Start ***\n")); - for(i=iFrom=0, pLevel=pWInfo->a; inSrc; i++, pLevel++){ + for(i=iFrom=0, pLevel=pWInfo->a; i=0 && bestJ<0; isOptimal--){ Bitmask mask = (isOptimal ? 0 : notReady); - assert( (pTabList->nSrc-iFrom)>1 || isOptimal ); - for(j=iFrom, pTabItem=&pTabList->a[j]; jnSrc; j++, pTabItem++){ + assert( (nTabList-iFrom)>1 || isOptimal ); + for(j=iFrom, pTabItem=&pTabList->a[j]; ja; inSrc; i++, pLevel++){ + for(i=0, pLevel=pWInfo->a; inSrc; i++){ + for(i=0; iiContinue = pWInfo->a[i].addrCont; } @@ -3888,7 +3936,7 @@ WhereInfo *sqlite3WhereBegin( ** the index is listed as "{}". If the primary key is used the ** index name is '*'. */ - for(i=0; inSrc; i++){ + for(i=0; ia[i]; @@ -3956,7 +4004,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ /* Generate loop termination code. */ sqlite3ExprCacheClear(pParse); - for(i=pTabList->nSrc-1; i>=0; i--){ + for(i=pWInfo->nLevel-1; i>=0; i--){ pLevel = &pWInfo->a[i]; sqlite3VdbeResolveLabel(v, pLevel->addrCont); if( pLevel->op!=OP_Noop ){ @@ -4002,7 +4050,8 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ /* Close all of the cursors that were opened by sqlite3WhereBegin. */ - for(i=0, pLevel=pWInfo->a; inSrc; i++, pLevel++){ + assert( pWInfo->nLevel==1 || pWInfo->nLevel==pTabList->nSrc ); + for(i=0, pLevel=pWInfo->a; inLevel; i++, pLevel++){ struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom]; Table *pTab = pTabItem->pTab; assert( pTab!=0 ); diff --git a/test/tkt-31338dca7e.test b/test/tkt-31338dca7e.test new file mode 100644 index 0000000000..3f66816d64 --- /dev/null +++ b/test/tkt-31338dca7e.test @@ -0,0 +1,77 @@ +# 2009 December 16 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# +# This file implements tests to verify that ticket [31338dca7e] has been +# fixed. Ticket [31338dca7e] demonstrates problems with the OR-clause +# optimization in joins where the WHERE clause is of the form +# +# (x AND y) OR z +# +# And the x and y subterms from from different tables of the join. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test tkt-31338-1.1 { + db eval { + CREATE TABLE t1(x); + CREATE TABLE t2(y); + INSERT INTO t1 VALUES(111); + INSERT INTO t1 VALUES(222); + INSERT INTO t2 VALUES(333); + INSERT INTO t2 VALUES(444); + SELECT * FROM t1, t2 + WHERE (x=111 AND y!=444) OR x=222 + ORDER BY x, y; + } +} {111 333 222 333 222 444} + +do_test tkt-31338-1.2 { + db eval { + CREATE INDEX t1x ON t1(x); + SELECT * FROM t1, t2 + WHERE (x=111 AND y!=444) OR x=222 + ORDER BY x, y; + } +} {111 333 222 333 222 444} + +do_test tkt-31338-2.1 { + db eval { + CREATE TABLE t3(v,w); + CREATE TABLE t4(x,y); + CREATE TABLE t5(z); + INSERT INTO t3 VALUES(111,222); + INSERT INTO t3 VALUES(333,444); + INSERT INTO t4 VALUES(222,333); + INSERT INTO t4 VALUES(444,555); + INSERT INTO t5 VALUES(888); + INSERT INTO t5 VALUES(999); + + SELECT * FROM t3, t4, t5 + WHERE (v=111 AND x=w AND z!=999) OR (v=333 AND x=444) + ORDER BY v, w, x, y, z; + } +} {111 222 222 333 888 333 444 444 555 888 333 444 444 555 999} + +do_test tkt-31338-2.2 { + db eval { + CREATE INDEX t3v ON t3(v); + CREATE INDEX t4x ON t4(x); + SELECT * FROM t3, t4, t5 + WHERE (v=111 AND x=w AND z!=999) OR (v=333 AND x=444) + ORDER BY v, w, x, y, z; + } +} {111 222 222 333 888 333 444 444 555 888 333 444 444 555 999} + + +finish_test diff --git a/test/where8.test b/test/where8.test index 04146fe60a..04657c0473 100644 --- a/test/where8.test +++ b/test/where8.test @@ -398,6 +398,10 @@ do_test where8-4.1 { INSERT INTO t4 VALUES('his', 'of', 378678316.5); INSERT INTO t4 VALUES(271.2019091, 'viewed', 3282306647); INSERT INTO t4 VALUES('hills', 'all', 'peak'); + CREATE TABLE t5(s); + INSERT INTO t5 VALUES('tab-t5'); + CREATE TABLE t6(t); + INSERT INTO t6 VALUES(123456); COMMIT; } } {} @@ -639,6 +643,14 @@ foreach idxsql { 197 { SELECT * FROM t3, t4 WHERE g = 2643383279 AND f = g } 198 { SELECT * FROM t3, t4 WHERE g < 8979323846 } 199 { SELECT * FROM t3, t4 WHERE 'are' <= b } +200 { SELECT * FROM t3, t4 WHERE (a=1415926535 AND f=8628034825) + OR (a=6939937510 AND f=2643383279) } +201 { SELECT * FROM t3, t4, t5, t6 + WHERE (a=1415926535 AND f=8628034825 AND s!='hello' AND t!=5) + OR (a=6939937510 AND f=2643383279 AND s='tab-t5' AND t=123456) } +202 { SELECT * FROM t3, t4, t5, t6 + WHERE (a=1415926535 AND f=8628034825 AND s!='hello' AND t==5) + OR (a=6939937510 AND f=2643383279 AND s='tab-t5' AND t!=123456) } } { do_test where8-4.$A.$B.1 {