1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-06 15:49:35 +03:00

Continuing improvements to the multi-index OR-clause optimizer. Added a

few simple test cases. (CVS 6062)

FossilOrigin-Name: 55d4f493e7df8515574a75caec9967d6c71b6012
This commit is contained in:
drh
2008-12-23 23:56:22 +00:00
parent 67ddef6922
commit 23d04d5a20
8 changed files with 229 additions and 66 deletions

View File

@@ -1,5 +1,5 @@
C Fix\sa\sproblem\swith\sthe\ssavepoint\scode\sand\sin-memory\sjournals.\s(CVS\s6061) C Continuing\simprovements\sto\sthe\smulti-index\sOR-clause\soptimizer.\s\sAdded\sa\nfew\ssimple\stest\scases.\s(CVS\s6062)
D 2008-12-23T19:15:57 D 2008-12-23T23:56:22
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
F Makefile.in 77635d0909c2067cee03889a1e04ce910d8fb809 F Makefile.in 77635d0909c2067cee03889a1e04ce910d8fb809
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -110,7 +110,7 @@ F src/build.c 92335a6c6a7c119580be605c5dd1439602d6cf5d
F src/callback.c bee8949d619b1b7b1e4dfac8a19c5116ae1dd12a F src/callback.c bee8949d619b1b7b1e4dfac8a19c5116ae1dd12a
F src/complete.c cb14e06dbe79dee031031f0d9e686ff306afe07c F src/complete.c cb14e06dbe79dee031031f0d9e686ff306afe07c
F src/date.c e010095d85c895761627bb1d0d61d021aea930a9 F src/date.c e010095d85c895761627bb1d0d61d021aea930a9
F src/delete.c e2392b6808496fc0a7f54662af3ba677a3e8e44a F src/delete.c 6249005bdd8f85db6ec5f31ddb5c07de023693cc
F src/expr.c a385202af56d622b11d05e8d386def256155152b F src/expr.c a385202af56d622b11d05e8d386def256155152b
F src/fault.c dc88c821842157460750d2d61a8a8b4197d047ff F src/fault.c dc88c821842157460750d2d61a8a8b4197d047ff
F src/func.c b0e1c61301f33d67b72ab15d85c80ed76e7c98ac F src/func.c b0e1c61301f33d67b72ab15d85c80ed76e7c98ac
@@ -154,11 +154,11 @@ F src/printf.c 9866a9a9c4a90f6d4147407f373df3fd5d5f9b6f
F src/random.c 676b9d7ac820fe81e6fb2394ac8c10cff7f38628 F src/random.c 676b9d7ac820fe81e6fb2394ac8c10cff7f38628
F src/resolve.c 18dc9f0df1d60048e012ce6632251063e0dd356a F src/resolve.c 18dc9f0df1d60048e012ce6632251063e0dd356a
F src/rowset.c 2256fa4a928f750e2f3d6fc733523034beceb1d6 F src/rowset.c 2256fa4a928f750e2f3d6fc733523034beceb1d6
F src/select.c a4316c5e8a417687e159b3d3ae689363d1dec5df F src/select.c 6c2a5675c21bef11d8160f3dc97e1adfbf26bbb9
F src/shell.c 65d19f8996a160f288087e31810f24025439c62a F src/shell.c 65d19f8996a160f288087e31810f24025439c62a
F src/sqlite.h.in 065a828e299960316aa34f05b9f0f10f33afe4c8 F src/sqlite.h.in 065a828e299960316aa34f05b9f0f10f33afe4c8
F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17 F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17
F src/sqliteInt.h 9411acda2959c3494bafb1ac98048a53ee920ea3 F src/sqliteInt.h 2362e805d375c547f6d91d4732da8f93e1e668af
F src/sqliteLimit.h f435e728c6b620ef7312814d660a81f9356eb5c8 F src/sqliteLimit.h f435e728c6b620ef7312814d660a81f9356eb5c8
F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76 F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76
F src/table.c 23db1e5f27c03160987c122a078b4bb51ef0b2f8 F src/table.c 23db1e5f27c03160987c122a078b4bb51ef0b2f8
@@ -194,7 +194,7 @@ F src/test_thread.c d74fc445e0dba0e00806117eb449b307c0b146bf
F src/test_wsd.c c297d7d6b8a990239e1bd25935e81d612d8ae31d F src/test_wsd.c c297d7d6b8a990239e1bd25935e81d612d8ae31d
F src/tokenize.c aaa5fa6a4536a9dd7c855a3f66f32508f1612138 F src/tokenize.c aaa5fa6a4536a9dd7c855a3f66f32508f1612138
F src/trigger.c 5a669d8fc9197db393ff85fa95ec882282162bb5 F src/trigger.c 5a669d8fc9197db393ff85fa95ec882282162bb5
F src/update.c 080889d241e4dcd1c545c8051eb6de86f4939295 F src/update.c 8c4925f9ca664effc8a1faaad67449d2074567b1
F src/utf.c 1da9c832dba0fa8f865b5b902d93f420a1ee4245 F src/utf.c 1da9c832dba0fa8f865b5b902d93f420a1ee4245
F src/util.c ea62608f66f33a7e8322de83024ae37c415c0c7f F src/util.c ea62608f66f33a7e8322de83024ae37c415c0c7f
F src/vacuum.c 383d6297bddc011ab04a9eed110db6eaf523e8e9 F src/vacuum.c 383d6297bddc011ab04a9eed110db6eaf523e8e9
@@ -207,7 +207,7 @@ F src/vdbeblob.c b0dcebfafedcf9c0addc7901ad98f6f986c08935
F src/vdbemem.c f9c859ac17e2e05a0f249868ce4f191f69edd31d F src/vdbemem.c f9c859ac17e2e05a0f249868ce4f191f69edd31d
F src/vtab.c e39e011d7443a8d574b1b9cde207a35522e6df43 F src/vtab.c e39e011d7443a8d574b1b9cde207a35522e6df43
F src/walker.c 488c2660e13224ff70c0c82761118efb547f8f0d F src/walker.c 488c2660e13224ff70c0c82761118efb547f8f0d
F src/where.c 3f4e8020d605b06139b877a9714a10cc3ba63906 F src/where.c ef69833cdf0e19a91ec1b305b506d100ac3f648f
F tclinstaller.tcl 4356d9d94d2b5ed5e68f9f0c80c4df3048dd7617 F tclinstaller.tcl 4356d9d94d2b5ed5e68f9f0c80c4df3048dd7617
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
F test/alias.test 597662c5d777a122f9a3df0047ea5c5bd383a911 F test/alias.test 597662c5d777a122f9a3df0047ea5c5bd383a911
@@ -654,6 +654,7 @@ F test/where3.test 97d3936e6a443b968f1a61cdcc0f673252000e94
F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2 F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2
F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2 F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2
F test/where6.test 42c4373595f4409d9c6a9987b4a60000ad664faf F test/where6.test 42c4373595f4409d9c6a9987b4a60000ad664faf
F test/where7.test b04da5cee08a573c120c95781d7413a7e25ac8d5
F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31 F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
F test/zeroblob.test 792124852ec61458a2eb527b5091791215e0be95 F test/zeroblob.test 792124852ec61458a2eb527b5091791215e0be95
F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
@@ -684,7 +685,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
P d2105f617eeb04c8177546c45bf6c63e72757f91 P 26ceebf38e7ae7bbda3284995b03f829a2d2493f
R 62a2a7b3ecef75a74f7c0626bd24ccb4 R 7db5298c8cbec76d68072c3747b105ad
U danielk1977 U drh
Z 52a2a6db5bd35226b01afc59433c7e60 Z 555ee7a0ef7229cf7451a3a18c0b778e

