]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix where.c handling of "IN (SELECT ...)" expressions when the SELECT returns more...
authordan <dan@noemail.net>
Tue, 26 Jul 2016 18:06:08 +0000 (18:06 +0000)
committerdan <dan@noemail.net>
Tue, 26 Jul 2016 18:06:08 +0000 (18:06 +0000)
FossilOrigin-Name: 061b8006034f06a0311b4304c8b14d2c8b0153df

14 files changed:
manifest
manifest.uuid
src/expr.c
src/resolve.c
src/where.c
src/whereInt.h
src/wherecode.c
src/whereexpr.c
test/e_expr.test
test/in.test
test/rowvalue3.test
test/select7.test
test/subselect.test
test/tester.tcl

index 52753bfb101386c456892ca87372d8618cf3bd75..c0f2b89a0dac6a99401c25cdc86c74cc12291479 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Allow\svector\sIN(SELECT\s...)\sexpressions\sto\suse\san\sindex\sif\seither\sall\sthe\sindexed\scolumns\sare\sdeclared\sNOT\sNULL\sor\sif\sthere\sis\sno\sdifference\sbetween\sthe\sexpression\sevaluating\sto\s0\sand\sNULL\s(as\sin\sa\sWHERE\sclause).
-D 2016-07-23T20:24:06.382
+C Fix\swhere.c\shandling\sof\s"IN\s(SELECT\s...)"\sexpressions\swhen\sthe\sSELECT\sreturns\smore\sthan\sone\sresult\scolumn.\sAlso\serror\shandling\sfor\sother\srow\svalue\sconstructor\scases.
+D 2016-07-26T18:06:08.100
 F Makefile.in 6c20d44f72d4564f11652b26291a214c8367e5db
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a
@@ -337,7 +337,7 @@ F src/ctime.c 61949e83c4c36e37195a8398ebc752780b534d95
 F src/date.c 1cc9fb516ec9932c6fd4d2a0d2f8bc4480145c39
 F src/dbstat.c 4f6f7f52b49beb9636ffbd517cfe44a402ba4ad0
 F src/delete.c 4aba4214a377ce8ddde2d2e609777bcc8235200f
-F src/expr.c 8ff9d70cc2077020327d1fa551558bb03e267da4
+F src/expr.c f84861eaaf557df45bb8f4513a78a05fbb0ad368
 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
 F src/fkey.c bc4145347595b7770f9a598cff1c848302cf5413
 F src/func.c 61a4114cf7004f10c542cfabbab9f2bcb9033045
@@ -381,7 +381,7 @@ F src/pragma.h 64c78a648751b9f4f297276c4eb7507b14b4628c
 F src/prepare.c 22df6171aec1d86904ed2ad30c2348a5748aa04e
 F src/printf.c a5f0ca08ddede803c241266abb46356ec748ded1
 F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
-F src/resolve.c 9680caadd54772699e5a0a8ebd680e014703e4ee
+F src/resolve.c 5c4d301a855d0245ddcc27365ddcbddd2f244665
 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
 F src/select.c 0115f5d222f5cf9b5511ec4072088417354d738a
 F src/shell.c a8a9e392a6a2777fabf5feb536931cb190f235e5
@@ -463,10 +463,10 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
 F src/wal.c 02eeecc265f6ffd0597378f5d8ae9070b62a406a
 F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2
 F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354
-F src/where.c 898a45969bae1298cbaaaf05e6aeacfb45971293
-F src/whereInt.h 1ad3be2a43cb821418e1100476a3a14cd57635c4
-F src/wherecode.c eb0f5e8700afb110cb96fb873c0e9a015a9f63ff
-F src/whereexpr.c d88ee6ce356cb9fd986d0e81249a2cd66a513093
+F src/where.c e7054b2c1fe31fef5136e5735d7958f5c2c7707d
+F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613
+F src/wherecode.c 03fbaa63909d7e4b8af986595b811ae591032240
+F src/whereexpr.c b896f8ff6a53cbd3daaee84ec33e39098762bb46
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
@@ -643,7 +643,7 @@ F test/e_createtable.test d4c6059d44dcd4b636de9aae322766062b471844
 F test/e_delete.test ab39084f26ae1f033c940b70ebdbbd523dc4962e
 F test/e_droptrigger.test 3cd080807622c13e5bbb61fc9a57bd7754da2412
 F test/e_dropview.test 0c9f7f60989164a70a67a9d9c26d1083bc808306
