-C Cherrypick\sthe\s[3513bf6ee090d9]\sso\sthat\sthe\ssqlite_source_id()\sfunction\sworks\scorrectly\seven\swith\snewer\sversions\sof\sFossil
-D 2011-10-25T21:18:17.179
+C Backport\scheck-in\s[62678be3df35cd]:\nWhen\sthe\ssame\sindex\sis\sused\sfor\sall\sOR-terms\sin\sa\sWHERE\sclause,\sthen\stry\sto\nuse\sthat\sindex\sas\sa\scovering\sindex.
+D 2012-08-25T00:49:08.886
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 543f91f24cd7fee774ecc0a61c19704c0c3e78fd
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
F src/ctime.c 2e39d3374e785a63117e077bcba9d4a6656df363
F src/date.c 5dd8448a0bfea8d31fb14cff487d0c06ff8c8b20
-F src/delete.c daff6cef77fe8ed57b8acfc5ecebce28244af2fa
+F src/delete.c e179bd660c559c630813b01d04db663de56a124d
F src/expr.c 9c8147b9c2ffcd792191bad51028bf11719bac09
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
-F src/fkey.c ca7cdb310a5095ce2b304c6524260595f73827dc
+F src/fkey.c b792889ef26dd4531e77de35821547c9d2b72ca1
F src/func.c 464b0dc70618b896c402c574eb04bc5eacf35341
F src/global.c 02335177cf6946fe5525c6f0755cf181140debf3
F src/hash.c 458488dcc159c301b8e7686280ab209f1fb915af
F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
F src/resolve.c 36368f44569208fa074e61f4dd0b6c4fb60ca2b4
F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
-F src/select.c a9828845d04c1f583b9fb5260c0cab33da88d16e
+F src/select.c 6a7353def8f6e5d800198214bf5477e8d0524e36
F src/shell.c 8517fc1f9c59ae4007e6cc8b9af91ab231ea2056
F src/sqlite.h.in 2d72a6242df41c517e38eec8791abcf5484a36f1
F src/sqlite3ext.h 69dfb8116af51b84a029cddb3b35062354270c89
-F src/sqliteInt.h e679eece2fb35bb324a56d614d91ccf961f70cf4
+F src/sqliteInt.h da99fe107f8f51d0b624428b773122cfccb49e2a
F src/sqliteLimit.h a17dcd3fb775d63b64a43a55c54cb282f9726f44
F src/status.c 496913d4e8441195f6f2a75b1c95993a45b9b30b
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/tokenize.c 604607d6813e9551cf5189d899e0a25c12681080
F src/trigger.c b8bedb9c0084ceb51a40f54fcca2ce048c8de852
-F src/update.c c6be6a5af1198aeac9b25d842d97e52695ffc9e6
+F src/update.c e85ead70c972dcb0655ee2ce91422b1d50bcb7b0
F src/utf.c 1baeeac91707a4df97ccc6141ec0f808278af685
F src/util.c 32aebf04c10e51ad3977a928b7416bed671b620b
F src/vacuum.c 241a8386727c1497eba4955933356dfba6ff8c9f
F src/wal.c 5ac2119e23ee4424599d4275b66dc88d612a0543
F src/wal.h 96669b645e27cd5a111ba59f0cae7743a207bc3c
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
-F src/where.c db40458b67dbd77c658178ec6c53432807759c3f
+F src/where.c a4b2ea85eeca3350354570c3b06eee2ce00f8075
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
F test/all.test 6745008c144bd2956d58864d21f7b304689c1cce
F test/fts3expr2.test 18da930352e5693eaa163a3eacf96233b7290d1a
F test/fts3malloc.test 059592c4f37ccd30138bbf8e3e5b7982cb5c8f2e
F test/fts3near.test 2e318ee434d32babd27c167142e2b94ddbab4844
-F test/fts3query.test 2468caf7938dbc3be2e049524320ce4faf2227b3
+F test/fts3query.test 682cf2855eebdc295f69ae1573b46f388dfac280
F test/fts3rnd.test 707533ce943f490443ce5e696236bb1675a37635
F test/fts3snippet.test 9f9a4a7e396c5d8ce2898be65ebabc429555430f
F test/fts4aa.test eadf85621c0a113d4c7ad3ccbf8441130e007b8f
F test/where9.test 24f19ad14bb1b831564ced5273e681e495662848
F test/whereA.test 24c234263c8fe358f079d5e57d884fb569d2da0a
F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5
+F test/whereD.test 6b6270480480a30038029dd6cbceef54d009dbc6
F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688
F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
-P 14bc58ca70336aed62069f223324304835991c55
-R af5db398d4569192dd77a70de5aadb2c
+P 89d63a0e1db6a86fa173fb8a4e3a3904ef9e488c
+R 1c2de8c77bbe509e9e60c8893c5a6225
U drh
-Z e43e421cba9e7717a13b85e140461f3f
+Z d3dad17d9f134d722fd6b0b2a2f50b29
-89d63a0e1db6a86fa173fb8a4e3a3904ef9e488c
\ No newline at end of file
+865dfcbaa59431422b5980c66c8e29e80c4ed7c9
\ No newline at end of file
*/
sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet);
pWInfo = sqlite3WhereBegin(
- pParse, pTabList, pWhere, 0, 0, WHERE_DUPLICATES_OK
+ pParse, pTabList, pWhere, 0, 0, WHERE_DUPLICATES_OK, 0
);
if( pWInfo==0 ) goto delete_from_cleanup;
regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, iRowid);
** clause. If the constraint is not deferred, throw an exception for
** each row found. Otherwise, for deferred constraints, increment the
** deferred constraint counter by nIncr for each row selected. */
- pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0);
+ pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0);
if( nIncr>0 && pFKey->isDeferred==0 ){
sqlite3ParseToplevel(pParse)->mayAbort = 1;
}
ExprList *pDist = (isDistinct ? p->pEList : 0);
/* Begin the database scan. */
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, pDist, 0);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, pDist, 0,0);
if( pWInfo==0 ) goto select_end;
if( pWInfo->nRowOut < p->nSelectRow ) p->nSelectRow = pWInfo->nRowOut;
** in the right order to begin with.
*/
sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy, 0, 0);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy, 0, 0, 0);
if( pWInfo==0 ) goto select_end;
if( pGroupBy==0 ){
/* The optimizer is able to deliver rows in group by order so
** of output.
*/
resetAccumulator(pParse, &sAggInfo);
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, 0, flag);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax,0,flag,0);
if( pWInfo==0 ){
sqlite3ExprListDelete(db, pDel);
goto select_end;
int addrInTop; /* Top of the IN loop */
} *aInLoop; /* Information about each nested IN operator */
} in; /* Used when plan.wsFlags&WHERE_IN_ABLE */
+ Index *pCovidx; /* Possible covering index for WHERE_MULTI_OR */
} u;
/* The following field is really not part of the current level. But
#endif
void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
-WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**,ExprList*,u16);
+WhereInfo *sqlite3WhereBegin(
+ Parse*,SrcList*,Expr*,ExprList**,ExprList*,u16,int);
void sqlite3WhereEnd(WhereInfo*);
int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int);
void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int);
*/
sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid);
pWInfo = sqlite3WhereBegin(
- pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED
+ pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED, 0
);
if( pWInfo==0 ) goto update_cleanup;
okOnePass = pWInfo->okOnePass;
int r1;
int k = pIdx->aiColumn[j];
pTerm = findTerm(pWC, iCur, k, notReady, pLevel->plan.wsFlags, pIdx);
- if( NEVER(pTerm==0) ) break;
+ if( pTerm==0 ) break;
/* The following true for indices with redundant columns.
** Ex: CREATE INDEX i1 ON t1(a,b,a); SELECT * FROM t1 WHERE a=0 AND b=0; */
testcase( (pTerm->wtFlags & TERM_CODED)!=0 );
WhereClause *pOrWc; /* The OR-clause broken out into subterms */
WhereTerm *pFinal; /* Final subterm within the OR-clause. */
SrcList *pOrTab; /* Shortened table list or OR-clause generation */
+ Index *pCov = 0; /* Potential covering index (or NULL) */
+ int iCovCur = pParse->nTab++; /* Cursor used for index scans (if any) */
int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */
int regRowset = 0; /* Register for RowSet object */
pLevel->op = OP_Return;
pLevel->p1 = regReturn;
- /* Set up a new SrcList ni pOrTab containing the table being scanned
+ /* Set up a new SrcList in 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().
*/
/* Loop through table entries that match term pOrTerm. */
pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrTerm->pExpr, 0, 0,
WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE |
- WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY);
+ WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY, iCovCur);
+ assert( pSubWInfo || pParse->nErr || pParse->db->mallocFailed );
if( pSubWInfo ){
+ WhereLevel *pLvl;
explainOneScan(
pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0
);
*/
if( pSubWInfo->untestedTerms ) untestedTerms = 1;
+ /* If all of the OR-connected terms are optimized using the same
+ ** index, and the index is opened using the same cursor number
+ ** by each call to sqlite3WhereBegin() made by this loop, it may
+ ** be possible to use that index as a covering index.
+ **
+ ** If the call to sqlite3WhereBegin() above resulted in a scan that
+ ** uses an index, and this is either the first OR-connected term
+ ** processed or the index is the same as that used by all previous
+ ** terms, set pCov to the candidate covering index. Otherwise, set
+ ** pCov to NULL to indicate that no candidate covering index will
+ ** be available.
+ */
+ pLvl = &pSubWInfo->a[0];
+ if( (pLvl->plan.wsFlags & WHERE_INDEXED)!=0
+ && (pLvl->plan.wsFlags & WHERE_TEMP_INDEX)==0
+ && (ii==0 || pLvl->plan.u.pIdx==pCov)
+ ){
+ assert( pLvl->iIdxCur==iCovCur );
+ pCov = pLvl->plan.u.pIdx;
+ }else{
+ pCov = 0;
+ }
+
/* Finish the loop through table entries that match term pOrTerm. */
sqlite3WhereEnd(pSubWInfo);
}
}
}
+ pLevel->u.pCovidx = pCov;
+ pLevel->iIdxCur = iCovCur;
sqlite3VdbeChangeP1(v, iRetInit, sqlite3VdbeCurrentAddr(v));
sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrBrk);
sqlite3VdbeResolveLabel(v, iLoopBody);
Expr *pWhere, /* The WHERE clause */
ExprList **ppOrderBy, /* An ORDER BY clause, or NULL */
ExprList *pDistinct, /* The select-list for DISTINCT queries - or NULL */
- u16 wctrlFlags /* One of the WHERE_* flags defined in sqliteInt.h */
+ u16 wctrlFlags, /* One of the WHERE_* flags defined in sqliteInt.h */
+ int iIdxCur /* If WHERE_ONETABLE_ONLY is set, index cursor number */
){
int i; /* Loop counter */
int nByteWInfo; /* Num. bytes allocated for WhereInfo struct */
testcase( bestPlan.plan.wsFlags & WHERE_INDEXED );
testcase( bestPlan.plan.wsFlags & WHERE_TEMP_INDEX );
if( bestPlan.plan.wsFlags & (WHERE_INDEXED|WHERE_TEMP_INDEX) ){
- pLevel->iIdxCur = pParse->nTab++;
+ if( (wctrlFlags & WHERE_ONETABLE_ONLY)
+ && (bestPlan.plan.wsFlags & WHERE_TEMP_INDEX)==0
+ ){
+ pLevel->iIdxCur = iIdxCur;
+ }else{
+ pLevel->iIdxCur = pParse->nTab++;
+ }
}else{
pLevel->iIdxCur = -1;
}
*/
assert( pWInfo->nLevel==1 || pWInfo->nLevel==pTabList->nSrc );
for(i=0, pLevel=pWInfo->a; i<pWInfo->nLevel; i++, pLevel++){
+ Index *pIdx = 0;
struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom];
Table *pTab = pTabItem->pTab;
assert( pTab!=0 );
** that reference the table and converts them into opcodes that
** reference the index.
*/
- if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 && !db->mallocFailed){
+ if( pLevel->plan.wsFlags & WHERE_INDEXED ){
+ pIdx = pLevel->plan.u.pIdx;
+ }else if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){
+ pIdx = pLevel->u.pCovidx;
+ }
+ if( pIdx && !db->mallocFailed){
int k, j, last;
VdbeOp *pOp;
- Index *pIdx = pLevel->plan.u.pIdx;
- assert( pIdx!=0 );
pOp = sqlite3VdbeGetOp(v, pWInfo->iTop);
last = sqlite3VdbeCurrentAddr(v);
for(k=pWInfo->iTop; k<last; k++, pOp++){
CREATE TABLE bt(title);
}
} {}
+if 0 {
do_test fts3query-4.2 {
eqp "SELECT t1.number FROM t1, ft WHERE t1.number=ft.rowid ORDER BY t1.date"
} {0 0 {TABLE t1 WITH INDEX i1 ORDER BY} 1 1 {TABLE ft VIRTUAL TABLE INDEX 1:}}
do_test fts3query-4.5 {
eqp "SELECT t1.number FROM bt, t1 WHERE t1.number=bt.rowid ORDER BY t1.date"
} {0 1 {TABLE t1 WITH INDEX i1 ORDER BY} 1 0 {TABLE bt USING PRIMARY KEY}}
-
+}
finish_test
-
--- /dev/null
+# 2012 August 24
+#
+# 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 that an index may be used as a covering
+# index when there are OR expressions in the WHERE clause.
+#
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set ::testprefix whereD
+
+do_execsql_test 1.1 {
+ CREATE TABLE t(i,j,k,m,n);
+ CREATE INDEX ijk ON t(i,j,k);
+ CREATE INDEX jmn ON t(j,m,n);
+
+ INSERT INTO t VALUES(3, 3, 'three', 3, 'tres');
+ INSERT INTO t VALUES(2, 2, 'two', 2, 'dos');
+ INSERT INTO t VALUES(1, 1, 'one', 1, 'uno');
+ INSERT INTO t VALUES(4, 4, 'four', 4, 'cuatro');
+} {}
+
+do_execsql_test 1.2 {
+ SELECT k FROM t WHERE (i=1 AND j=1) OR (i=2 AND j=2);
+} {one two}
+do_execsql_test 1.3 {
+ SELECT k FROM t WHERE (i=1 AND j=1) OR (+i=2 AND j=2);
+} {one two}
+do_execsql_test 1.4 {
+ SELECT n FROM t WHERE (i=1 AND j=1) OR (i=2 AND j=2);
+} {uno dos}
+do_execsql_test 1.5 {
+ SELECT k, n FROM t WHERE (i=1 AND j=1) OR (i=2 AND j=2);
+} {one uno two dos}
+do_execsql_test 1.6 {
+ SELECT k FROM t WHERE (i=1 AND j=1) OR (i=2 AND j=2) OR (i=3 AND j=3);
+} {one two three}
+do_execsql_test 1.7 {
+ SELECT n FROM t WHERE (i=1 AND j=1) OR (i=2 AND j=2) OR (i=3 AND j=3);
+} {uno dos tres}
+do_execsql_test 1.8 {
+ SELECT k FROM t WHERE (i=1 AND j=1) OR (j=2 AND m=2);
+} {one two}
+do_execsql_test 1.9 {
+ SELECT k FROM t WHERE (i=1 AND j=1) OR (i=2 AND j=2) OR (j=3 AND m=3);
+} {one two three}
+do_execsql_test 1.10 {
+ SELECT n FROM t WHERE (i=1 AND j=1) OR (i=2 AND j=2) OR (j=3 AND m=3);
+} {uno dos tres}
+do_execsql_test 1.11 {
+ SELECT k FROM t WHERE (i=1 AND j=1) OR (j=2 AND m=2) OR (i=3 AND j=3);
+} {one two three}
+do_execsql_test 1.12 {
+ SELECT n FROM t WHERE (i=1 AND j=1) OR (j=2 AND m=2) OR (i=3 AND j=3);
+} {uno dos tres}
+do_execsql_test 1.13 {
+ SELECT k FROM t WHERE (j=1 AND m=1) OR (i=2 AND j=2) OR (i=3 AND j=3);
+} {one two three}
+do_execsql_test 1.14 {
+ SELECT k FROM t WHERE (i=1 AND j=1) OR (j=2 AND i=2) OR (i=3 AND j=3);
+} {one two three}
+do_execsql_test 1.15 {
+ SELECT k FROM t WHERE (i=1 AND j=2) OR (i=2 AND j=1) OR (i=3 AND j=4);
+} {}
+do_execsql_test 1.16 {
+ SELECT k FROM t WHERE (i=1 AND (j=1 or j=2)) OR (i=3 AND j=3);
+} {one three}
+
+do_execsql_test 2.0 {
+ CREATE TABLE t1(a,b,c,d);
+ CREATE INDEX t1b ON t1(b);
+ CREATE INDEX t1c ON t1(c);
+ CREATE INDEX t1d ON t1(d);
+ CREATE TABLE t2(x,y);
+ CREATE INDEX t2y ON t2(y);
+
+ INSERT INTO t1 VALUES(1,2,3,4);
+ INSERT INTO t1 VALUES(5,6,7,8);
+ INSERT INTO t2 VALUES(1,2);
+ INSERT INTO t2 VALUES(2,7);
+ INSERT INTO t2 VALUES(3,4);
+} {}
+do_execsql_test 2.1 {
+ SELECT a, x FROM t1 JOIN t2 ON +y=d OR x=7 ORDER BY a, x;
+} {1 3}
+do_execsql_test 2.2 {
+ SELECT a, x FROM t1 JOIN t2 ON y=d OR x=7 ORDER BY a, x;
+} {1 3}
+
+
+# Similar to [do_execsql_test], except that two elements are appended
+# to the result - the string "search" and the number of times test variable
+# sqlite3_search_count is incremented by running the supplied SQL. e.g.
+#
+# do_searchcount_test 1.0 { SELECT * FROM t1 } {x y search 2}
+#
+proc do_searchcount_test {tn sql res} {
+ uplevel [subst -nocommands {
+ do_test $tn {
+ set ::sqlite_search_count 0
+ concat [db eval {$sql}] search [set ::sqlite_search_count]
+ } [list $res]
+ }]
+}
+
+do_execsql_test 3.0 {
+ CREATE TABLE t3(a, b, c);
+ CREATE UNIQUE INDEX i3 ON t3(a, b);
+ INSERT INTO t3 VALUES(1, 'one', 'i');
+ INSERT INTO t3 VALUES(3, 'three', 'iii');
+ INSERT INTO t3 VALUES(6, 'six', 'vi');
+ INSERT INTO t3 VALUES(2, 'two', 'ii');
+ INSERT INTO t3 VALUES(4, 'four', 'iv');
+ INSERT INTO t3 VALUES(5, 'five', 'v');
+
+ CREATE TABLE t4(x PRIMARY KEY, y);
+ INSERT INTO t4 VALUES('a', 'one');
+ INSERT INTO t4 VALUES('b', 'two');
+} {}
+
+do_searchcount_test 3.1 {
+ SELECT a, b FROM t3 WHERE (a=1 AND b='one') OR (a=2 AND b='two')
+} {1 one 2 two search 2}
+
+do_searchcount_test 3.2 {
+ SELECT a, c FROM t3 WHERE (a=1 AND b='one') OR (a=2 AND b='two')
+} {1 i 2 ii search 4}
+
+do_searchcount_test 3.4.1 {
+ SELECT y FROM t4 WHERE x='a'
+} {one search 2}
+do_searchcount_test 3.4.2 {
+ SELECT a, b FROM t3 WHERE
+ (a=1 AND b=(SELECT y FROM t4 WHERE x='a'))
+ OR (a=2 AND b='two')
+} {1 one 2 two search 4}
+do_searchcount_test 3.4.3 {
+ SELECT a, b FROM t3 WHERE
+ (a=2 AND b='two')
+ OR (a=1 AND b=(SELECT y FROM t4 WHERE x='a'))
+} {2 two 1 one search 4}
+do_searchcount_test 3.4.4 {
+ SELECT a, b FROM t3 WHERE
+ (a=2 AND b=(SELECT y FROM t4 WHERE x='b'))
+ OR (a=1 AND b=(SELECT y FROM t4 WHERE x='a'))
+} {2 two 1 one search 6}
+
+do_searchcount_test 3.5.1 {
+ SELECT a, b FROM t3 WHERE (a=1 AND b='one') OR rowid=4
+} {1 one 2 two search 2}
+do_searchcount_test 3.5.2 {
+ SELECT a, c FROM t3 WHERE (a=1 AND b='one') OR rowid=4
+} {1 i 2 ii search 2}
+
+finish_test