-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
-C Version\s3.6.21\srelease\scandidate\s3.
-D 2009-12-07T16:39:13
+C Fix\sfor\sthe\s"(x\sAND\sy)\sOR\sz"\sbug\sbackported\sto\sversion\s3.6.21.
+D 2009-12-16T23:28:32
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
F Makefile.in c5827ead754ab32b9585487177c93bb00b9497b3
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
F Makefile.vxworks 10010ddbf52e2503c7c49c7c0b7c7a096f8638a6
F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6
-F VERSION aa30c4b131a981624ab576df19eb4c5c9cd582ac
+F VERSION 1e6bf6cfcc755751b88c848e449f7ff02a049511
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
F addopcodes.awk 17dc593f791f874d2c23a0f9360850ded0286531
F art/2005osaward.gif 0d1851b2a7c1c9d0ccce545f3e14bca42d7fd248
F src/shell.c f4948cb6d30665d755a6b5e0ec313d1094aab828
F src/sqlite.h.in 2d34605565e021851255e0bbcb15f8c1930d1f6f
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
F src/vdbetrace.c 864cef96919323482ebd9986f2132435115e9cc2
F src/vtab.c 456fc226614569f0e46f216e33265bea268bd917
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
-F src/where.c 11b5b00c49d53e767a7eb855bc60790edeca6185
+F src/where.c f20ee9a15c1ccb8b94cd31e3f8c6868e1358fc07
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
F test/all.test 14165b3e32715b700b5f0cbf8f6e3833dda0be45
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
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
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
-P cd50acf37fd1e3b388f98fb2df7ed03cff454b24
-R 5d0b6a47a50c8a9cdc60d3d4d61a8cf2
+P 1ed88e9d01e9eda5cbc622e7614277f29bcc551c
+R e7d1ff5fd98ae55ae61bf91d3d4df1c9
U drh
-Z 8b76b0c0726e570aef93558fa0c3fdc7
+Z 49479a3b1562abdd14a885a8865e0fa5
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
-iD8DBQFLHS+0oxKgR168RlERArAKAJ97NTyJvzQHmWC2GXt2Xt7D52m04QCeLeQt
-HYPTNDWgvT/KRZ19RtHNjDQ=
-=tE2+
+iD8DBQFLKW0joxKgR168RlERAlZgAJoDPgJ9k9EK/kFQwfqfQ0gY5Pb57wCePWTc
+6VlIsxRe62S0m1PuLBEsM9E=
+=g+YX
-----END PGP SIGNATURE-----
*/
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;
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.
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);
}
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 */
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) ){
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 ){
+ assert( pWInfo->untestedTerms );
+ continue;
+ }
assert( pTerm->pExpr );
sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL);
pTerm->wtFlags |= TERM_CODED;
){
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 */
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
** 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) +
if( db->mallocFailed ){
goto whereBeginError;
}
- pWInfo->nLevel = pTabList->nSrc;
+ pWInfo->nLevel = nTabList;
pWInfo->pParse = pParse;
pWInfo->pTabList = pTabList;
pWInfo->iBreak = sqlite3VdbeMakeLabel(v);
/* 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;
}
** 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++){
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 */
*/
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 */
** 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 */
** 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;
}
** 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];
/* 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 ){
/* 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 );
--- /dev/null
+# 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