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
This commit is contained in:
drh 2009-12-16 22:10:49 +00:00
parent 27298ffde4
commit c01a3c1775
6 changed files with 185 additions and 34 deletions

View File

@ -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-----

View File

@ -1 +1 @@
1ba4f412325fc2ce552ad7feecf93891f7f95059
2c2de252666662f5459904fc33a9f2956cbff23c

View File

@ -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 */

View File

@ -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; i<pTabList->nSrc; i++){
@ -3654,7 +3702,7 @@ WhereInfo *sqlite3WhereBegin(
pLevel = pWInfo->a;
andFlags = ~0;
WHERETRACE(("*** Optimizer Start ***\n"));
for(i=iFrom=0, pLevel=pWInfo->a; i<pTabList->nSrc; i++, pLevel++){
for(i=iFrom=0, pLevel=pWInfo->a; i<nTabList; i++, pLevel++){
WhereCost bestPlan; /* Most efficient plan seen so far */
Index *pIdx; /* Index for FROM table at pTabItem */
int j; /* For looping over FROM tables */
@ -3699,8 +3747,8 @@ WhereInfo *sqlite3WhereBegin(
*/
for(isOptimal=1; isOptimal>=0 && bestJ<0; isOptimal--){
Bitmask mask = (isOptimal ? 0 : notReady);
assert( (pTabList->nSrc-iFrom)>1 || isOptimal );
for(j=iFrom, pTabItem=&pTabList->a[j]; j<pTabList->nSrc; j++, pTabItem++){
assert( (nTabList-iFrom)>1 || isOptimal );
for(j=iFrom, pTabItem=&pTabList->a[j]; j<nTabList; j++, pTabItem++){
int doNotReorder; /* True if this table should not be reordered */
WhereCost sCost; /* Cost information from best[Virtual]Index() */
ExprList *pOrderBy; /* ORDER BY clause for index to optimize */
@ -3797,7 +3845,7 @@ WhereInfo *sqlite3WhereBegin(
** searching those tables.
*/
sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */
for(i=0, pLevel=pWInfo->a; i<pTabList->nSrc; i++, pLevel++){
for(i=0, pLevel=pWInfo->a; i<nTabList; i++, pLevel++){
Table *pTab; /* Table to open */
int iDb; /* Index of database containing table/index */
@ -3876,7 +3924,7 @@ WhereInfo *sqlite3WhereBegin(
** program.
*/
notReady = ~(Bitmask)0;
for(i=0; i<pTabList->nSrc; i++){
for(i=0; i<nTabList; i++){
notReady = codeOneLoopStart(pWInfo, i, wctrlFlags, notReady);
pWInfo->iContinue = 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; i<pTabList->nSrc; i++){
for(i=0; i<nTabList; i++){
char *z;
int n;
pLevel = &pWInfo->a[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; i<pTabList->nSrc; i++, pLevel++){
assert( pWInfo->nLevel==1 || pWInfo->nLevel==pTabList->nSrc );
for(i=0, pLevel=pWInfo->a; i<pWInfo->nLevel; i++, pLevel++){
struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom];
Table *pTab = pTabItem->pTab;
assert( pTab!=0 );

77
test/tkt-31338dca7e.test Normal file
View File

@ -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

View File

@ -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 {