-F test/e_expr.test 03a84a6fa9bd3472112d6bd4599f5269f5f74803
+F test/e_expr.test 3f9e639b5df18de36c0aa700703589cd65281174
 F test/e_fkey.test a1783fe1f759e1990e6a11adfcf0702dac4d0707
 F test/e_fts3.test 5c02288842e4f941896fd44afdef564dd5fc1459
 F test/e_insert.test 3de217e95094d3d165992a6de1164bbc4bd92dc7
@@ -822,7 +822,7 @@ F test/hook.test 3b7b99d0eece6d279812c2aef6fa08bdfabc633e
 F test/icu.test 73956798bace8982909c00476b216714a6d0559a
 F test/ieee754.test 806fc0ce7f305f57e3331eaceeddcfec9339e607
 F test/imposter1.test c3f1db2d3db2c24611a6596a3fc0ffc14f1466c8
-F test/in.test 61a24ae38d4b64ec69f06ccdf022992f68a98176
+F test/in.test 41d18d4bcd27c55d0bc6b6ddc8ff9e85e91728a4
 F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75
 F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0
 F test/in4.test d2b38cba404bc4320f4fe1b595b3d163f212c068
@@ -1019,7 +1019,7 @@ F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
 F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d
 F test/rowvalue.test 979738b3d49f1d93e3fee56a71d4446217917abc
 F test/rowvalue2.test 8d5dfe75b8f4d1868a2f91f0356f20d36cba64ff
-F test/rowvalue3.test 72a9fe5ad30df2d422876466e180c148ab88dc42
+F test/rowvalue3.test eeec47b4de27217a012dd142956b01af4e9bd6f2
 F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798
 F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09
 F test/savepoint.test c671fdbd34cd3bfe1518a777526ada595180cf8d
@@ -1043,7 +1043,7 @@ F test/select3.test 2ce595f8fb8e2ac10071d3b4e424cadd4634a054
 F test/select4.test 5389d9895968d1196c457d59b3ee6515d771d328
 F test/select5.test e758b8ef94f69b111df4cb819008856655dcd535
 F test/select6.test 39eac4a5c03650b2b473c532882273283ee8b7a0
-F test/select7.test 95e370c42d47c3c52377d05e9ffc01ccff7c1f61
+F test/select7.test f659f231489349e8c5734e610803d7654207318f
 F test/select8.test 8c8f5ae43894c891efc5755ed905467d1d67ad5d
 F test/select9.test aebc2bb0c3bc44606125033cbcaac2c8d1f33a95
 F test/selectA.test 101e722370ac6e84978c2958b8931c78b10a1709
@@ -1111,7 +1111,7 @@ F test/statfault.test f525a7bf633e50afd027700e9a486090684b1ac1
 F test/stmt.test 64844332db69cf1a735fcb3e11548557fc95392f
 F test/subquery.test d7268d193dd33d5505df965399d3a594e76ae13f
 F test/subquery2.test 438f8a7da1457277b22e4176510f7659b286995f
-F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4
+F test/subselect.test ccec43f85d488c6c4b6f98ea2dfa95b6086871c0
 F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
 F test/subtype1.test 7fe09496352f97053af1437150751be2d0a0cae8
 F test/superlock.test ec94f0556b6488d97f71c79f9061ae08d9ab8f12
@@ -1131,7 +1131,7 @@ F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
 F test/temptable2.test cd396beb41117a5302fff61767c35fa4270a0d5e
 F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
 F test/temptrigger.test 8ec228b0db5d7ebc4ee9b458fc28cb9e7873f5e1
-F test/tester.tcl a52b5be1bb586afa1c8bcdcd4b86588645e1ae52
+F test/tester.tcl e1379282de5810a047c75d84eb6c914e00743b7e
 F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5
 F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
 F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