View File

@@ -1 +1 @@
26ceebf38e7ae7bbda3284995b03f829a2d2493f 55d4f493e7df8515574a75caec9967d6c71b6012

View File

@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** in order to generate code for DELETE FROM statements. ** 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" #include "sqliteInt.h"
@@ -392,21 +392,15 @@ void sqlite3DeleteFrom(
int iRowid = ++pParse->nMem; /* Used for storing rowid values. */ int iRowid = ++pParse->nMem; /* Used for storing rowid values. */
int iRowSet = ++pParse->nMem; /* Register for rowset of rows to delete */ 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; 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 ){ if( db->flags & SQLITE_CountRows ){
sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1); sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1);
} }
/* End the database scan loop.
*/
sqlite3WhereEnd(pWInfo); sqlite3WhereEnd(pWInfo);
/* Open the pseudo-table used to store OLD if there are triggers. /* Open the pseudo-table used to store OLD if there are triggers.

View File

@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** to handle SELECT statements in SQLite. ** 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" #include "sqliteInt.h"
@@ -3705,7 +3705,7 @@ int sqlite3Select(
/* This case is for non-aggregate queries /* This case is for non-aggregate queries
** Begin the database scan ** 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( pWInfo==0 ) goto select_end;
/* If sorting index that was created by a prior OP_OpenEphemeral /* If sorting index that was created by a prior OP_OpenEphemeral
@@ -3826,7 +3826,7 @@ int sqlite3Select(
** in the right order to begin with. ** in the right order to begin with.
*/ */
sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset); 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( pWInfo==0 ) goto select_end;
if( pGroupBy==0 ){ if( pGroupBy==0 ){
/* The optimizer is able to deliver rows in group by order so /* The optimizer is able to deliver rows in group by order so
@@ -4024,7 +4024,7 @@ int sqlite3Select(
** of output. ** of output.
*/ */
resetAccumulator(pParse, &sAggInfo); resetAccumulator(pParse, &sAggInfo);
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, flag); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, flag, 0);
if( pWInfo==0 ){ if( pWInfo==0 ){
sqlite3ExprListDelete(db, pDel); sqlite3ExprListDelete(db, pDel);
goto select_end; goto select_end;

View File

@@ -11,7 +11,7 @@
************************************************************************* *************************************************************************
** Internal interface definitions for SQLite. ** 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_ #ifndef _SQLITEINT_H_
#define _SQLITEINT_H_ #define _SQLITEINT_H_
@@ -1595,10 +1595,11 @@ struct WhereLevel {
/* /*
** Flags appropriate for the wctrlFlags parameter of sqlite3WhereBegin(). ** Flags appropriate for the wctrlFlags parameter of sqlite3WhereBegin().
*/ */
#define WHERE_ORDERBY_NORMAL 0 /* No-op */ #define WHERE_ORDERBY_NORMAL 0x000 /* No-op */
#define WHERE_ORDERBY_MIN 1 /* ORDER BY processing for min() func */ #define WHERE_ORDERBY_MIN 0x001 /* ORDER BY processing for min() func */
#define WHERE_ORDERBY_MAX 2 /* ORDER BY processing for max() func */ #define WHERE_ORDERBY_MAX 0x002 /* ORDER BY processing for max() func */
#define WHERE_ONEPASS_DESIRED 4 /* Want to do one-pass UPDATE/DELETE */ #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 ** The WHERE clause processing routine has two halves. The
@@ -1610,13 +1611,13 @@ struct WhereLevel {
struct WhereInfo { struct WhereInfo {
Parse *pParse; /* Parsing and code generating context */ Parse *pParse; /* Parsing and code generating context */
u8 okOnePass; /* Ok to use one-pass algorithm for UPDATE or DELETE */ 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 */ SrcList *pTabList; /* List of tables in the join */
int iTop; /* The very beginning of the WHERE loop */ int iTop; /* The very beginning of the WHERE loop */
int iContinue; /* Jump here to continue with next record */ int iContinue; /* Jump here to continue with next record */
int iBreak; /* Jump here to break out of the loop */ int iBreak; /* Jump here to break out of the loop */
int nLevel; /* Number of nested loop */ int nLevel; /* Number of nested loop */
struct WhereClause *pWC; /* Decomposition of the WHERE clause */ 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 */ WhereLevel a[1]; /* Information about each nest loop in WHERE */
}; };
@@ -2260,7 +2261,7 @@ Expr *sqlite3LimitWhere(Parse *, SrcList *, Expr *, ExprList *, Expr *, Expr *,
#endif #endif
void sqlite3DeleteFrom(Parse*, SrcList*, Expr*); void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int); 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*); void sqlite3WhereEnd(WhereInfo*);
int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, int); int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, int);
void sqlite3ExprCodeMove(Parse*, int, int, int); void sqlite3ExprCodeMove(Parse*, int, int, int);

View File

@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** to handle UPDATE statements. ** 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" #include "sqliteInt.h"
@@ -346,7 +346,7 @@ void sqlite3Update(
*/ */
sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid); sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid);
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0,
WHERE_ONEPASS_DESIRED); WHERE_ONEPASS_DESIRED, 0);
if( pWInfo==0 ) goto update_cleanup; if( pWInfo==0 ) goto update_cleanup;
okOnePass = pWInfo->okOnePass; okOnePass = pWInfo->okOnePass;

