Improved handling of OR terms in the WHERE clause with multi-column indexes.

FossilOrigin-Name: b23ae131874bc5c621f0f5ea8d76fce1ec089cc2
This commit is contained in:
drh
2011-10-07 17:52:40 +00:00
6 changed files with 177 additions and 64 deletions
+10 -10
View File
@@ -1,5 +1,5 @@
C Add\sthe\sSQLITE_FCNTL_OVERWRITE\sfile-control.\sUsed\sby\sSQLite\sto\sindicate\sto\sthe\sOS\slayer\sthat\sthe\scurrent\stransaction\swill\soverwrite\sthe\sentire\sfile.
D 2011-10-07T16:57:59.355
C Improved\shandling\sof\sOR\sterms\sin\sthe\sWHERE\sclause\swith\smulti-column\sindexes.
D 2011-10-07T17:52:40.878
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in a162fe39e249b8ed4a65ee947c30152786cfe897
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -183,7 +183,7 @@ F src/select.c d9b7d20b0365f80761846f00ef3638d4b33eeaf2
F src/shell.c e8fe1251aee84baa2fb232ce83d938de25aa650f
F src/sqlite.h.in b22e3187e048661d98a748ad54099c69dccfe497
F src/sqlite3ext.h 1a1a4f784aa9c3b00edd287940197de52487cd93
F src/sqliteInt.h 28cca77ebdaf6025ae5df52717dff429c7c6d4ef
F src/sqliteInt.h 2f66bf068131f0e499dd5e0abea3f68cd6b27b2d
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c 4568e72dfd36b6a5911f93457364deb072e0b03a
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
@@ -251,7 +251,7 @@ F src/vtab.c 901791a47318c0562cd0c676a2c6ff1bc530e582
F src/wal.c 3154756177d6219e233d84291d5b05f4e06ff5e9
F src/wal.h 66b40bd91bc29a5be1c88ddd1f5ade8f3f48728a
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
F src/where.c aafcb21a74e41f9aae76ea604e1e787ff8574125
F src/where.c a6d127dd05daf0f9ffa654edfab4f1236dc759b7
F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
@@ -916,10 +916,10 @@ F test/where3.test 8e1175c7ef710c70502858fc4fb08d784b3620b9
F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2
F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2
F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b
F test/where7.test aa4cfcd6f66e2a4ef87b6717327325bf4d547502
F test/where7.test 814d7373413398e074f134cff5f8872e2c08bd3b
F test/where8.test a6c740fd286d7883e274e17b6230a9d672a7ab1f
F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739
F test/where9.test 24f19ad14bb1b831564ced5273e681e495662848
F test/where9.test bed66dcfc69a54a99661c0c9906189cb5e58f4e2
F test/whereA.test 24c234263c8fe358f079d5e57d884fb569d2da0a
F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5
F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
@@ -967,7 +967,7 @@ F tool/symbols.sh caaf6ccc7300fd43353318b44524853e222557d5
F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
P 9913996e7b0f94ba1c51200b61433193002f3638
R 95c2b529419f78d1f0a0f16ca9ea56e3
U dan
Z 751eeeee5e1f94cafba63c56e2ffa2f0
P 1da87fcdacfa7d277c3ee98e410a9ea8b529c368 5c132592820e9dc2355e26ea14e155c797c335b3
R 8ca3076f2df3dea6526cd23b3be813bd
U drh
Z ddb3b7cdb3a83c191cd3bc7c8f9d4238
+1 -1
View File
@@ -1 +1 @@
1da87fcdacfa7d277c3ee98e410a9ea8b529c368
b23ae131874bc5c621f0f5ea8d76fce1ec089cc2
+4 -4
View File
@@ -1979,10 +1979,10 @@ struct WhereLevel {
#define WHERE_ORDERBY_MAX 0x0002 /* ORDER BY processing for max() func */
#define WHERE_ONEPASS_DESIRED 0x0004 /* Want to do one-pass UPDATE/DELETE */
#define WHERE_DUPLICATES_OK 0x0008 /* Ok to return a row more than once */
#define WHERE_OMIT_OPEN 0x0010 /* Table cursors 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 */
#define WHERE_OMIT_OPEN_CLOSE 0x0010 /* Table cursors are already open */
#define WHERE_FORCE_TABLE 0x0020 /* Do not use an index-only search */
#define WHERE_ONETABLE_ONLY 0x0040 /* Only code the 1st table in pTabList */
#define WHERE_AND_ONLY 0x0080 /* Don't use indices for OR terms */
/*
** The WHERE clause processing routine has two halves. The
+82 -41
View File
@@ -127,12 +127,22 @@ struct WhereTerm {
/*
** An instance of the following structure holds all information about a
** WHERE clause. Mostly this is a container for one or more WhereTerms.
**
** Explanation of pOuter: For a WHERE clause of the form
**
** a AND ((b AND c) OR (d AND e)) AND f
**
** There are separate WhereClause objects for the whole clause and for
** the subclauses "(b AND c)" and "(d AND e)". The pOuter field of the
** subclauses points to the WhereClause object for the whole clause.
*/
struct WhereClause {
Parse *pParse; /* The parser context */
WhereMaskSet *pMaskSet; /* Mapping of table cursor numbers to bitmasks */
Bitmask vmask; /* Bitmask identifying virtual table cursors */
WhereClause *pOuter; /* Outer conjunction */
u8 op; /* Split operator. TK_AND or TK_OR */
u16 wctrlFlags; /* Might include WHERE_AND_ONLY */
int nTerm; /* Number of terms */
int nSlot; /* Number of entries in a[] */
WhereTerm *a; /* Each a[] describes a term of the WHERE cluase */
@@ -261,14 +271,17 @@ struct WhereCost {
static void whereClauseInit(
WhereClause *pWC, /* The WhereClause to be initialized */
Parse *pParse, /* The parsing context */
WhereMaskSet *pMaskSet /* Mapping from table cursor numbers to bitmasks */
WhereMaskSet *pMaskSet, /* Mapping from table cursor numbers to bitmasks */
u16 wctrlFlags /* Might include WHERE_AND_ONLY */
){
pWC->pParse = pParse;
pWC->pMaskSet = pMaskSet;
pWC->pOuter = 0;
pWC->nTerm = 0;
pWC->nSlot = ArraySize(pWC->aStatic);
pWC->a = pWC->aStatic;
pWC->vmask = 0;
pWC->wctrlFlags = wctrlFlags;
}
/* Forward reference */
@@ -584,36 +597,38 @@ static WhereTerm *findTerm(
int k;
assert( iCur>=0 );
op &= WO_ALL;
for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){
if( pTerm->leftCursor==iCur
&& (pTerm->prereqRight & notReady)==0
&& pTerm->u.leftColumn==iColumn
&& (pTerm->eOperator & op)!=0
){
if( pIdx && pTerm->eOperator!=WO_ISNULL ){
Expr *pX = pTerm->pExpr;
CollSeq *pColl;
char idxaff;
int j;
Parse *pParse = pWC->pParse;
idxaff = pIdx->pTable->aCol[iColumn].affinity;
if( !sqlite3IndexAffinityOk(pX, idxaff) ) continue;
/* Figure out the collation sequence required from an index for
** it to be useful for optimising expression pX. Store this
** value in variable pColl.
*/
assert(pX->pLeft);
pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);
assert(pColl || pParse->nErr);
for(j=0; pIdx->aiColumn[j]!=iColumn; j++){
if( NEVER(j>=pIdx->nColumn) ) return 0;
for(; pWC; pWC=pWC->pOuter){
for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){
if( pTerm->leftCursor==iCur
&& (pTerm->prereqRight & notReady)==0
&& pTerm->u.leftColumn==iColumn
&& (pTerm->eOperator & op)!=0
){
if( pIdx && pTerm->eOperator!=WO_ISNULL ){
Expr *pX = pTerm->pExpr;
CollSeq *pColl;
char idxaff;
int j;
Parse *pParse = pWC->pParse;
idxaff = pIdx->pTable->aCol[iColumn].affinity;
if( !sqlite3IndexAffinityOk(pX, idxaff) ) continue;
/* Figure out the collation sequence required from an index for
** it to be useful for optimising expression pX. Store this
** value in variable pColl.
*/
assert(pX->pLeft);
pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);
assert(pColl || pParse->nErr);
for(j=0; pIdx->aiColumn[j]!=iColumn; j++){
if( NEVER(j>=pIdx->nColumn) ) return 0;
}
if( pColl && sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ) continue;
}
if( pColl && sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ) continue;
return pTerm;
}
return pTerm;
}
}
return 0;
@@ -877,7 +892,7 @@ static void exprAnalyzeOrTerm(
if( pOrInfo==0 ) return;
pTerm->wtFlags |= TERM_ORINFO;
pOrWc = &pOrInfo->wc;
whereClauseInit(pOrWc, pWC->pParse, pMaskSet);
whereClauseInit(pOrWc, pWC->pParse, pMaskSet, pWC->wctrlFlags);
whereSplit(pOrWc, pExpr, TK_OR);
exprAnalyzeAll(pSrc, pOrWc);
if( db->mallocFailed ) return;
@@ -904,9 +919,10 @@ static void exprAnalyzeOrTerm(
pOrTerm->wtFlags |= TERM_ANDINFO;
pOrTerm->eOperator = WO_AND;
pAndWC = &pAndInfo->wc;
whereClauseInit(pAndWC, pWC->pParse, pMaskSet);
whereClauseInit(pAndWC, pWC->pParse, pMaskSet, pWC->wctrlFlags);
whereSplit(pAndWC, pOrTerm->pExpr, TK_AND);
exprAnalyzeAll(pSrc, pAndWC);
pAndWC->pOuter = pWC;
testcase( db->mallocFailed );
if( !db->mallocFailed ){
for(j=0, pAndTerm=pAndWC->a; j<pAndWC->nTerm; j++, pAndTerm++){
@@ -1801,11 +1817,14 @@ static void bestOrClauseIndex(
WhereTerm * const pWCEnd = &pWC->a[pWC->nTerm]; /* End of pWC->a[] */
WhereTerm *pTerm; /* A single term of the WHERE clause */
/* No OR-clause optimization allowed if the INDEXED BY or NOT INDEXED clauses
** are used */
/* The OR-clause optimization is disallowed if the INDEXED BY or
** NOT INDEXED clauses are used or if the WHERE_AND_ONLY bit is set. */
if( pSrc->notIndexed || pSrc->pIndex!=0 ){
return;
}
if( pWC->wctrlFlags & WHERE_AND_ONLY ){
return;
}
/* Search the WHERE clause terms for a usable WO_OR term. */
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
@@ -1833,6 +1852,7 @@ static void bestOrClauseIndex(
WhereClause tempWC;
tempWC.pParse = pWC->pParse;
tempWC.pMaskSet = pWC->pMaskSet;
tempWC.pOuter = pWC;
tempWC.op = TK_AND;
tempWC.a = pOrTerm;
tempWC.nTerm = 1;
@@ -3012,6 +3032,7 @@ static void bestBtreeIndex(
pTerm = findTerm(pWC, iCur, j, notReady, eqTermMask, pIdx);
if( pTerm==0 ) break;
wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ);
testcase( pTerm->pWC!=pWC );
if( pTerm->eOperator & WO_IN ){
Expr *pExpr = pTerm->pExpr;
wsFlags |= WHERE_COLUMN_IN;
@@ -3043,11 +3064,13 @@ static void bestBtreeIndex(
nBound = 1;
wsFlags |= WHERE_TOP_LIMIT;
used |= pTop->prereqRight;
testcase( pTop->pWC!=pWC );
}
if( pBtm ){
nBound++;
wsFlags |= WHERE_BTM_LIMIT;
used |= pBtm->prereqRight;
testcase( pBtm->pWC!=pWC );
}
wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE);
}
@@ -3769,7 +3792,8 @@ static Bitmask codeOneLoopStart(
WhereInfo *pWInfo, /* Complete information about the WHERE clause */
int iLevel, /* Which level of pWInfo->a[] should be coded */
u16 wctrlFlags, /* One of the WHERE_* flags defined in sqliteInt.h */
Bitmask notReady /* Which tables are currently available */
Bitmask notReady, /* Which tables are currently available */
Expr *pWhere /* Complete WHERE clause */
){
int j, k; /* Loop counters */
int iCur; /* The VDBE cursor for the table */
@@ -4251,7 +4275,8 @@ static Bitmask codeOneLoopStart(
int iLoopBody = sqlite3VdbeMakeLabel(v); /* Start of loop body */
int iRetInit; /* Address of regReturn init */
int untestedTerms = 0; /* Some terms not completely tested */
int ii;
int ii; /* Loop counter */
Expr *pAndExpr = 0; /* An ".. AND (...)" expression */
pTerm = pLevel->plan.u.pTerm;
assert( pTerm!=0 );
@@ -4301,13 +4326,28 @@ static Bitmask codeOneLoopStart(
}
iRetInit = sqlite3VdbeAddOp2(v, OP_Integer, 0, regReturn);
/* If the original WHERE clause is z of the form: (x1 OR x2 OR ...) AND y
** Then for every term xN, evaluate as the subexpression: xN AND z
** That way, terms in y that are factored into the disjunction will
** be picked up by the recursive calls to sqlite3WhereBegin() below.
*/
if( pWC->nTerm>1 ){
pAndExpr = sqlite3ExprAlloc(pParse->db, TK_AND, 0, 0);
pAndExpr->pRight = pWhere;
}
for(ii=0; ii<pOrWc->nTerm; ii++){
WhereTerm *pOrTerm = &pOrWc->a[ii];
if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){
WhereInfo *pSubWInfo; /* Info for single OR-term scan */
Expr *pOrExpr = pOrTerm->pExpr;
if( pAndExpr ){
pAndExpr->pLeft = pOrExpr;
pOrExpr = pAndExpr;
}
/* Loop through table entries that match term pOrTerm. */
pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrTerm->pExpr, 0, 0,
WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE |
pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0,
WHERE_OMIT_OPEN_CLOSE | WHERE_AND_ONLY |
WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY);
if( pSubWInfo ){
explainOneScan(
@@ -4335,6 +4375,7 @@ static Bitmask codeOneLoopStart(
}
}
}
sqlite3DbFree(pParse->db, pAndExpr);
sqlite3VdbeChangeP1(v, iRetInit, sqlite3VdbeCurrentAddr(v));
sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrBrk);
sqlite3VdbeResolveLabel(v, iLoopBody);
@@ -4616,7 +4657,7 @@ WhereInfo *sqlite3WhereBegin(
** subexpression is separated by an AND operator.
*/
initMaskSet(pMaskSet);
whereClauseInit(pWC, pParse, pMaskSet);
whereClauseInit(pWC, pParse, pMaskSet, wctrlFlags);
sqlite3ExprCodeConstants(pParse, pWhere);
whereSplit(pWC, pWhere, TK_AND); /* IMP: R-15842-53296 */
@@ -4944,7 +4985,7 @@ WhereInfo *sqlite3WhereBegin(
}else
#endif
if( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0
&& (wctrlFlags & WHERE_OMIT_OPEN)==0 ){
&& (wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 ){
int op = pWInfo->okOnePass ? OP_OpenWrite : OP_OpenRead;
sqlite3OpenTable(pParse, pTabItem->iCursor, iDb, pTab, op);
testcase( pTab->nCol==BMS-1 );
@@ -4989,7 +5030,7 @@ WhereInfo *sqlite3WhereBegin(
for(i=0; i<nTabList; i++){
pLevel = &pWInfo->a[i];
explainOneScan(pParse, pTabList, pLevel, i, pLevel->iFrom, wctrlFlags);
notReady = codeOneLoopStart(pWInfo, i, wctrlFlags, notReady);
notReady = codeOneLoopStart(pWInfo, i, wctrlFlags, notReady, pWhere);
pWInfo->iContinue = pLevel->addrCont;
}
@@ -5124,7 +5165,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
assert( pTab!=0 );
if( (pTab->tabFlags & TF_Ephemeral)==0
&& pTab->pSelect==0
&& (pWInfo->wctrlFlags & WHERE_OMIT_CLOSE)==0
&& (pWInfo->wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0
){
int ws = pLevel->plan.wsFlags;
if( !pWInfo->okOnePass && (ws & WHERE_IDX_ONLY)==0 ){
+1 -3
View File
@@ -10,8 +10,6 @@
#***********************************************************************
# 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.9 2009/06/07 23:45:11 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -23341,7 +23339,7 @@ do_execsql_test where7-3.1 {
OR t301.c8 = 1407424651264000)
ORDER BY t302.c5 LIMIT 200;
} {
0 0 1 {SEARCH TABLE t301 USING COVERING INDEX t301_c4 (c4=?) (~10 rows)}
0 0 1 {SEARCH TABLE t301 USING COVERING INDEX t301_c4 (c4=?) (~5 rows)}
0 0 1 {SEARCH TABLE t301 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
0 1 0 {SEARCH TABLE t302 USING INDEX t302_c8_c3 (c8=? AND c3>?) (~2 rows)}
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
+79 -5
View File
@@ -11,7 +11,6 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing the multi-index OR clause optimizer.
#
# $Id: where9.test,v 1.9 2009/06/05 17:09:12 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -365,7 +364,7 @@ ifcapable explain {
} {
0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
0 1 1 {SEARCH TABLE t2 USING INDEX t2d (d=?) (~2 rows)}
0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?) (~10 rows)}
0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?) (~5 rows)}
}
do_execsql_test where9-3.2 {
EXPLAIN QUERY PLAN
@@ -375,7 +374,7 @@ ifcapable explain {
} {
0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
0 1 1 {SEARCH TABLE t2 USING INDEX t2d (d=?) (~2 rows)}
0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?) (~10 rows)}
0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?) (~5 rows)}
}
}
@@ -454,8 +453,8 @@ ifcapable explain {
do_execsql_test where9-5.1 {
EXPLAIN QUERY PLAN SELECT a FROM t1 WHERE b>1000 AND (c=31031 OR d IS NULL)
} {
0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c=?) (~10 rows)}
0 0 0 {SEARCH TABLE t1 USING INDEX t1d (d=?) (~10 rows)}
0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c=?) (~2 rows)}
0 0 0 {SEARCH TABLE t1 USING INDEX t1d (d=?) (~2 rows)}
}
# In contrast, b=1000 is preferred over any OR-clause.
@@ -783,4 +782,79 @@ do_test where9-6.8.2 {
}
} {1 {cannot use index: t1b}}
############################################################################
# Test cases where terms inside an OR series are combined with AND terms
# external to the OR clause. In other words, cases where
#
# x AND (y OR z)
#
# is able to use indices on x,y and x,z, or indices y,x and z,x.
#
do_test where9-7.0 {
execsql {
CREATE TABLE t5(a, b, c, d, e, f, g, x, y);
INSERT INTO t5
SELECT a, b, c, e, d, f, g,
CASE WHEN (a&1)!=0 THEN 'y' ELSE 'n' END,
CASE WHEN (a&2)!=0 THEN 'y' ELSE 'n' END
FROM t1;
CREATE INDEX t5xb ON t5(x, b);
CREATE INDEX t5xc ON t5(x, c);
CREATE INDEX t5xd ON t5(x, d);
CREATE INDEX t5xe ON t5(x, e);
CREATE INDEX t5xf ON t5(x, f);
CREATE INDEX t5xg ON t5(x, g);
CREATE INDEX t5yb ON t5(y, b);
CREATE INDEX t5yc ON t5(y, c);
CREATE INDEX t5yd ON t5(y, d);
CREATE INDEX t5ye ON t5(y, e);
CREATE INDEX t5yf ON t5(y, f);
CREATE INDEX t5yg ON t5(y, g);
CREATE TABLE t6(a, b, c, e, d, f, g, x, y);
INSERT INTO t6 SELECT * FROM t5;
ANALYZE t5;
}
} {}
do_test where9-7.1.1 {
count_steps {
SELECT a FROM t5 WHERE x='y' AND (b=913 OR c=27027) ORDER BY a;
}
} {79 81 83 scan 0 sort 1}
do_test where9-7.1.2 {
execsql {
SELECT a FROM t6 WHERE x='y' AND (b=913 OR c=27027) ORDER BY a;
}
} {79 81 83}
do_test where9-7.1.3 {
count_steps {
SELECT a FROM t5 WHERE x='n' AND (b=913 OR c=27027) ORDER BY a;
}
} {80 scan 0 sort 1}
do_test where9-7.1.4 {
execsql {
SELECT a FROM t6 WHERE x='n' AND (b=913 OR c=27027) ORDER BY a;
}
} {80}
do_test where9-7.2.1 {
count_steps {
SELECT a FROM t5 WHERE (x='y' OR y='y') AND b=913 ORDER BY a;
}
} {83 scan 0 sort 1}
do_test where9-7.2.2 {
execsql {
SELECT a FROM t6 WHERE (x='y' OR y='y') AND b=913 ORDER BY a;
}
} {83}
do_test where9-7.3.1 {
count_steps {
SELECT a FROM t5 WHERE (x='y' OR y='y') AND c=27027 ORDER BY a;
}
} {79 81 scan 0 sort 1}
do_test where9-7.3.2 {
execsql {
SELECT a FROM t6 WHERE (x='y' OR y='y') AND c=27027 ORDER BY a;
}
} {79 81}
finish_test