@@ -1509,7 +1509,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 60fed5cdd4a44aefa1b4d505adeb7936f2f0b952
-R 1dc4c60c16780168f167fe34d6f77ee9
+P e2fd6f49b1b145bec09c581cc982b89429643ae9
+R a369e1eed01f702cd78474a2d1d557f1
 U dan
-Z f82474a4f9b8fbec7649c9e9d834d395
+Z 9143269c80fa998d2598183a7a999846
index 1150e4a104a42615af1d24e90f55b8ac0f8e89ff..5c153725ad316b67acdee3194e112d5828ad865a 100644 (file)
@@ -1 +1 @@
-e2fd6f49b1b145bec09c581cc982b89429643ae9
\ No newline at end of file
+061b8006034f06a0311b4304c8b14d2c8b0153df
\ No newline at end of file
index 5792073228ec118f4e911c1ff115960b93bcecf9..f35ac56f80ffd969efcdd53654a13ce847d2ad75 100644 (file)
@@ -339,6 +339,19 @@ static Expr *exprVectorField(Expr *pVector, int i){
   return pVector->x.pList->a[i].pExpr;
 }
 
+static int exprVectorSubselect(Parse *pParse, Expr *pExpr){
+  int reg = 0;
+  if( pExpr->flags & EP_xIsSelect ){
+    assert( pExpr->op==TK_REGISTER || pExpr->op==TK_SELECT );
+    if( pExpr->op==TK_REGISTER ){
+      reg = pExpr->iTable;
+    }else{
+      reg = sqlite3CodeSubselect(pParse, pExpr, 0, 0);
+    }
+  }
+  return reg;
+}
+
 static void codeVectorCompare(Parse *pParse, Expr *pExpr, int dest){
   Vdbe *v = pParse->pVdbe;
   Expr *pLeft = pExpr->pLeft;
@@ -389,16 +402,8 @@ static void codeVectorCompare(Parse *pParse, Expr *pExpr, int dest){
         break;
     }
 
-    if( pLeft->flags & EP_xIsSelect ){
-      assert( pLeft->op==TK_SELECT || pLeft->op==TK_REGISTER );
-      regLeft = sqlite3ExprCodeTarget(pParse, pLeft, 1);
-      assert( regLeft!=1 );
-    }
-    if( pRight->flags & EP_xIsSelect ){
-      assert( pRight->op==TK_SELECT || pRight->op==TK_REGISTER );
-      regRight = sqlite3ExprCodeTarget(pParse, pRight, 1);
-      assert( regRight!=1 );
-    }
+    regLeft = exprVectorSubselect(pParse, pLeft);
+    regRight = exprVectorSubselect(pParse, pRight);
     if( pParse->nErr ) return;
 
     for(i=0; i<nLeft; i++){
@@ -2064,7 +2069,7 @@ static char *exprINAffinity(Parse *pParse, Expr *pExpr){
     for(i=0; i<nVal; i++){
       Expr *pA;
       char a;
-      if( nVal==1 ){
+      if( nVal==1 && 0 ){
         pA = pLeft;
       }else{    
         pA = exprVectorField(pLeft, i);
@@ -2077,6 +2082,19 @@ static char *exprINAffinity(Parse *pParse, Expr *pExpr){
   return zRet;
 }
 
+#ifndef SQLITE_OMIT_SUBQUERY
+/*
+** Load the Parse object passed as the first argument with an error 
+** message of the form:
+**
+**   "sub-select returns N columns - expected M"
+*/   
+void sqlite3SubselectError(Parse *pParse, int nActual, int nExpect){
+  const char *zFmt = "sub-select returns %d columns - expected %d";
+  sqlite3ErrorMsg(pParse, zFmt, nActual, nExpect);
+}
+#endif
+
 /*
 ** Generate code for scalar subqueries used as a subquery expression, EXISTS,
 ** or IN operators.  Examples:
@@ -2180,8 +2198,7 @@ int sqlite3CodeSubselect(
 
         assert( !isRowid );
         if( pEList->nExpr!=nVal ){
-          sqlite3ErrorMsg(pParse, "SELECT has %d columns - expected %d",
-              pEList->nExpr, nVal);
+          sqlite3SubselectError(pParse, pEList->nExpr, nVal);
         }else{
           SelectDest dest;
           int i;
@@ -3350,9 +3367,14 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
 #ifndef SQLITE_OMIT_SUBQUERY
     case TK_EXISTS:
     case TK_SELECT: {
+      int nCol;
       testcase( op==TK_EXISTS );
       testcase( op==TK_SELECT );
-      inReg = sqlite3CodeSubselect(pParse, pExpr, 0, 0);
+      if( op==TK_SELECT && (nCol = pExpr->x.pSelect->pEList->nExpr)!=1 ){
+        sqlite3SubselectError(pParse, nCol, 1);
+      }else{
+        inReg = sqlite3CodeSubselect(pParse, pExpr, 0, 0);
+      }
       break;
     }
     case TK_IN: {
index cd3d714613383e6e6d07f1369c4feb3bd287ef5d..cb9f2fbaa6ad1b5ffabf32edbe05db13f830d3e9 100644 (file)
@@ -767,7 +767,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
         }
 
         if( pExpr->op==TK_SELECT && pExpr->x.pSelect->pEList->nExpr>1 ){
-          if( !ExprHasProperty(pExpr, EP_VectorOk) ){
+          if( !ExprHasProperty(pExpr, EP_VectorOk) && 0 ){
             sqlite3ErrorMsg(pParse, "invalid use of row value");
           }else{
             ExprSetProperty(pExpr, EP_Vector);
index 36bf98b4446542e398a5187d7107025a8dd6ac2e..095891842f80e8e978c4ea0fc84ab38aca5a47b6 100644 (file)
@@ -4713,10 +4713,12 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
       sqlite3VdbeResolveLabel(v, pLevel->addrNxt);
       for(j=pLevel->u.in.nIn, pIn=&pLevel->u.in.aInLoop[j-1]; j>0; j--, pIn--){
         sqlite3VdbeJumpHere(v, pIn->addrInTop+1);
-        sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop);
-        VdbeCoverage(v);
-        VdbeCoverageIf(v, pIn->eEndLoopOp==OP_PrevIfOpen);
-        VdbeCoverageIf(v, pIn->eEndLoopOp==OP_NextIfOpen);
+        if( pIn->eEndLoopOp!=OP_Noop ){
+          sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop);
+          VdbeCoverage(v);
+          VdbeCoverageIf(v, pIn->eEndLoopOp==OP_PrevIfOpen);
+          VdbeCoverageIf(v, pIn->eEndLoopOp==OP_NextIfOpen);
+        }
         sqlite3VdbeJumpHere(v, pIn->addrInTop-1);
       }
     }
index d288fa6192aafccddc454b424955aa0b9b204657..d86373ecd51972f8c62d21e64a3efe8f4eacb294 100644 (file)
@@ -248,6 +248,7 @@ struct WhereTerm {
   Expr *pExpr;            /* Pointer to the subexpression that is this term */
   int iParent;            /* Disable pWC->a[iParent] when this term disabled */
   int leftCursor;         /* Cursor number of X in "X <op> <expr>" */
+  int iField;             /* Field in (?,?,?) IN (SELECT...) vector */
   union {
     int leftColumn;         /* Column number of X in "X <op> <expr>" */
     WhereOrInfo *pOrInfo;   /* Extra information if (eOperator & WO_OR)!=0 */
index 0f6d320241a932184393ace88e27088b3607f2fb..69dd2e97fb6ac568446672697c2a615276fa1584 100644 (file)
@@ -356,6 +356,7 @@ static int codeEqualityTerm(
   Vdbe *v = pParse->pVdbe;
   int iReg;                  /* Register holding results */
 
+  assert( pLevel->pWLoop->aLTerm[iEq]==pTerm );
   assert( iTarget>0 );
   if( pX->op==TK_EQ || pX->op==TK_IS ){
     iReg = sqlite3ExprCodeTarget(pParse, pX->pRight, iTarget);
@@ -368,6 +369,9 @@ static int codeEqualityTerm(
     int iTab;
     struct InLoop *pIn;
     WhereLoop *pLoop = pLevel->pWLoop;
+    int i;
+    int nEq = 0;
+    int *aiMap = 0;
 
     if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0
       && pLoop->u.btree.pIndex!=0
@@ -379,7 +383,52 @@ static int codeEqualityTerm(
     }
     assert( pX->op==TK_IN );
     iReg = iTarget;
-    eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0);
+
+    for(i=0; i<iEq; i++){
+      if( pLoop->aLTerm[i] && pLoop->aLTerm[i]->pExpr==pX ){
+        disableTerm(pLevel, pTerm);
+        return iTarget;
+      }
+    }
+    for(i=iEq;i<pLoop->nLTerm; i++){
+      if( pLoop->aLTerm[i] && pLoop->aLTerm[i]->pExpr==pX ) nEq++;
+    }
+
+    if( nEq>1 ){
+      aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int) * nEq);
+      if( !aiMap ) return 0;
+    }
+
+    if( (pX->flags & EP_xIsSelect)==0 || pX->x.pSelect->pEList->nExpr==1 ){
+      eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0);
+    }else{
+      sqlite3 *db = pParse->db;
+      ExprList *pOrigRhs = pX->x.pSelect->pEList;
+      ExprList *pOrigLhs = pX->pLeft->x.pList;
+      ExprList *pRhs = 0;         /* New Select.pEList for RHS */
+      ExprList *pLhs = 0;         /* New pX->pLeft vector */
+
+      for(i=iEq;i<pLoop->nLTerm; i++){
+        if( pLoop->aLTerm[i]->pExpr==pX ){
+          int iField = pLoop->aLTerm[i]->iField - 1;
+          Expr *pNewRhs = sqlite3ExprDup(db, pOrigRhs->a[iField].pExpr, 0);
+          Expr *pNewLhs = sqlite3ExprDup(db, pOrigLhs->a[iField].pExpr, 0);
+
+          pRhs = sqlite3ExprListAppend(pParse, pRhs, pNewRhs);
+          pLhs = sqlite3ExprListAppend(pParse, pLhs, pNewLhs);
+        }
+      }
+
+      pX->x.pSelect->pEList = pRhs;
+      pX->pLeft->x.pList = pLhs;
+
+      eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap);
+      pX->x.pSelect->pEList = pOrigRhs;
+      pX->pLeft->x.pList = pOrigLhs;
+      sqlite3ExprListDelete(pParse->db, pLhs);
+      sqlite3ExprListDelete(pParse->db, pRhs);
+    }
+
     if( eType==IN_INDEX_INDEX_DESC ){
       testcase( bRev );
       bRev = !bRev;
@@ -389,28 +438,44 @@ static int codeEqualityTerm(
     VdbeCoverageIf(v, bRev);
     VdbeCoverageIf(v, !bRev);
     assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 );
+
     pLoop->wsFlags |= WHERE_IN_ABLE;
     if( pLevel->u.in.nIn==0 ){
       pLevel->addrNxt = sqlite3VdbeMakeLabel(v);
     }
-    pLevel->u.in.nIn++;
+
+    i = pLevel->u.in.nIn;
+    pLevel->u.in.nIn += nEq;
     pLevel->u.in.aInLoop =
        sqlite3DbReallocOrFree(pParse->db, pLevel->u.in.aInLoop,
                               sizeof(pLevel->u.in.aInLoop[0])*pLevel->u.in.nIn);
     pIn = pLevel->u.in.aInLoop;
     if( pIn ){
-      pIn += pLevel->u.in.nIn - 1;
-      pIn->iCur = iTab;
-      if( eType==IN_INDEX_ROWID ){
-        pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iReg);
-      }else{
-        pIn->addrInTop = sqlite3VdbeAddOp3(v, OP_Column, iTab, 0, iReg);
+      int iMap = 0;               /* Index in aiMap[] */
+      pIn += i;
+      for(i=iEq;i<pLoop->nLTerm; i++, pIn++){
+        if( pLoop->aLTerm[i]->pExpr==pX ){
+          if( eType==IN_INDEX_ROWID ){
+            assert( nEq==1 && i==iEq );
+            pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iReg);
+          }else{
+            int iCol = aiMap ? aiMap[iMap++] : 0;
+            int iOut = iReg + i - iEq;
+            pIn->addrInTop = sqlite3VdbeAddOp3(v,OP_Column,iTab, iCol, iOut);
+          }
+          if( i==iEq ){
+            pIn->iCur = iTab;
+            pIn->eEndLoopOp = bRev ? OP_PrevIfOpen : OP_NextIfOpen;
+          }else{
+            pIn->eEndLoopOp = OP_Noop;
+          }
+        }
+        sqlite3VdbeAddOp1(v, OP_IsNull, iReg); VdbeCoverage(v);
       }
-      pIn->eEndLoopOp = bRev ? OP_PrevIfOpen : OP_NextIfOpen;
-      sqlite3VdbeAddOp1(v, OP_IsNull, iReg); VdbeCoverage(v);
     }else{
       pLevel->u.in.nIn = 0;
     }
+    sqlite3DbFree(pParse->db, aiMap);
 #endif
   }
   disableTerm(pLevel, pTerm);
index 35af70687bbcc456c2621d2d9106391fa15264f3..67b2e67baadbe6af77c9188026b8c1a1833db506 100644 (file)
@@ -960,6 +960,12 @@ static void exprAnalyze(
     Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft);
     Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight);
     u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV;
+
+    if( op==TK_IN && pTerm->iField>0 ){
+      assert( pLeft->op==TK_VECTOR );
+      pLeft = pLeft->x.pList->a[pTerm->iField-1].pExpr;
+    }
+
     if( exprMightBeIndexed(pSrc, op, prereqLeft, pLeft, &iCur, &iColumn) ){
       pTerm->leftCursor = iCur;
       pTerm->u.leftColumn = iColumn;
@@ -1195,6 +1201,19 @@ static void exprAnalyze(
     }
   }
 
+  if( pWC->op==TK_AND && pExpr->op==TK_IN && pTerm->iField==0
+   && pExpr->pLeft->op==TK_VECTOR
+  ){
+    int i;
+    for(i=0; i<sqlite3ExprVectorSize(pExpr->pLeft); i++){
+      int idxNew;
+      idxNew = whereClauseInsert(pWC, pExpr, TERM_VIRTUAL);
+      pWC->a[idxNew].iField = i+1;
+      exprAnalyze(pSrc, pWC, idxNew);
+      markTermAsChild(pWC, idxNew, idxTerm);
+    }
+  }
+
 #ifdef SQLITE_ENABLE_STAT3_OR_STAT4
   /* When sqlite_stat3 histogram data is available an operator of the
   ** form "x IS NOT NULL" can sometimes be evaluated more efficiently
index 8c0957f8d382728b266ea20cf80156487b4f5e1c..6ef55ce8a65dcdd2c30858baa207321a420dd513 100644 (file)
@@ -1809,7 +1809,7 @@ do_expr_test e_expr-35.1.6 {
 # The following block tests that errors are returned in a bunch of cases
 # where a subquery returns more than one column.
 #
-set M {only a single result allowed for a SELECT that is part of an expression}
+set M {/1 {sub-select returns [23] columns - expected 1}/}
 foreach {tn sql} {
   1     { SELECT (SELECT * FROM t2 UNION SELECT a+1, b+1 FROM t2) }
   2     { SELECT (SELECT * FROM t2 UNION SELECT a+1, b+1 FROM t2 ORDER BY 1) }
@@ -1818,7 +1818,7 @@ foreach {tn sql} {
   5     { SELECT (SELECT * FROM t2) }
   6     { SELECT (SELECT * FROM (SELECT 1, 2, 3)) }
 } {
-  do_catchsql_test e_expr-35.2.$tn $sql [list 1 $M]
+  do_catchsql_test e_expr-35.2.$tn $sql $M
 }
 
 # EVIDENCE-OF: R-35764-28041 The result of the expression is the value
index 3a42e84b9ab8b026620affb664a3b9a9bb53f385..c88a4a69525a7a9d80794c2f3d4067f8bc7b49c6 100644 (file)
@@ -314,7 +314,7 @@ do_test in-9.4 {
   catchsql {
     SELECT b FROM t1 WHERE a NOT IN tb;
   }
-} {1 {only a single result allowed for a SELECT that is part of an expression}}
+} {1 {sub-select returns 2 columns - expected 1}}
 
 # IN clauses in CHECK constraints.  Ticket #1645
 #
@@ -391,28 +391,28 @@ do_test in-12.2 {
       SELECT a, b FROM t3 UNION ALL SELECT a, b FROM t2
     );
   }
-} {1 {only a single result allowed for a SELECT that is part of an expression}}
+} {1 {sub-select returns 2 columns - expected 1}}
 do_test in-12.3 {
   catchsql {
     SELECT * FROM t2 WHERE a IN (
       SELECT a, b FROM t3 UNION SELECT a, b FROM t2
     );
   }
-} {1 {only a single result allowed for a SELECT that is part of an expression}}
+} {1 {sub-select returns 2 columns - expected 1}}
 do_test in-12.4 {
   catchsql {
     SELECT * FROM t2 WHERE a IN (
       SELECT a, b FROM t3 EXCEPT SELECT a, b FROM t2
     );
   }
-} {1 {only a single result allowed for a SELECT that is part of an expression}}
+} {1 {sub-select returns 2 columns - expected 1}}
 do_test in-12.5 {
   catchsql {
     SELECT * FROM t2 WHERE a IN (
       SELECT a, b FROM t3 INTERSECT SELECT a, b FROM t2
     );
   }
-} {1 {only a single result allowed for a SELECT that is part of an expression}}
+} {1 {sub-select returns 2 columns - expected 1}}
 do_test in-12.6 {
   catchsql {
     SELECT * FROM t2 WHERE a IN (
@@ -478,7 +478,7 @@ do_test in-12.14 {
       SELECT a, b FROM t3 UNION ALL SELECT a, b FROM t2
     );
   }
-} {1 {only a single result allowed for a SELECT that is part of an expression}}
+} {1 {sub-select returns 2 columns - expected 1}}
 do_test in-12.15 {
   catchsql {
     SELECT * FROM t2 WHERE a IN (
@@ -629,11 +629,12 @@ do_test in-13.14 {
   }
 } {}
 
+breakpoint
 do_test in-13.15 {
   catchsql {
     SELECT 0 WHERE (SELECT 0,0) OR (0 IN (1,2));
   }
-} {1 {only a single result allowed for a SELECT that is part of an expression}}
+} {1 {sub-select returns 2 columns - expected 1}}
 
 
 do_test in-13.X {
index 63f2c8939a30dca1697f6ca4054ff63d792aa223..78bf21b61599c28ace8dac16ba9f15c0fd707b31 100644 (file)
@@ -42,11 +42,56 @@ foreach {tn sql res} {
   do_execsql_test 1.$tn $sql $res
 }
 
-#explain_i { SELECT (4, NULL) IN (SELECT a, b FROM t1) } 
-#do_execsql_test 2 { SELECT (4, NULL) IN (SELECT a, b FROM t1) } {}
+#-------------------------------------------------------------------------
 
+do_execsql_test 2.0 {
+  CREATE TABLE z1(x, y, z);
+  CREATE TABLE kk(a, b);
 
+  INSERT INTO z1 VALUES('a', 'b', 'c');
+  INSERT INTO z1 VALUES('d', 'e', 'f');
+  INSERT INTO z1 VALUES('g', 'h', 'i');
 
+  -- INSERT INTO kk VALUES('y', 'y');
+  INSERT INTO kk VALUES('d', 'e');
+  -- INSERT INTO kk VALUES('x', 'x');
+
+}
+
+foreach {tn idx} {
+  1 { }
+  2 {  CREATE INDEX z1idx ON z1(x, y) }
+  3 {  CREATE UNIQUE INDEX z1idx ON z1(x, y) }
+} {
+  execsql "DROP INDEX IF EXISTS z1idx"
+  execsql $idx
+
+  do_execsql_test 2.$tn.1 {
+    SELECT * FROM z1 WHERE x IN (SELECT a FROM kk)
+  } {d e f}
+
+  do_execsql_test 2.$tn.2 {
+    SELECT * FROM z1 WHERE (x,y) IN (SELECT a, b FROM kk)
+  } {d e f}
+
+  do_execsql_test 2.$tn.3 {
+    SELECT * FROM z1 WHERE (x, +y) IN (SELECT a, b FROM kk)
+  } {d e f}
+  
+  do_execsql_test 2.$tn.4 {
+    SELECT * FROM z1 WHERE (x, +y) IN (SELECT a, b||'x' FROM kk)
+  } {}
+
+  do_execsql_test 2.$tn.5 {
+    SELECT * FROM z1 WHERE (+x, y) IN (SELECT a, b FROM kk)
+  } {d e f}
+}
+
+explain_i {
+  SELECT * FROM z1 WHERE (x, y) IN (SELECT a, b FROM kk)
+} 
 
 finish_test
 
+
+
index 0df84e13d97edfc871ca04f7f6e08f077135a9bf..d705ebfaf4e021fab7338d06abb2f5fbbcec390c 100644 (file)
@@ -114,26 +114,22 @@ ifcapable {subquery && compound} {
       CREATE TABLE t2(a,b);
       SELECT 5 IN (SELECT a,b FROM t2);
     }
-  } [list 1 \
-     {only a single result allowed for a SELECT that is part of an expression}]
+  } {1 {sub-select returns 2 columns - expected 1}}
   do_test select7-5.2 {
     catchsql {
       SELECT 5 IN (SELECT * FROM t2);
     }
-  } [list 1 \
-     {only a single result allowed for a SELECT that is part of an expression}]
+  } {1 {sub-select returns 2 columns - expected 1}}
   do_test select7-5.3 {
     catchsql {
       SELECT 5 IN (SELECT a,b FROM t2 UNION SELECT b,a FROM t2);
     }
-  } [list 1 \
-     {only a single result allowed for a SELECT that is part of an expression}]
+  } {1 {sub-select returns 2 columns - expected 1}}
   do_test select7-5.4 {
     catchsql {
       SELECT 5 IN (SELECT * FROM t2 UNION SELECT * FROM t2);
     }
-  } [list 1 \
-     {only a single result allowed for a SELECT that is part of an expression}]
+  } {1 {sub-select returns 2 columns - expected 1}}
 }
 
 # Verify that an error occurs if you have too many terms on a
index 247f68ee80ef6ebeb99375faf27c06fecc32d9aa..fc1c103085f97ad410584a9539eb636f587c6a51 100644 (file)
@@ -40,7 +40,7 @@ do_test subselect-1.1 {
 do_test subselect-1.2 {
   set v [catch {execsql {SELECT * FROM t1 WHERE a = (SELECT * FROM t1)}} msg]
   lappend v $msg
-} {1 {only a single result allowed for a SELECT that is part of an expression}}
+} {1 {sub-select returns 2 columns - expected 1}}
 
 # A subselect without an aggregate.
 #
index 3f1edc32ae81e7f6b94d869596ab1cfba45b32a2..f258425c47b987d517997dc19a4a311a5a58e46e 100644 (file)
@@ -1288,9 +1288,9 @@ proc explain_i {sql {db db}} {
     set D ""
   }
   foreach opcode {
-      Seek SeekGe SeekGt SeekLe SeekLt NotFound Last Rewind
+      Seek SeekGE SeekGT SeekLE SeekLT NotFound Last Rewind
       NoConflict Next Prev VNext VPrev VFilter
-      SorterSort SorterNext
+      SorterSort SorterNext NextIfOpen
   } {
     set color($opcode) $B
   }
@@ -1311,9 +1311,15 @@ proc explain_i {sql {db db}} {
       set bSeenGoto 1
     }
 
+    if {$opcode=="Once"} {
+      for {set i $addr} {$i<$p2} {incr i} {
+        set star($i) $addr
+      }
+    }
+
     if {$opcode=="Next"  || $opcode=="Prev" 
      || $opcode=="VNext" || $opcode=="VPrev"
-     || $opcode=="SorterNext"
+     || $opcode=="SorterNext" || $opcode=="NextIfOpen"
     } {
       for {set i $p2} {$i<$addr} {incr i} {
         incr x($i) 2
@@ -1337,6 +1343,12 @@ proc explain_i {sql {db db}} {
     }
     set I [string repeat " " $x($addr)]
 
+    if {[info exists star($addr)]} {
+      set ii [expr $x($star($addr))]
+      append I "  "
+      set I [string replace $I $ii $ii *]
+    }
+
     set col ""
     catch { set col $color($opcode) }