View File

@@ -16,7 +16,7 @@
** so is applicable. Because this module is responsible for selecting ** so is applicable. Because this module is responsible for selecting
** indices, you might also think of this module as the "query optimizer". ** 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" #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. /* Search for an OR-clause that can be used to look up the table.
*/ */
maskSrc = getMask(pWC->pMaskSet, iCur); 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 /* 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 ** use an index to satisfy IS NULL constraints on that table. This is
@@ -2174,6 +2174,25 @@ static int codeAllEqualityTerms(
return regBase; 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 ** Generate code for the start of the iLevel-th loop in the WHERE clause
** implementation described by pWInfo. ** implementation described by pWInfo.
@@ -2195,8 +2214,10 @@ static Bitmask codeOneLoopStart(
Parse *pParse; /* Parsing context */ Parse *pParse; /* Parsing context */
Vdbe *v; /* The prepared stmt under constructions */ Vdbe *v; /* The prepared stmt under constructions */
struct SrcList_item *pTabItem; /* FROM clause term being coded */ struct SrcList_item *pTabItem; /* FROM clause term being coded */
int addrBrk; int addrBrk; /* Jump here to break out of the loop */
int addrCont; 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; pParse = pWInfo->pParse;
@@ -2207,6 +2228,8 @@ static Bitmask codeOneLoopStart(
iCur = pTabItem->iCursor; iCur = pTabItem->iCursor;
bRev = (pLevel->plan.wsFlags & WHERE_REVERSE)!=0; bRev = (pLevel->plan.wsFlags & WHERE_REVERSE)!=0;
omitTable = (pLevel->plan.wsFlags & WHERE_IDX_ONLY)!=0; omitTable = (pLevel->plan.wsFlags & WHERE_IDX_ONLY)!=0;
regRowSet = pWInfo->regRowSet;
codeRowSetEarly = 0;
/* Create labels for the "break" and "continue" instructions /* Create labels for the "break" and "continue" instructions
** for the current loop. Jump to addrBrk to break out of a loop. ** 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); sqlite3VdbeAddOp2(v, OP_Integer, j-1, iReg+1);
sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrBrk, iReg, pVtabIdx->idxStr, sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrBrk, iReg, pVtabIdx->idxStr,
pVtabIdx->needToFreeIdxStr ? P4_MPRINTF : P4_STATIC); pVtabIdx->needToFreeIdxStr ? P4_MPRINTF : P4_STATIC);
sqlite3ReleaseTempRange(pParse, iReg, nConstraint+2);
pVtabIdx->needToFreeIdxStr = 0; pVtabIdx->needToFreeIdxStr = 0;
for(j=0; j<nConstraint; j++){ for(j=0; j<nConstraint; j++){
if( aUsage[j].omit ){ if( aUsage[j].omit ){
@@ -2274,6 +2296,12 @@ static Bitmask codeOneLoopStart(
pLevel->op = OP_VNext; pLevel->op = OP_VNext;
pLevel->p1 = iCur; pLevel->p1 = iCur;
pLevel->p2 = sqlite3VdbeCurrentAddr(v); 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 }else
#endif /* SQLITE_OMIT_VIRTUALTABLE */ #endif /* SQLITE_OMIT_VIRTUALTABLE */
@@ -2294,6 +2322,10 @@ static Bitmask codeOneLoopStart(
addrNxt = pLevel->addrNxt; addrNxt = pLevel->addrNxt;
sqlite3VdbeAddOp2(v, OP_MustBeInt, r1, addrNxt); sqlite3VdbeAddOp2(v, OP_MustBeInt, r1, addrNxt);
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addrNxt, r1); 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); sqlite3ReleaseTempReg(pParse, rtmp);
VdbeComment((v, "pk")); VdbeComment((v, "pk"));
pLevel->op = OP_Noop; pLevel->op = OP_Noop;
@@ -2360,11 +2392,17 @@ static Bitmask codeOneLoopStart(
pLevel->op = bRev ? OP_Prev : OP_Next; pLevel->op = bRev ? OP_Prev : OP_Next;
pLevel->p1 = iCur; pLevel->p1 = iCur;
pLevel->p2 = start; pLevel->p2 = start;
if( testOp!=OP_Noop ){ codeRowSetEarly = regRowSet>=0 ? whereRowReadyForOutput(pWC) : 0;
if( codeRowSetEarly || testOp!=OP_Noop ){
int r1 = sqlite3GetTempReg(pParse); int r1 = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp2(v, OP_Rowid, iCur, r1); sqlite3VdbeAddOp2(v, OP_Rowid, iCur, r1);
if( testOp!=OP_Noop ){
sqlite3VdbeAddOp3(v, testOp, memEndValue, addrBrk, r1); sqlite3VdbeAddOp3(v, testOp, memEndValue, addrBrk, r1);
sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC | SQLITE_JUMPIFNULL); sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC | SQLITE_JUMPIFNULL);
}
if( codeRowSetEarly ){
sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, r1);
}
sqlite3ReleaseTempReg(pParse, r1); sqlite3ReleaseTempReg(pParse, r1);
} }
}else if( pLevel->plan.wsFlags & (WHERE_COLUMN_RANGE|WHERE_COLUMN_EQ) ){ }else if( pLevel->plan.wsFlags & (WHERE_COLUMN_RANGE|WHERE_COLUMN_EQ) ){
@@ -2547,10 +2585,17 @@ static Bitmask codeOneLoopStart(
} }
/* Seek the table cursor, if required */ /* 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_IdxRowid, iIdxCur, r1);
if( codeRowSetEarly ){
sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, r1);
}else{
sqlite3VdbeAddOp2(v, OP_Seek, iCur, r1); /* Deferred seek */ sqlite3VdbeAddOp2(v, OP_Seek, iCur, r1); /* Deferred seek */
} }
}
sqlite3ReleaseTempReg(pParse, r1); sqlite3ReleaseTempReg(pParse, r1);
/* Record the instruction used to terminate the loop. Disable /* Record the instruction used to terminate the loop. Disable
@@ -2558,11 +2603,9 @@ static Bitmask codeOneLoopStart(
*/ */
pLevel->op = bRev ? OP_Prev : OP_Next; pLevel->op = bRev ? OP_Prev : OP_Next;
pLevel->p1 = iIdxCur; pLevel->p1 = iIdxCur;
disableTerm(pLevel, pRangeStart);
disableTerm(pLevel, pRangeEnd);
}else }else
#if SQLITE_ENABLE_MULTI_OR #ifndef SQLITE_OMIT_OR_OPTIMIZATION
if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){ if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){
/* Case 4: Two or more separately indexed terms connected by OR /* Case 4: Two or more separately indexed terms connected by OR
** **
@@ -2591,7 +2634,7 @@ static Bitmask codeOneLoopStart(
** Goto 0, A ** Goto 0, A
** B: ** B:
*/ */
int regRowset; /* Register holding the RowSet object */ int regOrRowset; /* Register holding the RowSet object */
int regNextRowid; /* Register holding next rowid */ int regNextRowid; /* Register holding next rowid */
WhereTerm *pTerm; /* The complete OR-clause */ WhereTerm *pTerm; /* The complete OR-clause */
WhereClause *pOrWc; /* The OR-clause broken out into subterms */ WhereClause *pOrWc; /* The OR-clause broken out into subterms */
@@ -2603,33 +2646,41 @@ static Bitmask codeOneLoopStart(
assert( pTerm->eOperator==WO_OR ); assert( pTerm->eOperator==WO_OR );
assert( (pTerm->wtFlags & TERM_ORINFO)!=0 ); assert( (pTerm->wtFlags & TERM_ORINFO)!=0 );
pOrWc = &pTerm->u.pOrInfo->wc; pOrWc = &pTerm->u.pOrInfo->wc;
codeRowSetEarly = (regRowSet>=0 && pWC->nTerm==1) ?1:0;
regRowset = sqlite3GetTempReg(pParse); if( codeRowSetEarly ){
regNextRowid = sqlite3GetTempReg(pParse); regOrRowset = regRowSet;
sqlite3VdbeAddOp2(v, OP_Null, 0, regRowset); }else{
regOrRowset = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp2(v, OP_Null, 0, regOrRowset);
}
oneTab.nSrc = 1; oneTab.nSrc = 1;
oneTab.nAlloc = 1; oneTab.nAlloc = 1;
oneTab.a[0] = *pTabItem; oneTab.a[0] = *pTabItem;
for(j=0, pOrTerm=pOrWc->a; j<pOrWc->nTerm; j++, pOrTerm++){ for(j=0, pOrTerm=pOrWc->a; j<pOrWc->nTerm; j++, pOrTerm++){
WhereInfo *pSubWInfo; WhereInfo *pSubWInfo;
if( pOrTerm->leftCursor!=iCur ) continue; 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 ){ 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; pSubWInfo->a[0].plan.wsFlags |= WHERE_IDX_ONLY;
sqlite3WhereEnd(pSubWInfo); sqlite3WhereEnd(pSubWInfo);
} }
} }
sqlite3VdbeResolveLabel(v, addrCont); sqlite3VdbeResolveLabel(v, addrCont);
if( !codeRowSetEarly ){
regNextRowid = sqlite3GetTempReg(pParse);
addrCont = addrCont =
sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowset, addrBrk, regNextRowid); sqlite3VdbeAddOp3(v, OP_RowSetRead, regOrRowset,addrBrk,regNextRowid);
sqlite3VdbeAddOp2(v, OP_Seek, iCur, regNextRowid); sqlite3VdbeAddOp2(v, OP_Seek, iCur, regNextRowid);
sqlite3ReleaseTempReg(pParse, regNextRowid); sqlite3ReleaseTempReg(pParse, regNextRowid);
/* sqlite3ReleaseTempReg(pParse, regOrRowset); // Preserve the RowSet */
pLevel->op = OP_Goto; pLevel->op = OP_Goto;
pLevel->p2 = addrCont; pLevel->p2 = addrCont;
}
disableTerm(pLevel, pTerm);
}else }else
#endif /* SQLITE_ENABLE_MULTI_OR */ #endif /* SQLITE_OMIT_OR_OPTIMIZATION */
{ {
/* Case 5: There is no usable index. We must do a complete /* Case 5: There is no usable index. We must do a complete
@@ -2641,6 +2692,7 @@ static Bitmask codeOneLoopStart(
pLevel->p1 = iCur; pLevel->p1 = iCur;
pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, OP_Rewind, iCur, addrBrk); pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, OP_Rewind, iCur, addrBrk);
pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
codeRowSetEarly = 0;
} }
notReady &= ~getMask(pWC->pMaskSet, iCur); notReady &= ~getMask(pWC->pMaskSet, iCur);
@@ -2685,6 +2737,25 @@ static Bitmask codeOneLoopStart(
pTerm->wtFlags |= TERM_CODED; 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; return notReady;
} }
@@ -2813,7 +2884,8 @@ WhereInfo *sqlite3WhereBegin(
SrcList *pTabList, /* A list of all tables to be scanned */ SrcList *pTabList, /* A list of all tables to be scanned */
Expr *pWhere, /* The WHERE clause */ Expr *pWhere, /* The WHERE clause */
ExprList **ppOrderBy, /* An ORDER BY clause, or NULL */ 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 */ int i; /* Loop counter */
WhereInfo *pWInfo; /* Will become the return value of this function */ WhereInfo *pWInfo; /* Will become the return value of this function */
@@ -2858,6 +2930,7 @@ WhereInfo *sqlite3WhereBegin(
pWInfo->pParse = pParse; pWInfo->pParse = pParse;
pWInfo->pTabList = pTabList; pWInfo->pTabList = pTabList;
pWInfo->iBreak = sqlite3VdbeMakeLabel(v); pWInfo->iBreak = sqlite3VdbeMakeLabel(v);
pWInfo->regRowSet = (wctrlFlags & WHERE_FILL_ROWSET) ? regRowSet : -1;
pWInfo->pWC = pWC = (WhereClause*)&pWInfo->a[pWInfo->nLevel]; pWInfo->pWC = pWC = (WhereClause*)&pWInfo->a[pWInfo->nLevel];
pMaskSet = (WhereMaskSet*)&pWC[1]; pMaskSet = (WhereMaskSet*)&pWC[1];

94
test/where7.test Normal file
View File

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