From: drh Date: Tue, 23 Dec 2008 23:56:22 +0000 (+0000) Subject: Continuing improvements to the multi-index OR-clause optimizer. Added a X-Git-Tag: version-3.6.10~122 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=23d04d5a204a728219255a3a2c1e6c6af7b9d73a;p=thirdparty%2Fsqlite.git Continuing improvements to the multi-index OR-clause optimizer. Added a few simple test cases. (CVS 6062) FossilOrigin-Name: 55d4f493e7df8515574a75caec9967d6c71b6012 --- diff --git a/manifest b/manifest index 85a395b528..a060a536f3 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sproblem\swith\sthe\ssavepoint\scode\sand\sin-memory\sjournals.\s(CVS\s6061) -D 2008-12-23T19:15:57 +C Continuing\simprovements\sto\sthe\smulti-index\sOR-clause\soptimizer.\s\sAdded\sa\nfew\ssimple\stest\scases.\s(CVS\s6062) +D 2008-12-23T23:56:22 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in 77635d0909c2067cee03889a1e04ce910d8fb809 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -110,7 +110,7 @@ F src/build.c 92335a6c6a7c119580be605c5dd1439602d6cf5d F src/callback.c bee8949d619b1b7b1e4dfac8a19c5116ae1dd12a F src/complete.c cb14e06dbe79dee031031f0d9e686ff306afe07c F src/date.c e010095d85c895761627bb1d0d61d021aea930a9 -F src/delete.c e2392b6808496fc0a7f54662af3ba677a3e8e44a +F src/delete.c 6249005bdd8f85db6ec5f31ddb5c07de023693cc F src/expr.c a385202af56d622b11d05e8d386def256155152b F src/fault.c dc88c821842157460750d2d61a8a8b4197d047ff F src/func.c b0e1c61301f33d67b72ab15d85c80ed76e7c98ac @@ -154,11 +154,11 @@ F src/printf.c 9866a9a9c4a90f6d4147407f373df3fd5d5f9b6f F src/random.c 676b9d7ac820fe81e6fb2394ac8c10cff7f38628 F src/resolve.c 18dc9f0df1d60048e012ce6632251063e0dd356a F src/rowset.c 2256fa4a928f750e2f3d6fc733523034beceb1d6 -F src/select.c a4316c5e8a417687e159b3d3ae689363d1dec5df +F src/select.c 6c2a5675c21bef11d8160f3dc97e1adfbf26bbb9 F src/shell.c 65d19f8996a160f288087e31810f24025439c62a F src/sqlite.h.in 065a828e299960316aa34f05b9f0f10f33afe4c8 F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17 -F src/sqliteInt.h 9411acda2959c3494bafb1ac98048a53ee920ea3 +F src/sqliteInt.h 2362e805d375c547f6d91d4732da8f93e1e668af F src/sqliteLimit.h f435e728c6b620ef7312814d660a81f9356eb5c8 F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76 F src/table.c 23db1e5f27c03160987c122a078b4bb51ef0b2f8 @@ -194,7 +194,7 @@ F src/test_thread.c d74fc445e0dba0e00806117eb449b307c0b146bf F src/test_wsd.c c297d7d6b8a990239e1bd25935e81d612d8ae31d F src/tokenize.c aaa5fa6a4536a9dd7c855a3f66f32508f1612138 F src/trigger.c 5a669d8fc9197db393ff85fa95ec882282162bb5 -F src/update.c 080889d241e4dcd1c545c8051eb6de86f4939295 +F src/update.c 8c4925f9ca664effc8a1faaad67449d2074567b1 F src/utf.c 1da9c832dba0fa8f865b5b902d93f420a1ee4245 F src/util.c ea62608f66f33a7e8322de83024ae37c415c0c7f F src/vacuum.c 383d6297bddc011ab04a9eed110db6eaf523e8e9 @@ -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 3f4e8020d605b06139b877a9714a10cc3ba63906 +F src/where.c ef69833cdf0e19a91ec1b305b506d100ac3f648f F tclinstaller.tcl 4356d9d94d2b5ed5e68f9f0c80c4df3048dd7617 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/alias.test 597662c5d777a122f9a3df0047ea5c5bd383a911 @@ -654,6 +654,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/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31 F test/zeroblob.test 792124852ec61458a2eb527b5091791215e0be95 F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b @@ -684,7 +685,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e -P d2105f617eeb04c8177546c45bf6c63e72757f91 -R 62a2a7b3ecef75a74f7c0626bd24ccb4 -U danielk1977 -Z 52a2a6db5bd35226b01afc59433c7e60 +P 26ceebf38e7ae7bbda3284995b03f829a2d2493f +R 7db5298c8cbec76d68072c3747b105ad +U drh +Z 555ee7a0ef7229cf7451a3a18c0b778e diff --git a/manifest.uuid b/manifest.uuid index 2aff376120..161065fe89 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -26ceebf38e7ae7bbda3284995b03f829a2d2493f \ No newline at end of file +55d4f493e7df8515574a75caec9967d6c71b6012 \ No newline at end of file diff --git a/src/delete.c b/src/delete.c index 934b76da9c..eb683f59b1 100644 --- a/src/delete.c +++ b/src/delete.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** in order to generate code for DELETE FROM statements. ** -** $Id: delete.c,v 1.190 2008/12/10 21:19:57 drh Exp $ +** $Id: delete.c,v 1.191 2008/12/23 23:56:22 drh Exp $ */ #include "sqliteInt.h" @@ -392,21 +392,15 @@ void sqlite3DeleteFrom( int iRowid = ++pParse->nMem; /* Used for storing rowid values. */ int iRowSet = ++pParse->nMem; /* Register for rowset of rows to delete */ - /* Begin the database scan + /* Collect rowids of every row to be deleted. */ - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0); + sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet); + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, + WHERE_FILL_ROWSET, iRowSet); if( pWInfo==0 ) goto delete_from_cleanup; - - /* Remember the rowid of every item to be deleted. - */ - sqlite3VdbeAddOp2(v, IsVirtual(pTab) ? OP_VRowid : OP_Rowid, iCur, iRowid); - sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iRowid); if( db->flags & SQLITE_CountRows ){ sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1); } - - /* End the database scan loop. - */ sqlite3WhereEnd(pWInfo); /* Open the pseudo-table used to store OLD if there are triggers. diff --git a/src/select.c b/src/select.c index 03046de31b..e83dda11d6 100644 --- a/src/select.c +++ b/src/select.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle SELECT statements in SQLite. ** -** $Id: select.c,v 1.494 2008/12/10 22:15:00 drh Exp $ +** $Id: select.c,v 1.495 2008/12/23 23:56:22 drh Exp $ */ #include "sqliteInt.h" @@ -3705,7 +3705,7 @@ int sqlite3Select( /* This case is for non-aggregate queries ** Begin the database scan */ - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, 0); + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, 0, 0); if( pWInfo==0 ) goto select_end; /* If sorting index that was created by a prior OP_OpenEphemeral @@ -3826,7 +3826,7 @@ int sqlite3Select( ** in the right order to begin with. */ sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset); - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy, 0); + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy, 0, 0); if( pWInfo==0 ) goto select_end; if( pGroupBy==0 ){ /* The optimizer is able to deliver rows in group by order so @@ -4024,7 +4024,7 @@ int sqlite3Select( ** of output. */ resetAccumulator(pParse, &sAggInfo); - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, flag); + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, flag, 0); if( pWInfo==0 ){ sqlite3ExprListDelete(db, pDel); goto select_end; diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 56d07f8a4a..221813b818 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.814 2008/12/23 13:35:23 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.815 2008/12/23 23:56:22 drh Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -1595,10 +1595,11 @@ struct WhereLevel { /* ** Flags appropriate for the wctrlFlags parameter of sqlite3WhereBegin(). */ -#define WHERE_ORDERBY_NORMAL 0 /* No-op */ -#define WHERE_ORDERBY_MIN 1 /* ORDER BY processing for min() func */ -#define WHERE_ORDERBY_MAX 2 /* ORDER BY processing for max() func */ -#define WHERE_ONEPASS_DESIRED 4 /* Want to do one-pass UPDATE/DELETE */ +#define WHERE_ORDERBY_NORMAL 0x000 /* No-op */ +#define WHERE_ORDERBY_MIN 0x001 /* ORDER BY processing for min() func */ +#define WHERE_ORDERBY_MAX 0x002 /* ORDER BY processing for max() func */ +#define WHERE_ONEPASS_DESIRED 0x004 /* Want to do one-pass UPDATE/DELETE */ +#define WHERE_FILL_ROWSET 0x008 /* Save results in a RowSet object */ /* ** The WHERE clause processing routine has two halves. The @@ -1610,13 +1611,13 @@ struct WhereLevel { struct WhereInfo { Parse *pParse; /* Parsing and code generating context */ u8 okOnePass; /* Ok to use one-pass algorithm for UPDATE or DELETE */ + int regRowSet; /* Store rowids in this rowset if >=0 */ 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 */ int iBreak; /* Jump here to break out of the loop */ int nLevel; /* Number of nested loop */ struct WhereClause *pWC; /* Decomposition of the WHERE clause */ - sqlite3_index_info **apInfo; /* Array of pointers to index info objects */ WhereLevel a[1]; /* Information about each nest loop in WHERE */ }; @@ -2260,7 +2261,7 @@ Expr *sqlite3LimitWhere(Parse *, SrcList *, Expr *, ExprList *, Expr *, Expr *, #endif void sqlite3DeleteFrom(Parse*, SrcList*, Expr*); void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int); -WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**, u8); +WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**, u8, int); void sqlite3WhereEnd(WhereInfo*); int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, int); void sqlite3ExprCodeMove(Parse*, int, int, int); diff --git a/src/update.c b/src/update.c index 24ad882e44..cd1fd218f9 100644 --- a/src/update.c +++ b/src/update.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. ** -** $Id: update.c,v 1.190 2008/12/10 22:15:00 drh Exp $ +** $Id: update.c,v 1.191 2008/12/23 23:56:22 drh Exp $ */ #include "sqliteInt.h" @@ -346,7 +346,7 @@ void sqlite3Update( */ sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, - WHERE_ONEPASS_DESIRED); + WHERE_ONEPASS_DESIRED, 0); if( pWInfo==0 ) goto update_cleanup; okOnePass = pWInfo->okOnePass; diff --git a/src/where.c b/src/where.c index 8f785e5647..aac9b39105 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.342 2008/12/23 16:23:05 drh Exp $ +** $Id: where.c,v 1.343 2008/12/23 23:56:22 drh Exp $ */ #include "sqliteInt.h" @@ -1814,7 +1814,7 @@ static void bestIndex( } } -#if SQLITE_ENABLE_MULTI_OR +#ifndef SQLITE_OMIT_OR_OPTIMIZATION /* Search for an OR-clause that can be used to look up the table. */ maskSrc = getMask(pWC->pMaskSet, iCur); @@ -1850,7 +1850,7 @@ static void bestIndex( } } } -#endif +#endif /* SQLITE_OMIT_OR_OPTIMIZATION */ /* If the pSrc table is the right table of a LEFT JOIN then we may not ** use an index to satisfy IS NULL constraints on that table. This is @@ -2174,6 +2174,25 @@ static int codeAllEqualityTerms( return regBase; } +/* +** Return TRUE if the WhereClause pWC contains no terms that +** are not virtual and which have not been coded. +** +** To put it another way, return TRUE if no additional WHERE clauses +** tests are required in order to establish that the current row +** should go to output and return FALSE if there are some terms of +** the WHERE clause that need to be validated before outputing the row. +*/ +static int whereRowReadyForOutput(WhereClause *pWC){ + WhereTerm *pTerm; + int j; + + for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){ + if( (pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED))==0 ) return 0; + } + return 1; +} + /* ** Generate code for the start of the iLevel-th loop in the WHERE clause ** implementation described by pWInfo. @@ -2195,8 +2214,10 @@ static Bitmask codeOneLoopStart( Parse *pParse; /* Parsing context */ Vdbe *v; /* The prepared stmt under constructions */ struct SrcList_item *pTabItem; /* FROM clause term being coded */ - int addrBrk; - int addrCont; + int addrBrk; /* Jump here to break out of the loop */ + int addrCont; /* Jump here to continue with next cycle */ + int regRowSet; /* Write rowids to this RowSet if non-negative */ + int codeRowSetEarly; /* True if index fully constrains the search */ pParse = pWInfo->pParse; @@ -2207,6 +2228,8 @@ static Bitmask codeOneLoopStart( iCur = pTabItem->iCursor; bRev = (pLevel->plan.wsFlags & WHERE_REVERSE)!=0; omitTable = (pLevel->plan.wsFlags & WHERE_IDX_ONLY)!=0; + regRowSet = pWInfo->regRowSet; + codeRowSetEarly = 0; /* Create labels for the "break" and "continue" instructions ** for the current loop. Jump to addrBrk to break out of a loop. @@ -2263,7 +2286,6 @@ static Bitmask codeOneLoopStart( sqlite3VdbeAddOp2(v, OP_Integer, j-1, iReg+1); sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrBrk, iReg, pVtabIdx->idxStr, pVtabIdx->needToFreeIdxStr ? P4_MPRINTF : P4_STATIC); - sqlite3ReleaseTempRange(pParse, iReg, nConstraint+2); pVtabIdx->needToFreeIdxStr = 0; for(j=0; jop = OP_VNext; pLevel->p1 = iCur; pLevel->p2 = sqlite3VdbeCurrentAddr(v); + codeRowSetEarly = regRowSet>=0 ? whereRowReadyForOutput(pWC) : 0; + if( codeRowSetEarly ){ + sqlite3VdbeAddOp2(v, OP_VRowid, iCur, iReg); + sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, iReg); + } + sqlite3ReleaseTempRange(pParse, iReg, nConstraint+2); }else #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -2294,6 +2322,10 @@ static Bitmask codeOneLoopStart( addrNxt = pLevel->addrNxt; sqlite3VdbeAddOp2(v, OP_MustBeInt, r1, addrNxt); sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addrNxt, r1); + codeRowSetEarly = (pWC->nTerm==1 && regRowSet>=0) ?1:0; + if( codeRowSetEarly ){ + sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, r1); + } sqlite3ReleaseTempReg(pParse, rtmp); VdbeComment((v, "pk")); pLevel->op = OP_Noop; @@ -2360,11 +2392,17 @@ static Bitmask codeOneLoopStart( pLevel->op = bRev ? OP_Prev : OP_Next; pLevel->p1 = iCur; pLevel->p2 = start; - if( testOp!=OP_Noop ){ + codeRowSetEarly = regRowSet>=0 ? whereRowReadyForOutput(pWC) : 0; + if( codeRowSetEarly || testOp!=OP_Noop ){ int r1 = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp2(v, OP_Rowid, iCur, r1); - sqlite3VdbeAddOp3(v, testOp, memEndValue, addrBrk, r1); - sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC | SQLITE_JUMPIFNULL); + if( testOp!=OP_Noop ){ + sqlite3VdbeAddOp3(v, testOp, memEndValue, addrBrk, r1); + sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC | SQLITE_JUMPIFNULL); + } + if( codeRowSetEarly ){ + sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, r1); + } sqlite3ReleaseTempReg(pParse, r1); } }else if( pLevel->plan.wsFlags & (WHERE_COLUMN_RANGE|WHERE_COLUMN_EQ) ){ @@ -2547,9 +2585,16 @@ static Bitmask codeOneLoopStart( } /* Seek the table cursor, if required */ - if( !omitTable ){ + disableTerm(pLevel, pRangeStart); + disableTerm(pLevel, pRangeEnd); + codeRowSetEarly = regRowSet>=0 ? whereRowReadyForOutput(pWC) : 0; + if( !omitTable || codeRowSetEarly ){ sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, r1); - sqlite3VdbeAddOp2(v, OP_Seek, iCur, r1); /* Deferred seek */ + if( codeRowSetEarly ){ + sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, r1); + }else{ + sqlite3VdbeAddOp2(v, OP_Seek, iCur, r1); /* Deferred seek */ + } } sqlite3ReleaseTempReg(pParse, r1); @@ -2558,11 +2603,9 @@ static Bitmask codeOneLoopStart( */ pLevel->op = bRev ? OP_Prev : OP_Next; pLevel->p1 = iIdxCur; - disableTerm(pLevel, pRangeStart); - disableTerm(pLevel, pRangeEnd); }else -#if SQLITE_ENABLE_MULTI_OR +#ifndef SQLITE_OMIT_OR_OPTIMIZATION if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){ /* Case 4: Two or more separately indexed terms connected by OR ** @@ -2591,7 +2634,7 @@ static Bitmask codeOneLoopStart( ** Goto 0, A ** B: */ - int regRowset; /* Register holding the RowSet object */ + int regOrRowset; /* Register holding the RowSet object */ int regNextRowid; /* Register holding next rowid */ WhereTerm *pTerm; /* The complete OR-clause */ WhereClause *pOrWc; /* The OR-clause broken out into subterms */ @@ -2603,33 +2646,41 @@ static Bitmask codeOneLoopStart( assert( pTerm->eOperator==WO_OR ); assert( (pTerm->wtFlags & TERM_ORINFO)!=0 ); pOrWc = &pTerm->u.pOrInfo->wc; - - regRowset = sqlite3GetTempReg(pParse); - regNextRowid = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp2(v, OP_Null, 0, regRowset); + codeRowSetEarly = (regRowSet>=0 && pWC->nTerm==1) ?1:0; + + if( codeRowSetEarly ){ + regOrRowset = regRowSet; + }else{ + regOrRowset = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp2(v, OP_Null, 0, regOrRowset); + } oneTab.nSrc = 1; oneTab.nAlloc = 1; oneTab.a[0] = *pTabItem; for(j=0, pOrTerm=pOrWc->a; jnTerm; j++, pOrTerm++){ WhereInfo *pSubWInfo; if( pOrTerm->leftCursor!=iCur ) continue; - pSubWInfo = sqlite3WhereBegin(pParse, &oneTab, pOrTerm->pExpr, 0, 0); + pSubWInfo = sqlite3WhereBegin(pParse, &oneTab, pOrTerm->pExpr, 0, + WHERE_FILL_ROWSET, regOrRowset); if( pSubWInfo ){ - sqlite3VdbeAddOp2(v, OP_Rowid, oneTab.a[0].iCursor, regNextRowid); - sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowset, regNextRowid); pSubWInfo->a[0].plan.wsFlags |= WHERE_IDX_ONLY; sqlite3WhereEnd(pSubWInfo); } } sqlite3VdbeResolveLabel(v, addrCont); - addrCont = - sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowset, addrBrk, regNextRowid); - sqlite3VdbeAddOp2(v, OP_Seek, iCur, regNextRowid); - sqlite3ReleaseTempReg(pParse, regNextRowid); - pLevel->op = OP_Goto; - pLevel->p2 = addrCont; + if( !codeRowSetEarly ){ + regNextRowid = sqlite3GetTempReg(pParse); + addrCont = + sqlite3VdbeAddOp3(v, OP_RowSetRead, regOrRowset,addrBrk,regNextRowid); + sqlite3VdbeAddOp2(v, OP_Seek, iCur, regNextRowid); + sqlite3ReleaseTempReg(pParse, regNextRowid); + /* sqlite3ReleaseTempReg(pParse, regOrRowset); // Preserve the RowSet */ + pLevel->op = OP_Goto; + pLevel->p2 = addrCont; + } + disableTerm(pLevel, pTerm); }else -#endif /* SQLITE_ENABLE_MULTI_OR */ +#endif /* SQLITE_OMIT_OR_OPTIMIZATION */ { /* Case 5: There is no usable index. We must do a complete @@ -2641,6 +2692,7 @@ static Bitmask codeOneLoopStart( pLevel->p1 = iCur; pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, OP_Rewind, iCur, addrBrk); pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; + codeRowSetEarly = 0; } notReady &= ~getMask(pWC->pMaskSet, iCur); @@ -2685,6 +2737,25 @@ static Bitmask codeOneLoopStart( pTerm->wtFlags |= TERM_CODED; } } + + /* + ** If it was requested to store the results in a rowset and that has + ** not already been do, then do so now. + */ + if( regRowSet>=0 && !codeRowSetEarly ){ + int r1 = sqlite3GetTempReg(pParse); +#ifndef SQLITE_OMIT_VIRTUALTABLE + if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){ + sqlite3VdbeAddOp2(v, OP_VRowid, iCur, r1); + }else +#endif + { + sqlite3VdbeAddOp2(v, OP_Rowid, iCur, r1); + } + sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, r1); + sqlite3ReleaseTempReg(pParse, r1); + } + return notReady; } @@ -2813,7 +2884,8 @@ WhereInfo *sqlite3WhereBegin( SrcList *pTabList, /* A list of all tables to be scanned */ Expr *pWhere, /* The WHERE clause */ ExprList **ppOrderBy, /* An ORDER BY clause, or NULL */ - u8 wctrlFlags /* One of the WHERE_* flags defined in sqliteInt.h */ + u8 wctrlFlags, /* One of the WHERE_* flags defined in sqliteInt.h */ + int regRowSet /* Register hold RowSet if WHERE_FILL_ROWSET is set */ ){ int i; /* Loop counter */ WhereInfo *pWInfo; /* Will become the return value of this function */ @@ -2858,6 +2930,7 @@ WhereInfo *sqlite3WhereBegin( pWInfo->pParse = pParse; pWInfo->pTabList = pTabList; pWInfo->iBreak = sqlite3VdbeMakeLabel(v); + pWInfo->regRowSet = (wctrlFlags & WHERE_FILL_ROWSET) ? regRowSet : -1; pWInfo->pWC = pWC = (WhereClause*)&pWInfo->a[pWInfo->nLevel]; pMaskSet = (WhereMaskSet*)&pWC[1]; diff --git a/test/where7.test b/test/where7.test new file mode 100644 index 0000000000..e7ca4f73d6 --- /dev/null +++ b/test/where7.test @@ -0,0 +1,94 @@ +# 2008 December 23 +# +# 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. 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 $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !or_opt { + finish_test + return +} + +# Evaluate SQL. Return the result set followed by the +# and the number of full-scan steps. +# +proc count_steps {sql} { + set r [db eval $sql] + lappend r scan [db status step] +} + +# Build some test data +# +do_test where7-1.1 { + execsql { + CREATE TABLE t1(a INTEGER PRIMARY KEY,b,c,d); + INSERT INTO t1 VALUES(1,2,3,4); + INSERT INTO t1 VALUES(2,3,4,5); + INSERT INTO t1 VALUES(3,4,6,8); + INSERT INTO t1 VALUES(4,5,10,15); + INSERT INTO t1 VALUES(5,10,100,1000); + CREATE INDEX t1b ON t1(b); + CREATE INDEX t1c ON t1(c); + SELECT * FROM t1; + } +} {1 2 3 4 2 3 4 5 3 4 6 8 4 5 10 15 5 10 100 1000} +do_test where7-1.2 { + count_steps { + SELECT a FROM t1 WHERE b=3 OR c=6 + } +} {2 3 scan 0} +do_test where7-1.3 { + count_steps { + SELECT a FROM t1 WHERE b=3 OR +c=6 + } +} {2 3 scan 4} +do_test where7-1.4 { + count_steps { + SELECT a FROM t1 WHERE +b=3 OR c=6 + } +} {2 3 scan 4} +do_test where7-1.5 { + count_steps { + SELECT a FROM t1 WHERE 3=b OR c=6 + } +} {2 3 scan 0} +do_test where7-1.6 { + count_steps { + SELECT a FROM t1 WHERE (3=b OR c=6) AND +a>0 + } +} {2 3 scan 0} +do_test where7-1.7 { + count_steps { + SELECT a FROM t1 WHERE (b=3 OR c>10) + } +} {2 5 scan 0} +do_test where7-1.8 { + count_steps { + SELECT a FROM t1 WHERE (b=3 OR c>=10) + } +} {2 4 5 scan 0} +do_test where7-1.9 { + count_steps { + SELECT a FROM t1 WHERE (b=3 OR c>=10 OR c=4) + } +} {2 4 5 scan 0} +do_test where7-1.10 { + count_steps { + SELECT a FROM t1 WHERE (b=3 OR c>=10 OR c=4 OR b>10) + } +} {2 4 5 scan 0} + + +finish_test