From fcd49531c9fc2b5a75b713ac8d817f8dd722e6fa Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 13 May 2015 15:24:07 +0000 Subject: [PATCH 1/7] An early attempt to get indexes to work with the IS operator. This code passes tests, but much more testing is needed to verify that it works on all corner cases. FossilOrigin-Name: 6f7f1673d00d216a5aa456acb44793a14f3b3d91 --- manifest | 19 +++++++++++-------- manifest.uuid | 2 +- src/where.c | 32 +++++++++++++++++++------------- src/whereInt.h | 1 + 4 files changed, 32 insertions(+), 22 deletions(-) diff --git a/manifest b/manifest index 0c5a5294e3..ec12a04f7c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Enhancements\sto\sthe\sMSVC\smakefile. -D 2015-05-13T04:50:30.732 +C An\searly\sattempt\sto\sget\sindexes\sto\swork\swith\sthe\sIS\soperator.\s\sThis\scode\npasses\stests,\sbut\smuch\smore\stesting\sis\sneeded\sto\sverify\sthat\sit\sworks\son\nall\scorner\scases. +D 2015-05-13T15:24:07.257 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in edfc69769e613a6359c42c06ea1d42c3bece1736 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -307,8 +307,8 @@ F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb F src/wal.c ce2cb2d06faab54d1bce3e739bec79e063dd9113 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804 -F src/where.c 85fff9c40569ccb79c3177419b339e7d7df566cb -F src/whereInt.h cbe4aa57326998d89e7698ca65bb7c28541d483c +F src/where.c 48e3c1f0a8a7ecbaeb5a57c195254243d49430ba +F src/whereInt.h 6b5a8ac7b4adc055176c3330d755735ed674335d F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggnested.test b35b4cd69fc913f90d39a575e171e1116c3a4bb7 @@ -1258,7 +1258,10 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P b33f1bacfdb34fe66b7b073e68bfac38498d6e88 -R 786cf9bafb843a30daaea508385a36b1 -U mistachkin -Z cf0d8cd171ac487c0fd87c289759c254 +P 59e3e9e764440b7feaafadff74f422535d21bca2 +R 9f6038338a4c8e424f8c4ba92f9afc16 +T *branch * index-is-operator +T *sym-index-is-operator * +T -sym-trunk * +U drh +Z a0eadd388567e992c46f2fabbded464e diff --git a/manifest.uuid b/manifest.uuid index af09740391..877d59c28e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -59e3e9e764440b7feaafadff74f422535d21bca2 \ No newline at end of file +6f7f1673d00d216a5aa456acb44793a14f3b3d91 \ No newline at end of file diff --git a/src/where.c b/src/where.c index 85eb00b46b..6bbfaca879 100644 --- a/src/where.c +++ b/src/where.c @@ -363,7 +363,7 @@ static int allowedOp(int op){ assert( TK_LT>TK_EQ && TK_LTTK_EQ && TK_LE=TK_EQ && op<=TK_GE) || op==TK_ISNULL; + return op==TK_IN || (op>=TK_EQ && op<=TK_GE) || op==TK_ISNULL || op==TK_IS; } /* @@ -416,6 +416,8 @@ static u16 operatorMask(int op){ c = WO_IN; }else if( op==TK_ISNULL ){ c = WO_ISNULL; + }else if( op==TK_IS ){ + c = WO_EQ; }else{ assert( (WO_EQ<<(op-TK_EQ)) < 0x7fff ); c = (u16)(WO_EQ<<(op-TK_EQ)); @@ -427,6 +429,7 @@ static u16 operatorMask(int op){ assert( op!=TK_LE || c==WO_LE ); assert( op!=TK_GT || c==WO_GT ); assert( op!=TK_GE || c==WO_GE ); + assert( op!=TK_IS || c==WO_EQ ); return c; } @@ -1254,6 +1257,7 @@ static void exprAnalyze( pTerm->u.leftColumn = pLeft->iColumn; pTerm->eOperator = operatorMask(op) & opMask; } + if( op==TK_IS ) pTerm->wtFlags |= TERM_NULLOK; if( pRight && pRight->op==TK_COLUMN ){ WhereTerm *pNew; Expr *pDup; @@ -1278,6 +1282,7 @@ static void exprAnalyze( pTerm->eOperator |= WO_EQUIV; eExtraOp = WO_EQUIV; } + if( op==TK_IS ) pNew->wtFlags |= TERM_NULLOK; }else{ pDup = pExpr; pNew = pTerm; @@ -1468,10 +1473,8 @@ static void exprAnalyze( ** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a ** virtual term of that form. ** - ** Note that the virtual term must be tagged with TERM_VNULL. This - ** TERM_VNULL tag will suppress the not-null check at the beginning - ** of the loop. Without the TERM_VNULL flag, the not-null check at - ** the start of the loop will prevent any results from being returned. + ** Note that the virtual term must be tagged with both TERM_VNULL + ** and TERM_NULLOK. */ if( pExpr->op==TK_NOTNULL && pExpr->pLeft->op==TK_COLUMN @@ -1488,7 +1491,7 @@ static void exprAnalyze( sqlite3PExpr(pParse, TK_NULL, 0, 0, 0), 0); idxNew = whereClauseInsert(pWC, pNewExpr, - TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL); + TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL|TERM_NULLOK); if( idxNew ){ pNewTerm = &pWC->a[idxNew]; pNewTerm->prereqRight = 0; @@ -2792,7 +2795,7 @@ static int codeEqualityTerm( int iReg; /* Register holding results */ assert( iTarget>0 ); - if( pX->op==TK_EQ ){ + if( pX->op==TK_EQ || pX->op==TK_IS ){ iReg = sqlite3ExprCodeTarget(pParse, pX->pRight, iTarget); }else if( pX->op==TK_ISNULL ){ iReg = iTarget; @@ -2977,7 +2980,9 @@ static int codeAllEqualityTerms( testcase( pTerm->eOperator & WO_IN ); if( (pTerm->eOperator & (WO_ISNULL|WO_IN))==0 ){ Expr *pRight = pTerm->pExpr->pRight; - if( sqlite3ExprCanBeNull(pRight) ){ + if( (pTerm->wtFlags & TERM_NULLOK)==0 + && sqlite3ExprCanBeNull(pRight) + ){ sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk); VdbeCoverage(v); } @@ -3626,7 +3631,7 @@ static Bitmask codeOneLoopStart( Expr *pRight = pRangeStart->pExpr->pRight; sqlite3ExprCode(pParse, pRight, regBase+nEq); whereLikeOptimizationStringFixup(v, pLevel, pRangeStart); - if( (pRangeStart->wtFlags & TERM_VNULL)==0 + if( (pRangeStart->wtFlags & TERM_NULLOK)==0 && sqlite3ExprCanBeNull(pRight) ){ sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt); @@ -3672,7 +3677,7 @@ static Bitmask codeOneLoopStart( sqlite3ExprCacheRemove(pParse, regBase+nEq, 1); sqlite3ExprCode(pParse, pRight, regBase+nEq); whereLikeOptimizationStringFixup(v, pLevel, pRangeEnd); - if( (pRangeEnd->wtFlags & TERM_VNULL)==0 + if( (pRangeEnd->wtFlags & TERM_NULLOK)==0 && sqlite3ExprCanBeNull(pRight) ){ sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt); @@ -4158,9 +4163,10 @@ static void whereTermPrint(WhereTerm *pTerm, int iTerm){ if( pTerm->wtFlags & TERM_VIRTUAL ) zType[0] = 'V'; if( pTerm->eOperator & WO_EQUIV ) zType[1] = 'E'; if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) zType[2] = 'L'; - sqlite3DebugPrintf("TERM-%-3d %p %s cursor=%-3d prob=%-3d op=0x%03x\n", - iTerm, pTerm, zType, pTerm->leftCursor, pTerm->truthProb, - pTerm->eOperator); + sqlite3DebugPrintf( + "TERM-%-3d %p %s cursor=%-3d prob=%-3d op=0x%03x wtFlags=0x%04x\n", + iTerm, pTerm, zType, pTerm->leftCursor, pTerm->truthProb, + pTerm->eOperator, pTerm->wtFlags); sqlite3TreeViewExpr(0, pTerm->pExpr, 0); } } diff --git a/src/whereInt.h b/src/whereInt.h index 04cc2029d8..915d7b8d68 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -280,6 +280,7 @@ struct WhereTerm { #define TERM_LIKEOPT 0x100 /* Virtual terms from the LIKE optimization */ #define TERM_LIKECOND 0x200 /* Conditionally this LIKE operator term */ #define TERM_LIKE 0x400 /* The original LIKE operator */ +#define TERM_NULLOK 0x800 /* Comparison operators against NULL work */ /* ** An instance of the WhereScan object is used as an iterator for locating From e0cc3c296caa6a62967b50238f3563215727d11c Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 13 May 2015 17:54:08 +0000 Subject: [PATCH 2/7] Add testcase() macros and comments and a few test-cases. FossilOrigin-Name: 24263d08b11c88416d270013bdaf5ff45125cb4d --- manifest | 17 +++++++---------- manifest.uuid | 2 +- src/where.c | 15 ++++++++++++--- test/where.test | 30 ++++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 14 deletions(-) diff --git a/manifest b/manifest index ec12a04f7c..4413996a08 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C An\searly\sattempt\sto\sget\sindexes\sto\swork\swith\sthe\sIS\soperator.\s\sThis\scode\npasses\stests,\sbut\smuch\smore\stesting\sis\sneeded\sto\sverify\sthat\sit\sworks\son\nall\scorner\scases. -D 2015-05-13T15:24:07.257 +C Add\stestcase()\smacros\sand\scomments\sand\sa\sfew\stest-cases. +D 2015-05-13T17:54:08.892 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in edfc69769e613a6359c42c06ea1d42c3bece1736 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -307,7 +307,7 @@ F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb F src/wal.c ce2cb2d06faab54d1bce3e739bec79e063dd9113 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804 -F src/where.c 48e3c1f0a8a7ecbaeb5a57c195254243d49430ba +F src/where.c 98964edb2a27d6f87976b85a8b00862558a277b2 F src/whereInt.h 6b5a8ac7b4adc055176c3330d755735ed674335d F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -1165,7 +1165,7 @@ F test/walro.test 34422d1d95aaff0388f0791ec20edb34e2a3ed57 F test/walshared.test 0befc811dcf0b287efae21612304d15576e35417 F test/walslow.test e7be6d9888f83aa5d3d3c7c08aa9b5c28b93609a F test/walthread.test de8dbaf6d9e41481c460ba31ca61e163d7348f8e -F test/where.test 28b64e93428961b07b0d486778d63fd672948f6b +F test/where.test 1ff3d9f8da0a6c0dc5ccfd38d9225b2cdb5b6afb F test/where2.test 23fdb5d8e756554aad4ca7ae03de9dd8367a2c6e F test/where3.test 1ad55ba900bd7747f98b6082e65bd3e442c5004e F test/where4.test d8420ceeb8323a41ceff1f1841fc528e824e1ecf @@ -1258,10 +1258,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 59e3e9e764440b7feaafadff74f422535d21bca2 -R 9f6038338a4c8e424f8c4ba92f9afc16 -T *branch * index-is-operator -T *sym-index-is-operator * -T -sym-trunk * +P 6f7f1673d00d216a5aa456acb44793a14f3b3d91 +R 3ff0212a0c4cf6ad597a241e62c5ab7c U drh -Z a0eadd388567e992c46f2fabbded464e +Z fddf4aa627339444877435638e7a9621 diff --git a/manifest.uuid b/manifest.uuid index 877d59c28e..d093009b2a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6f7f1673d00d216a5aa456acb44793a14f3b3d91 \ No newline at end of file +24263d08b11c88416d270013bdaf5ff45125cb4d \ No newline at end of file diff --git a/src/where.c b/src/where.c index 6bbfaca879..1c2c0931be 100644 --- a/src/where.c +++ b/src/where.c @@ -417,7 +417,7 @@ static u16 operatorMask(int op){ }else if( op==TK_ISNULL ){ c = WO_ISNULL; }else if( op==TK_IS ){ - c = WO_EQ; + c = WO_EQ; /* IS works like ==, just without the IsNull tests */ }else{ assert( (WO_EQ<<(op-TK_EQ)) < 0x7fff ); c = (u16)(WO_EQ<<(op-TK_EQ)); @@ -1117,6 +1117,7 @@ static void exprAnalyzeOrTerm( okToChngToIN = 1; for(; i>=0 && okToChngToIN; i--, pOrTerm++){ assert( pOrTerm->eOperator & WO_EQ ); + testcase( pOrTerm->pExpr->op==TK_IS ); if( pOrTerm->leftCursor!=iCursor ){ pOrTerm->wtFlags &= ~TERM_OR_OK; }else if( pOrTerm->u.leftColumn!=iColumn ){ @@ -1153,6 +1154,7 @@ static void exprAnalyzeOrTerm( assert( pOrTerm->eOperator & WO_EQ ); assert( pOrTerm->leftCursor==iCursor ); assert( pOrTerm->u.leftColumn==iColumn ); + testcase( pOrTerm->pExpr->op==TK_IS ); pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0); pList = sqlite3ExprListAppend(pWInfo->pParse, pList, pDup); pLeft = pOrTerm->pExpr->pLeft; @@ -1683,6 +1685,7 @@ static int termCanDriveIndex( if( pTerm->u.leftColumn<0 ) return 0; aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity; if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; + testcase( pTerm->pExpr->op==TK_IS ); return 1; } #endif @@ -2980,6 +2983,7 @@ static int codeAllEqualityTerms( testcase( pTerm->eOperator & WO_IN ); if( (pTerm->eOperator & (WO_ISNULL|WO_IN))==0 ){ Expr *pRight = pTerm->pExpr->pRight; + testcase( pTerm->pExpr->op==TK_IS ); if( (pTerm->wtFlags & TERM_NULLOK)==0 && sqlite3ExprCanBeNull(pRight) ){ @@ -3631,6 +3635,7 @@ static Bitmask codeOneLoopStart( Expr *pRight = pRangeStart->pExpr->pRight; sqlite3ExprCode(pParse, pRight, regBase+nEq); whereLikeOptimizationStringFixup(v, pLevel, pRangeStart); + testcase( pRangeStart->pExpr->op==TK_IS ); if( (pRangeStart->wtFlags & TERM_NULLOK)==0 && sqlite3ExprCanBeNull(pRight) ){ @@ -3677,6 +3682,7 @@ static Bitmask codeOneLoopStart( sqlite3ExprCacheRemove(pParse, regBase+nEq, 1); sqlite3ExprCode(pParse, pRight, regBase+nEq); whereLikeOptimizationStringFixup(v, pLevel, pRangeEnd); + testcase( pRangeEnd->pExpr->op==TK_IS ); if( (pRangeEnd->wtFlags & TERM_NULLOK)==0 && sqlite3ExprCanBeNull(pRight) ){ @@ -4113,7 +4119,7 @@ static Bitmask codeOneLoopStart( pAlt = findTerm(pWC, iCur, pTerm->u.leftColumn, notReady, WO_EQ|WO_IN, 0); if( pAlt==0 ) continue; if( pAlt->wtFlags & (TERM_CODED) ) continue; - testcase( pAlt->eOperator & WO_EQ ); + testcase( (pAlt->eOperator & WO_EQ)!=0 && pAlt->pExpr->op==TK_IS ); testcase( pAlt->eOperator & WO_IN ); VdbeModuleComment((v, "begin transitive constraint")); pEAlt = sqlite3StackAllocRaw(db, sizeof(*pEAlt)); @@ -4658,6 +4664,7 @@ static void whereLoopOutputAdjust( pLoop->nOut--; if( pTerm->eOperator&WO_EQ ){ Expr *pRight = pTerm->pExpr->pRight; + testcase( pTerm->pExpr->op==TK_IS ); if( sqlite3ExprIsInteger(pRight, &k) && k>=(-1) && k<=1 ){ k = 10; }else{ @@ -4859,7 +4866,7 @@ static int whereLoopAddBtreeIndex( ){ Expr *pExpr = pTerm->pExpr; if( (eOp & (WO_EQ|WO_ISNULL))!=0 ){ - testcase( eOp & WO_EQ ); + testcase( (eOp & WO_EQ)!=0 && pExpr->op==TK_IS ); testcase( eOp & WO_ISNULL ); rc = whereEqualScanEst(pParse, pBuilder, pExpr->pRight, &nOut); }else{ @@ -5707,6 +5714,7 @@ static i8 wherePathSatisfiesOrderBy( if( !pColl ) pColl = db->pDfltColl; z2 = pColl->zName; if( sqlite3StrICmp(z1, z2)!=0 ) continue; + testcase( pTerm->pExpr->op==TK_IS ); } obSat |= MASKBIT(i); } @@ -6329,6 +6337,7 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ pTerm = findTerm(pWC, iCur, pIdx->aiColumn[j], 0, WO_EQ, pIdx); if( pTerm==0 ) break; pLoop->aLTerm[j] = pTerm; + testcase( pTerm->pExpr->op==TK_IS ); } if( j!=pIdx->nKeyCol ) continue; pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_ONEROW|WHERE_INDEXED; diff --git a/test/where.test b/test/where.test index f560708cca..72fd696306 100644 --- a/test/where.test +++ b/test/where.test @@ -65,9 +65,15 @@ proc count sql { do_test where-1.1.1 { count {SELECT x, y, w FROM t1 WHERE w=10} } {3 121 10 3} +do_test where-1.1.1b { + count {SELECT x, y, w FROM t1 WHERE w IS 10} +} {3 121 10 3} do_eqp_test where-1.1.2 { SELECT x, y, w FROM t1 WHERE w=10 } {*SEARCH TABLE t1 USING INDEX i1w (w=?)*} +do_eqp_test where-1.1.2b { + SELECT x, y, w FROM t1 WHERE w IS 10 +} {*SEARCH TABLE t1 USING INDEX i1w (w=?)*} do_test where-1.1.3 { db status step } {0} @@ -101,12 +107,21 @@ do_test where-1.3.1 { do_test where-1.3.2 { count {SELECT x, y, w AS abc FROM t1 WHERE 11=abc} } {3 144 11 3} +do_test where-1.3.3 { + count {SELECT x, y, w AS abc FROM t1 WHERE 11 IS abc} +} {3 144 11 3} do_test where-1.4.1 { count {SELECT w, x, y FROM t1 WHERE 11=w AND x>2} } {11 3 144 3} +do_test where-1.4.1b { + count {SELECT w, x, y FROM t1 WHERE 11 IS w AND x>2} +} {11 3 144 3} do_eqp_test where-1.4.2 { SELECT w, x, y FROM t1 WHERE 11=w AND x>2 } {*SEARCH TABLE t1 USING INDEX i1w (w=?)*} +do_eqp_test where-1.4.2b { + SELECT w, x, y FROM t1 WHERE 11 IS w AND x>2 +} {*SEARCH TABLE t1 USING INDEX i1w (w=?)*} do_test where-1.4.3 { count {SELECT w AS a, x AS b, y FROM t1 WHERE 11=a AND b>2} } {11 3 144 3} @@ -143,6 +158,9 @@ do_test where-1.10 { do_test where-1.11 { count {SELECT x, y FROM t1 WHERE x=3 AND y=100 AND w<10} } {3 100 3} +do_test where-1.11b { + count {SELECT x, y FROM t1 WHERE x IS 3 AND y IS 100 AND w<10} +} {3 100 3} # New for SQLite version 2.1: Verify that that inequality constraints # are used correctly. @@ -150,12 +168,18 @@ do_test where-1.11 { do_test where-1.12 { count {SELECT w FROM t1 WHERE x=3 AND y<100} } {8 3} +do_test where-1.12b { + count {SELECT w FROM t1 WHERE x IS 3 AND y<100} +} {8 3} do_test where-1.13 { count {SELECT w FROM t1 WHERE x=3 AND 100>y} } {8 3} do_test where-1.14 { count {SELECT w FROM t1 WHERE 3=x AND y<100} } {8 3} +do_test where-1.14b { + count {SELECT w FROM t1 WHERE 3 IS x AND y<100} +} {8 3} do_test where-1.15 { count {SELECT w FROM t1 WHERE 3=x AND 100>y} } {8 3} @@ -168,6 +192,9 @@ do_test where-1.17 { do_test where-1.18 { count {SELECT w FROM t1 WHERE x=3 AND y>225} } {15 3} +do_test where-1.18b { + count {SELECT w FROM t1 WHERE x IS 3 AND y>225} +} {15 3} do_test where-1.19 { count {SELECT w FROM t1 WHERE x=3 AND 225121 AND y<196} } {11 12 5} +do_test where-1.22b { + count {SELECT w FROM t1 WHERE x IS 3 AND y>121 AND y<196} +} {11 12 5} do_test where-1.23 { count {SELECT w FROM t1 WHERE x=3 AND y>=121 AND y<=196} } {10 11 12 13 9} From 9be18709947d14443a4769c01e9be0050d247df2 Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 13 May 2015 19:33:41 +0000 Subject: [PATCH 3/7] Simplified implementation of indexing with the IS operator. FossilOrigin-Name: 95b1f9bf14e490c6c6bba9ea78aeab712a44aab5 --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/where.c | 20 +++++++------------- src/whereInt.h | 4 ++-- test/whereC.test | 1 + 5 files changed, 19 insertions(+), 24 deletions(-) diff --git a/manifest b/manifest index 4413996a08..88aafccee7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\stestcase()\smacros\sand\scomments\sand\sa\sfew\stest-cases. -D 2015-05-13T17:54:08.892 +C Simplified\simplementation\sof\sindexing\swith\sthe\sIS\soperator. +D 2015-05-13T19:33:41.990 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in edfc69769e613a6359c42c06ea1d42c3bece1736 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -307,8 +307,8 @@ F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb F src/wal.c ce2cb2d06faab54d1bce3e739bec79e063dd9113 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804 -F src/where.c 98964edb2a27d6f87976b85a8b00862558a277b2 -F src/whereInt.h 6b5a8ac7b4adc055176c3330d755735ed674335d +F src/where.c fa61aca146422a2a056aeb0381a6bb1c37d46c07 +F src/whereInt.h 31d30e83621c21358d031510d5c6a3e474cd8bb7 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggnested.test b35b4cd69fc913f90d39a575e171e1116c3a4bb7 @@ -1177,7 +1177,7 @@ F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739 F test/where9.test 729c3ba9b47e8f9f1aab96bae7dad2a524f1d1a2 F test/whereA.test 4d253178d135ec46d1671e440cd8f2b916aa6e6b F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5 -F test/whereC.test d6f4ecd4fa2d9429681a5b22a25d2bda8e86ab8a +F test/whereC.test cae295158703cb3fc23bf1a108a9ab730efff0f6 F test/whereD.test 9eba1f9b18e5b19a0b0bcaae5e8c037260195f2b F test/whereE.test b3a055eef928c992b0a33198a7b8dc10eea5ad2f F test/whereF.test 5b2ba0dbe8074aa13e416b37c753991f0a2492d7 @@ -1258,7 +1258,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 6f7f1673d00d216a5aa456acb44793a14f3b3d91 -R 3ff0212a0c4cf6ad597a241e62c5ab7c +P 24263d08b11c88416d270013bdaf5ff45125cb4d +R 16be24e7bac77474bad7ab9d3d121f18 U drh -Z fddf4aa627339444877435638e7a9621 +Z 3bc6b705447a86373e017a72613abb32 diff --git a/manifest.uuid b/manifest.uuid index d093009b2a..660fdcd247 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -24263d08b11c88416d270013bdaf5ff45125cb4d \ No newline at end of file +95b1f9bf14e490c6c6bba9ea78aeab712a44aab5 \ No newline at end of file diff --git a/src/where.c b/src/where.c index 1c2c0931be..aad9799211 100644 --- a/src/where.c +++ b/src/where.c @@ -1259,7 +1259,7 @@ static void exprAnalyze( pTerm->u.leftColumn = pLeft->iColumn; pTerm->eOperator = operatorMask(op) & opMask; } - if( op==TK_IS ) pTerm->wtFlags |= TERM_NULLOK; + if( op==TK_IS ) pTerm->wtFlags |= TERM_IS; if( pRight && pRight->op==TK_COLUMN ){ WhereTerm *pNew; Expr *pDup; @@ -1284,7 +1284,7 @@ static void exprAnalyze( pTerm->eOperator |= WO_EQUIV; eExtraOp = WO_EQUIV; } - if( op==TK_IS ) pNew->wtFlags |= TERM_NULLOK; + if( op==TK_IS ) pNew->wtFlags |= TERM_IS; }else{ pDup = pExpr; pNew = pTerm; @@ -1475,8 +1475,7 @@ static void exprAnalyze( ** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a ** virtual term of that form. ** - ** Note that the virtual term must be tagged with both TERM_VNULL - ** and TERM_NULLOK. + ** Note that the virtual term must be tagged with TERM_VNULL. */ if( pExpr->op==TK_NOTNULL && pExpr->pLeft->op==TK_COLUMN @@ -1493,7 +1492,7 @@ static void exprAnalyze( sqlite3PExpr(pParse, TK_NULL, 0, 0, 0), 0); idxNew = whereClauseInsert(pWC, pNewExpr, - TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL|TERM_NULLOK); + TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL); if( idxNew ){ pNewTerm = &pWC->a[idxNew]; pNewTerm->prereqRight = 0; @@ -2983,10 +2982,7 @@ static int codeAllEqualityTerms( testcase( pTerm->eOperator & WO_IN ); if( (pTerm->eOperator & (WO_ISNULL|WO_IN))==0 ){ Expr *pRight = pTerm->pExpr->pRight; - testcase( pTerm->pExpr->op==TK_IS ); - if( (pTerm->wtFlags & TERM_NULLOK)==0 - && sqlite3ExprCanBeNull(pRight) - ){ + if( (pTerm->wtFlags & TERM_IS)==0 && sqlite3ExprCanBeNull(pRight) ){ sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk); VdbeCoverage(v); } @@ -3635,8 +3631,7 @@ static Bitmask codeOneLoopStart( Expr *pRight = pRangeStart->pExpr->pRight; sqlite3ExprCode(pParse, pRight, regBase+nEq); whereLikeOptimizationStringFixup(v, pLevel, pRangeStart); - testcase( pRangeStart->pExpr->op==TK_IS ); - if( (pRangeStart->wtFlags & TERM_NULLOK)==0 + if( (pRangeStart->wtFlags & TERM_VNULL)==0 && sqlite3ExprCanBeNull(pRight) ){ sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt); @@ -3682,8 +3677,7 @@ static Bitmask codeOneLoopStart( sqlite3ExprCacheRemove(pParse, regBase+nEq, 1); sqlite3ExprCode(pParse, pRight, regBase+nEq); whereLikeOptimizationStringFixup(v, pLevel, pRangeEnd); - testcase( pRangeEnd->pExpr->op==TK_IS ); - if( (pRangeEnd->wtFlags & TERM_NULLOK)==0 + if( (pRangeEnd->wtFlags & TERM_VNULL)==0 && sqlite3ExprCanBeNull(pRight) ){ sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt); diff --git a/src/whereInt.h b/src/whereInt.h index 915d7b8d68..e7730d71ed 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -280,7 +280,7 @@ struct WhereTerm { #define TERM_LIKEOPT 0x100 /* Virtual terms from the LIKE optimization */ #define TERM_LIKECOND 0x200 /* Conditionally this LIKE operator term */ #define TERM_LIKE 0x400 /* The original LIKE operator */ -#define TERM_NULLOK 0x800 /* Comparison operators against NULL work */ +#define TERM_IS 0x800 /* Term.pExpr is an IS operator */ /* ** An instance of the WhereScan object is used as an iterator for locating @@ -430,7 +430,7 @@ struct WhereInfo { ** particular WhereTerms within a WhereClause. */ #define WO_IN 0x001 -#define WO_EQ 0x002 +#define WO_EQ 0x002 /* Used for both == and IS */ #define WO_LT (WO_EQ<<(TK_LT-TK_EQ)) #define WO_LE (WO_EQ<<(TK_LE-TK_EQ)) #define WO_GT (WO_EQ<<(TK_GT-TK_EQ)) diff --git a/test/whereC.test b/test/whereC.test index bd7fe9ad82..f172069675 100644 --- a/test/whereC.test +++ b/test/whereC.test @@ -59,6 +59,7 @@ foreach {tn sql res} { 12 "SELECT i FROM t1 WHERE a=2 AND b=2 AND i=NULL" {} 14 "SELECT i FROM t1 WHERE a=1 AND b='2' AND i<4.5" {3 4} + 15 "SELECT i FROM t1 WHERE rowid IS '12'" {12} } { do_execsql_test 1.$tn.1 $sql $res do_execsql_test 1.$tn.2 "$sql ORDER BY i ASC" [lsort -integer -inc $res] From e8d0c61f0aa723d698d36be3bcb13c4d3feb746d Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 14 May 2015 01:05:25 +0000 Subject: [PATCH 4/7] A new implementation of indexing with the IS operator that works correctly when the IS operator is in the WHERE clause and the operands are from opposite sides of a LEFT JOIN. FossilOrigin-Name: 4541688b3f56f5cd3d5b299594b58c577ad633bb --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/where.c | 49 +++++++++++++++++++++++++++--------------------- src/whereInt.h | 21 +++++++++++---------- test/where4.test | 15 +++++++++++++++ 5 files changed, 63 insertions(+), 40 deletions(-) diff --git a/manifest b/manifest index 88aafccee7..438ec23110 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Simplified\simplementation\sof\sindexing\swith\sthe\sIS\soperator. -D 2015-05-13T19:33:41.990 +C A\snew\simplementation\sof\sindexing\swith\sthe\sIS\soperator\sthat\sworks\scorrectly\nwhen\sthe\sIS\soperator\sis\sin\sthe\sWHERE\sclause\sand\sthe\soperands\sare\sfrom\s\nopposite\ssides\sof\sa\sLEFT\sJOIN. +D 2015-05-14T01:05:25.274 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in edfc69769e613a6359c42c06ea1d42c3bece1736 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -307,8 +307,8 @@ F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb F src/wal.c ce2cb2d06faab54d1bce3e739bec79e063dd9113 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804 -F src/where.c fa61aca146422a2a056aeb0381a6bb1c37d46c07 -F src/whereInt.h 31d30e83621c21358d031510d5c6a3e474cd8bb7 +F src/where.c 64afb483fe8bede1e31e6c66bc532530a4600543 +F src/whereInt.h a6f5a762bc1b4b1c76e1cea79976b437ac35a435 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggnested.test b35b4cd69fc913f90d39a575e171e1116c3a4bb7 @@ -1168,7 +1168,7 @@ F test/walthread.test de8dbaf6d9e41481c460ba31ca61e163d7348f8e F test/where.test 1ff3d9f8da0a6c0dc5ccfd38d9225b2cdb5b6afb F test/where2.test 23fdb5d8e756554aad4ca7ae03de9dd8367a2c6e F test/where3.test 1ad55ba900bd7747f98b6082e65bd3e442c5004e -F test/where4.test d8420ceeb8323a41ceff1f1841fc528e824e1ecf +F test/where4.test a4603fa0d018bd4b9430dac840c9c522af421dd5 F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2 F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b F test/where7.test 5a4b0abc207d71da4deecd734ad8579e8dd40aa8 @@ -1258,7 +1258,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 24263d08b11c88416d270013bdaf5ff45125cb4d -R 16be24e7bac77474bad7ab9d3d121f18 +P 95b1f9bf14e490c6c6bba9ea78aeab712a44aab5 +R 6e0cd1da1bcd5d739d5d402ab5201f6c U drh -Z 3bc6b705447a86373e017a72613abb32 +Z 3608a708c2f560b3e002545b6da23dda diff --git a/manifest.uuid b/manifest.uuid index 660fdcd247..2dcd8f7064 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -95b1f9bf14e490c6c6bba9ea78aeab712a44aab5 \ No newline at end of file +4541688b3f56f5cd3d5b299594b58c577ad633bb \ No newline at end of file diff --git a/src/where.c b/src/where.c index aad9799211..3c96f93a7e 100644 --- a/src/where.c +++ b/src/where.c @@ -417,7 +417,7 @@ static u16 operatorMask(int op){ }else if( op==TK_ISNULL ){ c = WO_ISNULL; }else if( op==TK_IS ){ - c = WO_EQ; /* IS works like ==, just without the IsNull tests */ + c = WO_IS; }else{ assert( (WO_EQ<<(op-TK_EQ)) < 0x7fff ); c = (u16)(WO_EQ<<(op-TK_EQ)); @@ -429,7 +429,7 @@ static u16 operatorMask(int op){ assert( op!=TK_LE || c==WO_LE ); assert( op!=TK_GT || c==WO_GT ); assert( op!=TK_GE || c==WO_GE ); - assert( op!=TK_IS || c==WO_EQ ); + assert( op!=TK_IS || c==WO_IS ); return c; } @@ -490,11 +490,12 @@ static WhereTerm *whereScanNext(WhereScan *pScan){ continue; } } - if( (pTerm->eOperator & WO_EQ)!=0 + if( (pTerm->eOperator & (WO_EQ|WO_IS))!=0 && (pX = pTerm->pExpr->pRight)->op==TK_COLUMN && pX->iTable==pScan->aEquiv[0] && pX->iColumn==pScan->aEquiv[1] ){ + testcase( pTerm->eOperator & WO_IS ); continue; } pScan->k = k+1; @@ -596,9 +597,11 @@ static WhereTerm *findTerm( WhereScan scan; p = whereScanInit(&scan, pWC, iCur, iColumn, op, pIdx); + op &= WO_EQ|WO_IS; while( p ){ if( (p->prereqRight & notReady)==0 ){ - if( p->prereqRight==0 && (p->eOperator&WO_EQ)!=0 ){ + if( p->prereqRight==0 && (p->eOperator&op)!=0 ){ + testcase( p->eOperator & WO_IS ); return p; } if( pResult==0 ) pResult = p; @@ -1679,7 +1682,7 @@ static int termCanDriveIndex( ){ char aff; if( pTerm->leftCursor!=pSrc->iCursor ) return 0; - if( (pTerm->eOperator & WO_EQ)==0 ) return 0; + if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) return 0; if( (pTerm->prereqRight & notReady)!=0 ) return 0; if( pTerm->u.leftColumn<0 ) return 0; aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity; @@ -1955,7 +1958,7 @@ static sqlite3_index_info *allocateIndexInfo( testcase( pTerm->eOperator & WO_IN ); testcase( pTerm->eOperator & WO_ISNULL ); testcase( pTerm->eOperator & WO_ALL ); - if( (pTerm->eOperator & ~(WO_ISNULL|WO_EQUIV))==0 ) continue; + if( (pTerm->eOperator & ~(WO_ISNULL|WO_EQUIV|WO_IS))==0 ) continue; if( pTerm->wtFlags & TERM_VNULL ) continue; pIdxCons[j].iColumn = pTerm->u.leftColumn; pIdxCons[j].iTermOffset = i; @@ -4104,16 +4107,18 @@ static Bitmask codeOneLoopStart( Expr *pE, *pEAlt; WhereTerm *pAlt; if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; - if( pTerm->eOperator!=(WO_EQUIV|WO_EQ) ) continue; + if( (pTerm->eOperator&(WO_EQUIV|WO_EQ|WO_IS))<=WO_EQUIV ) continue; if( pTerm->leftCursor!=iCur ) continue; if( pLevel->iLeftJoin ) continue; pE = pTerm->pExpr; assert( !ExprHasProperty(pE, EP_FromJoin) ); assert( (pTerm->prereqRight & pLevel->notReady)!=0 ); - pAlt = findTerm(pWC, iCur, pTerm->u.leftColumn, notReady, WO_EQ|WO_IN, 0); + pAlt = findTerm(pWC, iCur, pTerm->u.leftColumn, notReady, + WO_EQ|WO_IN|WO_IS, 0); if( pAlt==0 ) continue; if( pAlt->wtFlags & (TERM_CODED) ) continue; - testcase( (pAlt->eOperator & WO_EQ)!=0 && pAlt->pExpr->op==TK_IS ); + testcase( pAlt->eOperator & WO_EQ ); + testcase( pAlt->eOperator & WO_IS ); testcase( pAlt->eOperator & WO_IN ); VdbeModuleComment((v, "begin transitive constraint")); pEAlt = sqlite3StackAllocRaw(db, sizeof(*pEAlt)); @@ -4656,7 +4661,7 @@ static void whereLoopOutputAdjust( /* In the absence of explicit truth probabilities, use heuristics to ** guess a reasonable truth probability. */ pLoop->nOut--; - if( pTerm->eOperator&WO_EQ ){ + if( pTerm->eOperator&(WO_EQ|WO_IS) ){ Expr *pRight = pTerm->pExpr->pRight; testcase( pTerm->pExpr->op==TK_IS ); if( sqlite3ExprIsInteger(pRight, &k) && k>=(-1) && k<=1 ){ @@ -4729,7 +4734,7 @@ static int whereLoopAddBtreeIndex( }else if( pProbe->tnum<=0 || (pSrc->jointype & JT_LEFT)!=0 ){ opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE; }else{ - opMask = WO_EQ|WO_IN|WO_ISNULL|WO_GT|WO_GE|WO_LT|WO_LE; + opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS; } if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE); @@ -4792,7 +4797,7 @@ static int whereLoopAddBtreeIndex( assert( nIn>0 ); /* RHS always has 2 or more terms... The parser ** changes "x IN (?)" into "x=?". */ - }else if( eOp & (WO_EQ) ){ + }else if( eOp & (WO_EQ|WO_IS) ){ pNew->wsFlags |= WHERE_COLUMN_EQ; if( iCol<0 || (nInMul==0 && pNew->u.btree.nEq==pProbe->nKeyCol-1) ){ if( iCol>=0 && pProbe->uniqNotNull==0 ){ @@ -4842,7 +4847,7 @@ static int whereLoopAddBtreeIndex( whereRangeScanEst(pParse, pBuilder, pBtm, pTop, pNew); }else{ int nEq = ++pNew->u.btree.nEq; - assert( eOp & (WO_ISNULL|WO_EQ|WO_IN) ); + assert( eOp & (WO_ISNULL|WO_EQ|WO_IN|WO_IS) ); assert( pNew->nOut==saved_nOut ); if( pTerm->truthProb<=0 && iCol>=0 ){ @@ -4859,8 +4864,9 @@ static int whereLoopAddBtreeIndex( && ((eOp & WO_IN)==0 || !ExprHasProperty(pTerm->pExpr, EP_xIsSelect)) ){ Expr *pExpr = pTerm->pExpr; - if( (eOp & (WO_EQ|WO_ISNULL))!=0 ){ - testcase( (eOp & WO_EQ)!=0 && pExpr->op==TK_IS ); + if( (eOp & (WO_EQ|WO_ISNULL|WO_IS))!=0 ){ + testcase( eOp & WO_EQ ); + testcase( eOp & WO_IS ); testcase( eOp & WO_ISNULL ); rc = whereEqualScanEst(pParse, pBuilder, pExpr->pRight, &nOut); }else{ @@ -5697,9 +5703,9 @@ static i8 wherePathSatisfiesOrderBy( if( pOBExpr->op!=TK_COLUMN ) continue; if( pOBExpr->iTable!=iCur ) continue; pTerm = findTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn, - ~ready, WO_EQ|WO_ISNULL, 0); + ~ready, WO_EQ|WO_ISNULL|WO_IS, 0); if( pTerm==0 ) continue; - if( (pTerm->eOperator&WO_EQ)!=0 && pOBExpr->iColumn>=0 ){ + if( (pTerm->eOperator&(WO_EQ|WO_IS))!=0 && pOBExpr->iColumn>=0 ){ const char *z1, *z2; pColl = sqlite3ExprCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr); if( !pColl ) pColl = db->pDfltColl; @@ -5739,7 +5745,7 @@ static i8 wherePathSatisfiesOrderBy( /* Skip over == and IS NULL terms */ if( ju.btree.nEq && pLoop->nSkip==0 - && ((i = pLoop->aLTerm[j]->eOperator) & (WO_EQ|WO_ISNULL))!=0 + && ((i = pLoop->aLTerm[j]->eOperator) & (WO_EQ|WO_ISNULL|WO_IS))!=0 ){ if( i & WO_ISNULL ){ testcase( isOrderDistinct ); @@ -6312,8 +6318,9 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ pLoop = pBuilder->pNew; pLoop->wsFlags = 0; pLoop->nSkip = 0; - pTerm = findTerm(pWC, iCur, -1, 0, WO_EQ, 0); + pTerm = findTerm(pWC, iCur, -1, 0, WO_EQ|WO_IS, 0); if( pTerm ){ + testcase( pTerm->eOperator & WO_IS ); pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_IPK|WHERE_ONEROW; pLoop->aLTerm[0] = pTerm; pLoop->nLTerm = 1; @@ -6328,10 +6335,10 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ || pIdx->nKeyCol>ArraySize(pLoop->aLTermSpace) ) continue; for(j=0; jnKeyCol; j++){ - pTerm = findTerm(pWC, iCur, pIdx->aiColumn[j], 0, WO_EQ, pIdx); + pTerm = findTerm(pWC, iCur, pIdx->aiColumn[j], 0, WO_EQ|WO_IS, pIdx); if( pTerm==0 ) break; + testcase( pTerm->eOperator & WO_IS ); pLoop->aLTerm[j] = pTerm; - testcase( pTerm->pExpr->op==TK_IS ); } if( j!=pIdx->nKeyCol ) continue; pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_ONEROW|WHERE_INDEXED; diff --git a/src/whereInt.h b/src/whereInt.h index e7730d71ed..3a5a48e840 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -429,21 +429,22 @@ struct WhereInfo { ** OR-ed combination of these values can be used when searching for ** particular WhereTerms within a WhereClause. */ -#define WO_IN 0x001 -#define WO_EQ 0x002 /* Used for both == and IS */ +#define WO_IN 0x0001 +#define WO_EQ 0x0002 #define WO_LT (WO_EQ<<(TK_LT-TK_EQ)) #define WO_LE (WO_EQ<<(TK_LE-TK_EQ)) #define WO_GT (WO_EQ<<(TK_GT-TK_EQ)) #define WO_GE (WO_EQ<<(TK_GE-TK_EQ)) -#define WO_MATCH 0x040 -#define WO_ISNULL 0x080 -#define WO_OR 0x100 /* Two or more OR-connected terms */ -#define WO_AND 0x200 /* Two or more AND-connected terms */ -#define WO_EQUIV 0x400 /* Of the form A==B, both columns */ -#define WO_NOOP 0x800 /* This term does not restrict search space */ +#define WO_MATCH 0x0040 +#define WO_IS 0x0080 +#define WO_ISNULL 0x0100 +#define WO_OR 0x0200 /* Two or more OR-connected terms */ +#define WO_AND 0x0400 /* Two or more AND-connected terms */ +#define WO_EQUIV 0x0800 /* Of the form A==B, both columns */ +#define WO_NOOP 0x1000 /* This term does not restrict search space */ -#define WO_ALL 0xfff /* Mask of all possible WO_* values */ -#define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */ +#define WO_ALL 0x1fff /* Mask of all possible WO_* values */ +#define WO_SINGLE 0x01ff /* Mask of all non-compound WO_* values */ /* ** These are definitions of bits in the WhereLoop.wsFlags field. diff --git a/test/where4.test b/test/where4.test index a26e9ad355..6bceb9174b 100644 --- a/test/where4.test +++ b/test/where4.test @@ -57,6 +57,10 @@ proc count sql { do_test where4-1.1 { count {SELECT rowid FROM t1 WHERE w IS NULL} } {7 2} +do_test where4-1.1b { + unset -nocomplain null + count {SELECT rowid FROM t1 WHERE w IS $null} +} {7 2} do_test where4-1.2 { count {SELECT rowid FROM t1 WHERE +w IS NULL} } {7 6} @@ -143,6 +147,17 @@ do_test where4-3.2 { SELECT * FROM t2 LEFT JOIN t3 ON a=x WHERE y IS NULL; } } {2 2 {} 3 {} {}} +do_test where4-3.3 { + execsql { + SELECT * FROM t2 LEFT JOIN t3 ON a=x WHERE NULL is y; + } +} {2 2 {} 3 {} {}} +do_test where4-3.4 { + unset -nocomplain null + execsql { + SELECT * FROM t2 LEFT JOIN t3 ON a=x WHERE y IS $null; + } +} {2 2 {} 3 {} {}} # Ticket #2189. Probably the same bug as #2177. # From ee14587c3953f85e31ff38268ac91f268792b470 Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 14 May 2015 13:18:47 +0000 Subject: [PATCH 5/7] Add testcase() macros. Get transitive WHERE clause constraints on IS operators working again. FossilOrigin-Name: d195d4a65d7184e34a2a08c3ac3db7f6c8c1c21c --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/where.c | 11 +++++++---- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index 438ec23110..6fdcf36695 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C A\snew\simplementation\sof\sindexing\swith\sthe\sIS\soperator\sthat\sworks\scorrectly\nwhen\sthe\sIS\soperator\sis\sin\sthe\sWHERE\sclause\sand\sthe\soperands\sare\sfrom\s\nopposite\ssides\sof\sa\sLEFT\sJOIN. -D 2015-05-14T01:05:25.274 +C Add\stestcase()\smacros.\s\sGet\stransitive\sWHERE\sclause\sconstraints\son\sIS\soperators\nworking\sagain. +D 2015-05-14T13:18:47.128 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in edfc69769e613a6359c42c06ea1d42c3bece1736 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -307,7 +307,7 @@ F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb F src/wal.c ce2cb2d06faab54d1bce3e739bec79e063dd9113 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804 -F src/where.c 64afb483fe8bede1e31e6c66bc532530a4600543 +F src/where.c cabecc4a0f647f552b7467a86ff3c2e48487399d F src/whereInt.h a6f5a762bc1b4b1c76e1cea79976b437ac35a435 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -1258,7 +1258,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 95b1f9bf14e490c6c6bba9ea78aeab712a44aab5 -R 6e0cd1da1bcd5d739d5d402ab5201f6c +P 4541688b3f56f5cd3d5b299594b58c577ad633bb +R 201c526060c4a057a45ebf1fee55c845 U drh -Z 3608a708c2f560b3e002545b6da23dda +Z 9f199a1ff5417af5f644d4d006d00b95 diff --git a/manifest.uuid b/manifest.uuid index 2dcd8f7064..ccbe8e23d7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4541688b3f56f5cd3d5b299594b58c577ad633bb \ No newline at end of file +d195d4a65d7184e34a2a08c3ac3db7f6c8c1c21c \ No newline at end of file diff --git a/src/where.c b/src/where.c index 3c96f93a7e..4792b13e60 100644 --- a/src/where.c +++ b/src/where.c @@ -1280,7 +1280,7 @@ static void exprAnalyze( markTermAsChild(pWC, idxNew, idxTerm); pTerm = &pWC->a[idxTerm]; pTerm->wtFlags |= TERM_COPIED; - if( pExpr->op==TK_EQ + if( (op==TK_EQ || op==TK_IS) && !ExprHasProperty(pExpr, EP_FromJoin) && OptimizationEnabled(db, SQLITE_Transitive) ){ @@ -1904,8 +1904,9 @@ static sqlite3_index_info *allocateIndexInfo( assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); testcase( pTerm->eOperator & WO_IN ); testcase( pTerm->eOperator & WO_ISNULL ); + testcase( pTerm->eOperator & WO_IS ); testcase( pTerm->eOperator & WO_ALL ); - if( (pTerm->eOperator & ~(WO_ISNULL|WO_EQUIV))==0 ) continue; + if( (pTerm->eOperator & ~(WO_ISNULL|WO_EQUIV|WO_IS))==0 ) continue; if( pTerm->wtFlags & TERM_VNULL ) continue; nTerm++; } @@ -1956,6 +1957,7 @@ static sqlite3_index_info *allocateIndexInfo( if( pTerm->leftCursor != pSrc->iCursor ) continue; assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); testcase( pTerm->eOperator & WO_IN ); + testcase( pTerm->eOperator & WO_IS ); testcase( pTerm->eOperator & WO_ISNULL ); testcase( pTerm->eOperator & WO_ALL ); if( (pTerm->eOperator & ~(WO_ISNULL|WO_EQUIV|WO_IS))==0 ) continue; @@ -4107,7 +4109,8 @@ static Bitmask codeOneLoopStart( Expr *pE, *pEAlt; WhereTerm *pAlt; if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; - if( (pTerm->eOperator&(WO_EQUIV|WO_EQ|WO_IS))<=WO_EQUIV ) continue; + if( (pTerm->eOperator & WO_EQUIV)==0 ) continue; + if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) continue; if( pTerm->leftCursor!=iCur ) continue; if( pLevel->iLeftJoin ) continue; pE = pTerm->pExpr; @@ -4731,7 +4734,7 @@ static int whereLoopAddBtreeIndex( assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 ); if( pNew->wsFlags & WHERE_BTM_LIMIT ){ opMask = WO_LT|WO_LE; - }else if( pProbe->tnum<=0 || (pSrc->jointype & JT_LEFT)!=0 ){ + }else if( /*pProbe->tnum<=0 ||*/ (pSrc->jointype & JT_LEFT)!=0 ){ opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE; }else{ opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS; From 4a00b33c026622956adaa9bc13904b999de01269 Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 14 May 2015 13:41:22 +0000 Subject: [PATCH 6/7] More test cases. Remove some invalid testcase() macros. Rearrange some code for improved testability. FossilOrigin-Name: b3676377b257bd8bb7fefe9c365d76cdc9e44856 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/where.c | 4 +--- test/vtab1.test | 13 ++++++++++++- 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index 6fdcf36695..40483626d7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\stestcase()\smacros.\s\sGet\stransitive\sWHERE\sclause\sconstraints\son\sIS\soperators\nworking\sagain. -D 2015-05-14T13:18:47.128 +C More\stest\scases.\s\sRemove\ssome\sinvalid\stestcase()\smacros.\s\sRearrange\ssome\scode\nfor\simproved\stestability. +D 2015-05-14T13:41:22.643 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in edfc69769e613a6359c42c06ea1d42c3bece1736 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -307,7 +307,7 @@ F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb F src/wal.c ce2cb2d06faab54d1bce3e739bec79e063dd9113 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804 -F src/where.c cabecc4a0f647f552b7467a86ff3c2e48487399d +F src/where.c 08fadd0d211699348349be5449f3a1e391adf20e F src/whereInt.h a6f5a762bc1b4b1c76e1cea79976b437ac35a435 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -1120,7 +1120,7 @@ F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9 F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102 F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661 F test/view.test f311691d696a5cc27e3c1b875cec1b0866b4ccd9 -F test/vtab1.test d1e5ec7a818f1d3f0402382b6a1d0c06071b770f +F test/vtab1.test dbe0e9e121102d0ba365f20d126a72676aa2343f F test/vtab2.test 3644649aa8d1daac57fd541f6a5f914cac59203e F test/vtab3.test b45f47d20f225ccc9c28dc915d92740c2dee311e F test/vtab4.test 942f8b8280b3ea8a41dae20e7822d065ca1cb275 @@ -1258,7 +1258,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 4541688b3f56f5cd3d5b299594b58c577ad633bb -R 201c526060c4a057a45ebf1fee55c845 +P d195d4a65d7184e34a2a08c3ac3db7f6c8c1c21c +R f1988e6e31a1e471b8c207ba95b50e6f U drh -Z 9f199a1ff5417af5f644d4d006d00b95 +Z 903b5f8a9a56d496508b20810e19ed0d diff --git a/manifest.uuid b/manifest.uuid index ccbe8e23d7..0a0e87ef8a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d195d4a65d7184e34a2a08c3ac3db7f6c8c1c21c \ No newline at end of file +b3676377b257bd8bb7fefe9c365d76cdc9e44856 \ No newline at end of file diff --git a/src/where.c b/src/where.c index 4792b13e60..e6023dd035 100644 --- a/src/where.c +++ b/src/where.c @@ -1120,7 +1120,6 @@ static void exprAnalyzeOrTerm( okToChngToIN = 1; for(; i>=0 && okToChngToIN; i--, pOrTerm++){ assert( pOrTerm->eOperator & WO_EQ ); - testcase( pOrTerm->pExpr->op==TK_IS ); if( pOrTerm->leftCursor!=iCursor ){ pOrTerm->wtFlags &= ~TERM_OR_OK; }else if( pOrTerm->u.leftColumn!=iColumn ){ @@ -1157,7 +1156,6 @@ static void exprAnalyzeOrTerm( assert( pOrTerm->eOperator & WO_EQ ); assert( pOrTerm->leftCursor==iCursor ); assert( pOrTerm->u.leftColumn==iColumn ); - testcase( pOrTerm->pExpr->op==TK_IS ); pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0); pList = sqlite3ExprListAppend(pWInfo->pParse, pList, pDup); pLeft = pOrTerm->pExpr->pLeft; @@ -4109,8 +4107,8 @@ static Bitmask codeOneLoopStart( Expr *pE, *pEAlt; WhereTerm *pAlt; if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; - if( (pTerm->eOperator & WO_EQUIV)==0 ) continue; if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) continue; + if( (pTerm->eOperator & WO_EQUIV)==0 ) continue; if( pTerm->leftCursor!=iCur ) continue; if( pLevel->iLeftJoin ) continue; pE = pTerm->pExpr; diff --git a/test/vtab1.test b/test/vtab1.test index c0cf3e4e8d..cd21153f06 100644 --- a/test/vtab1.test +++ b/test/vtab1.test @@ -1085,11 +1085,22 @@ do_test vtab1.13-3 { SELECT * FROM echo_c WHERE b IS NULL } } {15 {} 16} -do_test vtab1.13-3 { +do_test vtab1.13-4 { + unset -nocomplain null + execsql { + SELECT * FROM echo_c WHERE b IS $null + } +} {15 {} 16} +do_test vtab1.13-5 { execsql { SELECT * FROM echo_c WHERE b IS NULL AND a = 15; } } {15 {} 16} +do_test vtab1.13-6 { + execsql { + SELECT * FROM echo_c WHERE NULL IS b AND a IS 15; + } +} {15 {} 16} do_test vtab1-14.001 { From 5d8806e07c90d80cfda629473c847970c25453db Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 14 May 2015 14:03:21 +0000 Subject: [PATCH 7/7] A few more test cases for the IS operator. FossilOrigin-Name: f397c8622ae5a36a71f81d9f2549ca314005ece5 --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- test/transitive1.test | 18 +++++++++++++++++- test/vtab2.test | 9 ++++++++- test/vtab6.test | 5 +++++ 5 files changed, 39 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index 40483626d7..be7f8e7025 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C More\stest\scases.\s\sRemove\ssome\sinvalid\stestcase()\smacros.\s\sRearrange\ssome\scode\nfor\simproved\stestability. -D 2015-05-14T13:41:22.643 +C A\sfew\smore\stest\scases\sfor\sthe\sIS\soperator. +D 2015-05-14T14:03:21.962 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in edfc69769e613a6359c42c06ea1d42c3bece1736 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -1082,7 +1082,7 @@ F test/trace2.test f5cb67ad3bc09e0c58e8cca78dfd0b5639259983 F test/trans.test 6e1b4c6a42dba31bd65f8fa5e61a2708e08ddde6 F test/trans2.test 62bd045bfc7a1c14c5ba83ba64d21ade31583f76 F test/trans3.test 91a100e5412b488e22a655fe423a14c26403ab94 -F test/transitive1.test 03f532954f46cdf5608f7766bff0b0c52bf2a7cd +F test/transitive1.test 875a9f0097a15b30a62431d183f989364d5accac F test/trigger1.test dc47573ac79ffe0ee3eecaa517d70d8dacbccd03 F test/trigger2.test 5cd7d69a7ba1143ee045e4ae2963ff32ae4c87a6 F test/trigger3.test aa640bb2bbb03edd5ff69c055117ea088f121945 @@ -1121,11 +1121,11 @@ F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102 F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661 F test/view.test f311691d696a5cc27e3c1b875cec1b0866b4ccd9 F test/vtab1.test dbe0e9e121102d0ba365f20d126a72676aa2343f -F test/vtab2.test 3644649aa8d1daac57fd541f6a5f914cac59203e +F test/vtab2.test f8cd1bb9aba7143eba97812d9617880a36d247ad F test/vtab3.test b45f47d20f225ccc9c28dc915d92740c2dee311e F test/vtab4.test 942f8b8280b3ea8a41dae20e7822d065ca1cb275 F test/vtab5.test 889f444970393c73f1e077e2bdc5d845e157a391 -F test/vtab6.test 5f5380c425e52993560ab4763db4f826d2ba7b09 +F test/vtab6.test d2986cf418dc51e7fb81d12366bea2caa8b812df F test/vtab7.test ae560ebea870ed04e9aa4177cc302f910faaabb5 F test/vtab8.test e19fa4a538fcd1bb66c22825fa8f71618fb13583 F test/vtab9.test ea58d2b95d61955f87226381716b2d0b1d4e4f9b @@ -1258,7 +1258,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P d195d4a65d7184e34a2a08c3ac3db7f6c8c1c21c -R f1988e6e31a1e471b8c207ba95b50e6f +P b3676377b257bd8bb7fefe9c365d76cdc9e44856 +R bdc88e4814b826734527d4ac723cdd70 U drh -Z 903b5f8a9a56d496508b20810e19ed0d +Z e30ac775b8d8ec0dc6dc3a554502ca78 diff --git a/manifest.uuid b/manifest.uuid index 0a0e87ef8a..db9f372632 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b3676377b257bd8bb7fefe9c365d76cdc9e44856 \ No newline at end of file +f397c8622ae5a36a71f81d9f2549ca314005ece5 \ No newline at end of file diff --git a/test/transitive1.test b/test/transitive1.test index 200dc610fa..3b76d2b01e 100644 --- a/test/transitive1.test +++ b/test/transitive1.test @@ -66,6 +66,12 @@ do_execsql_test transitive1-301 { WHERE w=y AND y IS NOT NULL ORDER BY w; } {1 2 1 3 3 4 3 6 5 6 5 7} +do_execsql_test transitive1-302 { + SELECT * + FROM t301 CROSS JOIN t302 + WHERE w IS y AND y IS NOT NULL + ORDER BY w; +} {1 2 1 3 3 4 3 6 5 6 5 7} do_execsql_test transitive1-310 { SELECT * FROM t301 CROSS JOIN t302 ON w=y @@ -103,7 +109,7 @@ do_execsql_test transitive1-332 { } {3 4 3 6 1 2 1 3} # Ticket [c620261b5b5dc] circa 2013-10-28. -# Make sureconstraints are not used with LEFT JOINs. +# Make sure constraints are not used with LEFT JOINs. # # The next case is from the ticket report. It outputs no rows in 3.8.1 # prior to the bug-fix. @@ -116,6 +122,16 @@ do_execsql_test transitive1-400 { INSERT INTO t403 VALUES(1); SELECT '1-row' FROM t401 LEFT JOIN t402 ON b=a JOIN t403 ON c=a; } {1-row} +do_execsql_test transitive1-401 { + SELECT '1-row' FROM t401 LEFT JOIN t402 ON b IS a JOIN t403 ON c=a; +} {1-row} +do_execsql_test transitive1-402 { + SELECT '1-row' FROM t401 LEFT JOIN t402 ON b=a JOIN t403 ON c IS a; +} {1-row} +do_execsql_test transitive1-403 { + SELECT '1-row' FROM t401 LEFT JOIN t402 ON b IS a JOIN t403 ON c IS a; +} {1-row} + # The following is a script distilled from the XBMC project where the # bug was originally encountered. The correct answer is a single row diff --git a/test/vtab2.test b/test/vtab2.test index 3884ec57df..f0616513bd 100644 --- a/test/vtab2.test +++ b/test/vtab2.test @@ -104,6 +104,14 @@ do_test vtab2-3.2 { WHERE a.rowid=1 } } {main schema 0 database {} 0 {} 0 {} {} {} {} {} {} {} {} {}} +do_test vtab2-3.3 { + execsql { + SELECT *, b.rowid + FROM schema a LEFT JOIN schema b ON a.dflt_value IS b.dflt_value + AND a.dflt_value IS NOT NULL + WHERE a.rowid=1 + } +} {main schema 0 database {} 0 {} 0 {} {} {} {} {} {} {} {} {}} do_test vtab2-4.1 { execsql { @@ -153,4 +161,3 @@ ifcapable fts3 { finish_test - diff --git a/test/vtab6.test b/test/vtab6.test index 10bf286ab0..f8e0935a28 100644 --- a/test/vtab6.test +++ b/test/vtab6.test @@ -233,6 +233,11 @@ do_test vtab6-2.4 { SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.d } } {1 2 3 {} {} {} 2 3 4 {} {} {} 3 4 5 1 2 3} +do_test vtab6-2.4.1 { + execsql { + SELECT * FROM t1 LEFT JOIN t2 ON t1.a IS t2.d + } +} {1 2 3 {} {} {} 2 3 4 {} {} {} 3 4 5 1 2 3} do_test vtab6-2.5 { execsql { SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.d WHERE t1.a>1