From 8da209b169b1ab73b70573de6d91fca6053852f2 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 26 Jul 2016 18:06:08 +0000 Subject: [PATCH] Fix where.c handling of "IN (SELECT ...)" expressions when the SELECT returns more than one result column. Also error handling for other row value constructor cases. FossilOrigin-Name: 061b8006034f06a0311b4304c8b14d2c8b0153df --- manifest | 34 +++++++++--------- manifest.uuid | 2 +- src/expr.c | 50 ++++++++++++++++++-------- src/resolve.c | 2 +- src/where.c | 10 +++--- src/whereInt.h | 1 + src/wherecode.c | 85 +++++++++++++++++++++++++++++++++++++++------ src/whereexpr.c | 19 ++++++++++ test/e_expr.test | 4 +-- test/in.test | 15 ++++---- test/rowvalue3.test | 49 ++++++++++++++++++++++++-- test/select7.test | 12 +++---- test/subselect.test | 2 +- test/tester.tcl | 18 ++++++++-- 14 files changed, 233 insertions(+), 70 deletions(-) diff --git a/manifest b/manifest index 52753bfb10..c0f2b89a0d 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index 1150e4a104..5c153725ad 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e2fd6f49b1b145bec09c581cc982b89429643ae9 \ No newline at end of file +061b8006034f06a0311b4304c8b14d2c8b0153df \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 5792073228..f35ac56f80 100644 --- a/src/expr.c +++ b/src/expr.c @@ -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; inExpr!=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: { diff --git a/src/resolve.c b/src/resolve.c index cd3d714613..cb9f2fbaa6 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -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); diff --git a/src/where.c b/src/where.c index 36bf98b444..095891842f 100644 --- a/src/where.c +++ b/src/where.c @@ -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); } } diff --git a/src/whereInt.h b/src/whereInt.h index d288fa6192..d86373ecd5 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -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 " */ + int iField; /* Field in (?,?,?) IN (SELECT...) vector */ union { int leftColumn; /* Column number of X in "X " */ WhereOrInfo *pOrInfo; /* Extra information if (eOperator & WO_OR)!=0 */ diff --git a/src/wherecode.c b/src/wherecode.c index 0f6d320241..69dd2e97fb 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -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; iaLTerm[i] && pLoop->aLTerm[i]->pExpr==pX ){ + disableTerm(pLevel, pTerm); + return iTarget; + } + } + for(i=iEq;inLTerm; 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;inLTerm; 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;inLTerm; 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); diff --git a/src/whereexpr.c b/src/whereexpr.c index 35af70687b..67b2e67baa 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -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; ipLeft); 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 diff --git a/test/e_expr.test b/test/e_expr.test index 8c0957f8d3..6ef55ce8a6 100644 --- a/test/e_expr.test +++ b/test/e_expr.test @@ -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 diff --git a/test/in.test b/test/in.test index 3a42e84b9a..c88a4a6952 100644 --- a/test/in.test +++ b/test/in.test @@ -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 { diff --git a/test/rowvalue3.test b/test/rowvalue3.test index 63f2c8939a..78bf21b615 100644 --- a/test/rowvalue3.test +++ b/test/rowvalue3.test @@ -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 + + diff --git a/test/select7.test b/test/select7.test index 0df84e13d9..d705ebfaf4 100644 --- a/test/select7.test +++ b/test/select7.test @@ -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 diff --git a/test/subselect.test b/test/subselect.test index 247f68ee80..fc1c103085 100644 --- a/test/subselect.test +++ b/test/subselect.test @@ -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. # diff --git a/test/tester.tcl b/test/tester.tcl index 3f1edc32ae..f258425c47 100644 --- a/test/tester.tcl +++ b/test/tester.tcl @@ -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) }