From 71c57db099b331c0c3294e1122448fb732a131bc Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 9 Jul 2016 20:23:55 +0000 Subject: [PATCH 01/97] Add some support for using row value constructors in certain parts of SQL expressions. There are many bugs on this branch. FossilOrigin-Name: b2204215b231202aef7a218411cc2ddaecf28f35 --- manifest | 39 +++-- manifest.uuid | 2 +- src/expr.c | 375 ++++++++++++++++++++++++++++++++++++-------- src/parse.y | 9 ++ src/resolve.c | 35 +++++ src/select.c | 35 ++--- src/sqliteInt.h | 6 +- src/vdbe.c | 40 ++++- src/where.c | 65 +++++++- src/whereInt.h | 2 + src/wherecode.c | 60 +++++-- src/whereexpr.c | 49 +++++- test/rowvalue.test | 123 +++++++++++++++ test/rowvalue2.test | 252 +++++++++++++++++++++++++++++ tool/addopcodes.tcl | 1 + 15 files changed, 975 insertions(+), 118 deletions(-) create mode 100644 test/rowvalue.test create mode 100644 test/rowvalue2.test diff --git a/manifest b/manifest index 679ecd4e0c..b5d0802698 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\s"#/value-list/"\sstyle\sof\sresults\sfor\sapproximate\svalue\smatching\nin\sthe\sdo_test\scommand\sof\sthe\stest\sinfrastructure.\s\sUse\sthis\snew\sresult\sstyle\nto\smake\sthe\sSQLITE_DBSTATUS_CACHE_SIZE_SHARED\stests\scross-platform. -D 2016-07-09T17:47:01.013 +C Add\ssome\ssupport\sfor\susing\srow\svalue\sconstructors\sin\scertain\sparts\sof\sSQL\sexpressions.\sThere\sare\smany\sbugs\son\sthis\sbranch. +D 2016-07-09T20:23:55.716 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 523a5b1db2b6d88c6eefb224877bf635a3bcfc92 +F src/expr.c 330854fe9fdea1d244abaef6d680f6b91df07cb4 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c bc4145347595b7770f9a598cff1c848302cf5413 F src/func.c 61a4114cf7004f10c542cfabbab9f2bcb9033045 @@ -372,7 +372,7 @@ F src/os_win.c 520f23475f1de530c435d30b67b7b15fe90874b0 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c c368634b888b1c8740aea83b36bfd266f2443e60 F src/pager.h 031a87445e5e0afc85312d1c380e123ad6c7aeaf -F src/parse.y f374ab20106362eb3f5c01b3e6a002f0bbead7ff +F src/parse.y fa040d742fe4922b219143fe2e04f74061daabcb F src/pcache.c 5583c8ade4b05075a60ba953ef471d1c1a9c05df F src/pcache.h 2cedcd8407eb23017d92790b112186886e179490 F src/pcache1.c 7f51d2b541aab57596adf62db2c4bb025d34f04d @@ -381,14 +381,14 @@ F src/pragma.h 64c78a648751b9f4f297276c4eb7507b14b4628c F src/prepare.c 22df6171aec1d86904ed2ad30c2348a5748aa04e F src/printf.c a5f0ca08ddede803c241266abb46356ec748ded1 F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 -F src/resolve.c cca3aa77b95706df5d635a2141a4d1de60ae6598 +F src/resolve.c 9680caadd54772699e5a0a8ebd680e014703e4ee F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac -F src/select.c f3c6e9065fb34f6a23af27ec7f1f717ffbfc2ee4 +F src/select.c 0115f5d222f5cf9b5511ec4072088417354d738a F src/shell.c 14ff7f660530a52b117d110ba3390b7b2eb719b6 F src/sqlite.h.in b9ba728c1083b7a8ab5f6a628b25cd2a00325fbf F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 2a170163d121095c6ab1ef05ed0413722f391d01 -F src/sqliteInt.h dcf43b8abc5605b70f54ba80f42b6ad054b8ba95 +F src/sqliteInt.h dd2dd1d880ffd33137d20dc6da21f169836b8f5a F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247 F src/status.c 5b18f9526900f61189ab0b83f1ef41d9f871a2ab F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9 @@ -449,7 +449,7 @@ F src/update.c 4f05ea8cddfa367d045e03589756c02199e8f9bd F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c F src/util.c 810ec3f22e2d1b62e66c30fe3621ebdedd23584d F src/vacuum.c feb1eabb20987983d9350cad98299b21fa811f52 -F src/vdbe.c 22b46c3b725e950e9f2760e2d76953d592600ad4 +F src/vdbe.c 680c118a20b4b496644001e7ff4819c3e3ff8d85 F src/vdbe.h 67bc551f7faf04c33493892e4b378aada823ed10 F src/vdbeInt.h c59381049af5c7751a83456c39b80d1a6fde1f9d F src/vdbeapi.c 02bcbc2ca5d2004b029088b05b468b394881e103 @@ -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 48eed8ebe319c6cbc7bf7682018f32af0f5189f5 -F src/whereInt.h e5b939701a7ceffc5a3a8188a37f9746416ebcd0 -F src/wherecode.c e20cb381ff621e56a4684c71e31999aca2547ca6 -F src/whereexpr.c d7dcbf14ce1b5876c1f76496162c30fcba669563 +F src/where.c 898a45969bae1298cbaaaf05e6aeacfb45971293 +F src/whereInt.h 1ad3be2a43cb821418e1100476a3a14cd57635c4 +F src/wherecode.c a66c589bdcaa9da45d9576d54663666083e0c109 +F src/whereexpr.c d88ee6ce356cb9fd986d0e81249a2cd66a513093 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -1017,6 +1017,8 @@ F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81 F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d +F test/rowvalue.test 979738b3d49f1d93e3fee56a71d4446217917abc +F test/rowvalue2.test 8d5dfe75b8f4d1868a2f91f0356f20d36cba64ff F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09 F test/savepoint.test c671fdbd34cd3bfe1518a777526ada595180cf8d @@ -1419,7 +1421,7 @@ F test/zerodamage.test e59a56443d6298ecf7435f618f0b27654f0c849e F tool/GetFile.cs a15e08acb5dd7539b75ba23501581d7c2b462cb5 F tool/GetTclKit.bat 629d87562e0487c386db630033931d12d62e6372 F tool/Replace.cs 02c67258801c2fb5f63231e0ac0f220b4b36ba91 -F tool/addopcodes.tcl 2b089684eb8b7d0db64cf9d8e6d2fe1b6d279e8d +F tool/addopcodes.tcl 7d4954564d7d4bfde5ba139ebced25542696fa52 F tool/build-all-msvc.bat 3e4e4043b53f1aede4308e0d2567bbd773614630 x F tool/build-shell.sh 950f47c6174f1eea171319438b93ba67ff5bf367 F tool/cg_anno.tcl 692ce4b8693d59e3a3de77ca97f4139ecfa641b0 x @@ -1505,7 +1507,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 29fb988f1afc3fe623097acee1a5d08bf8386626 -R 767a7d8abd9b9525d1b45069b4c3dba2 -U drh -Z df36acb2648e26f22b53c52b73476f6c +P c869bf34a8ee42ac6542862e59c7a4b89b042f79 +R e70f91ca2d7d2c655198f54d385cd62e +T *branch * rowvalue +T *sym-rowvalue * +T -sym-trunk * +U dan +Z e86f169a98e6cbe5718a428ba6e6f5e0 diff --git a/manifest.uuid b/manifest.uuid index d451076edf..8f187444bd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c869bf34a8ee42ac6542862e59c7a4b89b042f79 \ No newline at end of file +b2204215b231202aef7a218411cc2ddaecf28f35 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index ce3a476561..7941c228fd 100644 --- a/src/expr.c +++ b/src/expr.c @@ -309,6 +309,114 @@ static int codeCompare( return addr; } +int sqlite3ExprVectorSize(Expr *pExpr){ + if( (pExpr->flags & EP_Vector)==0 ) return 1; + if( pExpr->flags & EP_xIsSelect ){ + return pExpr->x.pSelect->pEList->nExpr; + } + return pExpr->x.pList->nExpr; +} + +static Expr *exprVectorField(Expr *pVector, int i){ + if( pVector->flags & EP_xIsSelect ){ + return pVector->x.pSelect->pEList->a[i].pExpr; + } + return pVector->x.pList->a[i].pExpr; +} + +static void codeVectorCompare(Parse *pParse, Expr *pExpr, int dest){ + Vdbe *v = pParse->pVdbe; + Expr *pLeft = pExpr->pLeft; + Expr *pRight = pExpr->pRight; + int nLeft = sqlite3ExprVectorSize(pLeft); + int nRight = sqlite3ExprVectorSize(pRight); + int addr = sqlite3VdbeMakeLabel(v); + + /* Check that both sides of the comparison are vectors, and that + ** both are the same length. */ + if( nLeft!=nRight ){ + sqlite3ErrorMsg(pParse, "invalid use of row value"); + }else{ + int p5 = (pExpr->op==TK_IS || pExpr->op==TK_ISNOT) ? SQLITE_NULLEQ : 0; + int opCmp; + int opTest; + int i; + int p3 = 1; + int regLeft = 0; + int regRight = 0; + + assert( pExpr->op==TK_EQ || pExpr->op==TK_NE + || pExpr->op==TK_IS || pExpr->op==TK_ISNOT + || pExpr->op==TK_LT || pExpr->op==TK_GT + || pExpr->op==TK_LE || pExpr->op==TK_GE + ); + + switch( pExpr->op ){ + case TK_EQ: + case TK_IS: + opTest = OP_IfNot; + opCmp = OP_Eq; + break; + + case TK_NE: + case TK_ISNOT: + opTest = OP_If; + opCmp = OP_Ne; + break; + + case TK_LT: + case TK_LE: + case TK_GT: + case TK_GE: + opCmp = OP_Cmp; + opTest = OP_CmpTest; + p3 = pExpr->op; + 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 ); + } + if( pParse->nErr ) return; + + for(i=0; ix.pSelect->pEList->a[i].pExpr; + r1 = regLeft+i; + }else{ + pL = pLeft->x.pList->a[i].pExpr; + r1 = sqlite3ExprCodeTemp(pParse, pL, ®Free1); + } + + if( regRight ){ + pR = pRight->x.pSelect->pEList->a[i].pExpr; + r2 = regRight+i; + }else{ + pR = pRight->x.pList->a[i].pExpr; + r2 = sqlite3ExprCodeTemp(pParse, pR, ®Free1); + } + + codeCompare(pParse, pL, pR, opCmp, r1, r2, dest, SQLITE_STOREP2 | p5); + sqlite3VdbeAddOp3(v, opTest, dest, addr, p3); + sqlite3ReleaseTempReg(pParse, regFree1); + sqlite3ReleaseTempReg(pParse, regFree2); + } + } + + sqlite3VdbeResolveLabel(v, addr); +} + #if SQLITE_MAX_EXPR_DEPTH>0 /* ** Check that argument nHeight is less than or equal to the maximum @@ -743,7 +851,7 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){ if( !ExprHasProperty(p, EP_TokenOnly) ){ /* The Expr.x union is never used at the same time as Expr.pRight */ assert( p->x.pList==0 || p->pRight==0 ); - sqlite3ExprDelete(db, p->pLeft); + if( p->op!=TK_SELECT_COLUMN ) sqlite3ExprDelete(db, p->pLeft); sqlite3ExprDelete(db, p->pRight); if( ExprHasProperty(p, EP_MemToken) ) sqlite3DbFree(db, p->u.zToken); if( ExprHasProperty(p, EP_xIsSelect) ){ @@ -1835,7 +1943,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int *prRhsHasNull){ /* If no preexisting index is available for the IN clause ** and IN_INDEX_NOOP is an allowed reply ** and the RHS of the IN operator is a list, not a subquery - ** and the RHS is not contant or has two or fewer terms, + ** and the RHS is not constant or has two or fewer terms, ** then it is not worth creating an ephemeral table to evaluate ** the IN operator so return IN_INDEX_NOOP. */ @@ -1872,6 +1980,30 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int *prRhsHasNull){ } #endif +static char *exprINAffinity(Parse *pParse, Expr *pExpr){ + Expr *pLeft = pExpr->pLeft; + int nVal = sqlite3ExprVectorSize(pLeft); + char *zRet; + + zRet = sqlite3DbMallocZero(pParse->db, nVal+1); + if( zRet ){ + int i; + for(i=0; ix.pSelect->pEList->a[i].pExpr, a); + } + zRet[nVal] = '\0'; + } + return zRet; +} + /* ** Generate code for scalar subqueries used as a subquery expression, EXISTS, ** or IN operators. Examples: @@ -1939,12 +2071,12 @@ int sqlite3CodeSubselect( switch( pExpr->op ){ case TK_IN: { - char affinity; /* Affinity of the LHS of the IN */ int addr; /* Address of OP_OpenEphemeral instruction */ Expr *pLeft = pExpr->pLeft; /* the LHS of the IN operator */ KeyInfo *pKeyInfo = 0; /* Key information */ - - affinity = sqlite3ExprAffinity(pLeft); + int nVal; /* Size of vector pLeft */ + + nVal = sqlite3ExprVectorSize(pLeft); /* Whether this is an 'x IN(SELECT...)' or an 'x IN()' ** expression it is handled the same way. An ephemeral table is @@ -1960,8 +2092,9 @@ int sqlite3CodeSubselect( ** is used. */ pExpr->iTable = pParse->nTab++; - addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, !isRowid); - pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, 1, 1); + addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, + pExpr->iTable, (isRowid?0:nVal)); + pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, nVal, 1); if( ExprHasProperty(pExpr, EP_xIsSelect) ){ /* Case 1: expr IN (SELECT ...) @@ -1970,27 +2103,38 @@ int sqlite3CodeSubselect( ** table allocated and opened above. */ Select *pSelect = pExpr->x.pSelect; - SelectDest dest; - ExprList *pEList; + ExprList *pEList = pSelect->pEList; assert( !isRowid ); - sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable); - dest.affSdst = (u8)affinity; - assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable ); - pSelect->iLimit = 0; - testcase( pSelect->selFlags & SF_Distinct ); - testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */ - if( sqlite3Select(pParse, pSelect, &dest) ){ - sqlite3KeyInfoUnref(pKeyInfo); - return 0; + if( pEList->nExpr!=nVal ){ + sqlite3ErrorMsg(pParse, "SELECT has %d columns - expected %d", + pEList->nExpr, nVal); + }else{ + SelectDest dest; + int i; + sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable); + dest.zAffSdst = exprINAffinity(pParse, pExpr); + assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable ); + pSelect->iLimit = 0; + testcase( pSelect->selFlags & SF_Distinct ); + testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */ + if( sqlite3Select(pParse, pSelect, &dest) ){ + sqlite3DbFree(pParse->db, dest.zAffSdst); + sqlite3KeyInfoUnref(pKeyInfo); + return 0; + } + sqlite3DbFree(pParse->db, dest.zAffSdst); + assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */ + assert( pEList!=0 ); + assert( pEList->nExpr>0 ); + assert( sqlite3KeyInfoIsWriteable(pKeyInfo) ); + for(i=0; i1) ? exprVectorField(pLeft, i) : pLeft; + pKeyInfo->aColl[i] = sqlite3BinaryCompareCollSeq( + pParse, p, pEList->a[i].pExpr + ); + } } - pEList = pSelect->pEList; - assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */ - assert( pEList!=0 ); - assert( pEList->nExpr>0 ); - assert( sqlite3KeyInfoIsWriteable(pKeyInfo) ); - pKeyInfo->aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft, - pEList->a[0].pExpr); }else if( ALWAYS(pExpr->x.pList!=0) ){ /* Case 2: expr IN (exprlist) ** @@ -1999,11 +2143,13 @@ int sqlite3CodeSubselect( ** that columns affinity when building index keys. If is not ** a column, use numeric affinity. */ + char affinity; /* Affinity of the LHS of the IN */ int i; ExprList *pList = pExpr->x.pList; struct ExprList_item *pItem; int r1, r2, r3; + affinity = sqlite3ExprAffinity(pLeft); if( !affinity ){ affinity = SQLITE_AFF_BLOB; } @@ -2067,18 +2213,22 @@ int sqlite3CodeSubselect( */ Select *pSel; /* SELECT statement to encode */ SelectDest dest; /* How to deal with SELECt result */ + int nReg; /* Registers to allocate */ testcase( pExpr->op==TK_EXISTS ); testcase( pExpr->op==TK_SELECT ); assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT ); - assert( ExprHasProperty(pExpr, EP_xIsSelect) ); + pSel = pExpr->x.pSelect; - sqlite3SelectDestInit(&dest, 0, ++pParse->nMem); + nReg = pExpr->op==TK_SELECT ? pSel->pEList->nExpr : 1; + sqlite3SelectDestInit(&dest, 0, pParse->nMem+1); + pParse->nMem += nReg; if( pExpr->op==TK_SELECT ){ dest.eDest = SRT_Mem; dest.iSdst = dest.iSDParm; - sqlite3VdbeAddOp2(v, OP_Null, 0, dest.iSDParm); + dest.nSdst = nReg; + sqlite3VdbeAddOp3(v, OP_Null, 0, dest.iSDParm, dest.iSDParm+nReg-1); VdbeComment((v, "Init subquery result")); }else{ dest.eDest = SRT_Exists; @@ -2112,6 +2262,75 @@ int sqlite3CodeSubselect( } #endif /* SQLITE_OMIT_SUBQUERY */ +#ifndef SQLITE_OMIT_SUBQUERY +void exprCodeVectorIN( + Parse *pParse, /* Parsing and code generating context */ + Expr *pExpr, /* The IN expression */ + int destIfFalse, /* Jump here if LHS is not contained in the RHS */ + int destIfNull /* Jump here if the results are unknown due to NULLs */ +){ + int i; + int addrNext; + int iSkip; + int r1; + int r2 = sqlite3GetTempReg(pParse); + int r3 = sqlite3GetTempReg(pParse); + int r4 = sqlite3GetTempReg(pParse); + int regResult = sqlite3GetTempReg(pParse); + int nVal = sqlite3ExprVectorSize(pExpr->pLeft); + + Expr *pLeft = pExpr->pLeft; + Vdbe *v = pParse->pVdbe; + + /* Code the LHS, the from " IN (...)". Leave the results in + ** an array of nVal registers starting at r1. */ + sqlite3ExprCachePush(pParse); + if( pLeft->flags & EP_xIsSelect ){ + r1 = sqlite3CodeSubselect(pParse, pLeft, 0, 0); + }else{ + r1 = pParse->nMem + 1; + pParse->nMem += nVal; + sqlite3ExprCodeExprList(pParse, pLeft->x.pList, r1, 0, 0); + } + + /* Generate an epheremal index containing the contents of the SELECT + ** to the right of the " IN (SELECT ...)" expression. The cursor + ** number for the epheremal table is left in pExpr->iTable. */ + assert( pExpr->flags & EP_xIsSelect ); + sqlite3CodeSubselect(pParse, pExpr, 0, 0); + + sqlite3VdbeAddOp2(v, OP_Integer, 0, regResult); + + /* Iterate through the ephemeral table just populated */ + addrNext = 1 + sqlite3VdbeAddOp2(v, OP_Rewind, pExpr->iTable, destIfFalse); + for(i=0; iiTable, i, r2); + sqlite3VdbeAddOp4(v, OP_Eq, r1+i, i==0?r3:r4, r2, (void*)pColl,P4_COLLSEQ); + sqlite3VdbeChangeP5(v, SQLITE_STOREP2); + VdbeCoverage(v); + if( i!=0 ){ + sqlite3VdbeAddOp3(v, OP_And, r3, r4, r4); + } + } + sqlite3VdbeAddOp2(v, OP_If, r4, sqlite3VdbeCurrentAddr(v)+6); + sqlite3VdbeAddOp2(v, OP_IfNot, r4, sqlite3VdbeCurrentAddr(v)+2); + sqlite3VdbeAddOp2(v, OP_Null, 0, regResult); + sqlite3VdbeAddOp2(v, OP_Next, pExpr->iTable, addrNext); + sqlite3VdbeAddOp3(v, OP_If, regResult, destIfNull, 1); + sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfFalse); + + sqlite3ReleaseTempReg(pParse, r2); + sqlite3ReleaseTempReg(pParse, r3); + sqlite3ReleaseTempReg(pParse, r4); + sqlite3ReleaseTempReg(pParse, regResult); + sqlite3ExprCachePop(pParse); +} +#endif + #ifndef SQLITE_OMIT_SUBQUERY /* ** Generate code for an IN expression. @@ -2142,6 +2361,10 @@ static void sqlite3ExprCodeIN( int r1; /* Temporary use register */ Vdbe *v; /* Statement under construction */ + if( pExpr->pLeft->flags & EP_Vector ){ + return exprCodeVectorIN(pParse, pExpr, destIfFalse, destIfNull); + } + /* Compute the RHS. After this step, the table with cursor ** pExpr->iTable will contains the values that make up the RHS. */ @@ -2669,6 +2892,8 @@ static void exprToRegister(Expr *p, int iReg){ ExprClearProperty(p, EP_Skip); } +static void exprCodeBetween(Parse*,Expr*,int,void(*)(Parse*,Expr*,int,int),int); + /* ** Generate code into the current Vdbe to evaluate the given ** expression. Attempt to store the results in register "target". @@ -2689,6 +2914,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ int r1, r2, r3, r4; /* Various register numbers */ sqlite3 *db = pParse->db; /* The database connection */ Expr tempX; /* Temporary expression node */ + int p5 = 0; assert( target>0 && target<=pParse->nMem ); if( v==0 ){ @@ -2801,39 +3027,34 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ break; } #endif /* SQLITE_OMIT_CAST */ + case TK_IS: + case TK_ISNOT: + op = (op==TK_IS) ? TK_EQ : TK_NE; + p5 = SQLITE_NULLEQ; + /* fall-through */ case TK_LT: case TK_LE: case TK_GT: case TK_GE: case TK_NE: case TK_EQ: { - r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); - r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); - codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, - r1, r2, inReg, SQLITE_STOREP2); - assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt); - assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le); - assert(TK_GT==OP_Gt); testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt); - assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge); - assert(TK_EQ==OP_Eq); testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq); - assert(TK_NE==OP_Ne); testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne); - testcase( regFree1==0 ); - testcase( regFree2==0 ); - break; - } - case TK_IS: - case TK_ISNOT: { - testcase( op==TK_IS ); - testcase( op==TK_ISNOT ); - r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); - r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); - op = (op==TK_IS) ? TK_EQ : TK_NE; - codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, - r1, r2, inReg, SQLITE_STOREP2 | SQLITE_NULLEQ); - VdbeCoverageIf(v, op==TK_EQ); - VdbeCoverageIf(v, op==TK_NE); - testcase( regFree1==0 ); - testcase( regFree2==0 ); + Expr *pLeft = pExpr->pLeft; + if( (pLeft->flags & EP_Vector) ){ + codeVectorCompare(pParse, pExpr, target); + }else{ + r1 = sqlite3ExprCodeTemp(pParse, pLeft, ®Free1); + r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); + codeCompare(pParse, pLeft, pExpr->pRight, op, + r1, r2, inReg, SQLITE_STOREP2 | p5); + assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt); + assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le); + assert(TK_GT==OP_Gt); testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt); + assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge); + assert(TK_EQ==OP_Eq); testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq); + assert(TK_NE==OP_Ne); testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne); + testcase( regFree1==0 ); + testcase( regFree2==0 ); + } break; } case TK_AND: @@ -3085,6 +3306,8 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ ** Z is stored in pExpr->pList->a[1].pExpr. */ case TK_BETWEEN: { + exprCodeBetween(pParse, pExpr, target, 0, 0); +#if 0 Expr *pLeft = pExpr->pLeft; struct ExprList_item *pLItem = pExpr->x.pList->a; Expr *pRight = pLItem->pExpr; @@ -3107,6 +3330,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ sqlite3VdbeAddOp3(v, OP_And, r3, r4, target); sqlite3ReleaseTempReg(pParse, r3); sqlite3ReleaseTempReg(pParse, r4); +#endif break; } case TK_SPAN: @@ -3172,6 +3396,22 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ break; } + case TK_VECTOR: { + sqlite3ErrorMsg(pParse, "invalid use of row value (1)"); + break; + } + + case TK_SELECT_COLUMN: { + Expr *pLeft = pExpr->pLeft; + assert( pLeft ); + assert( pLeft->op==TK_SELECT || pLeft->op==TK_REGISTER ); + if( pLeft->op==TK_SELECT ){ + pLeft->iTable = sqlite3CodeSubselect(pParse, pLeft, 0, 0); + pLeft->op = TK_REGISTER; + } + inReg = pLeft->iTable + pExpr->iColumn; + break; + } /* ** Form A: @@ -3500,7 +3740,7 @@ static void exprCodeBetween( Parse *pParse, /* Parsing and code generating context */ Expr *pExpr, /* The BETWEEN expression */ int dest, /* Jump here if the jump is taken */ - int jumpIfTrue, /* Take the jump if the BETWEEN is true */ + void (*xJumpIf)(Parse*,Expr*,int,int), int jumpIfNull /* Take the jump if the BETWEEN is NULL */ ){ Expr exprAnd; /* The AND operator in x>=y AND x<=z */ @@ -3509,6 +3749,10 @@ static void exprCodeBetween( Expr exprX; /* The x subexpression */ int regFree1 = 0; /* Temporary use register */ + memset(&compLeft, 0, sizeof(Expr)); + memset(&compRight, 0, sizeof(Expr)); + memset(&exprAnd, 0, sizeof(Expr)); + assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); exprX = *pExpr->pLeft; exprAnd.op = TK_AND; @@ -3520,11 +3764,14 @@ static void exprCodeBetween( compRight.op = TK_LE; compRight.pLeft = &exprX; compRight.pRight = pExpr->x.pList->a[1].pExpr; - exprToRegister(&exprX, sqlite3ExprCodeTemp(pParse, &exprX, ®Free1)); - if( jumpIfTrue ){ - sqlite3ExprIfTrue(pParse, &exprAnd, dest, jumpIfNull); + if( (exprX.flags & EP_Vector)==0 ){ + exprToRegister(&exprX, sqlite3ExprCodeTemp(pParse, &exprX, ®Free1)); + } + if( xJumpIf ){ + xJumpIf(pParse, &exprAnd, dest, jumpIfNull); }else{ - sqlite3ExprIfFalse(pParse, &exprAnd, dest, jumpIfNull); + exprX.flags |= EP_FromJoin; + sqlite3ExprCodeTarget(pParse, &exprAnd, dest); } sqlite3ReleaseTempReg(pParse, regFree1); @@ -3564,7 +3811,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ if( NEVER(v==0) ) return; /* Existence of VDBE checked by caller */ if( NEVER(pExpr==0) ) return; /* No way this can happen */ op = pExpr->op; - switch( op ){ + switch( op | (pExpr->pLeft ? (pExpr->pLeft->flags & EP_Vector) : 0)){ case TK_AND: { int d2 = sqlite3VdbeMakeLabel(v); testcase( jumpIfNull==0 ); @@ -3633,7 +3880,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ } case TK_BETWEEN: { testcase( jumpIfNull==0 ); - exprCodeBetween(pParse, pExpr, dest, 1, jumpIfNull); + exprCodeBetween(pParse, pExpr, dest, sqlite3ExprIfTrue, jumpIfNull); break; } #ifndef SQLITE_OMIT_SUBQUERY @@ -3716,7 +3963,7 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ assert( pExpr->op!=TK_GT || op==OP_Le ); assert( pExpr->op!=TK_GE || op==OP_Lt ); - switch( pExpr->op ){ + switch( pExpr->op | (pExpr->pLeft ? (pExpr->pLeft->flags & EP_Vector) : 0)){ case TK_AND: { testcase( jumpIfNull==0 ); sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull); @@ -3783,7 +4030,7 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ } case TK_BETWEEN: { testcase( jumpIfNull==0 ); - exprCodeBetween(pParse, pExpr, dest, 0, jumpIfNull); + exprCodeBetween(pParse, pExpr, dest, sqlite3ExprIfFalse, jumpIfNull); break; } #ifndef SQLITE_OMIT_SUBQUERY diff --git a/src/parse.y b/src/parse.y index 8a65131e28..b107057fe0 100644 --- a/src/parse.y +++ b/src/parse.y @@ -217,6 +217,7 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);} %left CONCAT. %left COLLATE. %right BITNOT. +%right VECTOR. // An IDENTIFIER can be a generic identifier, or one of several // keywords. Any non-standard keyword can also be an identifier. @@ -946,6 +947,14 @@ term(A) ::= CTIME_KW(OP). { } } +expr(A) ::= LP(L) nexprlist(X) COMMA expr(Y) RP(R). { + A.pExpr = sqlite3PExpr(pParse, TK_VECTOR, 0, 0, 0); + if( A.pExpr ){ + A.pExpr->x.pList = sqlite3ExprListAppend(pParse, X, Y.pExpr); + spanSet(&A, &L, &R); + } +} + expr(A) ::= expr(A) AND(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);} expr(A) ::= expr(A) OR(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);} expr(A) ::= expr(A) LT|GT|GE|LE(OP) expr(Y). diff --git a/src/resolve.c b/src/resolve.c index 77ce37f6d7..cd3d714613 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -765,6 +765,17 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ ExprSetProperty(pExpr, EP_VarSelect); pNC->ncFlags |= NC_VarSelect; } + + if( pExpr->op==TK_SELECT && pExpr->x.pSelect->pEList->nExpr>1 ){ + if( !ExprHasProperty(pExpr, EP_VectorOk) ){ + sqlite3ErrorMsg(pParse, "invalid use of row value"); + }else{ + ExprSetProperty(pExpr, EP_Vector); + } + } + if( pExpr->op==TK_IN ){ + ExprSetProperty(pExpr->pLeft, EP_VectorOk); + } } break; } @@ -772,6 +783,30 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ notValid(pParse, pNC, "parameters", NC_IsCheck|NC_PartIdx|NC_IdxExpr); break; } + + case TK_BETWEEN: { + ExprSetProperty(pExpr->pLeft, EP_VectorOk); + ExprSetProperty(pExpr->x.pList->a[0].pExpr, EP_VectorOk); + ExprSetProperty(pExpr->x.pList->a[1].pExpr, EP_VectorOk); + break; + } + + case TK_EQ: case TK_NE: case TK_IS: case TK_ISNOT: + case TK_LE: case TK_LT: case TK_GE: case TK_GT: + { + ExprSetProperty(pExpr->pLeft, EP_VectorOk); + ExprSetProperty(pExpr->pRight, EP_VectorOk); + break; + }; + + case TK_VECTOR: { + if( !ExprHasProperty(pExpr, EP_VectorOk) ){ + sqlite3ErrorMsg(pParse, "invalid use of row value"); + }else{ + ExprSetProperty(pExpr, EP_Vector); + } + break; + } } return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue; } diff --git a/src/select.c b/src/select.c index e21ac57dc5..f49a5c1fed 100644 --- a/src/select.c +++ b/src/select.c @@ -88,7 +88,7 @@ static void clearSelect(sqlite3 *db, Select *p, int bFree){ void sqlite3SelectDestInit(SelectDest *pDest, int eDest, int iParm){ pDest->eDest = (u8)eDest; pDest->iSDParm = iParm; - pDest->affSdst = 0; + pDest->zAffSdst = 0; pDest->iSdst = 0; pDest->nSdst = 0; } @@ -673,7 +673,7 @@ static int checkForMultiColumnSelectError( int nExpr /* Number of result columns returned by SELECT */ ){ int eDest = pDest->eDest; - if( nExpr>1 && (eDest==SRT_Mem || eDest==SRT_Set) ){ + if( 0 && nExpr>1 && eDest==SRT_Set ){ sqlite3ErrorMsg(pParse, "only a single result allowed for " "a SELECT that is part of an expression"); return 1; @@ -892,9 +892,6 @@ static void selectInnerLoop( ** item into the set table with bogus data. */ case SRT_Set: { - assert( nResultCol==1 ); - pDest->affSdst = - sqlite3CompareAffinity(pEList->a[0].pExpr, pDest->affSdst); if( pSort ){ /* At first glance you would think we could optimize out the ** ORDER BY in this case since the order of entries in the set @@ -903,8 +900,10 @@ static void selectInnerLoop( pushOntoSorter(pParse, pSort, p, regResult, regResult, 1, nPrefixReg); }else{ int r1 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult,1,r1, &pDest->affSdst, 1); - sqlite3ExprCacheAffinityChange(pParse, regResult, 1); + assert( sqlite3Strlen30(pDest->zAffSdst)==nResultCol ); + sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult, nResultCol, + r1, pDest->zAffSdst, 1); + sqlite3ExprCacheAffinityChange(pParse, regResult, nResultCol); sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1); sqlite3ReleaseTempReg(pParse, r1); } @@ -924,9 +923,9 @@ static void selectInnerLoop( ** of the scan loop. */ case SRT_Mem: { - assert( nResultCol==1 ); + assert( nResultCol==pDest->nSdst ); if( pSort ){ - pushOntoSorter(pParse, pSort, p, regResult, regResult, 1, nPrefixReg); + pushOntoSorter(pParse, pSort, p, regResult, regResult, nResultCol, nPrefixReg); }else{ assert( regResult==iParm ); /* The LIMIT clause will jump out of the loop for us */ @@ -1241,7 +1240,7 @@ static void generateSortTail( sqlite3VdbeResolveLabel(v, pSort->labelBkOut); } iTab = pSort->iECursor; - if( eDest==SRT_Output || eDest==SRT_Coroutine ){ + if( eDest==SRT_Output || eDest==SRT_Coroutine || eDest==SRT_Mem ){ regRowid = 0; regRow = pDest->iSdst; nSortData = nColumn; @@ -1283,16 +1282,15 @@ static void generateSortTail( } #ifndef SQLITE_OMIT_SUBQUERY case SRT_Set: { - assert( nColumn==1 ); - sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, 1, regRowid, - &pDest->affSdst, 1); - sqlite3ExprCacheAffinityChange(pParse, regRow, 1); + assert( nColumn==sqlite3Strlen30(pDest->zAffSdst) ); + sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, nColumn, regRowid, + pDest->zAffSdst, 1); + sqlite3ExprCacheAffinityChange(pParse, regRow, nColumn); sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regRowid); break; } case SRT_Mem: { - assert( nColumn==1 ); - sqlite3ExprCodeMove(pParse, regRow, iParm, 1); + /* sqlite3ExprCodeMove(pParse, regRow, iParm, nColumn); */ /* The LIMIT clause will terminate the loop for us */ break; } @@ -2659,10 +2657,9 @@ static int generateOutputSubroutine( case SRT_Set: { int r1; assert( pIn->nSdst==1 || pParse->nErr>0 ); - pDest->affSdst = - sqlite3CompareAffinity(p->pEList->a[0].pExpr, pDest->affSdst); r1 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iSdst, 1, r1, &pDest->affSdst,1); + sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst, + r1, pDest->zAffSdst,1); sqlite3ExprCacheAffinityChange(pParse, pIn->iSdst, 1); sqlite3VdbeAddOp2(v, OP_IdxInsert, pDest->iSDParm, r1); sqlite3ReleaseTempReg(pParse, r1); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 998a17dac3..3caa65d8e3 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2317,6 +2317,8 @@ struct Expr { #define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */ #define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */ #define EP_Alias 0x400000 /* Is an alias for a result set column */ +#define EP_VectorOk 0x800000 /* This expression may be a row value */ +#define EP_Vector 0x1000000/* This expression is a row value */ /* ** Combinations of two or more EP_* flags @@ -2762,7 +2764,7 @@ struct Select { */ struct SelectDest { u8 eDest; /* How to dispose of the results. On of SRT_* above. */ - char affSdst; /* Affinity used when eDest==SRT_Set */ + char *zAffSdst; /* Affinity used when eDest==SRT_Set */ int iSDParm; /* A parameter used by the eDest disposal method */ int iSdst; /* Base register where results are written */ int nSdst; /* Number of registers allocated */ @@ -4237,4 +4239,6 @@ int sqlite3ThreadJoin(SQLiteThread*, void**); int sqlite3DbstatRegister(sqlite3*); #endif +int sqlite3ExprVectorSize(Expr *pExpr); + #endif /* SQLITEINT_H */ diff --git a/src/vdbe.c b/src/vdbe.c index 6adbcbbec1..3f2fdf9cff 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -1958,6 +1958,7 @@ case OP_Cast: { /* in1 */ ** the content of register P3 is greater than or equal to the content of ** register P1. See the Lt opcode for additional information. */ +case OP_Cmp: /* in1, in3 */ case OP_Eq: /* same as TK_EQ, jump, in1, in3 */ case OP_Ne: /* same as TK_NE, jump, in1, in3 */ case OP_Lt: /* same as TK_LT, jump, in1, in3 */ @@ -2056,7 +2057,8 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ case OP_Lt: res = res<0; break; case OP_Le: res = res<=0; break; case OP_Gt: res = res>0; break; - default: res = res>=0; break; + case OP_Ge: res = res>=0; break; + default: assert( pOp->opcode==OP_Cmp ); break; } /* Undo any changes made by applyAffinity() to the input registers. */ @@ -2072,6 +2074,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ pOut->u.i = res; REGISTER_TRACE(pOp->p2, pOut); }else{ + assert( pOp->opcode!=OP_Cmp ); VdbeBranchTaken(res!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3); if( res ){ goto jump_to_p2; @@ -3867,7 +3870,42 @@ seek_not_found: } break; } + +/* Opcode: CmpTest P1 P2 P3 * * +** +** P2 is a jump destination. Register P1 is guaranteed to contain either +** an integer value or a NULL. The jump is taken if P1 contains any value +** other than 0 (i.e. NULL does cause a jump). +** +** If P1 is not NULL, its value is modified to integer value 0 or 1 +** according to the value of the P3 operand: +** +** P3 modification +** -------------------------- +** OP_Lt (P1 = (P1 < 0)) +** OP_Le (P1 = (P1 <= 0)) +** OP_Gt (P1 = (P1 > 0)) +** OP_Ge (P1 = (P1 >= 0)) +*/ +case OP_CmpTest: { /* in1, jump */ + int bJump; + pIn1 = &aMem[pOp->p1]; + if( (pIn1->flags & MEM_Int) ){ + bJump = (pIn1->u.i!=0); + switch( pOp->p3 ){ + case OP_Lt: pIn1->u.i = (pIn1->u.i < 0); break; + case OP_Le: pIn1->u.i = (pIn1->u.i <= 0); break; + case OP_Gt: pIn1->u.i = (pIn1->u.i > 0); break; + default: assert( pOp->p3==OP_Ge ); pIn1->u.i = (pIn1->u.i >= 0); break; + } + }else{ + bJump = 1; + } + + if( bJump ) goto jump_to_p2; + break; +} /* Opcode: Found P1 P2 P3 P4 * ** Synopsis: key=r[P3@P4] diff --git a/src/where.c b/src/where.c index a65f30968e..36bf98b444 100644 --- a/src/where.c +++ b/src/where.c @@ -2187,6 +2187,51 @@ static void whereLoopOutputAdjust( if( pLoop->nOut > nRow-iReduce ) pLoop->nOut = nRow - iReduce; } +/* +** Term pTerm is a vector range comparison operation. The first comparison +** in the vector can be optimized using column nEq of the index. +*/ +int whereRangeVectorLen( + Parse *pParse, int iCur, Index *pIdx, int nEq, WhereTerm *pTerm +){ + int nCmp = sqlite3ExprVectorSize(pTerm->pExpr->pLeft); + int i; + + nCmp = MIN(nCmp, (pIdx->nColumn - nEq)); + for(i=1; ipExpr->pLeft->x.pList->a[i].pExpr; + Expr *pRhs = pTerm->pExpr->pRight; + if( pRhs->flags & EP_xIsSelect ){ + pRhs = pRhs->x.pSelect->pEList->a[i].pExpr; + }else{ + pRhs = pRhs->x.pList->a[i].pExpr; + } + + /* Check that the LHS of the comparison is a column reference to + ** the right column of the right source table. + */ + if( pLhs->op!=TK_COLUMN + || pLhs->iTable!=iCur + || pLhs->iColumn!=pIdx->aiColumn[i+nEq] + ){ + break; + } + + aff = sqlite3CompareAffinity(pRhs, sqlite3ExprAffinity(pLhs)); + idxaff = pIdx->pTable->aCol[pLhs->iColumn].affinity; + if( aff!=idxaff ) break; + + pColl = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs); + if( sqlite3StrICmp(pColl->zName, pIdx->azColl[i+nEq]) ) break; + } + return i; +} + /* ** Adjust the cost C by the costMult facter T. This only occurs if ** compiled with -DSQLITE_ENABLE_COSTMULT @@ -2225,6 +2270,8 @@ static int whereLoopAddBtreeIndex( Bitmask saved_prereq; /* Original value of pNew->prereq */ u16 saved_nLTerm; /* Original value of pNew->nLTerm */ u16 saved_nEq; /* Original value of pNew->u.btree.nEq */ + u16 saved_nBtm; /* Original value of pNew->u.btree.nBtm */ + u16 saved_nTop; /* Original value of pNew->u.btree.nTop */ u16 saved_nSkip; /* Original value of pNew->nSkip */ u32 saved_wsFlags; /* Original value of pNew->wsFlags */ LogEst saved_nOut; /* Original value of pNew->nOut */ @@ -2241,6 +2288,7 @@ static int whereLoopAddBtreeIndex( if( pNew->wsFlags & WHERE_BTM_LIMIT ){ opMask = WO_LT|WO_LE; }else{ + assert( pNew->u.btree.nBtm==0 ); 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); @@ -2248,6 +2296,8 @@ static int whereLoopAddBtreeIndex( assert( pNew->u.btree.nEqnColumn ); saved_nEq = pNew->u.btree.nEq; + saved_nBtm = pNew->u.btree.nBtm; + saved_nTop = pNew->u.btree.nTop; saved_nSkip = pNew->nSkip; saved_nLTerm = pNew->nLTerm; saved_wsFlags = pNew->wsFlags; @@ -2291,6 +2341,8 @@ static int whereLoopAddBtreeIndex( pNew->wsFlags = saved_wsFlags; pNew->u.btree.nEq = saved_nEq; + pNew->u.btree.nBtm = saved_nBtm; + pNew->u.btree.nTop = saved_nTop; pNew->nLTerm = saved_nLTerm; if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */ pNew->aLTerm[pNew->nLTerm++] = pTerm; @@ -2334,6 +2386,9 @@ static int whereLoopAddBtreeIndex( testcase( eOp & WO_GT ); testcase( eOp & WO_GE ); pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT; + pNew->u.btree.nBtm = whereRangeVectorLen( + pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm + ); pBtm = pTerm; pTop = 0; if( pTerm->wtFlags & TERM_LIKEOPT ){ @@ -2346,12 +2401,16 @@ static int whereLoopAddBtreeIndex( if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */ pNew->aLTerm[pNew->nLTerm++] = pTop; pNew->wsFlags |= WHERE_TOP_LIMIT; + pNew->u.btree.nTop = 1; } }else{ assert( eOp & (WO_LT|WO_LE) ); testcase( eOp & WO_LT ); testcase( eOp & WO_LE ); pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT; + pNew->u.btree.nTop = whereRangeVectorLen( + pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm + ); pTop = pTerm; pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ? pNew->aLTerm[pNew->nLTerm-2] : 0; @@ -2451,6 +2510,8 @@ static int whereLoopAddBtreeIndex( } pNew->prereq = saved_prereq; pNew->u.btree.nEq = saved_nEq; + pNew->u.btree.nBtm = saved_nBtm; + pNew->u.btree.nTop = saved_nTop; pNew->nSkip = saved_nSkip; pNew->wsFlags = saved_wsFlags; pNew->nOut = saved_nOut; @@ -2572,7 +2633,7 @@ static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){ /* ** Add all WhereLoop objects for a single table of the join where the table -** is idenfied by pBuilder->pNew->iTab. That table is guaranteed to be +** is identified by pBuilder->pNew->iTab. That table is guaranteed to be ** a b-tree table, not a virtual table. ** ** The costs (WhereLoop.rRun) of the b-tree loops added by this function @@ -2726,6 +2787,8 @@ static int whereLoopAddBtree( } rSize = pProbe->aiRowLogEst[0]; pNew->u.btree.nEq = 0; + pNew->u.btree.nBtm = 0; + pNew->u.btree.nTop = 0; pNew->nSkip = 0; pNew->nLTerm = 0; pNew->iSortIdx = 0; diff --git a/src/whereInt.h b/src/whereInt.h index 075c04e5e5..d288fa6192 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -122,6 +122,8 @@ struct WhereLoop { union { struct { /* Information for internal btree tables */ u16 nEq; /* Number of equality constraints */ + u16 nBtm; /* Size of BTM vector */ + u16 nTop; /* Size of TOP vector */ Index *pIndex; /* Index used, or NULL */ } btree; struct { /* Information for virtual tables */ diff --git a/src/wherecode.c b/src/wherecode.c index a017b40c7d..3060d8d324 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -861,6 +861,30 @@ static void codeDeferredSeek( } } +static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){ + assert( nReg>0 ); + if( p->flags & EP_Vector ){ + int i; + if( (p->flags & EP_xIsSelect)==0 ){ + ExprList *pList = p->x.pList; + assert( nReg<=pList->nExpr ); + for(i=0; ia[i].pExpr, iReg+i); + } + }else{ + Vdbe *v = pParse->pVdbe; + int iSelect = sqlite3CodeSubselect(pParse, p, 0, 0); + sqlite3VdbeAddOp3(v, OP_Copy, iSelect, iReg, nReg-1); + p->op2 = p->op; + p->op = TK_REGISTER; + p->iTable = iSelect; + } + }else{ + assert( nReg==1 ); + sqlite3ExprCode(pParse, p, iReg); + } +} + /* ** Generate code for the start of the iLevel-th loop in the WHERE clause ** implementation described by pWInfo. @@ -1185,6 +1209,8 @@ Bitmask sqlite3WhereCodeOneLoopStart( OP_IdxLT, /* 3: (end_constraints && bRev && endEq) */ }; u16 nEq = pLoop->u.btree.nEq; /* Number of == or IN terms */ + u16 nBtm = pLoop->u.btree.nBtm; /* Length of BTM vector */ + u16 nTop = pLoop->u.btree.nTop; /* Length of TOP vector */ int regBase; /* Base register holding constraint values */ WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */ WhereTerm *pRangeEnd = 0; /* Inequality constraint at range end */ @@ -1231,14 +1257,14 @@ Bitmask sqlite3WhereCodeOneLoopStart( j = nEq; if( pLoop->wsFlags & WHERE_BTM_LIMIT ){ pRangeStart = pLoop->aLTerm[j++]; - nExtraReg = 1; + nExtraReg = MAX(nExtraReg, pLoop->u.btree.nBtm); /* Like optimization range constraints always occur in pairs */ assert( (pRangeStart->wtFlags & TERM_LIKEOPT)==0 || (pLoop->wsFlags & WHERE_TOP_LIMIT)!=0 ); } if( pLoop->wsFlags & WHERE_TOP_LIMIT ){ pRangeEnd = pLoop->aLTerm[j++]; - nExtraReg = 1; + nExtraReg = MAX(nExtraReg, pLoop->u.btree.nTop); #ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS if( (pRangeEnd->wtFlags & TERM_LIKEOPT)!=0 ){ assert( pRangeStart!=0 ); /* LIKE opt constraints */ @@ -1274,6 +1300,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( ){ SWAP(WhereTerm *, pRangeEnd, pRangeStart); SWAP(u8, bSeekPastNull, bStopAtNull); + SWAP(u8, nBtm, nTop); } /* Generate code to evaluate all constraint terms using == or IN @@ -1298,7 +1325,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( nConstraint = nEq; if( pRangeStart ){ Expr *pRight = pRangeStart->pExpr->pRight; - sqlite3ExprCode(pParse, pRight, regBase+nEq); + codeExprOrVector(pParse, pRight, regBase+nEq, nBtm); whereLikeOptimizationStringFixup(v, pLevel, pRangeStart); if( (pRangeStart->wtFlags & TERM_VNULL)==0 && sqlite3ExprCanBeNull(pRight) @@ -1317,8 +1344,13 @@ Bitmask sqlite3WhereCodeOneLoopStart( zStartAff[nEq] = SQLITE_AFF_BLOB; } } - nConstraint++; + nConstraint += nBtm; testcase( pRangeStart->wtFlags & TERM_VIRTUAL ); + if( (pRight->flags & EP_Vector)==0 ){ + disableTerm(pLevel, pRangeStart); + }else{ + startEq = 1; + } }else if( bSeekPastNull ){ sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq); nConstraint++; @@ -1350,7 +1382,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( if( pRangeEnd ){ Expr *pRight = pRangeEnd->pExpr->pRight; sqlite3ExprCacheRemove(pParse, regBase+nEq, 1); - sqlite3ExprCode(pParse, pRight, regBase+nEq); + codeExprOrVector(pParse, pRight, regBase+nEq, nTop); whereLikeOptimizationStringFixup(v, pLevel, pRangeEnd); if( (pRangeEnd->wtFlags & TERM_VNULL)==0 && sqlite3ExprCanBeNull(pRight) @@ -1363,8 +1395,14 @@ Bitmask sqlite3WhereCodeOneLoopStart( ){ codeApplyAffinity(pParse, regBase+nEq, 1, &cEndAff); } - nConstraint++; + nConstraint += nTop; testcase( pRangeEnd->wtFlags & TERM_VIRTUAL ); + + if( (pRight->flags & EP_Vector)==0 ){ + disableTerm(pLevel, pRangeEnd); + }else{ + endEq = 1; + } }else if( bStopAtNull ){ sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq); endEq = 0; @@ -1385,9 +1423,11 @@ Bitmask sqlite3WhereCodeOneLoopStart( testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE ); } + /* Disable the start and end range terms if possible */ + /* disableTerm(pLevel, pRangeStart); */ + /* disableTerm(pLevel, pRangeEnd); */ + /* Seek the table cursor, if required */ - disableTerm(pLevel, pRangeStart); - disableTerm(pLevel, pRangeEnd); if( omitTable ){ /* pIdx is a covering index. No need to access the main table. */ }else if( HasRowid(pIdx->pTable) ){ @@ -1411,9 +1451,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( iRowidReg, pPk->nKeyCol); VdbeCoverage(v); } - /* Record the instruction used to terminate the loop. Disable - ** WHERE clause terms made redundant by the index range scan. - */ + /* Record the instruction used to terminate the loop. */ if( pLoop->wsFlags & WHERE_ONEROW ){ pLevel->op = OP_Noop; }else if( bRev ){ diff --git a/src/whereexpr.c b/src/whereexpr.c index 1cb6be8458..35af70687b 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -95,7 +95,7 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){ /* ** Return TRUE if the given operator is one of the operators that is ** allowed for an indexable WHERE clause term. The allowed operators are -** "=", "<", ">", "<=", ">=", "IN", and "IS NULL" +** "=", "<", ">", "<=", ">=", "IN", "IS", and "IS NULL" */ static int allowedOp(int op){ assert( TK_GT>TK_EQ && TK_GT, <, >= or <=), perform the processing + ** on the first element of the vector. */ + assert( TK_GT+1==TK_LE && TK_GT+2==TK_LT && TK_GT+3==TK_GE ); + if( (pExpr->flags & (EP_Vector|EP_xIsSelect))==EP_Vector + && (op>=TK_GT && op<=TK_GE) + ){ + pExpr = pExpr->x.pList->a[0].pExpr; + } + if( pExpr->op==TK_COLUMN ){ *piCur = pExpr->iTable; *piColumn = pExpr->iColumn; @@ -862,6 +874,17 @@ static int exprMightBeIndexed( return 0; } +static Expr *exprVectorExpr(Parse *pParse, Expr *p, int iField){ + Expr *pRet; + if( p->flags & EP_xIsSelect ){ + pRet = sqlite3PExpr(pParse, TK_SELECT_COLUMN, p, 0, 0); + if( pRet ) pRet->iColumn = iField; + }else{ + pRet = sqlite3ExprDup(pParse->db, p->x.pList->a[iField].pExpr, 0); + } + return pRet; +} + /* ** The input to this routine is an WhereTerm structure with only the ** "pExpr" field filled in. The job of this routine is to analyze the @@ -937,14 +960,14 @@ static void exprAnalyze( Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft); Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight); u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV; - if( exprMightBeIndexed(pSrc, prereqLeft, pLeft, &iCur, &iColumn) ){ + if( exprMightBeIndexed(pSrc, op, prereqLeft, pLeft, &iCur, &iColumn) ){ pTerm->leftCursor = iCur; pTerm->u.leftColumn = iColumn; pTerm->eOperator = operatorMask(op) & opMask; } if( op==TK_IS ) pTerm->wtFlags |= TERM_IS; if( pRight - && exprMightBeIndexed(pSrc, pTerm->prereqRight, pRight, &iCur, &iColumn) + && exprMightBeIndexed(pSrc, op, pTerm->prereqRight, pRight, &iCur,&iColumn) ){ WhereTerm *pNew; Expr *pDup; @@ -1152,6 +1175,26 @@ static void exprAnalyze( } #endif /* SQLITE_OMIT_VIRTUALTABLE */ + if( pWC->op==TK_AND + && (pExpr->op==TK_EQ || pExpr->op==TK_IS) + && (pExpr->pLeft->flags & EP_Vector) + && ( (pExpr->pLeft->flags & EP_xIsSelect)==0 + || (pExpr->pRight->flags & EP_xIsSelect)==0 + )){ + int i; + for(i=0; ipLeft); i++){ + int idxNew; + Expr *pNew; + Expr *pLeft = exprVectorExpr(pParse, pExpr->pLeft, i); + Expr *pRight = exprVectorExpr(pParse, pExpr->pRight, i); + + pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight, 0); + idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC); + 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/rowvalue.test b/test/rowvalue.test new file mode 100644 index 0000000000..3bfcfe2c12 --- /dev/null +++ b/test/rowvalue.test @@ -0,0 +1,123 @@ +# 2016 June 17 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing the SELECT statement. +# + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix rowvalue + +do_execsql_test 0.0 { + CREATE TABLE one(o); + INSERT INTO one VALUES(1); +} + +foreach {tn v1 v2 eq ne is isnot} { + 1 "1, 2, 3" "1, 2, 3" 1 0 1 0 + 2 "1, 0, 3" "1, 2, 3" 0 1 0 1 + 3 "1, 2, NULL" "1, 2, 3" {} {} 0 1 + 4 "1, 2, NULL" "1, 2, NULL" {} {} 1 0 + 5 "NULL, NULL, NULL" "NULL, NULL, NULL" {} {} 1 0 +} { + do_execsql_test 1.$tn.eq "SELECT ($v1) == ($v2)" [list $eq] + do_execsql_test 1.$tn.ne "SELECT ($v1) != ($v2)" [list $ne] + + do_execsql_test 1.$tn.is "SELECT ($v1) IS ($v2)" [list $is] + do_execsql_test 1.$tn.isnot "SELECT ($v1) IS NOT ($v2)" [list $isnot] + + do_execsql_test 1.$tn.2.eq "SELECT (SELECT $v1) == (SELECT $v2)" [list $eq] + do_execsql_test 1.$tn.2.ne "SELECT (SELECT $v1) != (SELECT $v2)" [list $ne] +} + +foreach {tn v1 v2 lt gt le ge} { + 1 "(1, 1, 3)" "(1, 2, 3)" 1 0 1 0 + 2 "(1, 2, 3)" "(1, 2, 3)" 0 0 1 1 + 3 "(1, 3, 3)" "(1, 2, 3)" 0 1 0 1 + + 4 "(1, NULL, 3)" "(1, 2, 3)" {} {} {} {} + 5 "(1, 3, 3)" "(1, NULL, 3)" {} {} {} {} + 6 "(1, NULL, 3)" "(1, NULL, 3)" {} {} {} {} +} { + foreach {tn2 expr res} [list \ + 2.$tn.lt "$v1 < $v2" $lt \ + 2.$tn.gt "$v1 > $v2" $gt \ + 2.$tn.le "$v1 <= $v2" $le \ + 2.$tn.ge "$v1 >= $v2" $ge \ + ] { + do_execsql_test $tn2 "SELECT $expr" [list $res] + + set map(0) [list] + set map() [list] + set map(1) [list 1] + do_execsql_test $tn2.where1 "SELECT * FROM one WHERE $expr" $map($res) + + set map(0) [list 1] + set map() [list] + set map(1) [list] + do_execsql_test $tn2.where2 "SELECT * FROM one WHERE NOT $expr" $map($res) + } +} + +do_execsql_test 3.0 { + CREATE TABLE t1(x, y); + INSERT INTO t1 VALUES(1, 1); + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t1 VALUES(2, 3); + INSERT INTO t1 VALUES(2, 4); + INSERT INTO t1 VALUES(3, 5); + INSERT INTO t1 VALUES(3, 6); +} + +foreach {tn r order} { + 1 "(1, 1)" "ORDER BY y" + 2 "(1, 1)" "ORDER BY x, y" + 3 "(1, 2)" "ORDER BY x, y DESC" + 4 "(3, 6)" "ORDER BY x DESC, y DESC" + 5 "((3, 5))" "ORDER BY x DESC, y" + 6 "(SELECT 3, 5)" "ORDER BY x DESC, y" +} { + do_execsql_test 3.$tn.1 "SELECT $r == (SELECT x,y FROM t1 $order)" 1 + do_execsql_test 3.$tn.2 "SELECT $r == (SELECT * FROM t1 $order)" 1 + + do_execsql_test 3.$tn.3 " + SELECT (SELECT * FROM t1 $order) == (SELECT * FROM t1 $order) + " 1 + do_execsql_test 3.$tn.4 " + SELECT (SELECT 0, 0) == (SELECT * FROM t1 $order) + " 0 +} + +foreach {tn expr res} { + 1 {(2, 2) BETWEEN (2, 2) AND (3, 3)} 1 + 2 {(2, 2) BETWEEN (2, NULL) AND (3, 3)} {} + 3 {(2, 2) BETWEEN (3, NULL) AND (3, 3)} 0 +} { + do_execsql_test 4.$tn "SELECT $expr" [list $res] +} + +foreach {tn expr res} { + 1 {(2, 4) IN (SELECT * FROM t1)} 1 + 2 {(3, 4) IN (SELECT * FROM t1)} 0 + + 3 {(NULL, 4) IN (SELECT * FROM t1)} {} + 4 {(NULL, 0) IN (SELECT * FROM t1)} 0 + + 5 {(NULL, 4) NOT IN (SELECT * FROM t1)} {} + 6 {(NULL, 0) NOT IN (SELECT * FROM t1)} 1 +} { + do_execsql_test 5.$tn "SELECT $expr" [list $res] +} + +finish_test + + diff --git a/test/rowvalue2.test b/test/rowvalue2.test new file mode 100644 index 0000000000..c7d061e3e2 --- /dev/null +++ b/test/rowvalue2.test @@ -0,0 +1,252 @@ +# 2016 June 17 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing the SELECT statement. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix rowvalue2 + +do_execsql_test 1.0 { + CREATE TABLE t1(a, b, c); + INSERT INTO t1 VALUES(0, 0, 0); + INSERT INTO t1 VALUES(0, 1, 1); + INSERT INTO t1 VALUES(1, 0, 2); + INSERT INTO t1 VALUES(1, 1, 3); + + CREATE INDEX i1 ON t1(a, b); +} + +do_execsql_test 1.1.1 { SELECT c FROM t1 WHERE (a, b) >= (1, 0) } {2 3} +do_execsql_test 1.1.2 { SELECT c FROM t1 WHERE (a, b) > (1, 0) } {3} + +#------------------------------------------------------------------------- + +do_execsql_test 2.0.1 { + CREATE TABLE t2(a INTEGER, b INTEGER, c INTEGER, d INTEGER); + CREATE INDEX i2 ON t2(a, b, c); +} +do_test 2.0.2 { + foreach a {0 1 2 3} { + foreach b {0 1 2 3} { + foreach c {0 1 2 3} { + execsql { INSERT INTO t2 VALUES($a, $b, $c, $c + $b*4 + $a*16); } + }}} +} {} + +do_execsql_test 2.1 { + SELECT d FROM t2 WHERE (a, b) > (2, 2); +} [db eval { SELECT d FROM t2 WHERE a>2 OR (a=2 AND b>2) }] + +do_execsql_test 2.2 { + SELECT d FROM t2 WHERE (a, b) >= (2, 2); +} [db eval { SELECT d FROM t2 WHERE a>2 OR (a=2 AND b>=2) }] + +do_execsql_test 2.3 { + SELECT d FROM t2 WHERE a=1 AND (b, c) >= (1, 2); +} [db eval { SELECT d FROM t2 WHERE +a=1 AND (b>1 OR (b==1 AND c>=2)) }] + +do_execsql_test 2.4 { + SELECT d FROM t2 WHERE a=1 AND (b, c) > (1, 2); +} [db eval { SELECT d FROM t2 WHERE +a=1 AND (b>1 OR (b==1 AND c>2)) }] + +#------------------------------------------------------------------------- + +set words { +airfare airfield airfields airflow airfoil +airfoils airframe airframes airily airing +airings airless airlift airlifts airline +airliner airlines airlock airlocks airmail +airmails airman airmen airplane airplanes + +arraignment arraignments arraigns arrange arranged +arrangement arrangements arranger arrangers arranges +arranging arrant array arrayed arrays +arrears arrest arrested arrester arresters +arresting arrestingly arrestor arrestors arrests + +edifices edit edited editing edition +editions editor editorial editorially editorials +editors edits educable educate educated +educates educating education educational educationally +educations educator educators eel eelgrass +} + +do_test 3.0 { + execsql { CREATE TABLE t3(a, b, c, w); } + foreach w $words { + set a [string range $w 0 2] + set b [string range $w 3 5] + set c [string range $w 6 end] + execsql { INSERT INTO t3 VALUES($a, $b, $c, $w) } + } +} {} + + +foreach {tn idx} { + IDX1 {} + IDX2 { CREATE INDEX i3 ON t3(a, b, c); } + IDX3 { CREATE INDEX i3 ON t3(a, b); } + IDX4 { CREATE INDEX i3 ON t3(a); } +} { + execsql { DROP INDEX IF EXISTS i3 } + execsql $idx + + foreach w $words { + set a [string range $w 0 2] + set b [string range $w 3 5] + set c [string range $w 6 end] + + foreach op [list > >= < <= == IS] { + do_execsql_test 3.1.$tn.$w.$op [subst -novar { + SELECT rowid FROM t3 WHERE (a, b, c) [set op] ($a, $b, $c) + ORDER BY +rowid + }] [db eval [subst -novar { + SELECT rowid FROM t3 WHERE w [set op] $w ORDER BY +rowid + }]] + + do_execsql_test 3.1.$tn.$w.$op.subselect [subst -novar { + SELECT rowid FROM t3 WHERE (a, b, c) [set op] ( + SELECT a, b, c FROM t3 WHERE w = $w + ) + ORDER BY +rowid + }] [db eval [subst -novar { + SELECT rowid FROM t3 WHERE w [set op] $w ORDER BY +rowid + }]] + } + + } +} + +#------------------------------------------------------------------------- +# + +do_execsql_test 4.0 { + CREATE TABLE t4(a, b, c); + INSERT INTO t4 VALUES(NULL, NULL, NULL); + INSERT INTO t4 VALUES(NULL, NULL, 0); + INSERT INTO t4 VALUES(NULL, NULL, 1); + INSERT INTO t4 VALUES(NULL, 0, NULL); + INSERT INTO t4 VALUES(NULL, 0, 0); + INSERT INTO t4 VALUES(NULL, 0, 1); + INSERT INTO t4 VALUES(NULL, 1, NULL); + INSERT INTO t4 VALUES(NULL, 1, 0); + INSERT INTO t4 VALUES(NULL, 1, 1); + + INSERT INTO t4 VALUES( 0, NULL, NULL); + INSERT INTO t4 VALUES( 0, NULL, 0); + INSERT INTO t4 VALUES( 0, NULL, 1); + INSERT INTO t4 VALUES( 0, 0, NULL); + INSERT INTO t4 VALUES( 0, 0, 0); + INSERT INTO t4 VALUES( 0, 0, 1); + INSERT INTO t4 VALUES( 0, 1, NULL); + INSERT INTO t4 VALUES( 0, 1, 0); + INSERT INTO t4 VALUES( 0, 1, 1); + + INSERT INTO t4 VALUES( 1, NULL, NULL); + INSERT INTO t4 VALUES( 1, NULL, 0); + INSERT INTO t4 VALUES( 1, NULL, 1); + INSERT INTO t4 VALUES( 1, 0, NULL); + INSERT INTO t4 VALUES( 1, 0, 0); + INSERT INTO t4 VALUES( 1, 0, 1); + INSERT INTO t4 VALUES( 1, 1, NULL); + INSERT INTO t4 VALUES( 1, 1, 0); + INSERT INTO t4 VALUES( 1, 1, 1); +} + +proc make_expr1 {cList vList op} { + return "([join $cList ,]) $op ([join $vList ,])" +} + +proc make_expr3 {cList vList op} { + set n [llength $cList] + + set aList [list] + foreach c [lrange $cList 0 end-1] v [lrange $vList 0 end-1] { + lappend aList "$c == $v" + } + lappend aList "[lindex $cList end] $op [lindex $vList end]" + + return "([join $aList { AND }])" +} + +proc make_expr2 {cList vList op} { + set ret "" + + switch -- $op { + == - IS { + set aList [list] + foreach c $cList v $vList { lappend aList "($c $op $v)" } + set ret [join $aList " AND "] + } + + < - > { + set oList [list] + for {set i 0} {$i < [llength $cList]} {incr i} { + lappend oList [make_expr3 [lrange $cList 0 $i] [lrange $vList 0 $i] $op] + } + set ret [join $oList " OR "] + } + + <= - >= { + set o2 [string range $op 0 0] + set oList [list] + for {set i 0} {$i < [llength $cList]-1} {incr i} { + lappend oList [make_expr3 [lrange $cList 0 $i] [lrange $vList 0 $i] $o2] + } + lappend oList [make_expr3 $cList $vList $op] + set ret [join $oList " OR "] + } + + + default { + error "Unknown op: $op" + } + } + + set ret +} + +foreach {tn idx} { + IDX1 {} + IDX2 { CREATE INDEX i4 ON t4(a, b, c); } + IDX3 { CREATE INDEX i4 ON t4(a, b); } + IDX4 { CREATE INDEX i4 ON t4(a); } +} { + execsql { DROP INDEX IF EXISTS i4 } + execsql $idx + + foreach {tn2 vector} { + 1 {0 0 0} + 2 {1 1 1} + 3 {0 0 NULL} + 4 {0 NULL 0} + 5 {NULL 0 0} + 6 {1 1 NULL} + 7 {1 NULL 1} + 8 {NULL 1 1} + } { + foreach op { IS == < <= > >= } { + set e1 [make_expr1 {a b c} $vector $op] + set e2 [make_expr2 {a b c} $vector $op] + + do_execsql_test 4.$tn.$tn2.$op \ + "SELECT rowid FROM t4 WHERE $e2 ORDER BY +rowid" [ + db eval "SELECT rowid FROM t4 WHERE $e1 ORDER BY +rowid" + ] + } + } +} + + +finish_test + diff --git a/tool/addopcodes.tcl b/tool/addopcodes.tcl index 9b34cf2e03..29409a4900 100644 --- a/tool/addopcodes.tcl +++ b/tool/addopcodes.tcl @@ -37,6 +37,7 @@ set extras { UMINUS UPLUS REGISTER + SELECT_COLUMN ASTERISK SPAN SPACE From cfbb5e82db9f7af586883ea886a927ac5460924a Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 13 Jul 2016 19:48:13 +0000 Subject: [PATCH 02/97] Modifications towards better vector IN(...) support on this branch. Not activated yet. FossilOrigin-Name: 34e35c71b25b0aa2d8931040feb260a78cc48c49 --- manifest | 14 +++---- manifest.uuid | 2 +- src/expr.c | 105 ++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 84 insertions(+), 37 deletions(-) diff --git a/manifest b/manifest index 43f53685aa..c71b40dd0d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\scomment\stypo\sfixes\sfrom\strunk. -D 2016-07-10T19:35:45.597 +C Modifications\stowards\sbetter\svector\sIN(...)\ssupport\son\sthis\sbranch.\sNot\sactivated\syet. +D 2016-07-13T19:48:13.115 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 330854fe9fdea1d244abaef6d680f6b91df07cb4 +F src/expr.c 939362d26f5e99a4802ae94ae6e47d4def72b8f3 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c bc4145347595b7770f9a598cff1c848302cf5413 F src/func.c 61a4114cf7004f10c542cfabbab9f2bcb9033045 @@ -1507,7 +1507,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 b2204215b231202aef7a218411cc2ddaecf28f35 77c692a6704cd877ba35d0afb774ab9b46364d59 -R 1a7620cc6269220b48a9fcbcbb9b68d1 -U mistachkin -Z cf1dd7dc395b0586dda9fa40ccf71a27 +P 728c5aa436a5f55c86b019c415a2b71d1b0a8fd6 +R 6c0b20cb1573f5c43e78ae5a07a06597 +U dan +Z 35590bc4128842477cd3208768d4edc0 diff --git a/manifest.uuid b/manifest.uuid index b148ca6043..722c9bb7cc 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -728c5aa436a5f55c86b019c415a2b71d1b0a8fd6 \ No newline at end of file +34e35c71b25b0aa2d8931040feb260a78cc48c49 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 7941c228fd..c43c6c04de 100644 --- a/src/expr.c +++ b/src/expr.c @@ -309,6 +309,12 @@ static int codeCompare( return addr; } +/* +** If the expression passed as the only argument is of type TK_VECTOR +** return the number of expressions in the vector. Or, if the expression +** is a sub-select, return the number of columns in the sub-select. For +** any other type of expression, return 1. +*/ int sqlite3ExprVectorSize(Expr *pExpr){ if( (pExpr->flags & EP_Vector)==0 ) return 1; if( pExpr->flags & EP_xIsSelect ){ @@ -318,7 +324,10 @@ int sqlite3ExprVectorSize(Expr *pExpr){ } static Expr *exprVectorField(Expr *pVector, int i){ - if( pVector->flags & EP_xIsSelect ){ + if( (pVector->flags & EP_Vector)==0 ){ + assert( i==0 ); + return pVector; + }else if( pVector->flags & EP_xIsSelect ){ return pVector->x.pSelect->pEList->a[i].pExpr; } return pVector->x.pList->a[i].pExpr; @@ -1703,12 +1712,12 @@ int sqlite3IsRowid(const char *z){ ** table, then return NULL. */ #ifndef SQLITE_OMIT_SUBQUERY -static Select *isCandidateForInOpt(Expr *pX){ +static Select *isCandidateForInOpt(Expr *pX, int bNullSensitive){ Select *p; SrcList *pSrc; ExprList *pEList; - Expr *pRes; Table *pTab; + int i; if( !ExprHasProperty(pX, EP_xIsSelect) ) return 0; /* Not a subquery */ if( ExprHasProperty(pX, EP_VarSelect) ) return 0; /* Correlated subq */ p = pX->x.pSelect; @@ -1731,10 +1740,18 @@ static Select *isCandidateForInOpt(Expr *pX){ assert( pTab->pSelect==0 ); /* FROM clause is not a view */ if( IsVirtual(pTab) ) return 0; /* FROM clause not a virtual table */ pEList = p->pEList; - if( pEList->nExpr!=1 ) return 0; /* One column in the result set */ - pRes = pEList->a[0].pExpr; - if( pRes->op!=TK_COLUMN ) return 0; /* Result is a column */ - assert( pRes->iTable==pSrc->a[0].iCursor ); /* Not a correlated subquery */ + + /* All SELECT results must be columns. If the SELECT returns more than + ** one column and the bNullSensitive flag is set, all returned columns + ** must be declared NOT NULL. */ + for(i=0; inExpr; i++){ + Expr *pRes = pEList->a[i].pExpr; + if( pRes->op!=TK_COLUMN ) return 0; + assert( pRes->iTable==pSrc->a[0].iCursor ); /* Not a correlated subquery */ + if( pEList->nExpr>1 && bNullSensitive ){ + if( pTab->aCol[pRes->iColumn].notNull==0 ) return 0; + } + } return p; } #endif /* SQLITE_OMIT_SUBQUERY */ @@ -1867,20 +1884,18 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int *prRhsHasNull){ ** satisfy the query. This is preferable to generating a new ** ephemeral table. */ - if( pParse->nErr==0 && (p = isCandidateForInOpt(pX))!=0 ){ + if( pParse->nErr==0 && (p = isCandidateForInOpt(pX, prRhsHasNull!=0))!=0 ){ sqlite3 *db = pParse->db; /* Database connection */ Table *pTab; /* Table . */ - Expr *pExpr; /* Expression */ - i16 iCol; /* Index of column */ + ExprList *pEList = p->pEList; + int nExpr = pEList->nExpr; i16 iDb; /* Database idx for pTab */ assert( p->pEList!=0 ); /* Because of isCandidateForInOpt(p) */ assert( p->pEList->a[0].pExpr!=0 ); /* Because of isCandidateForInOpt(p) */ assert( p->pSrc!=0 ); /* Because of isCandidateForInOpt(p) */ pTab = p->pSrc->a[0].pTab; - pExpr = p->pEList->a[0].pExpr; - iCol = (i16)pExpr->iColumn; - + /* Code an OP_Transaction and OP_TableLock for
. */ iDb = sqlite3SchemaToIndex(db, pTab->pSchema); sqlite3CodeVerifySchema(pParse, iDb); @@ -1891,7 +1906,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int *prRhsHasNull){ ** successful here. */ assert(v); - if( iCol<0 ){ + if( nExpr==1 && pEList->a[0].pExpr->iColumn<0 ){ int iAddr = sqlite3CodeOnce(pParse); VdbeCoverage(v); @@ -1901,23 +1916,54 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int *prRhsHasNull){ sqlite3VdbeJumpHere(v, iAddr); }else{ Index *pIdx; /* Iterator variable */ + int affinity_ok = 1; + int i; + + /* Check that the affinity that will be used to perform each + ** comparison is the same as the affinity of each column. If + ** it not, it is not possible to use any index. */ + for(i=0; ipLeft, i); + int iCol = pEList->a[i].pExpr->iColumn; + char idxaff = pTab->aCol[iCol].affinity; + char cmpaff = sqlite3CompareAffinity(pLhs, idxaff); + switch( cmpaff ){ + case SQLITE_AFF_BLOB: + break; + case SQLITE_AFF_TEXT: + affinity_ok = (idxaff==SQLITE_AFF_TEXT); + break; + default: + affinity_ok = sqlite3IsNumericAffinity(idxaff); + } + } /* The collation sequence used by the comparison. If an index is to ** be used in place of a temp-table, it must be ordered according ** to this collation sequence. */ - CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pExpr); - - /* Check that the affinity that will be used to perform the - ** comparison is the same as the affinity of the column. If - ** it is not, it is not possible to use any index. - */ - int affinity_ok = sqlite3IndexAffinityOk(pX, pTab->aCol[iCol].affinity); for(pIdx=pTab->pIndex; pIdx && eType==0 && affinity_ok; pIdx=pIdx->pNext){ - if( (pIdx->aiColumn[0]==iCol) - && sqlite3FindCollSeq(db, ENC(db), pIdx->azColl[0], 0)==pReq - && (!mustBeUnique || (pIdx->nKeyCol==1 && IsUniqueIndex(pIdx))) - ){ + if( pIdx->nKeyColnKeyCol!=nExpr || !IsUniqueIndex(pIdx)) ){ + continue; + } + + for(i=0; ipLeft, i); + Expr *pRhs = pEList->a[i].pExpr; + CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs); + int j; + + for(j=0; jaiColumn[j]!=pRhs->iColumn ) continue; + assert( pIdx->azColl[j] ); + if( sqlite3StrICmp(pReq->zName, pIdx->azColl[j])!=0 ) continue; + break; + } + if( j==nExpr ) break; + } + + if( i==nExpr ){ int iAddr = sqlite3CodeOnce(pParse); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_OpenRead, iTab, pIdx->tnum, iDb); sqlite3VdbeSetP4KeyInfo(pParse, pIdx); @@ -1925,11 +1971,13 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int *prRhsHasNull){ assert( IN_INDEX_INDEX_DESC == IN_INDEX_INDEX_ASC+1 ); eType = IN_INDEX_INDEX_ASC + pIdx->aSortOrder[0]; - if( prRhsHasNull && !pTab->aCol[iCol].notNull ){ + if( prRhsHasNull && nExpr==1 + && !pTab->aCol[pEList->a[0].pExpr->iColumn].notNull + ){ #ifdef SQLITE_ENABLE_COLUMN_USED_MASK - const i64 sOne = 1; + i64 mask = (1<nMem; sqlite3SetHasNullFlag(v, iTab, *prRhsHasNull); @@ -1954,7 +2002,6 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int *prRhsHasNull){ ){ eType = IN_INDEX_NOOP; } - if( eType==0 ){ /* Could not find an existing table or index to use as the RHS b-tree. From ba00e30a3a55e3800ce808373d9604983a4d1df5 Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 23 Jul 2016 20:24:06 +0000 Subject: [PATCH 03/97] Allow vector IN(SELECT ...) expressions to use an index if either all the indexed columns are declared NOT NULL or if there is no difference between the expression evaluating to 0 and NULL (as in a WHERE clause). FossilOrigin-Name: e2fd6f49b1b145bec09c581cc982b89429643ae9 --- manifest | 17 ++-- manifest.uuid | 2 +- src/expr.c | 217 +++++++++++++++++++++++++------------------- src/sqliteInt.h | 2 +- src/wherecode.c | 2 +- test/rowvalue3.test | 52 +++++++++++ 6 files changed, 188 insertions(+), 104 deletions(-) create mode 100644 test/rowvalue3.test diff --git a/manifest b/manifest index 65a4d1d413..52753bfb10 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\slatest\strunk\schanges\swith\sthis\sbranch. -D 2016-07-22T17:58:05.771 +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 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 939362d26f5e99a4802ae94ae6e47d4def72b8f3 +F src/expr.c 8ff9d70cc2077020327d1fa551558bb03e267da4 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c bc4145347595b7770f9a598cff1c848302cf5413 F src/func.c 61a4114cf7004f10c542cfabbab9f2bcb9033045 @@ -388,7 +388,7 @@ F src/shell.c a8a9e392a6a2777fabf5feb536931cb190f235e5 F src/sqlite.h.in b9ba728c1083b7a8ab5f6a628b25cd2a00325fbf F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 2a170163d121095c6ab1ef05ed0413722f391d01 -F src/sqliteInt.h dd2dd1d880ffd33137d20dc6da21f169836b8f5a +F src/sqliteInt.h c4877fb0519c13558d18d08775bc8e79476cb56c F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247 F src/status.c 5b18f9526900f61189ab0b83f1ef41d9f871a2ab F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9 @@ -465,7 +465,7 @@ F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354 F src/where.c 898a45969bae1298cbaaaf05e6aeacfb45971293 F src/whereInt.h 1ad3be2a43cb821418e1100476a3a14cd57635c4 -F src/wherecode.c 877ceb19cf00a5fd5aeea4e3ff633dcdf173f164 +F src/wherecode.c eb0f5e8700afb110cb96fb873c0e9a015a9f63ff F src/whereexpr.c d88ee6ce356cb9fd986d0e81249a2cd66a513093 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd @@ -1019,6 +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/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09 F test/savepoint.test c671fdbd34cd3bfe1518a777526ada595180cf8d @@ -1508,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 34e35c71b25b0aa2d8931040feb260a78cc48c49 87e25fc472604b3978811be53991104c665a95e7 -R 2fbaab60c614a16797a19958a11968ca +P 60fed5cdd4a44aefa1b4d505adeb7936f2f0b952 +R 1dc4c60c16780168f167fe34d6f77ee9 U dan -Z c65487e55e425163e0e854585a16b213 +Z f82474a4f9b8fbec7649c9e9d834d395 diff --git a/manifest.uuid b/manifest.uuid index 9342dcf6ce..1150e4a104 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -60fed5cdd4a44aefa1b4d505adeb7936f2f0b952 \ No newline at end of file +e2fd6f49b1b145bec09c581cc982b89429643ae9 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index c43c6c04de..5792073228 100644 --- a/src/expr.c +++ b/src/expr.c @@ -323,6 +323,12 @@ int sqlite3ExprVectorSize(Expr *pExpr){ return pExpr->x.pList->nExpr; } +/* +** If the expression passed as the first argument is a TK_VECTOR, return +** a pointer to the i'th field of the vector. Or, if the first argument +** points to a sub-select, return a pointer to the i'th returned column +** value. Otherwise, return a copy of the first argument. +*/ static Expr *exprVectorField(Expr *pVector, int i){ if( (pVector->flags & EP_Vector)==0 ){ assert( i==0 ); @@ -1710,6 +1716,13 @@ int sqlite3IsRowid(const char *z){ ** a pointer to the SELECT statement. If pX is not a SELECT statement, ** or if the SELECT statement needs to be manifested into a transient ** table, then return NULL. +** +** If parameter bNullSensitive is 0, then this operation will be +** used in a context in which there is no difference between a result +** of 0 and one of NULL. For example: +** +** ... WHERE (?,?) IN (SELECT ...) +** */ #ifndef SQLITE_OMIT_SUBQUERY static Select *isCandidateForInOpt(Expr *pX, int bNullSensitive){ @@ -1870,7 +1883,13 @@ static int sqlite3InRhsIsConstant(Expr *pIn){ ** NULL values. */ #ifndef SQLITE_OMIT_SUBQUERY -int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int *prRhsHasNull){ +int sqlite3FindInIndex( + Parse *pParse, + Expr *pX, + u32 inFlags, + int *prRhsHasNull, + int *aiMap +){ Select *p; /* SELECT to the right of IN operator */ int eType = 0; /* Type of RHS table. IN_INDEX_* */ int iTab = pParse->nTab++; /* Cursor of the RHS table */ @@ -1887,9 +1906,9 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int *prRhsHasNull){ if( pParse->nErr==0 && (p = isCandidateForInOpt(pX, prRhsHasNull!=0))!=0 ){ sqlite3 *db = pParse->db; /* Database connection */ Table *pTab; /* Table
. */ + i16 iDb; /* Database idx for pTab */ ExprList *pEList = p->pEList; int nExpr = pEList->nExpr; - i16 iDb; /* Database idx for pTab */ assert( p->pEList!=0 ); /* Because of isCandidateForInOpt(p) */ assert( p->pEList->a[0].pExpr!=0 ); /* Because of isCandidateForInOpt(p) */ @@ -1961,6 +1980,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int *prRhsHasNull){ break; } if( j==nExpr ) break; + if( aiMap ) aiMap[i] = j; } if( i==nExpr ){ @@ -2023,6 +2043,12 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int *prRhsHasNull){ }else{ pX->iTable = iTab; } + + if( aiMap && eType!=IN_INDEX_INDEX_ASC && eType!=IN_INDEX_INDEX_DESC ){ + int i, n; + n = sqlite3ExprVectorSize(pX->pLeft); + for(i=0; ipLeft); - - Expr *pLeft = pExpr->pLeft; - Vdbe *v = pParse->pVdbe; - - /* Code the LHS, the from " IN (...)". Leave the results in - ** an array of nVal registers starting at r1. */ - sqlite3ExprCachePush(pParse); - if( pLeft->flags & EP_xIsSelect ){ - r1 = sqlite3CodeSubselect(pParse, pLeft, 0, 0); - }else{ - r1 = pParse->nMem + 1; - pParse->nMem += nVal; - sqlite3ExprCodeExprList(pParse, pLeft->x.pList, r1, 0, 0); - } - - /* Generate an epheremal index containing the contents of the SELECT - ** to the right of the " IN (SELECT ...)" expression. The cursor - ** number for the epheremal table is left in pExpr->iTable. */ - assert( pExpr->flags & EP_xIsSelect ); - sqlite3CodeSubselect(pParse, pExpr, 0, 0); - - sqlite3VdbeAddOp2(v, OP_Integer, 0, regResult); - - /* Iterate through the ephemeral table just populated */ - addrNext = 1 + sqlite3VdbeAddOp2(v, OP_Rewind, pExpr->iTable, destIfFalse); - for(i=0; iiTable, i, r2); - sqlite3VdbeAddOp4(v, OP_Eq, r1+i, i==0?r3:r4, r2, (void*)pColl,P4_COLLSEQ); - sqlite3VdbeChangeP5(v, SQLITE_STOREP2); - VdbeCoverage(v); - if( i!=0 ){ - sqlite3VdbeAddOp3(v, OP_And, r3, r4, r4); - } - } - sqlite3VdbeAddOp2(v, OP_If, r4, sqlite3VdbeCurrentAddr(v)+6); - sqlite3VdbeAddOp2(v, OP_IfNot, r4, sqlite3VdbeCurrentAddr(v)+2); - sqlite3VdbeAddOp2(v, OP_Null, 0, regResult); - sqlite3VdbeAddOp2(v, OP_Next, pExpr->iTable, addrNext); - sqlite3VdbeAddOp3(v, OP_If, regResult, destIfNull, 1); - sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfFalse); - - sqlite3ReleaseTempReg(pParse, r2); - sqlite3ReleaseTempReg(pParse, r3); - sqlite3ReleaseTempReg(pParse, r4); - sqlite3ReleaseTempReg(pParse, regResult); - sqlite3ExprCachePop(pParse); -} -#endif - #ifndef SQLITE_OMIT_SUBQUERY /* ** Generate code for an IN expression. @@ -2403,36 +2360,64 @@ static void sqlite3ExprCodeIN( int destIfNull /* Jump here if the results are unknown due to NULLs */ ){ int rRhsHasNull = 0; /* Register that is true if RHS contains NULL values */ - char affinity; /* Comparison affinity to use */ int eType; /* Type of the RHS */ int r1; /* Temporary use register */ Vdbe *v; /* Statement under construction */ + int *aiMap = 0; /* Map from vector field to index column */ + char *zAff = 0; /* Affinity string for comparisons */ + int nVector; /* Size of vectors for this IN(...) op */ + int regSelect = 0; + Expr *pLeft = pExpr->pLeft; + int i; - if( pExpr->pLeft->flags & EP_Vector ){ - return exprCodeVectorIN(pParse, pExpr, destIfFalse, destIfNull); - } + nVector = sqlite3ExprVectorSize(pExpr->pLeft); + aiMap = (int*)sqlite3DbMallocZero( + pParse->db, nVector*(sizeof(int) + sizeof(char)) + 1 + ); + if( !aiMap ) return; + zAff = (char*)&aiMap[nVector]; - /* Compute the RHS. After this step, the table with cursor - ** pExpr->iTable will contains the values that make up the RHS. - */ + /* Attempt to compute the RHS. After this step, if anything other than + ** IN_INDEX_NOOP is returned, the table opened ith cursor pExpr->iTable + ** contains the values that make up the RHS. If IN_INDEX_NOOP is returned, + ** the RHS has not yet been coded. */ v = pParse->pVdbe; assert( v!=0 ); /* OOM detected prior to this routine */ VdbeNoopComment((v, "begin IN expr")); eType = sqlite3FindInIndex(pParse, pExpr, IN_INDEX_MEMBERSHIP | IN_INDEX_NOOP_OK, - destIfFalse==destIfNull ? 0 : &rRhsHasNull); + destIfFalse==destIfNull ? 0 : &rRhsHasNull, aiMap); - /* Figure out the affinity to use to create a key from the results - ** of the expression. affinityStr stores a static string suitable for - ** P4 of OP_MakeRecord. - */ - affinity = comparisonAffinity(pExpr); + assert( pParse->nErr || nVector==1 || eType==IN_INDEX_EPH + || eType==IN_INDEX_INDEX_ASC || eType==IN_INDEX_INDEX_DESC + ); - /* Code the LHS, the from " IN (...)". + /* Code the LHS, the from " IN (...)". If the LHS is a + ** vector, then it is stored in an array of nVector registers starting + ** at r1. */ + r1 = sqlite3GetTempRange(pParse, nVector); sqlite3ExprCachePush(pParse); - r1 = sqlite3GetTempReg(pParse); - sqlite3ExprCode(pParse, pExpr->pLeft, r1); + if( nVector>1 && (pLeft->flags & EP_xIsSelect) ){ + regSelect = sqlite3CodeSubselect(pParse, pLeft, 0, 0); + } + for(i=0; iflags & EP_xIsSelect ){ + zAff[iCol] = sqlite3CompareAffinity( + pExpr->x.pSelect->pEList->a[iCol].pExpr, zAff[iCol] + ); + } + } /* If sqlite3FindInIndex() did not find or create an index that is ** suitable for evaluating the IN operator, then evaluate using a @@ -2460,12 +2445,12 @@ static void sqlite3ExprCodeIN( (void*)pColl, P4_COLLSEQ); VdbeCoverageIf(v, iinExpr-1); VdbeCoverageIf(v, ii==pList->nExpr-1); - sqlite3VdbeChangeP5(v, affinity); + sqlite3VdbeChangeP5(v, zAff[0]); }else{ assert( destIfNull==destIfFalse ); sqlite3VdbeAddOp4(v, OP_Ne, r1, destIfFalse, r2, (void*)pColl, P4_COLLSEQ); VdbeCoverage(v); - sqlite3VdbeChangeP5(v, affinity | SQLITE_JUMPIFNULL); + sqlite3VdbeChangeP5(v, zAff[0] | SQLITE_JUMPIFNULL); } sqlite3ReleaseTempReg(pParse, regToFree); } @@ -2480,7 +2465,7 @@ static void sqlite3ExprCodeIN( /* If the LHS is NULL, then the result is either false or NULL depending ** on whether the RHS is empty or not, respectively. */ - if( sqlite3ExprCanBeNull(pExpr->pLeft) ){ + if( nVector==1 && sqlite3ExprCanBeNull(pExpr->pLeft) ){ if( destIfNull==destIfFalse ){ /* Shortcut for the common case where the false and NULL outcomes are ** the same. */ @@ -2499,10 +2484,50 @@ static void sqlite3ExprCodeIN( */ sqlite3VdbeAddOp3(v, OP_SeekRowid, pExpr->iTable, destIfFalse, r1); VdbeCoverage(v); + }else if( nVector>1 && eType==IN_INDEX_EPH ){ + int regNull = sqlite3GetTempReg(pParse); + int r2 = sqlite3GetTempReg(pParse); + int r3 = sqlite3GetTempReg(pParse); + int r4 = sqlite3GetTempReg(pParse); + int addrNext; + int addrIf; + + if( destIfFalse!=destIfNull ){ + sqlite3VdbeAddOp2(v, OP_Integer, 0, regNull); + } + addrNext = sqlite3VdbeAddOp2(v, OP_Rewind, pExpr->iTable, destIfFalse); + for(i=0; iiTable, i, r2); + sqlite3VdbeAddOp4(v, OP_Eq, r1+i, i?r3:r4, r2, (void*)pColl,P4_COLLSEQ); + sqlite3VdbeChangeP5(v, SQLITE_STOREP2); + if( i!=0 ){ + sqlite3VdbeAddOp3(v, OP_And, r3, r4, r4); + } + } + addrIf = sqlite3VdbeAddOp1(v, OP_If, r4); + if( destIfNull!=destIfFalse ){ + sqlite3VdbeAddOp2(v, OP_IfNot, r4, sqlite3VdbeCurrentAddr(v)+2); + sqlite3VdbeAddOp2(v, OP_Integer, 1, regNull); + } + sqlite3VdbeAddOp2(v, OP_Next, pExpr->iTable, addrNext+1); + if( destIfNull!=destIfFalse ){ + sqlite3VdbeAddOp2(v, OP_If, regNull, destIfNull); + } + sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfFalse); + sqlite3VdbeChangeP2(v, addrIf, sqlite3VdbeCurrentAddr(v)); + sqlite3ReleaseTempReg(pParse, regNull); + sqlite3ReleaseTempReg(pParse, r2); + sqlite3ReleaseTempReg(pParse, r3); + sqlite3ReleaseTempReg(pParse, r4); }else{ /* In this case, the RHS is an index b-tree. */ - sqlite3VdbeAddOp4(v, OP_Affinity, r1, 1, 0, &affinity, 1); + sqlite3VdbeAddOp4(v, OP_Affinity, r1, nVector, 0, zAff, nVector); /* If the set membership test fails, then the result of the ** "x IN (...)" expression must be either 0 or NULL. If the set @@ -2519,7 +2544,9 @@ static void sqlite3ExprCodeIN( ** Also run this branch if NULL is equivalent to FALSE ** for this particular IN operator. */ - sqlite3VdbeAddOp4Int(v, OP_NotFound, pExpr->iTable, destIfFalse, r1, 1); + sqlite3VdbeAddOp4Int( + v, OP_NotFound, pExpr->iTable, destIfFalse, r1, nVector + ); VdbeCoverage(v); }else{ /* In this branch, the RHS of the IN might contain a NULL and @@ -2545,6 +2572,7 @@ static void sqlite3ExprCodeIN( } sqlite3ReleaseTempReg(pParse, r1); sqlite3ExprCachePop(pParse); + sqlite3DbFree(pParse->db, aiMap); VdbeComment((v, "end IN expr")); } #endif /* SQLITE_OMIT_SUBQUERY */ @@ -4010,7 +4038,7 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ assert( pExpr->op!=TK_GT || op==OP_Le ); assert( pExpr->op!=TK_GE || op==OP_Lt ); - switch( pExpr->op | (pExpr->pLeft ? (pExpr->pLeft->flags & EP_Vector) : 0)){ + switch( pExpr->op ){ case TK_AND: { testcase( jumpIfNull==0 ); sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull); @@ -4047,6 +4075,8 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ case TK_GE: case TK_NE: case TK_EQ: { + if( pExpr->pLeft->flags & EP_Vector ) goto default_expr; + testcase( jumpIfNull==0 ); r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); @@ -4093,6 +4123,7 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ } #endif default: { + default_expr: if( exprAlwaysFalse(pExpr) ){ sqlite3VdbeGoto(v, dest); }else if( exprAlwaysTrue(pExpr) ){ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 3caa65d8e3..b8a326cfbc 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4134,7 +4134,7 @@ const char *sqlite3JournalModename(int); #define IN_INDEX_NOOP_OK 0x0001 /* OK to return IN_INDEX_NOOP */ #define IN_INDEX_MEMBERSHIP 0x0002 /* IN operator used for membership test */ #define IN_INDEX_LOOP 0x0004 /* IN operator used as a loop */ -int sqlite3FindInIndex(Parse *, Expr *, u32, int*); +int sqlite3FindInIndex(Parse *, Expr *, u32, int*, int*); int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int); int sqlite3JournalSize(sqlite3_vfs *); diff --git a/src/wherecode.c b/src/wherecode.c index e660b35387..0f6d320241 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -379,7 +379,7 @@ static int codeEqualityTerm( } assert( pX->op==TK_IN ); iReg = iTarget; - eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0); + eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0); if( eType==IN_INDEX_INDEX_DESC ){ testcase( bRev ); bRev = !bRev; diff --git a/test/rowvalue3.test b/test/rowvalue3.test new file mode 100644 index 0000000000..63f2c8939a --- /dev/null +++ b/test/rowvalue3.test @@ -0,0 +1,52 @@ +# 2016 June 17 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing "(...) IN (SELECT ...)" expressions +# where the SELECT statement returns more than one column. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix rowvalue3 + + +do_execsql_test 1.0 { + CREATE TABLE t1(a, b, c); + CREATE INDEX i1 ON t1(a, b); + INSERT INTO t1 VALUES(1, 2, 3); + INSERT INTO t1 VALUES(4, 5, 6); + INSERT INTO t1 VALUES(7, 8, 9); +} + +foreach {tn sql res} { + 1 "SELECT 1 WHERE (4, 5) IN (SELECT a, b FROM t1)" 1 + 2 "SELECT 1 WHERE (5, 5) IN (SELECT a, b FROM t1)" {} + 3 "SELECT 1 WHERE (5, 4) IN (SELECT a, b FROM t1)" {} + 4 "SELECT 1 WHERE (5, 4) IN (SELECT b, a FROM t1)" 1 + 5 "SELECT 1 WHERE (SELECT a, b FROM t1 WHERE c=6) IN (SELECT a, b FROM t1)" 1 + 6 "SELECT (5, 4) IN (SELECT a, b FROM t1)" 0 + 7 "SELECT 1 WHERE (5, 4) IN (SELECT +b, +a FROM t1)" 1 + 8 "SELECT (5, 4) IN (SELECT +b, +a FROM t1)" 1 + 9 "SELECT (1, 2) IN (SELECT rowid, b FROM t1)" 1 + 10 "SELECT 1 WHERE (1, 2) IN (SELECT rowid, b FROM t1)" 1 + 11 "SELECT 1 WHERE (1, NULL) IN (SELECT rowid, b FROM t1)" {} +} { + 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) } {} + + + + +finish_test + From 8da209b169b1ab73b70573de6d91fca6053852f2 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 26 Jul 2016 18:06:08 +0000 Subject: [PATCH 04/97] 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) } From 7b35a77b1a389d87cb5df356266b400e07d55afb Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 28 Jul 2016 19:47:15 +0000 Subject: [PATCH 05/97] Fix further issues with multi-column IN(...) operators. Also some error handling cases surrounding row values. FossilOrigin-Name: cc3f6542bec99b00d2698889bcea2aa0b587efa0 --- manifest | 15 +-- manifest.uuid | 2 +- src/expr.c | 218 ++++++++++++++++++++++++-------------------- src/whereexpr.c | 1 + test/rowvalue4.test | 48 ++++++++++ 5 files changed, 175 insertions(+), 109 deletions(-) create mode 100644 test/rowvalue4.test diff --git a/manifest b/manifest index 98dcf120d9..a1321d1be1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\slatest\strunk\schanges\sinto\sthis\sbranch. -D 2016-07-28T13:59:21.728 +C Fix\sfurther\sissues\swith\smulti-column\sIN(...)\soperators.\sAlso\ssome\serror\shandling\scases\ssurrounding\srow\svalues. +D 2016-07-28T19:47:15.803 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 82bc40bb27bc68b45303b560ab14eb16e39786a7 +F src/expr.c 473ce0ac25868b750f3bf9f59bbe7e8f06a0bdd5 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c bc4145347595b7770f9a598cff1c848302cf5413 F src/func.c 61a4114cf7004f10c542cfabbab9f2bcb9033045 @@ -466,7 +466,7 @@ F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354 F src/where.c df58d6ad7878a08aa96c652ccbc6d0949f8fa472 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 F src/wherecode.c 3aff7683566af3428f865904aafa7efb1fbd8701 -F src/whereexpr.c b896f8ff6a53cbd3daaee84ec33e39098762bb46 +F src/whereexpr.c bc85d04c6751ca40cab99a10de308e44893c76b9 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -1021,6 +1021,7 @@ F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d F test/rowvalue.test 979738b3d49f1d93e3fee56a71d4446217917abc F test/rowvalue2.test 8d5dfe75b8f4d1868a2f91f0356f20d36cba64ff F test/rowvalue3.test 587c1056016fa6b7a40a9bf6cb85d5443fa47d96 +F test/rowvalue4.test 4480898d62d6813e3e38d9d38c02b9a0be5f94be F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09 F test/savepoint.test c671fdbd34cd3bfe1518a777526ada595180cf8d @@ -1511,7 +1512,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 719a3b2035a335ca8b9704646b1d641011e3ea0e 6feff15cae8f0427be790355841d49c479c1c586 -R 3c287799720eaa100d938a8b5fd4645d +P 9685880f7baeb670739fdcf2d9df08e22abaa699 +R 66ab918b4b1a726fe3dea7b50bb8fdb0 U dan -Z 3b75e2ba5b9150b77f0261ae152b7ba4 +Z 5baa8fdab10ded1fdce6b30d35067148 diff --git a/manifest.uuid b/manifest.uuid index 88af2bcaa6..c951167b81 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9685880f7baeb670739fdcf2d9df08e22abaa699 \ No newline at end of file +cc3f6542bec99b00d2698889bcea2aa0b587efa0 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 26587309f2..2921898d35 100644 --- a/src/expr.c +++ b/src/expr.c @@ -1730,7 +1730,7 @@ int sqlite3IsRowid(const char *z){ ** */ #ifndef SQLITE_OMIT_SUBQUERY -static Select *isCandidateForInOpt(Expr *pX, int bNullSensitive){ +static Select *isCandidateForInOpt(Expr *pX){ Select *p; SrcList *pSrc; ExprList *pEList; @@ -1759,16 +1759,11 @@ static Select *isCandidateForInOpt(Expr *pX, int bNullSensitive){ if( IsVirtual(pTab) ) return 0; /* FROM clause not a virtual table */ pEList = p->pEList; - /* All SELECT results must be columns. If the SELECT returns more than - ** one column and the bNullSensitive flag is set, all returned columns - ** must be declared NOT NULL. */ + /* All SELECT results must be columns. */ for(i=0; inExpr; i++){ Expr *pRes = pEList->a[i].pExpr; if( pRes->op!=TK_COLUMN ) return 0; assert( pRes->iTable==pSrc->a[0].iCursor ); /* Not a correlated subquery */ - if( pEList->nExpr>1 && bNullSensitive ){ - if( pTab->aCol[pRes->iColumn].notNull==0 ) return 0; - } } return p; } @@ -1904,11 +1899,27 @@ int sqlite3FindInIndex( assert( pX->op==TK_IN ); mustBeUnique = (inFlags & IN_INDEX_LOOP)!=0; + /* If the RHS of this IN(...) operator is a SELECT, and if it matters + ** whether or not the SELECT result contains NULL values, check whether + ** or not NULL is actuall possible (it may not be, for example, due + ** to NOT NULL constraints in the schema). If no NULL values are possible, + ** set prRhsHasNull to 0 before continuing. + */ + if( prRhsHasNull && (pX->flags & EP_xIsSelect) ){ + int i; + ExprList *pEList = pX->x.pSelect->pEList; + for(i=0; inExpr; i++){ + if( sqlite3ExprCanBeNull(pEList->a[i].pExpr) ) break; + } + if( i==pEList->nExpr ){ + prRhsHasNull = 0; + } + } + /* Check to see if an existing table or index can be used to ** satisfy the query. This is preferable to generating a new - ** ephemeral table. - */ - if( pParse->nErr==0 && (p = isCandidateForInOpt(pX, prRhsHasNull!=0))!=0 ){ + ** ephemeral table. */ + if( pParse->nErr==0 && (p = isCandidateForInOpt(pX))!=0 ){ sqlite3 *db = pParse->db; /* Database connection */ Table *pTab; /* Table
. */ i16 iDb; /* Database idx for pTab */ @@ -1996,16 +2007,16 @@ int sqlite3FindInIndex( assert( IN_INDEX_INDEX_DESC == IN_INDEX_INDEX_ASC+1 ); eType = IN_INDEX_INDEX_ASC + pIdx->aSortOrder[0]; - if( prRhsHasNull && nExpr==1 - && !pTab->aCol[pEList->a[0].pExpr->iColumn].notNull - ){ + if( prRhsHasNull ){ + *prRhsHasNull = ++pParse->nMem; #ifdef SQLITE_ENABLE_COLUMN_USED_MASK i64 mask = (1<nMem; - sqlite3SetHasNullFlag(v, iTab, *prRhsHasNull); + if( nExpr==1 ){ + sqlite3SetHasNullFlag(v, iTab, *prRhsHasNull); + } } sqlite3VdbeJumpHere(v, iAddr); } @@ -2352,6 +2363,32 @@ int sqlite3CodeSubselect( } #endif /* SQLITE_OMIT_SUBQUERY */ +#ifndef SQLITE_OMIT_SUBQUERY +/* +** Expr pIn is an IN(...) expression. This function checks that the +** sub-select on the RHS of the IN() operator has the same number of +** columns as the vector on the LHS. Or, if the RHS of the IN() is not +** a sub-query, that the LHS is a vector of size 1. +*/ +int sqlite3ExprCheckIN(Parse *pParse, Expr *pIn){ + int nVector = sqlite3ExprVectorSize(pIn->pLeft); + if( (pIn->flags & EP_xIsSelect) ){ + if( nVector!=pIn->x.pSelect->pEList->nExpr ){ + sqlite3SubselectError(pParse, pIn->x.pSelect->pEList->nExpr, nVector); + return 1; + } + }else if( nVector!=1 ){ + if( (pIn->pLeft->flags & EP_xIsSelect) ){ + sqlite3SubselectError(pParse, nVector, 1); + }else{ + sqlite3ErrorMsg(pParse, "invalid use of row value"); + } + return 1; + } + return 0; +} +#endif + #ifndef SQLITE_OMIT_SUBQUERY /* ** Generate code for an IN expression. @@ -2387,6 +2424,7 @@ static void sqlite3ExprCodeIN( Expr *pLeft = pExpr->pLeft; int i; + if( sqlite3ExprCheckIN(pParse, pExpr) ) return; nVector = sqlite3ExprVectorSize(pExpr->pLeft); aiMap = (int*)sqlite3DbMallocZero( pParse->db, nVector*(sizeof(int) + sizeof(char)) + 1 @@ -2394,6 +2432,7 @@ static void sqlite3ExprCodeIN( if( !aiMap ) return; zAff = (char*)&aiMap[nVector]; + /* Attempt to compute the RHS. After this step, if anything other than ** IN_INDEX_NOOP is returned, the table opened ith cursor pExpr->iTable ** contains the values that make up the RHS. If IN_INDEX_NOOP is returned, @@ -2479,8 +2518,15 @@ static void sqlite3ExprCodeIN( sqlite3ReleaseTempReg(pParse, regCkNull); }else{ - /* If the LHS is NULL, then the result is either false or NULL depending - ** on whether the RHS is empty or not, respectively. */ + /* If any value on the LHS is NULL, the result of the IN(...) operator + ** must be either false or NULL. If these two are handled identically, + ** test the LHS for NULLs and jump directly to destIfNull if any are + ** found. + ** + ** Otherwise, if NULL and false are handled differently, and the + ** IN(...) operation is not a vector operation, and the LHS of the + ** operator is NULL, then the result is false if the index is + ** completely empty, or NULL otherwise. */ if( destIfNull==destIfFalse ){ for(i=0; ipLeft, i); @@ -2497,70 +2543,62 @@ static void sqlite3ExprCodeIN( } if( eType==IN_INDEX_ROWID ){ - /* In this case, the RHS is the ROWID of table b-tree - */ + /* In this case, the RHS is the ROWID of table b-tree */ sqlite3VdbeAddOp3(v, OP_SeekRowid, pExpr->iTable, destIfFalse, r1); VdbeCoverage(v); - }else if( nVector>1 && eType==IN_INDEX_EPH && destIfNull!=destIfFalse ){ - int regNull = sqlite3GetTempReg(pParse); - int r2 = sqlite3GetTempReg(pParse); - int r3 = sqlite3GetTempReg(pParse); - int r4 = sqlite3GetTempReg(pParse); - int addrNext; - int addrIf; - - if( destIfFalse!=destIfNull ){ - sqlite3VdbeAddOp2(v, OP_Integer, 0, regNull); - } - addrNext = sqlite3VdbeAddOp2(v, OP_Rewind, pExpr->iTable, destIfFalse); - for(i=0; iiTable, i, r2); - sqlite3VdbeAddOp4(v, OP_Eq, r1+i, i?r3:r4, r2, (void*)pColl,P4_COLLSEQ); - sqlite3VdbeChangeP5(v, SQLITE_STOREP2); - if( i!=0 ){ - sqlite3VdbeAddOp3(v, OP_And, r3, r4, r4); - } - } - addrIf = sqlite3VdbeAddOp1(v, OP_If, r4); - if( destIfNull!=destIfFalse ){ - sqlite3VdbeAddOp2(v, OP_IfNot, r4, sqlite3VdbeCurrentAddr(v)+2); - sqlite3VdbeAddOp2(v, OP_Integer, 1, regNull); - } - sqlite3VdbeAddOp2(v, OP_Next, pExpr->iTable, addrNext+1); - if( destIfNull!=destIfFalse ){ - sqlite3VdbeAddOp2(v, OP_If, regNull, destIfNull); - } - sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfFalse); - sqlite3VdbeChangeP2(v, addrIf, sqlite3VdbeCurrentAddr(v)); - sqlite3ReleaseTempReg(pParse, regNull); - sqlite3ReleaseTempReg(pParse, r2); - sqlite3ReleaseTempReg(pParse, r3); - sqlite3ReleaseTempReg(pParse, r4); }else{ - /* In this case, the RHS is an index b-tree. - */ + /* In this case, the RHS is an index b-tree. Apply the comparison + ** affinities to each value on the LHS of the operator. */ sqlite3VdbeAddOp4(v, OP_Affinity, r1, nVector, 0, zAff, nVector); - - /* If the set membership test fails, then the result of the - ** "x IN (...)" expression must be either 0 or NULL. If the set - ** contains no NULL values, then the result is 0. If the set - ** contains one or more NULL values, then the result of the - ** expression is also NULL. - */ - assert( destIfFalse!=destIfNull || rRhsHasNull==0 ); - if( rRhsHasNull==0 ){ + + if( nVector>1 && destIfNull!=destIfFalse ){ + int iIdx = pExpr->iTable; + int addr; + int addrNext; + + /* Search the index for the key. */ + addr = sqlite3VdbeAddOp4Int(v, OP_Found, iIdx, 0, r1, nVector); + + /* At this point the specified key is not present in the index, + ** so the result of the IN(..) operator must be either NULL or + ** 0. The vdbe code generated below figures out which. */ + addrNext = 1+sqlite3VdbeAddOp2(v, OP_Rewind, iIdx, destIfFalse); + + for(i=0; ipLeft, i); + if( sqlite3ExprCanBeNull(p) ){ + sqlite3VdbeAddOp2(v, OP_IsNull, r1+aiMap[i], destIfNull); + } + } + + }else if( rRhsHasNull==0 ){ /* This branch runs if it is known at compile time that the RHS - ** cannot contain NULL values. This happens as the result - ** of a "NOT NULL" constraint in the database schema. + ** cannot contain NULL values. This happens as a result + ** of "NOT NULL" constraints in the database schema. ** ** Also run this branch if NULL is equivalent to FALSE - ** for this particular IN operator. - */ + ** for this particular IN operator. */ sqlite3VdbeAddOp4Int( v, OP_NotFound, pExpr->iTable, destIfFalse, r1, nVector ); @@ -2571,7 +2609,7 @@ static void sqlite3ExprCodeIN( ** outcome. */ int addr1; - + /* First check to see if the LHS is contained in the RHS. If so, ** then the answer is TRUE the presence of NULLs in the RHS does ** not matter. If the LHS is not contained in the RHS, then the @@ -3003,7 +3041,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ int inReg = target; /* Results stored in register inReg */ int regFree1 = 0; /* If non-zero free this temporary register */ int regFree2 = 0; /* If non-zero free this temporary register */ - int r1, r2, r3, r4; /* Various register numbers */ + int r1, r2; /* Various register numbers */ sqlite3 *db = pParse->db; /* The database connection */ Expr tempX; /* Temporary expression node */ int p5 = 0; @@ -3404,30 +3442,6 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ */ case TK_BETWEEN: { exprCodeBetween(pParse, pExpr, target, 0, 0); -#if 0 - Expr *pLeft = pExpr->pLeft; - struct ExprList_item *pLItem = pExpr->x.pList->a; - Expr *pRight = pLItem->pExpr; - - r1 = sqlite3ExprCodeTemp(pParse, pLeft, ®Free1); - r2 = sqlite3ExprCodeTemp(pParse, pRight, ®Free2); - testcase( regFree1==0 ); - testcase( regFree2==0 ); - r3 = sqlite3GetTempReg(pParse); - r4 = sqlite3GetTempReg(pParse); - codeCompare(pParse, pLeft, pRight, OP_Ge, - r1, r2, r3, SQLITE_STOREP2); VdbeCoverage(v); - pLItem++; - pRight = pLItem->pExpr; - sqlite3ReleaseTempReg(pParse, regFree2); - r2 = sqlite3ExprCodeTemp(pParse, pRight, ®Free2); - testcase( regFree2==0 ); - codeCompare(pParse, pLeft, pRight, OP_Le, r1, r2, r4, SQLITE_STOREP2); - VdbeCoverage(v); - sqlite3VdbeAddOp3(v, OP_And, r3, r4, target); - sqlite3ReleaseTempReg(pParse, r3); - sqlite3ReleaseTempReg(pParse, r4); -#endif break; } case TK_SPAN: @@ -3908,7 +3922,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ if( NEVER(v==0) ) return; /* Existence of VDBE checked by caller */ if( NEVER(pExpr==0) ) return; /* No way this can happen */ op = pExpr->op; - switch( op | (pExpr->pLeft ? (pExpr->pLeft->flags & EP_Vector) : 0)){ + switch( op ){ case TK_AND: { int d2 = sqlite3VdbeMakeLabel(v); testcase( jumpIfNull==0 ); @@ -3945,6 +3959,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ case TK_GE: case TK_NE: case TK_EQ: { + if( pExpr->pLeft->flags & EP_Vector ) goto default_expr; testcase( jumpIfNull==0 ); r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); @@ -3991,6 +4006,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ } #endif default: { + default_expr: if( exprAlwaysTrue(pExpr) ){ sqlite3VdbeGoto(v, dest); }else if( exprAlwaysFalse(pExpr) ){ diff --git a/src/whereexpr.c b/src/whereexpr.c index 67b2e67baa..9c9be99039 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -934,6 +934,7 @@ static void exprAnalyze( op = pExpr->op; if( op==TK_IN ){ assert( pExpr->pRight==0 ); + if( sqlite3ExprCheckIN(pParse, pExpr) ) return; if( ExprHasProperty(pExpr, EP_xIsSelect) ){ pTerm->prereqRight = exprSelectUsage(pMaskSet, pExpr->x.pSelect); }else{ diff --git a/test/rowvalue4.test b/test/rowvalue4.test new file mode 100644 index 0000000000..1dfcfb903a --- /dev/null +++ b/test/rowvalue4.test @@ -0,0 +1,48 @@ +# 2016 July 29 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is syntax errors involving row-value constructors +# and sub-selects that return multiple arguments. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix rowvalue4 + +do_execsql_test 0 { + CREATE TABLE t1(a, b, c); + CREATE INDEX t1bac ON t1(b, a, c); +} + +foreach {tn e} { + 1 "(1, 2, 3)" + 2 "1 + (1, 2)" + 3 "(1,2,3) == (1, 2)" +} { + do_catchsql_test 1.$tn "SELECT $e" {1 {invalid use of row value}} +} + +foreach {tn s error} { + 1 "SELECT * FROM t1 WHERE a = (1, 2)" {invalid use of row value} + 2 "SELECT * FROM t1 WHERE b = (1, 2)" {invalid use of row value} + 3 "SELECT * FROM t1 WHERE NOT (b = (1, 2))" {invalid use of row value} + 4 "SELECT * FROM t1 LIMIT (1, 2)" {invalid use of row value} + 5 "SELECT (a, b) IN (SELECT * FROM t1) FROM t1" + {sub-select returns 3 columns - expected 2} + + 6 "SELECT * FROM t1 WHERE (a, b) IN (SELECT * FROM t1)" + {sub-select returns 3 columns - expected 2} +} { + do_catchsql_test 2.$tn "$s" [list 1 $error] +} + +finish_test + From 145b4ea519b7826e7905628c479549994e405b0b Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 29 Jul 2016 18:12:12 +0000 Subject: [PATCH 06/97] Change the way "(a, b) = (SELECT *)" expressions are handled in where.c if there is an index on one of the columns only. FossilOrigin-Name: 4dfebff2924f46284d5b9cda69175f79b29d6028 --- manifest | 20 ++++++++++---------- manifest.uuid | 2 +- src/expr.c | 12 ------------ src/sqliteInt.h | 3 ++- src/wherecode.c | 32 ++++++++++++++++++++++++++------ src/whereexpr.c | 4 +++- test/rowvalue3.test | 3 +++ 7 files changed, 45 insertions(+), 31 deletions(-) diff --git a/manifest b/manifest index a1321d1be1..4ed47839f9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sfurther\sissues\swith\smulti-column\sIN(...)\soperators.\sAlso\ssome\serror\shandling\scases\ssurrounding\srow\svalues. -D 2016-07-28T19:47:15.803 +C Change\sthe\sway\s"(a,\sb)\s=\s(SELECT\s*)"\sexpressions\sare\shandled\sin\swhere.c\sif\sthere\sis\san\sindex\son\sone\sof\sthe\scolumns\sonly. +D 2016-07-29T18:12:12.900 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 473ce0ac25868b750f3bf9f59bbe7e8f06a0bdd5 +F src/expr.c d21255d0b778cf819d74092463df7630bbcd9fe7 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c bc4145347595b7770f9a598cff1c848302cf5413 F src/func.c 61a4114cf7004f10c542cfabbab9f2bcb9033045 @@ -388,7 +388,7 @@ F src/shell.c 9351fc6de11e1d908648c0a92d85627138e3dee5 F src/sqlite.h.in c6e68a4a47610631822a4f8f83a44c9f75339331 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 46f300b6e300e0fa916d7d58c44b53415b8471a9 -F src/sqliteInt.h 65cf752e1a62cedb2430ecc0547a09833ea13616 +F src/sqliteInt.h 65473a83bccce3556425f764900cb1b716b6cfa1 F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247 F src/status.c 5b18f9526900f61189ab0b83f1ef41d9f871a2ab F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9 @@ -465,8 +465,8 @@ F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354 F src/where.c df58d6ad7878a08aa96c652ccbc6d0949f8fa472 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 -F src/wherecode.c 3aff7683566af3428f865904aafa7efb1fbd8701 -F src/whereexpr.c bc85d04c6751ca40cab99a10de308e44893c76b9 +F src/wherecode.c e259a0b9b0eed28a9b4f5085fa1dfef4c3145361 +F src/whereexpr.c a58e878dad5cad54c100792eb8bafb9840d699f5 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -1020,7 +1020,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 587c1056016fa6b7a40a9bf6cb85d5443fa47d96 +F test/rowvalue3.test 5127afb4414bf62546161497c04840c46e371770 F test/rowvalue4.test 4480898d62d6813e3e38d9d38c02b9a0be5f94be F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09 @@ -1512,7 +1512,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 9685880f7baeb670739fdcf2d9df08e22abaa699 -R 66ab918b4b1a726fe3dea7b50bb8fdb0 +P cc3f6542bec99b00d2698889bcea2aa0b587efa0 +R 02d8106109a69b80c3fcb6354dcce9eb U dan -Z 5baa8fdab10ded1fdce6b30d35067148 +Z 1ebe0c81a88c1eb2162a3b6fe26c472e diff --git a/manifest.uuid b/manifest.uuid index c951167b81..0edf69435d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -cc3f6542bec99b00d2698889bcea2aa0b587efa0 \ No newline at end of file +4dfebff2924f46284d5b9cda69175f79b29d6028 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 2921898d35..3dcb73ff10 100644 --- a/src/expr.c +++ b/src/expr.c @@ -3512,18 +3512,6 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ break; } - case TK_SELECT_COLUMN: { - Expr *pLeft = pExpr->pLeft; - assert( pLeft ); - assert( pLeft->op==TK_SELECT || pLeft->op==TK_REGISTER ); - if( pLeft->op==TK_SELECT ){ - pLeft->iTable = sqlite3CodeSubselect(pParse, pLeft, 0, 0); - pLeft->op = TK_REGISTER; - } - inReg = pLeft->iTable + pExpr->iColumn; - break; - } - /* ** Form A: ** CASE x WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END diff --git a/src/sqliteInt.h b/src/sqliteInt.h index ea41e65755..9dfc0bcaa9 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3936,7 +3936,8 @@ void sqlite3AlterRenameTable(Parse*, SrcList*, Token*); int sqlite3GetToken(const unsigned char *, int *); void sqlite3NestedParse(Parse*, const char*, ...); void sqlite3ExpirePreparedStatements(sqlite3*); -int sqlite3CodeSubselect(Parse *, Expr *, int, int); +int sqlite3CodeSubselect(Parse*, Expr *, int, int); +int sqlite3ExprCheckIN(Parse*, Expr*); void sqlite3SelectPrep(Parse*, Select*, NameContext*); void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p); int sqlite3MatchSpanName(const char*, const char*, const char*, const char*); diff --git a/src/wherecode.c b/src/wherecode.c index 892831d821..9db4a0146a 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -359,7 +359,31 @@ static int codeEqualityTerm( assert( pLevel->pWLoop->aLTerm[iEq]==pTerm ); assert( iTarget>0 ); if( pX->op==TK_EQ || pX->op==TK_IS ){ - iReg = sqlite3ExprCodeTarget(pParse, pX->pRight, iTarget); + Expr *pRight = pX->pRight; + if( pRight->op==TK_SELECT_COLUMN ){ + /* This case occurs for expressions like "(a, b) == (SELECT ...)". */ + WhereLoop *pLoop = pLevel->pWLoop; + int i; + Expr *pSub = pRight->pLeft; + assert( pSub->op==TK_SELECT ); + for(i=pLoop->nSkip; iaLTerm[i]->pExpr->pRight; + if( pExpr && pExpr->op==TK_SELECT_COLUMN && pExpr->pLeft==pSub ) break; + } + + if( i==iEq ){ + iReg = sqlite3CodeSubselect(pParse, pSub, 0, 0); + for(/*no-op*/; inLTerm; i++){ + Expr *pExpr = pLoop->aLTerm[i]->pExpr->pRight; + if( pExpr && pExpr->op==TK_SELECT_COLUMN && pExpr->pLeft==pSub ){ + sqlite3VdbeAddOp2(v, OP_Copy, iReg+pExpr->iColumn, iTarget-iEq+i); + } + } + } + iReg = iTarget; + }else{ + iReg = sqlite3ExprCodeTarget(pParse, pRight, iTarget); + } }else if( pX->op==TK_ISNULL ){ iReg = iTarget; sqlite3VdbeAddOp2(v, OP_Null, 0, iReg); @@ -1489,10 +1513,6 @@ Bitmask sqlite3WhereCodeOneLoopStart( testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE ); } - /* Disable the start and end range terms if possible */ - /* disableTerm(pLevel, pRangeStart); */ - /* disableTerm(pLevel, pRangeEnd); */ - /* Seek the table cursor, if required */ if( omitTable ){ /* pIdx is a covering index. No need to access the main table. */ @@ -1594,7 +1614,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( u16 wctrlFlags; /* Flags for sub-WHERE clause */ Expr *pAndExpr = 0; /* An ".. AND (...)" expression */ Table *pTab = pTabItem->pTab; - + pTerm = pLoop->aLTerm[0]; assert( pTerm!=0 ); assert( pTerm->eOperator & WO_OR ); diff --git a/src/whereexpr.c b/src/whereexpr.c index 9c9be99039..4b96e2faa4 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -962,7 +962,8 @@ static void exprAnalyze( Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight); u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV; - if( op==TK_IN && pTerm->iField>0 ){ + if( pTerm->iField>0 ){ + assert( op==TK_IN ); assert( pLeft->op==TK_VECTOR ); pLeft = pLeft->x.pList->a[pTerm->iField-1].pExpr; } @@ -979,6 +980,7 @@ static void exprAnalyze( WhereTerm *pNew; Expr *pDup; u16 eExtraOp = 0; /* Extra bits for pNew->eOperator */ + assert( pTerm->iField==0 ); if( pTerm->leftCursor>=0 ){ int idxNew; pDup = sqlite3ExprDup(db, pExpr, 0); diff --git a/test/rowvalue3.test b/test/rowvalue3.test index 32c86251d0..2da141ec72 100644 --- a/test/rowvalue3.test +++ b/test/rowvalue3.test @@ -46,6 +46,7 @@ foreach {tn sql res} { 9 "SELECT (1, 2) IN (SELECT rowid, b FROM t1)" 1 10 "SELECT 1 WHERE (1, 2) IN (SELECT rowid, b FROM t1)" 1 11 "SELECT 1 WHERE (1, NULL) IN (SELECT rowid, b FROM t1)" {} + 12 "SELECT 1 FROM t1 WHERE (a, b) = (SELECT +a, +b FROM t1)" {1} } { do_execsql_test 1.$tn $sql $res } @@ -208,6 +209,8 @@ foreach {tn idx} { } } +#------------------------------------------------------------------------- + finish_test From 19ff12dd76b7b57cd9feed6298a399fc5d8f9cec Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 29 Jul 2016 20:58:19 +0000 Subject: [PATCH 07/97] Fix some issues with vector range constraints and the column cache. Also vector range constraints and rowid columns. FossilOrigin-Name: 42607366bfc2dceb1013797a973b3b8df75dcb4d --- manifest | 18 +++++------ manifest.uuid | 2 +- src/expr.c | 2 ++ src/wherecode.c | 21 ++++++++----- src/whereexpr.c | 23 ++++++++------ test/rowvalue.test | 76 +++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 113 insertions(+), 29 deletions(-) diff --git a/manifest b/manifest index 4ed47839f9..2dae50f85a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Change\sthe\sway\s"(a,\sb)\s=\s(SELECT\s*)"\sexpressions\sare\shandled\sin\swhere.c\sif\sthere\sis\san\sindex\son\sone\sof\sthe\scolumns\sonly. -D 2016-07-29T18:12:12.900 +C Fix\ssome\sissues\swith\svector\srange\sconstraints\sand\sthe\scolumn\scache.\sAlso\svector\srange\sconstraints\sand\srowid\scolumns. +D 2016-07-29T20:58:19.781 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 d21255d0b778cf819d74092463df7630bbcd9fe7 +F src/expr.c d904598a807ec20500a987566b453307cf2552cd F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c bc4145347595b7770f9a598cff1c848302cf5413 F src/func.c 61a4114cf7004f10c542cfabbab9f2bcb9033045 @@ -465,8 +465,8 @@ F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354 F src/where.c df58d6ad7878a08aa96c652ccbc6d0949f8fa472 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 -F src/wherecode.c e259a0b9b0eed28a9b4f5085fa1dfef4c3145361 -F src/whereexpr.c a58e878dad5cad54c100792eb8bafb9840d699f5 +F src/wherecode.c e3159b1d6ecd2d39f99f3ec7926d02c1f04f3053 +F src/whereexpr.c c63e0e48fe32eeb99c0d32e4130ae56af80fcbf5 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -1018,7 +1018,7 @@ F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81 F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d -F test/rowvalue.test 979738b3d49f1d93e3fee56a71d4446217917abc +F test/rowvalue.test 8656a46fac138f2a964aebbc37fc256d0a956b9c F test/rowvalue2.test 8d5dfe75b8f4d1868a2f91f0356f20d36cba64ff F test/rowvalue3.test 5127afb4414bf62546161497c04840c46e371770 F test/rowvalue4.test 4480898d62d6813e3e38d9d38c02b9a0be5f94be @@ -1512,7 +1512,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 cc3f6542bec99b00d2698889bcea2aa0b587efa0 -R 02d8106109a69b80c3fcb6354dcce9eb +P 4dfebff2924f46284d5b9cda69175f79b29d6028 +R 6db81bb19ebb4412c650189f8ab65d8e U dan -Z 1ebe0c81a88c1eb2162a3b6fe26c472e +Z 49cec86004a136f8782fa45723e38de1 diff --git a/manifest.uuid b/manifest.uuid index 0edf69435d..38cde2bfda 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4dfebff2924f46284d5b9cda69175f79b29d6028 \ No newline at end of file +42607366bfc2dceb1013797a973b3b8df75dcb4d \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 3dcb73ff10..264f27dbfd 100644 --- a/src/expr.c +++ b/src/expr.c @@ -411,6 +411,7 @@ static void codeVectorCompare(Parse *pParse, Expr *pExpr, int dest){ Expr *pL, *pR; int r1, r2; + if( i ) sqlite3ExprCachePush(pParse); if( regLeft ){ pL = pLeft->x.pSelect->pEList->a[i].pExpr; r1 = regLeft+i; @@ -431,6 +432,7 @@ static void codeVectorCompare(Parse *pParse, Expr *pExpr, int dest){ sqlite3VdbeAddOp3(v, opTest, dest, addr, p3); sqlite3ReleaseTempReg(pParse, regFree1); sqlite3ReleaseTempReg(pParse, regFree2); + if( i ) sqlite3ExprCachePop(pParse); } } diff --git a/src/wherecode.c b/src/wherecode.c index 9db4a0146a..5eb520eb3c 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -964,9 +964,6 @@ static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){ Vdbe *v = pParse->pVdbe; int iSelect = sqlite3CodeSubselect(pParse, p, 0, 0); sqlite3VdbeAddOp3(v, OP_Copy, iSelect, iReg, nReg-1); - p->op2 = p->op; - p->op = TK_REGISTER; - p->iTable = iSelect; } }else{ assert( nReg==1 ); @@ -1183,6 +1180,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( if( pStart ){ Expr *pX; /* The expression that defines the start bound */ int r1, rTemp; /* Registers for holding the start boundary */ + int op; /* Cursor seek operation */ /* The following constant maps TK_xx codes into corresponding ** seek opcodes. It depends on a particular ordering of TK_xx @@ -1202,8 +1200,16 @@ Bitmask sqlite3WhereCodeOneLoopStart( pX = pStart->pExpr; assert( pX!=0 ); testcase( pStart->leftCursor!=iCur ); /* transitive constraints */ - r1 = sqlite3ExprCodeTemp(pParse, pX->pRight, &rTemp); - sqlite3VdbeAddOp3(v, aMoveOp[pX->op-TK_GT], iCur, addrBrk, r1); + if( pX->pRight->flags & EP_Vector ){ + r1 = rTemp = sqlite3GetTempReg(pParse); + codeExprOrVector(pParse, pX->pRight, r1, 1); + op = aMoveOp[(pX->op - TK_GT) | 0x0001]; + }else{ + r1 = sqlite3ExprCodeTemp(pParse, pX->pRight, &rTemp); + disableTerm(pLevel, pStart); + op = aMoveOp[(pX->op - TK_GT)]; + } + sqlite3VdbeAddOp3(v, op, iCur, addrBrk, r1); VdbeComment((v, "pk")); VdbeCoverageIf(v, pX->op==TK_GT); VdbeCoverageIf(v, pX->op==TK_LE); @@ -1211,7 +1217,6 @@ Bitmask sqlite3WhereCodeOneLoopStart( VdbeCoverageIf(v, pX->op==TK_GE); sqlite3ExprCacheAffinityChange(pParse, r1, 1); sqlite3ReleaseTempReg(pParse, rTemp); - disableTerm(pLevel, pStart); }else{ sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, addrBrk); VdbeCoverageIf(v, bRev==0); @@ -1225,8 +1230,8 @@ Bitmask sqlite3WhereCodeOneLoopStart( testcase( pEnd->leftCursor!=iCur ); /* Transitive constraints */ testcase( pEnd->wtFlags & TERM_VIRTUAL ); memEndValue = ++pParse->nMem; - sqlite3ExprCode(pParse, pX->pRight, memEndValue); - if( pX->op==TK_LT || pX->op==TK_GT ){ + codeExprOrVector(pParse, pX->pRight, memEndValue, 1); + if( !(pX->pRight->flags&EP_Vector) && (pX->op==TK_LT || pX->op==TK_GT) ){ testOp = bRev ? OP_Le : OP_Ge; }else{ testOp = bRev ? OP_Lt : OP_Gt; diff --git a/src/whereexpr.c b/src/whereexpr.c index 4b96e2faa4..93908e6d32 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -1190,17 +1190,20 @@ static void exprAnalyze( && ( (pExpr->pLeft->flags & EP_xIsSelect)==0 || (pExpr->pRight->flags & EP_xIsSelect)==0 )){ - int i; - for(i=0; ipLeft); i++){ - int idxNew; - Expr *pNew; - Expr *pLeft = exprVectorExpr(pParse, pExpr->pLeft, i); - Expr *pRight = exprVectorExpr(pParse, pExpr->pRight, i); + int nLeft = sqlite3ExprVectorSize(pExpr->pLeft); + if( nLeft==sqlite3ExprVectorSize(pExpr->pRight) ){ + int i; + for(i=0; ipLeft); i++){ + int idxNew; + Expr *pNew; + Expr *pLeft = exprVectorExpr(pParse, pExpr->pLeft, i); + Expr *pRight = exprVectorExpr(pParse, pExpr->pRight, i); - pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight, 0); - idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC); - exprAnalyze(pSrc, pWC, idxNew); - markTermAsChild(pWC, idxNew, idxTerm); + pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight, 0); + idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC); + exprAnalyze(pSrc, pWC, idxNew); + markTermAsChild(pWC, idxNew, idxTerm); + } } } diff --git a/test/rowvalue.test b/test/rowvalue.test index 3bfcfe2c12..3989c39513 100644 --- a/test/rowvalue.test +++ b/test/rowvalue.test @@ -118,6 +118,80 @@ foreach {tn expr res} { do_execsql_test 5.$tn "SELECT $expr" [list $res] } +do_execsql_test 6.0 { + CREATE TABLE hh(a, b, c); + INSERT INTO hh VALUES('abc', 1, 'i'); + INSERT INTO hh VALUES('ABC', 1, 'ii'); + INSERT INTO hh VALUES('def', 2, 'iii'); + INSERT INTO hh VALUES('DEF', 2, 'iv'); + INSERT INTO hh VALUES('GHI', 3, 'v'); + INSERT INTO hh VALUES('ghi', 3, 'vi'); + + CREATE INDEX hh_ab ON hh(a, b); +} + +do_execsql_test 6.1 { + SELECT c FROM hh WHERE (a, b) = (SELECT 'abc', 1); +} {i} +do_execsql_test 6.2 { + SELECT c FROM hh WHERE (a, b) = (SELECT 'abc' COLLATE nocase, 1); +} {i} +do_execsql_test 6.3 { + SELECT c FROM hh WHERE a = (SELECT 'abc' COLLATE nocase) AND b = (SELECT 1); +} {i} +do_execsql_test 6.4 { + SELECT c FROM hh WHERE +a = (SELECT 'abc' COLLATE nocase) AND b = (SELECT 1); +} {i} +do_execsql_test 6.5 { + SELECT c FROM hh WHERE a = (SELECT 'abc') COLLATE nocase AND b = (SELECT 1); +} {i ii} +do_catchsql_test 6.6 { + SELECT c FROM hh WHERE (a, b) = (SELECT 'abc', 1) COLLATE nocase; +} {1 {invalid use of row value}} +do_catchsql_test 6.7 { + SELECT c FROM hh WHERE (a, b) = 1; +} {1 {invalid use of row value}} +do_execsql_test 6.8 { + SELECT c FROM hh WHERE (a COLLATE nocase, b) = (SELECT 'def', 2); +} {iii iv} +do_execsql_test 6.9 { + SELECT c FROM hh WHERE (a COLLATE nocase, b) IS NOT (SELECT 'def', 2); +} {i ii v vi} +do_execsql_test 6.10 { + SELECT c FROM hh WHERE (b, a) = (SELECT 2, 'def'); +} {iii} + +do_execsql_test 7.0 { + CREATE TABLE xy(i INTEGER PRIMARY KEY, j, k); + INSERT INTO xy VALUES(1, 1, 1); + INSERT INTO xy VALUES(2, 2, 2); + INSERT INTO xy VALUES(3, 3, 3); + INSERT INTO xy VALUES(4, 4, 4); +} + + +foreach {tn sql res eqp} { + 1 "SELECT * FROM xy WHERE (i, j) IS (2, 2)" {2 2 2} + "0 0 0 {SEARCH TABLE xy USING INTEGER PRIMARY KEY (rowid=?)}" + + 2 "SELECT * FROM xy WHERE (k, j) < (2, 3)" {1 1 1 2 2 2} + "0 0 0 {SCAN TABLE xy}" + + 3 "SELECT * FROM xy WHERE (i, j) < (2, 3)" {1 1 1 2 2 2} + "0 0 0 {SEARCH TABLE xy USING INTEGER PRIMARY KEY (rowid (2, 1)" {2 2 2 3 3 3 4 4 4} + "0 0 0 {SEARCH TABLE xy USING INTEGER PRIMARY KEY (rowid>?)}" + + 5 "SELECT * FROM xy WHERE (i, j) > ('2', 1)" {2 2 2 3 3 3 4 4 4} + "0 0 0 {SEARCH TABLE xy USING INTEGER PRIMARY KEY (rowid>?)}" + +} { + do_eqp_test 7.$tn.1 $sql $eqp + do_execsql_test 7.$tn.2 $sql $res +} + + + finish_test - From 625015e0c99a689eb362853c03123aa8eabd032e Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 30 Jul 2016 16:39:28 +0000 Subject: [PATCH 08/97] Remove the EP_Vector expression flag. FossilOrigin-Name: e9d9c6d46b46160fad6aa6e3441a65a09157515f --- manifest | 20 ++++++++++---------- manifest.uuid | 2 +- src/expr.c | 23 ++++++++++++++++------- src/resolve.c | 9 --------- src/sqliteInt.h | 2 +- src/wherecode.c | 12 +++++++----- src/whereexpr.c | 6 ++---- 7 files changed, 37 insertions(+), 37 deletions(-) diff --git a/manifest b/manifest index 2dae50f85a..c185b508a4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\ssome\sissues\swith\svector\srange\sconstraints\sand\sthe\scolumn\scache.\sAlso\svector\srange\sconstraints\sand\srowid\scolumns. -D 2016-07-29T20:58:19.781 +C Remove\sthe\sEP_Vector\sexpression\sflag. +D 2016-07-30T16:39:28.446 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 d904598a807ec20500a987566b453307cf2552cd +F src/expr.c 4e79d7a355a59d5d0635f0a718a07abf21fe69bc F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c bc4145347595b7770f9a598cff1c848302cf5413 F src/func.c 61a4114cf7004f10c542cfabbab9f2bcb9033045 @@ -381,14 +381,14 @@ F src/pragma.h 64c78a648751b9f4f297276c4eb7507b14b4628c F src/prepare.c 22df6171aec1d86904ed2ad30c2348a5748aa04e F src/printf.c a5f0ca08ddede803c241266abb46356ec748ded1 F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 -F src/resolve.c d5516a3818748c50f98c18e4601a746e90929f1c +F src/resolve.c cca3aa77b95706df5d635a2141a4d1de60ae6598 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac F src/select.c 0115f5d222f5cf9b5511ec4072088417354d738a F src/shell.c 9351fc6de11e1d908648c0a92d85627138e3dee5 F src/sqlite.h.in c6e68a4a47610631822a4f8f83a44c9f75339331 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 46f300b6e300e0fa916d7d58c44b53415b8471a9 -F src/sqliteInt.h 65473a83bccce3556425f764900cb1b716b6cfa1 +F src/sqliteInt.h 618346a0ad21589a177aeaa3725a6dfe0b2dbcf3 F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247 F src/status.c 5b18f9526900f61189ab0b83f1ef41d9f871a2ab F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9 @@ -465,8 +465,8 @@ F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354 F src/where.c df58d6ad7878a08aa96c652ccbc6d0949f8fa472 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 -F src/wherecode.c e3159b1d6ecd2d39f99f3ec7926d02c1f04f3053 -F src/whereexpr.c c63e0e48fe32eeb99c0d32e4130ae56af80fcbf5 +F src/wherecode.c 6131be0cb19702665c3decbf243dae58ecc15937 +F src/whereexpr.c 82196ee82ca9de9d133ae742786bb89cf6ee890d F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -1512,7 +1512,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 4dfebff2924f46284d5b9cda69175f79b29d6028 -R 6db81bb19ebb4412c650189f8ab65d8e +P 42607366bfc2dceb1013797a973b3b8df75dcb4d +R eaef7077e544c307dde0b934686aad76 U dan -Z 49cec86004a136f8782fa45723e38de1 +Z c4dca5e01854a7cf15bed46750135eb4 diff --git a/manifest.uuid b/manifest.uuid index 38cde2bfda..1b7e127455 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -42607366bfc2dceb1013797a973b3b8df75dcb4d \ No newline at end of file +e9d9c6d46b46160fad6aa6e3441a65a09157515f \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 264f27dbfd..5f5e1d058c 100644 --- a/src/expr.c +++ b/src/expr.c @@ -316,13 +316,23 @@ static int codeCompare( ** any other type of expression, return 1. */ int sqlite3ExprVectorSize(Expr *pExpr){ - if( (pExpr->flags & EP_Vector)==0 ) return 1; + if( sqlite3ExprIsVector(pExpr)==0 ) return 1; if( pExpr->flags & EP_xIsSelect ){ return pExpr->x.pSelect->pEList->nExpr; } return pExpr->x.pList->nExpr; } +/* +** Return true if expression pExpr is a vector, or false otherwise. +*/ +int sqlite3ExprIsVector(Expr *pExpr){ + return ( + pExpr->op==TK_VECTOR + || (pExpr->op==TK_SELECT && pExpr->x.pSelect->pEList->nExpr>1) + ); +} + /* ** If the expression passed as the first argument is a TK_VECTOR, return ** a pointer to the i'th field of the vector. Or, if the first argument @@ -330,7 +340,7 @@ int sqlite3ExprVectorSize(Expr *pExpr){ ** value. Otherwise, return a copy of the first argument. */ static Expr *exprVectorField(Expr *pVector, int i){ - if( (pVector->flags & EP_Vector)==0 ){ + if( sqlite3ExprIsVector(pVector)==0 ){ assert( i==0 ); return pVector; }else if( pVector->flags & EP_xIsSelect ){ @@ -3171,7 +3181,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ case TK_NE: case TK_EQ: { Expr *pLeft = pExpr->pLeft; - if( (pLeft->flags & EP_Vector) ){ + if( sqlite3ExprIsVector(pLeft) ){ codeVectorCompare(pParse, pExpr, target); }else{ r1 = sqlite3ExprCodeTemp(pParse, pLeft, ®Free1); @@ -3865,7 +3875,7 @@ static void exprCodeBetween( compRight.op = TK_LE; compRight.pLeft = &exprX; compRight.pRight = pExpr->x.pList->a[1].pExpr; - if( (exprX.flags & EP_Vector)==0 ){ + if( sqlite3ExprIsVector(&exprX)==0 ){ exprToRegister(&exprX, sqlite3ExprCodeTemp(pParse, &exprX, ®Free1)); } if( xJumpIf ){ @@ -3949,7 +3959,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ case TK_GE: case TK_NE: case TK_EQ: { - if( pExpr->pLeft->flags & EP_Vector ) goto default_expr; + if( sqlite3ExprIsVector(pExpr->pLeft) ) goto default_expr; testcase( jumpIfNull==0 ); r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); @@ -4103,8 +4113,7 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ case TK_GE: case TK_NE: case TK_EQ: { - if( pExpr->pLeft->flags & EP_Vector ) goto default_expr; - + if( sqlite3ExprIsVector(pExpr->pLeft) ) goto default_expr; testcase( jumpIfNull==0 ); r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); diff --git a/src/resolve.c b/src/resolve.c index 4d8873963a..77ce37f6d7 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -765,10 +765,6 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ ExprSetProperty(pExpr, EP_VarSelect); pNC->ncFlags |= NC_VarSelect; } - - if( pExpr->op==TK_SELECT && pExpr->x.pSelect->pEList->nExpr>1 ){ - ExprSetProperty(pExpr, EP_Vector); - } } break; } @@ -776,11 +772,6 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ notValid(pParse, pNC, "parameters", NC_IsCheck|NC_PartIdx|NC_IdxExpr); break; } - - case TK_VECTOR: { - ExprSetProperty(pExpr, EP_Vector); - break; - } } return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue; } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 9dfc0bcaa9..5b3826586e 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2327,7 +2327,6 @@ struct Expr { #define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */ #define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */ #define EP_Alias 0x400000 /* Is an alias for a result set column */ -#define EP_Vector 0x800000 /* This expression is a row value */ /* ** Combinations of two or more EP_* flags @@ -4256,5 +4255,6 @@ int sqlite3DbstatRegister(sqlite3*); #endif int sqlite3ExprVectorSize(Expr *pExpr); +int sqlite3ExprIsVector(Expr *pExpr); #endif /* SQLITEINT_H */ diff --git a/src/wherecode.c b/src/wherecode.c index 5eb520eb3c..4f4f141123 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -952,7 +952,7 @@ static void codeDeferredSeek( static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){ assert( nReg>0 ); - if( p->flags & EP_Vector ){ + if( sqlite3ExprIsVector(p) ){ int i; if( (p->flags & EP_xIsSelect)==0 ){ ExprList *pList = p->x.pList; @@ -1200,7 +1200,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( pX = pStart->pExpr; assert( pX!=0 ); testcase( pStart->leftCursor!=iCur ); /* transitive constraints */ - if( pX->pRight->flags & EP_Vector ){ + if( sqlite3ExprIsVector(pX->pRight) ){ r1 = rTemp = sqlite3GetTempReg(pParse); codeExprOrVector(pParse, pX->pRight, r1, 1); op = aMoveOp[(pX->op - TK_GT) | 0x0001]; @@ -1231,7 +1231,9 @@ Bitmask sqlite3WhereCodeOneLoopStart( testcase( pEnd->wtFlags & TERM_VIRTUAL ); memEndValue = ++pParse->nMem; codeExprOrVector(pParse, pX->pRight, memEndValue, 1); - if( !(pX->pRight->flags&EP_Vector) && (pX->op==TK_LT || pX->op==TK_GT) ){ + if( 0==sqlite3ExprIsVector(pX->pRight) + && (pX->op==TK_LT || pX->op==TK_GT) + ){ testOp = bRev ? OP_Le : OP_Ge; }else{ testOp = bRev ? OP_Lt : OP_Gt; @@ -1440,7 +1442,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( } nConstraint += nBtm; testcase( pRangeStart->wtFlags & TERM_VIRTUAL ); - if( (pRight->flags & EP_Vector)==0 ){ + if( sqlite3ExprIsVector(pRight)==0 ){ disableTerm(pLevel, pRangeStart); }else{ startEq = 1; @@ -1493,7 +1495,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( nConstraint += nTop; testcase( pRangeEnd->wtFlags & TERM_VIRTUAL ); - if( (pRight->flags & EP_Vector)==0 ){ + if( sqlite3ExprIsVector(pRight)==0 ){ disableTerm(pLevel, pRangeEnd); }else{ endEq = 1; diff --git a/src/whereexpr.c b/src/whereexpr.c index 93908e6d32..5997da1e3a 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -845,9 +845,7 @@ static int exprMightBeIndexed( ** inequality constraint (>, <, >= or <=), perform the processing ** on the first element of the vector. */ assert( TK_GT+1==TK_LE && TK_GT+2==TK_LT && TK_GT+3==TK_GE ); - if( (pExpr->flags & (EP_Vector|EP_xIsSelect))==EP_Vector - && (op>=TK_GT && op<=TK_GE) - ){ + if( pExpr->op==TK_VECTOR && (op>=TK_GT && op<=TK_GE) ){ pExpr = pExpr->x.pList->a[0].pExpr; } @@ -1186,7 +1184,7 @@ static void exprAnalyze( if( pWC->op==TK_AND && (pExpr->op==TK_EQ || pExpr->op==TK_IS) - && (pExpr->pLeft->flags & EP_Vector) + && sqlite3ExprIsVector(pExpr->pLeft) && ( (pExpr->pLeft->flags & EP_xIsSelect)==0 || (pExpr->pRight->flags & EP_xIsSelect)==0 )){ From 5c288b929a80fb532c00e74853f097b267eff860 Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 30 Jul 2016 21:02:33 +0000 Subject: [PATCH 09/97] Fix problems with vector == comparisons and NULL values. FossilOrigin-Name: 059d0d05354e6efab7892c97b339ffa0b5303587 --- manifest | 16 ++--- manifest.uuid | 2 +- src/expr.c | 146 +++++++++++++++++++++++++++++---------------- src/vdbe.c | 63 ++++++++++++++----- test/rowvalue.test | 3 + 5 files changed, 154 insertions(+), 76 deletions(-) diff --git a/manifest b/manifest index fdb83a0054..6cdc700a0e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\slatest\strunk\swith\sthis\sbranch. -D 2016-07-30T17:59:39.678 +C Fix\sproblems\swith\svector\s==\scomparisons\sand\sNULL\svalues. +D 2016-07-30T21:02:33.694 F Makefile.in 6c20d44f72d4564f11652b26291a214c8367e5db F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 3340e479e5221f06c3d61726f8f7efff885e4233 @@ -337,7 +337,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 1cc9fb516ec9932c6fd4d2a0d2f8bc4480145c39 F src/dbstat.c 4f6f7f52b49beb9636ffbd517cfe44a402ba4ad0 F src/delete.c 4aba4214a377ce8ddde2d2e609777bcc8235200f -F src/expr.c 4e79d7a355a59d5d0635f0a718a07abf21fe69bc +F src/expr.c f33dcbaf364c5c54a2f1aab7cf1de9fbd0c88f3d F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c bc4145347595b7770f9a598cff1c848302cf5413 F src/func.c 61a4114cf7004f10c542cfabbab9f2bcb9033045 @@ -449,7 +449,7 @@ F src/update.c 4f05ea8cddfa367d045e03589756c02199e8f9bd F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c F src/util.c 810ec3f22e2d1b62e66c30fe3621ebdedd23584d F src/vacuum.c 9dd2f5d276bc6094d8f1d85ecd41b30c1a002a43 -F src/vdbe.c 44d75e3585d93bf56c72de5e2ec4c5a16cd40370 +F src/vdbe.c 9f15129214a55044f918a983e1560fd87b0130a8 F src/vdbe.h 67bc551f7faf04c33493892e4b378aada823ed10 F src/vdbeInt.h c59381049af5c7751a83456c39b80d1a6fde1f9d F src/vdbeapi.c c3f6715a99995c11748ecad91d25e93fd9fc390b @@ -1018,7 +1018,7 @@ F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81 F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d -F test/rowvalue.test 8656a46fac138f2a964aebbc37fc256d0a956b9c +F test/rowvalue.test f4c06c37d8e3212156435495da453e12bdeef7e5 F test/rowvalue2.test 8d5dfe75b8f4d1868a2f91f0356f20d36cba64ff F test/rowvalue3.test 5127afb4414bf62546161497c04840c46e371770 F test/rowvalue4.test 4480898d62d6813e3e38d9d38c02b9a0be5f94be @@ -1512,7 +1512,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 e9d9c6d46b46160fad6aa6e3441a65a09157515f 9fae75c08b7d3b3e13734193ad8398ef6971cbac -R 9891078e76bc1592d3c3f0175b7e1e59 +P 63ae02d084a332250ff6fd8d8c80e53bf5422a68 +R 27dbfd24654b70fef0137ceb387adc85 U dan -Z b42ff5e1f9b23663ffe713255f8923fa +Z 07f670f92b12ec2bcc84d4a9aa33539d diff --git a/manifest.uuid b/manifest.uuid index b26f57e106..f6c7658784 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -63ae02d084a332250ff6fd8d8c80e53bf5422a68 \ No newline at end of file +059d0d05354e6efab7892c97b339ffa0b5303587 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 5f5e1d058c..5acce2f0c7 100644 --- a/src/expr.c +++ b/src/expr.c @@ -349,19 +349,59 @@ static Expr *exprVectorField(Expr *pVector, int i){ return pVector->x.pList->a[i].pExpr; } -static int exprVectorSubselect(Parse *pParse, Expr *pExpr){ +/* +** If expression pExpr is of type TK_SELECT, generate code to evaluate +** it. Return the register in which the result is stored (or, if the +** sub-select returns more than one column, the first in an array +** of registers in which the result is stored). +** +** If pExpr is not a TK_SELECT expression, return 0. +*/ +static int exprCodeSubselect(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); - } + if( pExpr->op==TK_SELECT ){ + reg = sqlite3CodeSubselect(pParse, pExpr, 0, 0); } return reg; } +/* +** Argument pVector points to a vector expression - either a TK_VECTOR +** or TK_SELECT that returns more than one column. This function generates +** code to evaluate expression iElem of the vector. The number of the +** register containing the result is returned. +** +** Before returning, output parameter (*ppExpr) is set to point to the +** Expr object corresponding to element iElem of the vector. +** +** If pVector is a TK_SELECT expression, then argument regSelect is +** passed the first in an array of registers that contain the results +** of the sub-select. +** +** If output parameter (*pRegFree) is set to a non-zero value by this +** function, it is the value of a temporary register that should be +** freed by the caller. +*/ +static int exprVectorRegister( + Parse *pParse, /* Parse context */ + Expr *pVector, /* Vector to extract element from */ + int iElem, /* Element to extract from pVector */ + int regSelect, /* First in array of registers */ + Expr **ppExpr, /* OUT: Expression element */ + int *pRegFree /* OUT: Temp register to free */ +){ + if( regSelect ){ + *ppExpr = pVector->x.pSelect->pEList->a[iElem].pExpr; + return regSelect+iElem; + } + *ppExpr = pVector->x.pList->a[iElem].pExpr; + return sqlite3ExprCodeTemp(pParse, *ppExpr, pRegFree); +} + +/* +** Expression pExpr is a comparison between two vector values. Compute +** the result of the comparison and write it to register dest. +*/ static void codeVectorCompare(Parse *pParse, Expr *pExpr, int dest){ Vdbe *v = pParse->pVdbe; Expr *pLeft = pExpr->pLeft; @@ -377,11 +417,12 @@ static void codeVectorCompare(Parse *pParse, Expr *pExpr, int dest){ }else{ int p5 = (pExpr->op==TK_IS || pExpr->op==TK_ISNOT) ? SQLITE_NULLEQ : 0; int opCmp; - int opTest; int i; - int p3 = 1; + int p3 = 0; + int p4 = 0; int regLeft = 0; int regRight = 0; + int regTmp = 0; assert( pExpr->op==TK_EQ || pExpr->op==TK_NE || pExpr->op==TK_IS || pExpr->op==TK_ISNOT @@ -389,61 +430,64 @@ static void codeVectorCompare(Parse *pParse, Expr *pExpr, int dest){ || pExpr->op==TK_LE || pExpr->op==TK_GE ); - switch( pExpr->op ){ - case TK_EQ: - case TK_IS: - opTest = OP_IfNot; - opCmp = OP_Eq; - break; - - case TK_NE: - case TK_ISNOT: - opTest = OP_If; - opCmp = OP_Ne; - break; - - case TK_LT: - case TK_LE: - case TK_GT: - case TK_GE: - opCmp = OP_Cmp; - opTest = OP_CmpTest; - p3 = pExpr->op; - break; + if( pExpr->op==TK_EQ || pExpr->op==TK_NE ){ + regTmp = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp2(v, OP_Integer, (pExpr->op==TK_EQ), dest); } - regLeft = exprVectorSubselect(pParse, pLeft); - regRight = exprVectorSubselect(pParse, pRight); - if( pParse->nErr ) return; + regLeft = exprCodeSubselect(pParse, pLeft); + regRight = exprCodeSubselect(pParse, pRight); for(i=0; ix.pSelect->pEList->a[i].pExpr; - r1 = regLeft+i; - }else{ - pL = pLeft->x.pList->a[i].pExpr; - r1 = sqlite3ExprCodeTemp(pParse, pL, ®Free1); + r1 = exprVectorRegister(pParse, pLeft, i, regLeft, &pL, ®Free1); + r2 = exprVectorRegister(pParse, pRight, i, regRight, &pR, ®Free2); + + switch( pExpr->op ){ + case TK_IS: + codeCompare( + pParse, pL, pR, OP_Eq, r1, r2, dest, SQLITE_STOREP2|SQLITE_NULLEQ + ); + sqlite3VdbeAddOp3(v, OP_IfNot, dest, addr, 1); + VdbeCoverage(v); + break; + + case TK_ISNOT: + codeCompare( + pParse, pL, pR, OP_Ne, r1, r2, dest, SQLITE_STOREP2|SQLITE_NULLEQ + ); + sqlite3VdbeAddOp3(v, OP_If, dest, addr, 1); + VdbeCoverage(v); + break; + + case TK_EQ: + case TK_NE: + codeCompare(pParse, pL, pR, OP_Cmp, r1, r2, regTmp,SQLITE_STOREP2|p5); + sqlite3VdbeAddOp4Int( + v, OP_CmpTest, regTmp, addr, dest, pExpr->op==TK_NE + ); + VdbeCoverage(v); + break; + + case TK_LT: + case TK_LE: + case TK_GT: + case TK_GE: + codeCompare(pParse, pL, pR, OP_Cmp, r1, r2, dest, SQLITE_STOREP2|p5); + sqlite3VdbeAddOp4Int(v, OP_CmpTest, dest, addr, 0, pExpr->op); + VdbeCoverage(v); + break; } - if( regRight ){ - pR = pRight->x.pSelect->pEList->a[i].pExpr; - r2 = regRight+i; - }else{ - pR = pRight->x.pList->a[i].pExpr; - r2 = sqlite3ExprCodeTemp(pParse, pR, ®Free1); - } - - codeCompare(pParse, pL, pR, opCmp, r1, r2, dest, SQLITE_STOREP2 | p5); - sqlite3VdbeAddOp3(v, opTest, dest, addr, p3); sqlite3ReleaseTempReg(pParse, regFree1); sqlite3ReleaseTempReg(pParse, regFree2); if( i ) sqlite3ExprCachePop(pParse); } + + sqlite3ReleaseTempReg(pParse, regTmp); } sqlite3VdbeResolveLabel(v, addr); diff --git a/src/vdbe.c b/src/vdbe.c index e2e1afb504..054f1997f3 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -1961,6 +1961,15 @@ case OP_Cast: { /* in1 */ ** This works just like the Lt opcode except that the jump is taken if ** the content of register P3 is greater than or equal to the content of ** register P1. See the Lt opcode for additional information. +** +** Opcode: Cmp P1 P2 P3 P4 P5 +** Synopsis: P2 = cmp(P1, P3) +** +** The SQLITE_STOREP2 flag must be set for this opcode. It compares the +** values in registers P1 and P3 and stores the result of the comparison +** in register P2. The results is NULL if either of the two operands are +** NULL. Otherwise, it is an integer value less than zero, zero or greater +** than zero if P3 is less than, equal to or greater than P1, respectively. */ case OP_Cmp: /* in1, in3 */ case OP_Eq: /* same as TK_EQ, jump, in1, in3 */ @@ -1974,6 +1983,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ u16 flags1; /* Copy of initial value of pIn1->flags */ u16 flags3; /* Copy of initial value of pIn3->flags */ + assert( pOp->opcode!=OP_Cmp || (pOp->p5 & SQLITE_STOREP2) ); pIn1 = &aMem[pOp->p1]; pIn3 = &aMem[pOp->p3]; flags1 = pIn1->flags; @@ -3875,16 +3885,21 @@ seek_not_found: break; } -/* Opcode: CmpTest P1 P2 P3 * * +/* Opcode: CmpTest P1 P2 P3 P4 * ** ** P2 is a jump destination. Register P1 is guaranteed to contain either -** an integer value or a NULL. The jump is taken if P1 contains any value -** other than 0 (i.e. NULL does cause a jump). -** -** If P1 is not NULL, its value is modified to integer value 0 or 1 -** according to the value of the P3 operand: +** an integer value or a NULL. ** -** P3 modification +** If P3 is non-zero, it identifies an output register. In this case, if +** P1 is NULL, P3 is also set to NULL. Or, if P1 is any integer value +** other than 0, P3 is set to the value of P4 and a jump to P2 is taken. +** +** If P3 is 0, the jump is taken if P1 contains any value other than 0 (i.e. +** NULL does cause a jump). Additionally, if P1 is not NULL, its value is +** modified to integer value 0 or 1 according to the value of the P4 integer +** operand: +** +** P4 modification ** -------------------------- ** OP_Lt (P1 = (P1 < 0)) ** OP_Le (P1 = (P1 <= 0)) @@ -3893,18 +3908,34 @@ seek_not_found: */ case OP_CmpTest: { /* in1, jump */ int bJump; - pIn1 = &aMem[pOp->p1]; - if( (pIn1->flags & MEM_Int) ){ - bJump = (pIn1->u.i!=0); - switch( pOp->p3 ){ - case OP_Lt: pIn1->u.i = (pIn1->u.i < 0); break; - case OP_Le: pIn1->u.i = (pIn1->u.i <= 0); break; - case OP_Gt: pIn1->u.i = (pIn1->u.i > 0); break; - default: assert( pOp->p3==OP_Ge ); pIn1->u.i = (pIn1->u.i >= 0); break; + + if( pOp->p3 ){ + bJump = 0; + if( pIn1->flags & MEM_Null ){ + memAboutToChange(p, &aMem[pOp->p3]); + MemSetTypeFlag(&aMem[pOp->p3], MEM_Null); + }else if( pIn1->u.i!=0 ){ + memAboutToChange(p, &aMem[pOp->p3]); + MemSetTypeFlag(&aMem[pOp->p3], MEM_Int); + aMem[pOp->p3].u.i = pOp->p4.i; + bJump = 1; } }else{ - bJump = 1; + if( (pIn1->flags & MEM_Int) ){ + bJump = (pIn1->u.i!=0); + switch( pOp->p4.i ){ + case OP_Lt: pIn1->u.i = (pIn1->u.i < 0); break; + case OP_Le: pIn1->u.i = (pIn1->u.i <= 0); break; + case OP_Gt: pIn1->u.i = (pIn1->u.i > 0); break; + default: + assert( pOp->p4.i==OP_Ge ); + pIn1->u.i = (pIn1->u.i >= 0); + break; + } + }else{ + bJump = 1; + } } if( bJump ) goto jump_to_p2; diff --git a/test/rowvalue.test b/test/rowvalue.test index 3989c39513..efa870009f 100644 --- a/test/rowvalue.test +++ b/test/rowvalue.test @@ -28,6 +28,9 @@ foreach {tn v1 v2 eq ne is isnot} { 3 "1, 2, NULL" "1, 2, 3" {} {} 0 1 4 "1, 2, NULL" "1, 2, NULL" {} {} 1 0 5 "NULL, NULL, NULL" "NULL, NULL, NULL" {} {} 1 0 + + 6 "1, NULL, 1" "1, 1, 1" {} {} 0 1 + 7 "1, NULL, 1" "1, 1, 2" 0 1 0 1 } { do_execsql_test 1.$tn.eq "SELECT ($v1) == ($v2)" [list $eq] do_execsql_test 1.$tn.ne "SELECT ($v1) != ($v2)" [list $ne] From 870a0705feb00a9c60c3442431beeeff37ca0c6b Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 1 Aug 2016 16:37:43 +0000 Subject: [PATCH 10/97] Fix a problem with IN(...) constraints where the LHS is a sub-select that is an aggregate query. FossilOrigin-Name: 1f4dba87da4a44ad26223ad965731164c0d9bad9 --- manifest | 18 +++++----- manifest.uuid | 2 +- src/expr.c | 86 +++++++++++++++++++++------------------------- src/select.c | 41 +++------------------- src/whereexpr.c | 24 +++++++++---- test/rowvalue.test | 6 ++++ 6 files changed, 78 insertions(+), 99 deletions(-) diff --git a/manifest b/manifest index 6cdc700a0e..ea0371ab19 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sproblems\swith\svector\s==\scomparisons\sand\sNULL\svalues. -D 2016-07-30T21:02:33.694 +C Fix\sa\sproblem\swith\sIN(...)\sconstraints\swhere\sthe\sLHS\sis\sa\ssub-select\sthat\sis\san\saggregate\squery. +D 2016-08-01T16:37:43.292 F Makefile.in 6c20d44f72d4564f11652b26291a214c8367e5db F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 3340e479e5221f06c3d61726f8f7efff885e4233 @@ -337,7 +337,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 1cc9fb516ec9932c6fd4d2a0d2f8bc4480145c39 F src/dbstat.c 4f6f7f52b49beb9636ffbd517cfe44a402ba4ad0 F src/delete.c 4aba4214a377ce8ddde2d2e609777bcc8235200f -F src/expr.c f33dcbaf364c5c54a2f1aab7cf1de9fbd0c88f3d +F src/expr.c cdde4d3ed7f7cf1911b961141d61a4f06e6f7ebd F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c bc4145347595b7770f9a598cff1c848302cf5413 F src/func.c 61a4114cf7004f10c542cfabbab9f2bcb9033045 @@ -383,7 +383,7 @@ F src/printf.c a5f0ca08ddede803c241266abb46356ec748ded1 F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 F src/resolve.c cca3aa77b95706df5d635a2141a4d1de60ae6598 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac -F src/select.c 0115f5d222f5cf9b5511ec4072088417354d738a +F src/select.c bb2a1583fddbf8c2bbba2abbe411882b89e14a12 F src/shell.c 9351fc6de11e1d908648c0a92d85627138e3dee5 F src/sqlite.h.in cd10e4206b91c4bf03f121ab9209a14af0b48f14 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 @@ -466,7 +466,7 @@ F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354 F src/where.c 61db3a409b6b89383abd0e746965da424676f8ea F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 F src/wherecode.c 6131be0cb19702665c3decbf243dae58ecc15937 -F src/whereexpr.c 82196ee82ca9de9d133ae742786bb89cf6ee890d +F src/whereexpr.c 3f5d76b585ab193bb9ae15aadb8deb94346c93e7 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -1018,7 +1018,7 @@ F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81 F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d -F test/rowvalue.test f4c06c37d8e3212156435495da453e12bdeef7e5 +F test/rowvalue.test 5f00f33fb1aa9259b35b44261b651361580d5a34 F test/rowvalue2.test 8d5dfe75b8f4d1868a2f91f0356f20d36cba64ff F test/rowvalue3.test 5127afb4414bf62546161497c04840c46e371770 F test/rowvalue4.test 4480898d62d6813e3e38d9d38c02b9a0be5f94be @@ -1512,7 +1512,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 63ae02d084a332250ff6fd8d8c80e53bf5422a68 -R 27dbfd24654b70fef0137ceb387adc85 +P 059d0d05354e6efab7892c97b339ffa0b5303587 +R b99b47b8f7c934c8cffa76149cc68eeb U dan -Z 07f670f92b12ec2bcc84d4a9aa33539d +Z 9e6109253ab0bc1e8a84d8639e4d1848 diff --git a/manifest.uuid b/manifest.uuid index f6c7658784..7f308a68d6 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -059d0d05354e6efab7892c97b339ffa0b5303587 \ No newline at end of file +1f4dba87da4a44ad26223ad965731164c0d9bad9 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 5acce2f0c7..3bbd015fd1 100644 --- a/src/expr.c +++ b/src/expr.c @@ -309,6 +309,15 @@ static int codeCompare( return addr; } +/* +** Return true if expression pExpr is a vector, or false otherwise. +*/ +int sqlite3ExprIsVector(Expr *pExpr){ + return ( (pExpr->op==TK_VECTOR) + || (pExpr->op==TK_SELECT && pExpr->x.pSelect->pEList->nExpr>1) + ); +} + /* ** If the expression passed as the only argument is of type TK_VECTOR ** return the number of expressions in the vector. Or, if the expression @@ -323,30 +332,23 @@ int sqlite3ExprVectorSize(Expr *pExpr){ return pExpr->x.pList->nExpr; } -/* -** Return true if expression pExpr is a vector, or false otherwise. -*/ -int sqlite3ExprIsVector(Expr *pExpr){ - return ( - pExpr->op==TK_VECTOR - || (pExpr->op==TK_SELECT && pExpr->x.pSelect->pEList->nExpr>1) - ); -} - /* ** If the expression passed as the first argument is a TK_VECTOR, return ** a pointer to the i'th field of the vector. Or, if the first argument -** points to a sub-select, return a pointer to the i'th returned column -** value. Otherwise, return a copy of the first argument. +** points to a sub-select that returns more than one column, return a +** pointer to the i'th returned column value. Otherwise, return a copy +** of the first argument. */ static Expr *exprVectorField(Expr *pVector, int i){ - if( sqlite3ExprIsVector(pVector)==0 ){ - assert( i==0 ); - return pVector; - }else if( pVector->flags & EP_xIsSelect ){ - return pVector->x.pSelect->pEList->a[i].pExpr; + assert( iop==TK_SELECT ){ + return pVector->x.pSelect->pEList->a[i].pExpr; + }else{ + return pVector->x.pList->a[i].pExpr; + } } - return pVector->x.pList->a[i].pExpr; + return pVector; } /* @@ -367,34 +369,37 @@ static int exprCodeSubselect(Parse *pParse, Expr *pExpr){ /* ** Argument pVector points to a vector expression - either a TK_VECTOR -** or TK_SELECT that returns more than one column. This function generates -** code to evaluate expression iElem of the vector. The number of the -** register containing the result is returned. +** or TK_SELECT that returns more than one column. This function returns +** the register number of a register that contains the value of +** element iField of the vector. +** +** If pVector is a TK_SELECT expression, then code for it must have +** already been generated using the exprCodeSubselect() routine. In this +** case parameter regSelect should be the first in an array of registers +** containing the results of the sub-select. +** +** If pVector is of type TK_VECTOR, then code for the requested field +** is generated. In this case (*pRegFree) may be set to the number of +** a temporary register to be freed by the caller before returning. ** ** Before returning, output parameter (*ppExpr) is set to point to the ** Expr object corresponding to element iElem of the vector. -** -** If pVector is a TK_SELECT expression, then argument regSelect is -** passed the first in an array of registers that contain the results -** of the sub-select. -** -** If output parameter (*pRegFree) is set to a non-zero value by this -** function, it is the value of a temporary register that should be -** freed by the caller. */ static int exprVectorRegister( Parse *pParse, /* Parse context */ Expr *pVector, /* Vector to extract element from */ - int iElem, /* Element to extract from pVector */ + int iField, /* Field to extract from pVector */ int regSelect, /* First in array of registers */ Expr **ppExpr, /* OUT: Expression element */ int *pRegFree /* OUT: Temp register to free */ ){ + assert( pVector->op==TK_VECTOR || pVector->op==TK_SELECT ); + assert( (pVector->op==TK_VECTOR)==(regSelect==0) ); if( regSelect ){ - *ppExpr = pVector->x.pSelect->pEList->a[iElem].pExpr; - return regSelect+iElem; + *ppExpr = pVector->x.pSelect->pEList->a[iField].pExpr; + return regSelect+iField; } - *ppExpr = pVector->x.pList->a[iElem].pExpr; + *ppExpr = pVector->x.pList->a[iField].pExpr; return sqlite3ExprCodeTemp(pParse, *ppExpr, pRegFree); } @@ -416,10 +421,7 @@ static void codeVectorCompare(Parse *pParse, Expr *pExpr, int dest){ sqlite3ErrorMsg(pParse, "invalid use of row value"); }else{ int p5 = (pExpr->op==TK_IS || pExpr->op==TK_ISNOT) ? SQLITE_NULLEQ : 0; - int opCmp; int i; - int p3 = 0; - int p4 = 0; int regLeft = 0; int regRight = 0; int regTmp = 0; @@ -1777,13 +1779,6 @@ int sqlite3IsRowid(const char *z){ ** a pointer to the SELECT statement. If pX is not a SELECT statement, ** or if the SELECT statement needs to be manifested into a transient ** table, then return NULL. -** -** If parameter bNullSensitive is 0, then this operation will be -** used in a context in which there is no difference between a result -** of 0 and one of NULL. For example: -** -** ... WHERE (?,?) IN (SELECT ...) -** */ #ifndef SQLITE_OMIT_SUBQUERY static Select *isCandidateForInOpt(Expr *pX){ @@ -1957,10 +1952,9 @@ int sqlite3FindInIndex( /* If the RHS of this IN(...) operator is a SELECT, and if it matters ** whether or not the SELECT result contains NULL values, check whether - ** or not NULL is actuall possible (it may not be, for example, due + ** or not NULL is actually possible (it may not be, for example, due ** to NOT NULL constraints in the schema). If no NULL values are possible, - ** set prRhsHasNull to 0 before continuing. - */ + ** set prRhsHasNull to 0 before continuing. */ if( prRhsHasNull && (pX->flags & EP_xIsSelect) ){ int i; ExprList *pEList = pX->x.pSelect->pEList; diff --git a/src/select.c b/src/select.c index f49a5c1fed..fb7217c1b4 100644 --- a/src/select.c +++ b/src/select.c @@ -659,30 +659,6 @@ static void codeDistinct( sqlite3ReleaseTempReg(pParse, r1); } -#ifndef SQLITE_OMIT_SUBQUERY -/* -** Generate an error message when a SELECT is used within a subexpression -** (example: "a IN (SELECT * FROM table)") but it has more than 1 result -** column. We do this in a subroutine because the error used to occur -** in multiple places. (The error only occurs in one place now, but we -** retain the subroutine to minimize code disruption.) -*/ -static int checkForMultiColumnSelectError( - Parse *pParse, /* Parse context. */ - SelectDest *pDest, /* Destination of SELECT results */ - int nExpr /* Number of result columns returned by SELECT */ -){ - int eDest = pDest->eDest; - if( 0 && nExpr>1 && eDest==SRT_Set ){ - sqlite3ErrorMsg(pParse, "only a single result allowed for " - "a SELECT that is part of an expression"); - return 1; - }else{ - return 0; - } -} -#endif - /* ** This routine generates the code for the inside of the inner loop ** of a SELECT. @@ -919,13 +895,14 @@ static void selectInnerLoop( } /* If this is a scalar select that is part of an expression, then - ** store the results in the appropriate memory cell and break out - ** of the scan loop. + ** store the results in the appropriate memory cell or array of + ** memory cells and break out of the scan loop. */ case SRT_Mem: { assert( nResultCol==pDest->nSdst ); if( pSort ){ - pushOntoSorter(pParse, pSort, p, regResult, regResult, nResultCol, nPrefixReg); + pushOntoSorter( + pParse, pSort, p, regResult, regResult, nResultCol, nPrefixReg); }else{ assert( regResult==iParm ); /* The LIMIT clause will jump out of the loop for us */ @@ -4894,16 +4871,6 @@ int sqlite3Select( } #endif - - /* If writing to memory or generating a set - ** only a single column may be output. - */ -#ifndef SQLITE_OMIT_SUBQUERY - if( checkForMultiColumnSelectError(pParse, pDest, p->pEList->nExpr) ){ - goto select_end; - } -#endif - /* Try to flatten subqueries in the FROM clause up into the main query */ #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) diff --git a/src/whereexpr.c b/src/whereexpr.c index 5997da1e3a..33ce7edb38 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -872,13 +872,25 @@ static int exprMightBeIndexed( return 0; } -static Expr *exprVectorExpr(Parse *pParse, Expr *p, int iField){ +/* +** The expression passed as the second argument is a vector (either a +** TK_VECTOR node or a TK_SELECT that returns more than one column). This +** function returns a pointer to a new expression object representing +** field iField of the vector. +** +** If pVector is of type TK_VECTOR, the returned object is just a copy of +** the iField'th element of the vector. Or, if pVector is of type TK_SELECT, +** the return value points to a new expression object of type +** TK_SELECT_COLUMN. +*/ +static Expr *exprExtractVectorField(Parse *pParse, Expr *pVector, int iField){ Expr *pRet; - if( p->flags & EP_xIsSelect ){ - pRet = sqlite3PExpr(pParse, TK_SELECT_COLUMN, p, 0, 0); + assert( sqlite3ExprIsVector(pVector) ); + if( pVector->flags & EP_xIsSelect ){ + pRet = sqlite3PExpr(pParse, TK_SELECT_COLUMN, pVector, 0, 0); if( pRet ) pRet->iColumn = iField; }else{ - pRet = sqlite3ExprDup(pParse->db, p->x.pList->a[iField].pExpr, 0); + pRet = sqlite3ExprDup(pParse->db, pVector->x.pList->a[iField].pExpr, 0); } return pRet; } @@ -1194,8 +1206,8 @@ static void exprAnalyze( for(i=0; ipLeft); i++){ int idxNew; Expr *pNew; - Expr *pLeft = exprVectorExpr(pParse, pExpr->pLeft, i); - Expr *pRight = exprVectorExpr(pParse, pExpr->pRight, i); + Expr *pLeft = exprExtractVectorField(pParse, pExpr->pLeft, i); + Expr *pRight = exprExtractVectorField(pParse, pExpr->pRight, i); pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight, 0); idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC); diff --git a/test/rowvalue.test b/test/rowvalue.test index efa870009f..d8231563dc 100644 --- a/test/rowvalue.test +++ b/test/rowvalue.test @@ -194,6 +194,12 @@ foreach {tn sql res eqp} { do_execsql_test 7.$tn.2 $sql $res } +do_execsql_test 8.0 { + CREATE TABLE j1(a); +} +do_execsql_test 8.1 { + SELECT * FROM j1 WHERE (select min(a) FROM j1) IN (?, ?, ?) +} finish_test From 553168c7067589478a72219b974c7b0824e00f58 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 1 Aug 2016 20:14:31 +0000 Subject: [PATCH 11/97] Fix a problem with vector range constraints involving the rowid column. And other issues. FossilOrigin-Name: 3ef75d45ebcd8ede91596d69e55fe7d685008a60 --- manifest | 20 +++++------ manifest.uuid | 2 +- src/expr.c | 85 ++++++++++++++++++++++++++-------------------- src/select.c | 5 ++- src/where.c | 15 +++++++- src/wherecode.c | 13 ++++++- test/rowvalue.test | 18 ++++++++++ 7 files changed, 106 insertions(+), 52 deletions(-) diff --git a/manifest b/manifest index ea0371ab19..cf088bf8f1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sproblem\swith\sIN(...)\sconstraints\swhere\sthe\sLHS\sis\sa\ssub-select\sthat\sis\san\saggregate\squery. -D 2016-08-01T16:37:43.292 +C Fix\sa\sproblem\swith\svector\srange\sconstraints\sinvolving\sthe\srowid\scolumn.\sAnd\sother\sissues. +D 2016-08-01T20:14:31.976 F Makefile.in 6c20d44f72d4564f11652b26291a214c8367e5db F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 3340e479e5221f06c3d61726f8f7efff885e4233 @@ -337,7 +337,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 1cc9fb516ec9932c6fd4d2a0d2f8bc4480145c39 F src/dbstat.c 4f6f7f52b49beb9636ffbd517cfe44a402ba4ad0 F src/delete.c 4aba4214a377ce8ddde2d2e609777bcc8235200f -F src/expr.c cdde4d3ed7f7cf1911b961141d61a4f06e6f7ebd +F src/expr.c 4db65a0c33003a00314fb56dca32d9cdbb6448a6 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c bc4145347595b7770f9a598cff1c848302cf5413 F src/func.c 61a4114cf7004f10c542cfabbab9f2bcb9033045 @@ -383,7 +383,7 @@ F src/printf.c a5f0ca08ddede803c241266abb46356ec748ded1 F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 F src/resolve.c cca3aa77b95706df5d635a2141a4d1de60ae6598 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac -F src/select.c bb2a1583fddbf8c2bbba2abbe411882b89e14a12 +F src/select.c 952aa1b4148ed4c0024586e9049b8742a765c6f9 F src/shell.c 9351fc6de11e1d908648c0a92d85627138e3dee5 F src/sqlite.h.in cd10e4206b91c4bf03f121ab9209a14af0b48f14 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 @@ -463,9 +463,9 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c 02eeecc265f6ffd0597378f5d8ae9070b62a406a F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354 -F src/where.c 61db3a409b6b89383abd0e746965da424676f8ea +F src/where.c a1c2fde60e806f1e045fdf745bfa449e3be82f55 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 -F src/wherecode.c 6131be0cb19702665c3decbf243dae58ecc15937 +F src/wherecode.c c01c8af9311b5d6d65de311101f72d94a11ae506 F src/whereexpr.c 3f5d76b585ab193bb9ae15aadb8deb94346c93e7 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd @@ -1018,7 +1018,7 @@ F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81 F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d -F test/rowvalue.test 5f00f33fb1aa9259b35b44261b651361580d5a34 +F test/rowvalue.test 56b34d31d91340a6e922e753b798880170cc1aa7 F test/rowvalue2.test 8d5dfe75b8f4d1868a2f91f0356f20d36cba64ff F test/rowvalue3.test 5127afb4414bf62546161497c04840c46e371770 F test/rowvalue4.test 4480898d62d6813e3e38d9d38c02b9a0be5f94be @@ -1512,7 +1512,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 059d0d05354e6efab7892c97b339ffa0b5303587 -R b99b47b8f7c934c8cffa76149cc68eeb +P 1f4dba87da4a44ad26223ad965731164c0d9bad9 +R 4b01df8319f11080e804e7c115ca8550 U dan -Z 9e6109253ab0bc1e8a84d8639e4d1848 +Z c86f923fed6f969ee44eaf1f0098c9a2 diff --git a/manifest.uuid b/manifest.uuid index 7f308a68d6..1477f7e016 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1f4dba87da4a44ad26223ad965731164c0d9bad9 \ No newline at end of file +3ef75d45ebcd8ede91596d69e55fe7d685008a60 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 3bbd015fd1..53c00952f3 100644 --- a/src/expr.c +++ b/src/expr.c @@ -1888,7 +1888,7 @@ static int sqlite3InRhsIsConstant(Expr *pIn){ ** An existing b-tree might be used if the RHS expression pX is a simple ** subquery such as: ** -** SELECT FROM
+** SELECT , ... FROM
** ** If the RHS of the IN operator is a list or a more complex subquery, then ** an ephemeral table might need to be generated from the RHS and then @@ -1904,14 +1904,14 @@ static int sqlite3InRhsIsConstant(Expr *pIn){ ** ** When IN_INDEX_LOOP is used (and the b-tree will be used to iterate ** through the set members) then the b-tree must not contain duplicates. -** An epheremal table must be used unless the selected is guaranteed -** to be unique - either because it is an INTEGER PRIMARY KEY or it -** has a UNIQUE constraint or UNIQUE index. +** An epheremal table must be used unless the selected columns are guaranteed +** to be unique - either because it is an INTEGER PRIMARY KEY or due to +** a UNIQUE constraint or index. ** ** When IN_INDEX_MEMBERSHIP is used (and the b-tree will be used ** for fast set membership tests) then an epheremal table must -** be used unless is an INTEGER PRIMARY KEY or an index can -** be found with as its left-most column. +** be used unless is a single INTEGER PRIMARY KEY column or an +** index can be found with the specified as its left-most. ** ** If the IN_INDEX_NOOP_OK and IN_INDEX_MEMBERSHIP are both set and ** if the RHS of the IN operator is a list (not a subquery) then this @@ -1932,6 +1932,17 @@ static int sqlite3InRhsIsConstant(Expr *pIn){ ** the value in that register will be NULL if the b-tree contains one or more ** NULL values, and it will be some non-NULL value if the b-tree contains no ** NULL values. +** +** If the aiMap parameter is not NULL, it must point to an array containing +** one element for each column returned by the SELECT statement on the RHS +** of the IN(...) operator. The i'th entry of the array is populated with the +** offset of the index column that matches the i'th column returned by the +** SELECT. For example, if the expression and selected index are: +** +** (?,?,?) IN (SELECT a, b, c FROM t1) +** CREATE INDEX i1 ON t1(b, c, a); +** +** then aiMap[] is populated with {2, 0, 1}. */ #ifndef SQLITE_OMIT_SUBQUERY int sqlite3FindInIndex( @@ -2119,24 +2130,32 @@ int sqlite3FindInIndex( } #endif +/* +** Argument pExpr is an (?, ?...) IN(...) expression. This +** function allocates and returns a nul-terminated string containing +** the affinities to be used for each column of the comparison. +** +** It is the responsibility of the caller to ensure that the returned +** string is eventually freed using sqlite3DbFree(). +*/ static char *exprINAffinity(Parse *pParse, Expr *pExpr){ Expr *pLeft = pExpr->pLeft; int nVal = sqlite3ExprVectorSize(pLeft); + Select *pSelect = (pExpr->flags & EP_xIsSelect) ? pExpr->x.pSelect : 0; char *zRet; + assert( pExpr->op==TK_IN ); zRet = sqlite3DbMallocZero(pParse->db, nVal+1); if( zRet ){ int i; for(i=0; ipEList->a[i].pExpr, a); + }else{ + zRet[i] = a; } - a = sqlite3ExprAffinity(pA); - zRet[i] = sqlite3CompareAffinity(pExpr->x.pSelect->pEList->a[i].pExpr, a); } zRet[nVal] = '\0'; } @@ -2229,11 +2248,12 @@ int sqlite3CodeSubselect( int nVal; /* Size of vector pLeft */ nVal = sqlite3ExprVectorSize(pLeft); + assert( !isRowid || nVal==1 ); /* Whether this is an 'x IN(SELECT...)' or an 'x IN()' ** expression it is handled the same way. An ephemeral table is - ** filled with single-field index keys representing the results - ** from the SELECT or the . + ** filled with index keys representing the results from the + ** SELECT or the . ** ** If the 'x' expression is a column value, or the SELECT... ** statement returns a column value, then the affinity of that @@ -2470,18 +2490,19 @@ static void sqlite3ExprCodeIN( int *aiMap = 0; /* Map from vector field to index column */ char *zAff = 0; /* Affinity string for comparisons */ int nVector; /* Size of vectors for this IN(...) op */ - int regSelect = 0; Expr *pLeft = pExpr->pLeft; int i; if( sqlite3ExprCheckIN(pParse, pExpr) ) return; + zAff = exprINAffinity(pParse, pExpr); nVector = sqlite3ExprVectorSize(pExpr->pLeft); aiMap = (int*)sqlite3DbMallocZero( pParse->db, nVector*(sizeof(int) + sizeof(char)) + 1 ); - if( !aiMap ) return; - zAff = (char*)&aiMap[nVector]; - + if( !zAff || !aiMap ){ + sqlite3DbFree(pParse->db, aiMap); + return; + } /* Attempt to compute the RHS. After this step, if anything other than ** IN_INDEX_NOOP is returned, the table opened ith cursor pExpr->iTable @@ -2505,23 +2526,14 @@ static void sqlite3ExprCodeIN( r1 = sqlite3GetTempRange(pParse, nVector); sqlite3ExprCachePush(pParse); if( nVector>1 && (pLeft->flags & EP_xIsSelect) ){ - regSelect = sqlite3CodeSubselect(pParse, pLeft, 0, 0); - } - for(i=0; iflags & EP_xIsSelect ){ - zAff[iCol] = sqlite3CompareAffinity( - pExpr->x.pSelect->pEList->a[iCol].pExpr, zAff[iCol] - ); + }else{ + for(i=0; idb, aiMap); + sqlite3DbFree(pParse->db, zAff); VdbeComment((v, "end IN expr")); } #endif /* SQLITE_OMIT_SUBQUERY */ diff --git a/src/select.c b/src/select.c index fb7217c1b4..6aba96730a 100644 --- a/src/select.c +++ b/src/select.c @@ -878,7 +878,7 @@ static void selectInnerLoop( int r1 = sqlite3GetTempReg(pParse); assert( sqlite3Strlen30(pDest->zAffSdst)==nResultCol ); sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult, nResultCol, - r1, pDest->zAffSdst, 1); + r1, pDest->zAffSdst, nResultCol); sqlite3ExprCacheAffinityChange(pParse, regResult, nResultCol); sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1); sqlite3ReleaseTempReg(pParse, r1); @@ -1261,13 +1261,12 @@ static void generateSortTail( case SRT_Set: { assert( nColumn==sqlite3Strlen30(pDest->zAffSdst) ); sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, nColumn, regRowid, - pDest->zAffSdst, 1); + pDest->zAffSdst, nColumn); sqlite3ExprCacheAffinityChange(pParse, regRow, nColumn); sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regRowid); break; } case SRT_Mem: { - /* sqlite3ExprCodeMove(pParse, regRow, iParm, nColumn); */ /* The LIMIT clause will terminate the loop for us */ break; } diff --git a/src/where.c b/src/where.c index 588ae8cb04..346614fe7e 100644 --- a/src/where.c +++ b/src/where.c @@ -2189,7 +2189,20 @@ static void whereLoopOutputAdjust( /* ** Term pTerm is a vector range comparison operation. The first comparison -** in the vector can be optimized using column nEq of the index. +** in the vector can be optimized using column nEq of the index. This +** function returns the total number of vector elements that can be used +** as part of the range comparison. +** +** For example, if the query is: +** +** WHERE a = ? AND (b, c, d) > (?, ?, ?) +** +** and the index: +** +** CREATE INDEX ... ON (a, b, c, d, e) +** +** then this function would be invoked with nEq=1. The value returned in +** this case is 3. */ int whereRangeVectorLen( Parse *pParse, int iCur, Index *pIdx, int nEq, WhereTerm *pTerm diff --git a/src/wherecode.c b/src/wherecode.c index 4f4f141123..11e9bbda34 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -950,6 +950,15 @@ static void codeDeferredSeek( } } +/* +** If the expression passed as the second argument is a vector, generate +** code to write the first nReg elements of the vector into an array +** of registers starting with iReg. +** +** If the expression is not a vector, then nReg must be passed 1. In +** this case, generate code to evaluate the expression and leave the +** result in register iReg. +*/ static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){ assert( nReg>0 ); if( sqlite3ExprIsVector(p) ){ @@ -1238,7 +1247,9 @@ Bitmask sqlite3WhereCodeOneLoopStart( }else{ testOp = bRev ? OP_Lt : OP_Gt; } - disableTerm(pLevel, pEnd); + if( 0==sqlite3ExprIsVector(pX->pRight) ){ + disableTerm(pLevel, pEnd); + } } start = sqlite3VdbeCurrentAddr(v); pLevel->op = bRev ? OP_Prev : OP_Next; diff --git a/test/rowvalue.test b/test/rowvalue.test index d8231563dc..a1faba97ab 100644 --- a/test/rowvalue.test +++ b/test/rowvalue.test @@ -201,6 +201,24 @@ do_execsql_test 8.1 { SELECT * FROM j1 WHERE (select min(a) FROM j1) IN (?, ?, ?) } +do_execsql_test 9.0 { + CREATE TABLE t2(a INTEGER PRIMARY KEY, b, c); + INSERT INTO t2 VALUES(1, 1, 1); + INSERT INTO t2 VALUES(2, 2, 2); + INSERT INTO t2 VALUES(3, 3, 3); + INSERT INTO t2 VALUES(4, 4, 4); + INSERT INTO t2 VALUES(5, 5, 5); +} + +foreach {tn q res} { + 1 "(a, b) > (2, 1)" {2 3 4 5} + 2 "(a, b) > (2, 2)" {3 4 5} + 3 "(a, b) < (4, 5)" {1 2 3 4} + 4 "(a, b) < (4, 3)" {1 2 3} +} { + do_execsql_test 9.$tn "SELECT c FROM t2 WHERE $q" $res +} + finish_test From 95a08c065a4111099fd0f330be8c5b4a6152da83 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 2 Aug 2016 16:18:35 +0000 Subject: [PATCH 12/97] Add missing comments and make some code on this branch clearer. FossilOrigin-Name: 6937677cc2c2db6b21f997559f88a339466cd15b --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/whereexpr.c | 11 ++++++++++- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index cf088bf8f1..12d2b31833 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sproblem\swith\svector\srange\sconstraints\sinvolving\sthe\srowid\scolumn.\sAnd\sother\sissues. -D 2016-08-01T20:14:31.976 +C Add\smissing\scomments\sand\smake\ssome\scode\son\sthis\sbranch\sclearer. +D 2016-08-02T16:18:35.278 F Makefile.in 6c20d44f72d4564f11652b26291a214c8367e5db F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 3340e479e5221f06c3d61726f8f7efff885e4233 @@ -466,7 +466,7 @@ F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354 F src/where.c a1c2fde60e806f1e045fdf745bfa449e3be82f55 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 F src/wherecode.c c01c8af9311b5d6d65de311101f72d94a11ae506 -F src/whereexpr.c 3f5d76b585ab193bb9ae15aadb8deb94346c93e7 +F src/whereexpr.c 4a8cefc7c122132ac9f3ed125c61629a0e3de094 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -1512,7 +1512,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 1f4dba87da4a44ad26223ad965731164c0d9bad9 -R 4b01df8319f11080e804e7c115ca8550 +P 3ef75d45ebcd8ede91596d69e55fe7d685008a60 +R ede02eaf849a0a0aa8962160f6f4e818 U dan -Z c86f923fed6f969ee44eaf1f0098c9a2 +Z d2db5fc489c89774ca610a9face0568f diff --git a/manifest.uuid b/manifest.uuid index 1477f7e016..026bb0a0e8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3ef75d45ebcd8ede91596d69e55fe7d685008a60 \ No newline at end of file +6937677cc2c2db6b21f997559f88a339466cd15b \ No newline at end of file diff --git a/src/whereexpr.c b/src/whereexpr.c index 33ce7edb38..6366133577 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -831,7 +831,7 @@ static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){ */ static int exprMightBeIndexed( SrcList *pFrom, /* The FROM clause */ - int op, + int op, /* The specific comparison operator */ Bitmask mPrereq, /* Bitmask of FROM clause terms referenced by pExpr */ Expr *pExpr, /* An operand of a comparison operator */ int *piCur, /* Write the referenced table cursor number here */ @@ -1194,6 +1194,10 @@ static void exprAnalyze( } #endif /* SQLITE_OMIT_VIRTUALTABLE */ + /* If there is a vector == or IS term - e.g. "(a, b) == (?, ?)" - create + ** a virtual term for each component comparison - "a = ?" and "b = ?". + ** This is only required if at least one side of the comparison operation + ** is not a sub-select. */ if( pWC->op==TK_AND && (pExpr->op==TK_EQ || pExpr->op==TK_IS) && sqlite3ExprIsVector(pExpr->pLeft) @@ -1217,6 +1221,11 @@ static void exprAnalyze( } } + /* If there is a vector IN term - e.g. "(a, b) IN (SELECT ...)" - create + ** a virtual term for each vector component. The expression object + ** used by each such virtual term is pExpr (the full vector IN(...) + ** expression). The WhereTerm.iField variable identifies the index within + ** the vector on the LHS that the virtual term represents. */ if( pWC->op==TK_AND && pExpr->op==TK_IN && pTerm->iField==0 && pExpr->pLeft->op==TK_VECTOR ){ From d05a7144cd7bcdbe5f5274198198e5120e846703 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 2 Aug 2016 17:07:51 +0000 Subject: [PATCH 13/97] Fix a problem with vector range constraints and mixed ASC/DESC indexes. FossilOrigin-Name: e2ad30c8b5366fd8e50f36c62345ed03ec613c47 --- manifest | 18 ++++++------- manifest.uuid | 2 +- src/where.c | 6 +++-- test/rowvalue3.test | 9 ------- test/rowvalue4.test | 64 ++++++++++++++++++++++++++++++++++++++++++++- test/tester.tcl | 11 ++++++++ 6 files changed, 88 insertions(+), 22 deletions(-) diff --git a/manifest b/manifest index b50f6287da..ba89d2088a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\slatest\strunk\schanges\sinto\sthis\sbranch. -D 2016-08-02T16:24:10.101 +C Fix\sa\sproblem\swith\svector\srange\sconstraints\sand\smixed\sASC/DESC\sindexes. +D 2016-08-02T17:07:51.146 F Makefile.in 6c20d44f72d4564f11652b26291a214c8367e5db F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 3340e479e5221f06c3d61726f8f7efff885e4233 @@ -463,7 +463,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c 02eeecc265f6ffd0597378f5d8ae9070b62a406a F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354 -F src/where.c 9dcbc1be0e8e84b1d323dae55983bbb902a6c484 +F src/where.c 25eae2e051809c75a8a1a23288f335382ac0215f F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 F src/wherecode.c c01c8af9311b5d6d65de311101f72d94a11ae506 F src/whereexpr.c 4a8cefc7c122132ac9f3ed125c61629a0e3de094 @@ -1020,8 +1020,8 @@ F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81 F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d F test/rowvalue.test 56b34d31d91340a6e922e753b798880170cc1aa7 F test/rowvalue2.test 8d5dfe75b8f4d1868a2f91f0356f20d36cba64ff -F test/rowvalue3.test 5127afb4414bf62546161497c04840c46e371770 -F test/rowvalue4.test 4480898d62d6813e3e38d9d38c02b9a0be5f94be +F test/rowvalue3.test dbe935260851b197dfbbbcb0ac2a15cb5f324fd4 +F test/rowvalue4.test 9e720652d4db9ef3bea50227c69e33d7e64801c6 F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09 F test/savepoint.test c671fdbd34cd3bfe1518a777526ada595180cf8d @@ -1133,7 +1133,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 542e38e307a6c1c362122d186f580ec3e58a288c +F test/tester.tcl 4ce5afd5e192db4cae178e1a983b060e0f08c5d6 F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5 F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58 F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7 @@ -1513,7 +1513,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 6937677cc2c2db6b21f997559f88a339466cd15b b23c10ac8f65bc88a6c7d88e140217222eb4cbe5 -R bdbd7298ee0fa9cbc2f39cf393323b85 +P d468101b421e073e9debd7381bde1d36af31369e +R e4db8db490358e0865afed7cef96935f U dan -Z eb5884061bde99d3b56b31ee8ac9fde1 +Z 8c6bb4cf90fd1cc092b885dc43b4c6ef diff --git a/manifest.uuid b/manifest.uuid index 5800b91b48..9c30fb99ac 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d468101b421e073e9debd7381bde1d36af31369e \ No newline at end of file +e2ad30c8b5366fd8e50f36c62345ed03ec613c47 \ No newline at end of file diff --git a/src/where.c b/src/where.c index 43445c08a2..3e8dea70a1 100644 --- a/src/where.c +++ b/src/where.c @@ -2226,11 +2226,13 @@ int whereRangeVectorLen( } /* Check that the LHS of the comparison is a column reference to - ** the right column of the right source table. - */ + ** the right column of the right source table. And that the sort + ** order of the index column is the same as the sort order of the + ** leftmost index column. */ if( pLhs->op!=TK_COLUMN || pLhs->iTable!=iCur || pLhs->iColumn!=pIdx->aiColumn[i+nEq] + || pIdx->aSortOrder[i]!=pIdx->aSortOrder[0] ){ break; } diff --git a/test/rowvalue3.test b/test/rowvalue3.test index 2da141ec72..17bf96a0f4 100644 --- a/test/rowvalue3.test +++ b/test/rowvalue3.test @@ -17,15 +17,6 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl set ::testprefix rowvalue3 -# Drop all auxiliary indexes from the main database opened by handle [db]. -# -proc drop_all_indexes {} { - set L [db eval { - SELECT name FROM sqlite_master WHERE type='index' AND sql LIKE 'create%' - }] - foreach idx $L { db eval "DROP INDEX $idx" } -} - do_execsql_test 1.0 { CREATE TABLE t1(a, b, c); CREATE INDEX i1 ON t1(a, b); diff --git a/test/rowvalue4.test b/test/rowvalue4.test index 1dfcfb903a..ccae0498e1 100644 --- a/test/rowvalue4.test +++ b/test/rowvalue4.test @@ -17,7 +17,14 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl set ::testprefix rowvalue4 -do_execsql_test 0 { +#------------------------------------------------------------------------- +# Test some error conditions: +# +# * row values used where they are not supported, +# * row values or sub-selects that contain/return the wrong number +# of elements. +# +do_execsql_test 1.0 { CREATE TABLE t1(a, b, c); CREATE INDEX t1bac ON t1(b, a, c); } @@ -44,5 +51,60 @@ foreach {tn s error} { do_catchsql_test 2.$tn "$s" [list 1 $error] } +#------------------------------------------------------------------------- +do_execsql_test 2.0 { + CREATE TABLE t2(a, b, c, d); + INSERT INTO t2 VALUES(1, 1, 1, 1); + INSERT INTO t2 VALUES(1, 1, 2, 2); + INSERT INTO t2 VALUES(1, 1, 3, 3); + INSERT INTO t2 VALUES(1, 2, 1, 4); + INSERT INTO t2 VALUES(1, 2, 2, 5); + INSERT INTO t2 VALUES(1, 2, 3, 6); + INSERT INTO t2 VALUES(1, 3, 1, 7); + INSERT INTO t2 VALUES(1, 3, 2, 8); + INSERT INTO t2 VALUES(1, 3, 3, 9); + + INSERT INTO t2 VALUES(2, 1, 1, 10); + INSERT INTO t2 VALUES(2, 1, 2, 11); + INSERT INTO t2 VALUES(2, 1, 3, 12); + INSERT INTO t2 VALUES(2, 2, 1, 13); + INSERT INTO t2 VALUES(2, 2, 2, 14); + INSERT INTO t2 VALUES(2, 2, 3, 15); + INSERT INTO t2 VALUES(2, 3, 1, 16); + INSERT INTO t2 VALUES(2, 3, 2, 17); + INSERT INTO t2 VALUES(2, 3, 3, 18); + + INSERT INTO t2 VALUES(3, 1, 1, 19); + INSERT INTO t2 VALUES(3, 1, 2, 20); + INSERT INTO t2 VALUES(3, 1, 3, 21); + INSERT INTO t2 VALUES(3, 2, 1, 22); + INSERT INTO t2 VALUES(3, 2, 2, 23); + INSERT INTO t2 VALUES(3, 2, 3, 24); + INSERT INTO t2 VALUES(3, 3, 1, 25); + INSERT INTO t2 VALUES(3, 3, 2, 26); + INSERT INTO t2 VALUES(3, 3, 3, 27); +} + +foreach {nm idx} { + idx1 {} + idx2 { CREATE INDEX t2abc ON t2(a, b, c); } + idx3 { CREATE INDEX t2abc ON t2(a, b DESC, c); } + idx4 { CREATE INDEX t2abc ON t2(a DESC, b DESC, c DESC); } + idx5 { CREATE INDEX t2abc ON t2(a ASC, b ASC, c ASC); } + idx6 { CREATE INDEX t2abc ON t2(a DESC, b, c); } +} { + drop_all_indexes + execsql $idx + + foreach {tn where res} { + 1 "(a, b, c) < (2, 2, 2)" {1 2 3 4 5 6 7 8 9 10 11 12 13} + 2 "(a, b, c) <= (2, 2, 2)" {1 2 3 4 5 6 7 8 9 10 11 12 13 14} + 3 "(a, b, c) > (2, 2, 2)" {15 16 17 18 19 20 21 22 23 24 25 26 27} + 4 "(a, b, c) >= (2, 2, 2)" {14 15 16 17 18 19 20 21 22 23 24 25 26 27} + } { + set result [db eval "SELECT d FROM t2 WHERE $where"] + do_test 2.$nm.$tn { lsort -integer $result } $res + } +} finish_test diff --git a/test/tester.tcl b/test/tester.tcl index 8e470cc9e3..814788ba45 100644 --- a/test/tester.tcl +++ b/test/tester.tcl @@ -25,6 +25,7 @@ # copy_file FROM TO # delete_file FILENAME # drop_all_tables ?DB? +# drop_all_indexes ?DB? # forcecopy FROM TO # forcedelete FILENAME # @@ -1951,6 +1952,16 @@ proc drop_all_tables {{db db}} { } } +# Drop all auxiliary indexes from the main database opened by handle [db]. +# +proc drop_all_indexes {{db db}} { + set L [$db eval { + SELECT name FROM sqlite_master WHERE type='index' AND sql LIKE 'create%' + }] + foreach idx $L { $db eval "DROP INDEX $idx" } +} + + #------------------------------------------------------------------------- # If a test script is executed with global variable $::G(perm:name) set to # "wal", then the tests are run in WAL mode. Otherwise, they should be run From f9b2e05c75e55ca795caa825bfcba1c0760ea35f Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 2 Aug 2016 17:45:00 +0000 Subject: [PATCH 14/97] Fix SQLITE_OMIT_SUBQUERY builds. FossilOrigin-Name: 339f85f414a484e44d2502d1ff7281caf9b7c838 --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/expr.c | 8 ++++++++ src/sqliteInt.h | 7 ++++++- src/wherecode.c | 20 +++++++++++++------- 5 files changed, 36 insertions(+), 17 deletions(-) diff --git a/manifest b/manifest index ba89d2088a..c6971b1622 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sproblem\swith\svector\srange\sconstraints\sand\smixed\sASC/DESC\sindexes. -D 2016-08-02T17:07:51.146 +C Fix\sSQLITE_OMIT_SUBQUERY\sbuilds. +D 2016-08-02T17:45:00.556 F Makefile.in 6c20d44f72d4564f11652b26291a214c8367e5db F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 3340e479e5221f06c3d61726f8f7efff885e4233 @@ -337,7 +337,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 1cc9fb516ec9932c6fd4d2a0d2f8bc4480145c39 F src/dbstat.c 4f6f7f52b49beb9636ffbd517cfe44a402ba4ad0 F src/delete.c 4aba4214a377ce8ddde2d2e609777bcc8235200f -F src/expr.c 4db65a0c33003a00314fb56dca32d9cdbb6448a6 +F src/expr.c 200cad2bc4eaaea03d36d1a13c47a90f6595154d F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c bc4145347595b7770f9a598cff1c848302cf5413 F src/func.c 61a4114cf7004f10c542cfabbab9f2bcb9033045 @@ -388,7 +388,7 @@ F src/shell.c 79dda477be6c96eba6e952a934957ad36f87acc7 F src/sqlite.h.in e011dcc3942e6ddc8dd7b894e9e6702e4269161e F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 8648034aa702469afb553231677306cc6492a1ae -F src/sqliteInt.h ccfffd24330a373a971fee69b6b06a9b7e9ddced +F src/sqliteInt.h 9d6623807cc94dfa49d0eab6380ad77091e97019 F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247 F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1 F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9 @@ -465,7 +465,7 @@ F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354 F src/where.c 25eae2e051809c75a8a1a23288f335382ac0215f F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 -F src/wherecode.c c01c8af9311b5d6d65de311101f72d94a11ae506 +F src/wherecode.c c2392fa30bcb0c555a8ae402d646b357ca428ad6 F src/whereexpr.c 4a8cefc7c122132ac9f3ed125c61629a0e3de094 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd @@ -1513,7 +1513,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 d468101b421e073e9debd7381bde1d36af31369e -R e4db8db490358e0865afed7cef96935f +P e2ad30c8b5366fd8e50f36c62345ed03ec613c47 +R 4d69da3a911755cf5dde753475b8adf2 U dan -Z 8c6bb4cf90fd1cc092b885dc43b4c6ef +Z aa590e568ce86faba8eaef4a3eef0582 diff --git a/manifest.uuid b/manifest.uuid index 9c30fb99ac..fc2a84a3ab 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e2ad30c8b5366fd8e50f36c62345ed03ec613c47 \ No newline at end of file +339f85f414a484e44d2502d1ff7281caf9b7c838 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 53c00952f3..c469b463e2 100644 --- a/src/expr.c +++ b/src/expr.c @@ -332,6 +332,7 @@ int sqlite3ExprVectorSize(Expr *pExpr){ return pExpr->x.pList->nExpr; } +#ifndef SQLITE_OMIT_SUBQUERY /* ** If the expression passed as the first argument is a TK_VECTOR, return ** a pointer to the i'th field of the vector. Or, if the first argument @@ -350,6 +351,7 @@ static Expr *exprVectorField(Expr *pVector, int i){ } return pVector; } +#endif /* ** If expression pExpr is of type TK_SELECT, generate code to evaluate @@ -361,9 +363,11 @@ static Expr *exprVectorField(Expr *pVector, int i){ */ static int exprCodeSubselect(Parse *pParse, Expr *pExpr){ int reg = 0; +#ifndef SQLITE_OMIT_SUBQUERY if( pExpr->op==TK_SELECT ){ reg = sqlite3CodeSubselect(pParse, pExpr, 0, 0); } +#endif return reg; } @@ -1829,6 +1833,7 @@ int sqlite3CodeOnce(Parse *pParse){ return sqlite3VdbeAddOp1(v, OP_Once, pParse->nOnce++); } +#ifndef SQLITE_OMIT_SUBQUERY /* ** Generate code that checks the left-most column of index table iCur to see if ** it contains any NULL entries. Cause the register at regHasNull to be set @@ -1844,6 +1849,7 @@ static void sqlite3SetHasNullFlag(Vdbe *v, int iCur, int regHasNull){ VdbeComment((v, "first_entry_in(%d)", iCur)); sqlite3VdbeJumpHere(v, addr1); } +#endif #ifndef SQLITE_OMIT_SUBQUERY @@ -2130,6 +2136,7 @@ int sqlite3FindInIndex( } #endif +#ifndef SQLITE_OMIT_SUBQUERY /* ** Argument pExpr is an (?, ?...) IN(...) expression. This ** function allocates and returns a nul-terminated string containing @@ -2161,6 +2168,7 @@ static char *exprINAffinity(Parse *pParse, Expr *pExpr){ } return zRet; } +#endif #ifndef SQLITE_OMIT_SUBQUERY /* diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 4c91d4657d..56da61a2aa 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3944,7 +3944,6 @@ int sqlite3GetToken(const unsigned char *, int *); void sqlite3NestedParse(Parse*, const char*, ...); void sqlite3ExpirePreparedStatements(sqlite3*); int sqlite3CodeSubselect(Parse*, Expr *, int, int); -int sqlite3ExprCheckIN(Parse*, Expr*); void sqlite3SelectPrep(Parse*, Select*, NameContext*); void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p); int sqlite3MatchSpanName(const char*, const char*, const char*, const char*); @@ -3999,6 +3998,12 @@ Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int); void sqlite3BackupRestart(sqlite3_backup *); void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *); +#ifndef SQLITE_OMIT_SUBQUERY +int sqlite3ExprCheckIN(Parse*, Expr*); +#else +# define sqlite3ExprCheckIN(x,y) SQLITE_OK +#endif + #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 void sqlite3AnalyzeFunctions(void); int sqlite3Stat4ProbeSetValue(Parse*,Index*,UnpackedRecord**,Expr*,u8,int,int*); diff --git a/src/wherecode.c b/src/wherecode.c index 11e9bbda34..9aafa83665 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -360,6 +360,7 @@ static int codeEqualityTerm( assert( iTarget>0 ); if( pX->op==TK_EQ || pX->op==TK_IS ){ Expr *pRight = pX->pRight; +#ifndef SQLITE_OMIT_SUBQUERY if( pRight->op==TK_SELECT_COLUMN ){ /* This case occurs for expressions like "(a, b) == (SELECT ...)". */ WhereLoop *pLoop = pLevel->pWLoop; @@ -381,7 +382,9 @@ static int codeEqualityTerm( } } iReg = iTarget; - }else{ + }else +#endif + { iReg = sqlite3ExprCodeTarget(pParse, pRight, iTarget); } }else if( pX->op==TK_ISNULL ){ @@ -962,17 +965,20 @@ static void codeDeferredSeek( static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){ assert( nReg>0 ); if( sqlite3ExprIsVector(p) ){ - int i; - if( (p->flags & EP_xIsSelect)==0 ){ +#ifndef SQLITE_OMIT_SUBQUERY + if( (p->flags & EP_xIsSelect) ){ + Vdbe *v = pParse->pVdbe; + int iSelect = sqlite3CodeSubselect(pParse, p, 0, 0); + sqlite3VdbeAddOp3(v, OP_Copy, iSelect, iReg, nReg-1); + }else +#endif + { + int i; ExprList *pList = p->x.pList; assert( nReg<=pList->nExpr ); for(i=0; ia[i].pExpr, iReg+i); } - }else{ - Vdbe *v = pParse->pVdbe; - int iSelect = sqlite3CodeSubselect(pParse, p, 0, 0); - sqlite3VdbeAddOp3(v, OP_Copy, iSelect, iReg, nReg-1); } }else{ assert( nReg==1 ); From 51d82d1d2443c55669c60630ed28d55ae03a57b0 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 2 Aug 2016 18:50:15 +0000 Subject: [PATCH 15/97] Add tests and fixes for vector operations that use sub-queries with different combinations of LIMIT, OFFSET and ORDER BY clauses. FossilOrigin-Name: 092b1c5ff53c9f3cfed079c46e3353d93f99303e --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/select.c | 13 +++++++++++-- test/rowvalue4.test | 40 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 58 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index c6971b1622..b7b01adb06 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sSQLITE_OMIT_SUBQUERY\sbuilds. -D 2016-08-02T17:45:00.556 +C Add\stests\sand\sfixes\sfor\svector\soperations\sthat\suse\ssub-queries\swith\sdifferent\scombinations\sof\sLIMIT,\sOFFSET\sand\sORDER\sBY\sclauses. +D 2016-08-02T18:50:15.542 F Makefile.in 6c20d44f72d4564f11652b26291a214c8367e5db F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 3340e479e5221f06c3d61726f8f7efff885e4233 @@ -383,7 +383,7 @@ F src/printf.c a5f0ca08ddede803c241266abb46356ec748ded1 F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 F src/resolve.c cca3aa77b95706df5d635a2141a4d1de60ae6598 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac -F src/select.c 952aa1b4148ed4c0024586e9049b8742a765c6f9 +F src/select.c 228eec644a778a31763b3d384d1ee1a5e3cf2349 F src/shell.c 79dda477be6c96eba6e952a934957ad36f87acc7 F src/sqlite.h.in e011dcc3942e6ddc8dd7b894e9e6702e4269161e F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 @@ -1021,7 +1021,7 @@ F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d F test/rowvalue.test 56b34d31d91340a6e922e753b798880170cc1aa7 F test/rowvalue2.test 8d5dfe75b8f4d1868a2f91f0356f20d36cba64ff F test/rowvalue3.test dbe935260851b197dfbbbcb0ac2a15cb5f324fd4 -F test/rowvalue4.test 9e720652d4db9ef3bea50227c69e33d7e64801c6 +F test/rowvalue4.test 9aa6a5efe6069b34bcbefe004bb481cdaaca0dc5 F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09 F test/savepoint.test c671fdbd34cd3bfe1518a777526ada595180cf8d @@ -1513,7 +1513,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 e2ad30c8b5366fd8e50f36c62345ed03ec613c47 -R 4d69da3a911755cf5dde753475b8adf2 +P 339f85f414a484e44d2502d1ff7281caf9b7c838 +R c52410c242ecf248d644374975097b8c U dan -Z aa590e568ce86faba8eaef4a3eef0582 +Z 38a047c096aac032ca966b61ad2f4616 diff --git a/manifest.uuid b/manifest.uuid index fc2a84a3ab..eacf0abe20 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -339f85f414a484e44d2502d1ff7281caf9b7c838 \ No newline at end of file +092b1c5ff53c9f3cfed079c46e3353d93f99303e \ No newline at end of file diff --git a/src/select.c b/src/select.c index 6aba96730a..7bcf3eca16 100644 --- a/src/select.c +++ b/src/select.c @@ -873,7 +873,8 @@ static void selectInnerLoop( ** ORDER BY in this case since the order of entries in the set ** does not matter. But there might be a LIMIT clause, in which ** case the order does matter */ - pushOntoSorter(pParse, pSort, p, regResult, regResult, 1, nPrefixReg); + pushOntoSorter( + pParse, pSort, p, regResult, regResult, nResultCol, nPrefixReg); }else{ int r1 = sqlite3GetTempReg(pParse); assert( sqlite3Strlen30(pDest->zAffSdst)==nResultCol ); @@ -1221,6 +1222,10 @@ static void generateSortTail( regRowid = 0; regRow = pDest->iSdst; nSortData = nColumn; + }else if( eDest==SRT_Set ){ + regRowid = sqlite3GetTempReg(pParse); + regRow = sqlite3GetTempRange(pParse, nColumn); + nSortData = nColumn; }else{ regRowid = sqlite3GetTempReg(pParse); regRow = sqlite3GetTempReg(pParse); @@ -1285,7 +1290,11 @@ static void generateSortTail( } } if( regRowid ){ - sqlite3ReleaseTempReg(pParse, regRow); + if( eDest==SRT_Set ){ + sqlite3ReleaseTempRange(pParse, regRow, nColumn); + }else{ + sqlite3ReleaseTempReg(pParse, regRow); + } sqlite3ReleaseTempReg(pParse, regRowid); } /* The bottom of the loop diff --git a/test/rowvalue4.test b/test/rowvalue4.test index ccae0498e1..c0e0933ebd 100644 --- a/test/rowvalue4.test +++ b/test/rowvalue4.test @@ -92,6 +92,9 @@ foreach {nm idx} { idx4 { CREATE INDEX t2abc ON t2(a DESC, b DESC, c DESC); } idx5 { CREATE INDEX t2abc ON t2(a ASC, b ASC, c ASC); } idx6 { CREATE INDEX t2abc ON t2(a DESC, b, c); } + idx7 { CREATE INDEX t2abc ON t2(a DESC, b DESC) } + idx8 { CREATE INDEX t2abc ON t2(c, b, a); } + idx9 { CREATE INDEX t2d ON t2(d); } } { drop_all_indexes execsql $idx @@ -101,10 +104,45 @@ foreach {nm idx} { 2 "(a, b, c) <= (2, 2, 2)" {1 2 3 4 5 6 7 8 9 10 11 12 13 14} 3 "(a, b, c) > (2, 2, 2)" {15 16 17 18 19 20 21 22 23 24 25 26 27} 4 "(a, b, c) >= (2, 2, 2)" {14 15 16 17 18 19 20 21 22 23 24 25 26 27} + 5 "(a, b, c) >= (2, 2, NULL)" {16 17 18 19 20 21 22 23 24 25 26 27} + 6 "(a, b, c) <= (2, 2, NULL)" {1 2 3 4 5 6 7 8 9 10 11 12} + 7 "(a, b, c) >= (2, NULL, NULL)" {19 20 21 22 23 24 25 26 27} + 8 "(a, b, c) <= (2, NULL, NULL)" {1 2 3 4 5 6 7 8 9} + + 9 "(a, b, c) < (SELECT a, b, c FROM t2 WHERE d=14)" + {1 2 3 4 5 6 7 8 9 10 11 12 13} + + 10 "(a, b, c) = (SELECT a, b, c FROM t2 WHERE d=14)" 14 } { set result [db eval "SELECT d FROM t2 WHERE $where"] - do_test 2.$nm.$tn { lsort -integer $result } $res + do_test 2.1.$nm.$tn { lsort -integer $result } $res + } + + foreach {tn e res} { + 1 "(2, 1) IN (SELECT a, b FROM t2)" 1 + 2 "(2, 1) IN (SELECT a, b FROM t2 ORDER BY d)" 1 + 3 "(2, 1) IN (SELECT a, b FROM t2 ORDER BY d LIMIT 9)" 0 + 4 "(2, 1) IN (SELECT a, b FROM t2 ORDER BY d LIMIT 10)" 1 + + 5 "(3, 3) = (SELECT a, b FROM t2 ORDER BY d DESC LIMIT 1)" 1 + 6 "(3, 3) = (SELECT a, b FROM t2 ORDER BY d ASC LIMIT 1)" 0 + 7 "(1, NULL) = (SELECT a, b FROM t2 ORDER BY d ASC LIMIT 1)" {{}} + + 8 "(3, 1) = (SELECT b, c FROM t2 ORDER BY d DESC LIMIT 1 OFFSET 2)" 1 + 9 "(3, 1) = (SELECT b, c FROM t2 ORDER BY d ASC LIMIT 1 OFFSET 2)" 0 + 10 "(1, NULL) = (SELECT b, c FROM t2 ORDER BY d ASC LIMIT 1 OFFSET 2)" {{}} + + 11 "(3, 3) = (SELECT max(a), max(b) FROM t2)" 1 + 12 "(3, 1) = (SELECT max(a), min(b) FROM t2)" 1 + 13 "(NULL, NULL) = (SELECT max(a), min(b) FROM t2)" {{}} + + 14 "(2, 1) IN (SELECT a, b FROM t2 ORDER BY d LIMIT 5 OFFSET 11)" 1 + 15 "(2, 1) IN (SELECT a, b FROM t2 ORDER BY d LIMIT 5 OFFSET 12)" 0 + } { + do_execsql_test 2.2.$nm.$tn "SELECT $e" $res } } + + finish_test From 78f9bb6c717275422a25dadd67df1bb3a2e3f971 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 2 Aug 2016 20:45:56 +0000 Subject: [PATCH 16/97] Add new test file rowvaluefault.test. FossilOrigin-Name: e496b2d63984311e6ae117677e6c2417ae24b6bc --- manifest | 11 ++++--- manifest.uuid | 2 +- test/rowvaluefault.test | 72 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 test/rowvaluefault.test diff --git a/manifest b/manifest index b7b01adb06..0b758663bc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\stests\sand\sfixes\sfor\svector\soperations\sthat\suse\ssub-queries\swith\sdifferent\scombinations\sof\sLIMIT,\sOFFSET\sand\sORDER\sBY\sclauses. -D 2016-08-02T18:50:15.542 +C Add\snew\stest\sfile\srowvaluefault.test. +D 2016-08-02T20:45:56.795 F Makefile.in 6c20d44f72d4564f11652b26291a214c8367e5db F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 3340e479e5221f06c3d61726f8f7efff885e4233 @@ -1022,6 +1022,7 @@ F test/rowvalue.test 56b34d31d91340a6e922e753b798880170cc1aa7 F test/rowvalue2.test 8d5dfe75b8f4d1868a2f91f0356f20d36cba64ff F test/rowvalue3.test dbe935260851b197dfbbbcb0ac2a15cb5f324fd4 F test/rowvalue4.test 9aa6a5efe6069b34bcbefe004bb481cdaaca0dc5 +F test/rowvaluefault.test 7b16485e3f2b371f3e3d05455b8ded6d0c090244 F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09 F test/savepoint.test c671fdbd34cd3bfe1518a777526ada595180cf8d @@ -1513,7 +1514,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 339f85f414a484e44d2502d1ff7281caf9b7c838 -R c52410c242ecf248d644374975097b8c +P 092b1c5ff53c9f3cfed079c46e3353d93f99303e +R 7575cd5084e4e0e048e98a52c8b1c879 U dan -Z 38a047c096aac032ca966b61ad2f4616 +Z 1ac631fd43d573ba36f8f7688c8dc1d7 diff --git a/manifest.uuid b/manifest.uuid index eacf0abe20..4cf8fbb9bc 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -092b1c5ff53c9f3cfed079c46e3353d93f99303e \ No newline at end of file +e496b2d63984311e6ae117677e6c2417ae24b6bc \ No newline at end of file diff --git a/test/rowvaluefault.test b/test/rowvaluefault.test new file mode 100644 index 0000000000..0a4da6ba47 --- /dev/null +++ b/test/rowvaluefault.test @@ -0,0 +1,72 @@ +# 2016 June 17 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/malloc_common.tcl +set ::testprefix rowvaluefault + +do_execsql_test 1.0 { + CREATE TABLE xyz(one, two, thr, fou); + INSERT INTO xyz VALUES('A', 'A', 'A', 1); + INSERT INTO xyz VALUES('B', 'B', 'B', 2); + INSERT INTO xyz VALUES('C', 'C', 'C', 3); + INSERT INTO xyz VALUES('D', 'D', 'D', 4); + + CREATE UNIQUE INDEX xyz_one_two ON xyz(one, two); +} + +do_faultsim_test 1 -faults oom* -body { + execsql { SELECT fou FROM xyz WHERE (one, two, thr) = ('B', 'B', 'B') } +} -test { + faultsim_test_result {0 2} +} + +do_faultsim_test 2 -faults oom* -body { + execsql { SELECT fou FROM xyz WHERE (two, thr) IS ('C', 'C') } +} -test { + faultsim_test_result {0 3} +} + +do_faultsim_test 3 -faults oom* -body { + execsql { SELECT fou FROM xyz WHERE (one, two, thr) > ('B', 'B', 'B') } +} -test { + faultsim_test_result {0 {3 4}} +} + +do_faultsim_test 4 -faults oom* -body { + execsql { SELECT fou FROM xyz WHERE (one, two) IN (SELECT one, two FROM xyz) } +} -test { + faultsim_test_result {0 {1 2 3 4}} +} + +do_faultsim_test 5 -faults oom* -body { + execsql { + SELECT fou FROM xyz + WHERE (one, two, thr) IN (SELECT one, two, thr FROM xyz) + } +} -test { + faultsim_test_result {0 {1 2 3 4}} +} + +do_faultsim_test 6 -faults oom* -body { + execsql { + SELECT fou FROM xyz + WHERE (one, two, thr) BETWEEN ('B', 'B', 'B') AND ('C', 'C', 'C') } +} -test { + faultsim_test_result {0 {2 3}} +} + +finish_test + From d66e5794d1507ca44e9874401a59c081fec3132c Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 3 Aug 2016 16:14:33 +0000 Subject: [PATCH 17/97] Fix stat4-based cost estimates for vector range constraints. FossilOrigin-Name: 18af74abc8ceae47ab9fbee3e3e5bb37db8fcba5 --- manifest | 20 +++++++------- manifest.uuid | 2 +- src/expr.c | 18 ++++++------- src/sqliteInt.h | 5 +++- src/vdbemem.c | 53 +++++++++++++++++++++++-------------- src/where.c | 32 ++++++++++++----------- test/rowvalue4.test | 64 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 139 insertions(+), 55 deletions(-) diff --git a/manifest b/manifest index 0b758663bc..6c59839b10 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\snew\stest\sfile\srowvaluefault.test. -D 2016-08-02T20:45:56.795 +C Fix\sstat4-based\scost\sestimates\sfor\svector\srange\sconstraints. +D 2016-08-03T16:14:33.444 F Makefile.in 6c20d44f72d4564f11652b26291a214c8367e5db F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 3340e479e5221f06c3d61726f8f7efff885e4233 @@ -337,7 +337,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 1cc9fb516ec9932c6fd4d2a0d2f8bc4480145c39 F src/dbstat.c 4f6f7f52b49beb9636ffbd517cfe44a402ba4ad0 F src/delete.c 4aba4214a377ce8ddde2d2e609777bcc8235200f -F src/expr.c 200cad2bc4eaaea03d36d1a13c47a90f6595154d +F src/expr.c 9539a6e0941248b63d631757406e089f48bea50c F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c bc4145347595b7770f9a598cff1c848302cf5413 F src/func.c 61a4114cf7004f10c542cfabbab9f2bcb9033045 @@ -388,7 +388,7 @@ F src/shell.c 79dda477be6c96eba6e952a934957ad36f87acc7 F src/sqlite.h.in e011dcc3942e6ddc8dd7b894e9e6702e4269161e F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 8648034aa702469afb553231677306cc6492a1ae -F src/sqliteInt.h 9d6623807cc94dfa49d0eab6380ad77091e97019 +F src/sqliteInt.h a1cf00afd6a5666a160e81c7a600418a3b59a8a6 F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247 F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1 F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9 @@ -455,7 +455,7 @@ F src/vdbeInt.h c59381049af5c7751a83456c39b80d1a6fde1f9d F src/vdbeapi.c c3f6715a99995c11748ecad91d25e93fd9fc390b F src/vdbeaux.c a32d79aeaa88dc2b97c261172d952d395254a055 F src/vdbeblob.c 83d2d266383157b02e2b809350bb197e89d7895b -F src/vdbemem.c 1ecaa5ee0caff07255f25d04e8dc88befb6f88d1 +F src/vdbemem.c 77d6505956bf4e45c328ab3ebef6b461334cab5d F src/vdbesort.c 91fda3909326860382b0ca8aa251e609c6a9d62c F src/vdbetrace.c 41963d5376f0349842b5fc4aaaaacd7d9cdc0834 F src/vtab.c 6fece06fdd50eb2b0673e37e627ce6710e4af5be @@ -463,7 +463,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c 02eeecc265f6ffd0597378f5d8ae9070b62a406a F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354 -F src/where.c 25eae2e051809c75a8a1a23288f335382ac0215f +F src/where.c 21095414c4bf8d5fdf05f3be790bf8b65e370f94 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 F src/wherecode.c c2392fa30bcb0c555a8ae402d646b357ca428ad6 F src/whereexpr.c 4a8cefc7c122132ac9f3ed125c61629a0e3de094 @@ -1021,7 +1021,7 @@ F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d F test/rowvalue.test 56b34d31d91340a6e922e753b798880170cc1aa7 F test/rowvalue2.test 8d5dfe75b8f4d1868a2f91f0356f20d36cba64ff F test/rowvalue3.test dbe935260851b197dfbbbcb0ac2a15cb5f324fd4 -F test/rowvalue4.test 9aa6a5efe6069b34bcbefe004bb481cdaaca0dc5 +F test/rowvalue4.test 86a04529ab1da3879d6ef56defe15446e4575b3d F test/rowvaluefault.test 7b16485e3f2b371f3e3d05455b8ded6d0c090244 F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09 @@ -1514,7 +1514,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 092b1c5ff53c9f3cfed079c46e3353d93f99303e -R 7575cd5084e4e0e048e98a52c8b1c879 +P e496b2d63984311e6ae117677e6c2417ae24b6bc +R 2ab02159e20122e5dc19c46ffdea23b5 U dan -Z 1ac631fd43d573ba36f8f7688c8dc1d7 +Z 1b607e01edb5341321c1446f2f3437dc diff --git a/manifest.uuid b/manifest.uuid index 4cf8fbb9bc..61422f33d0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e496b2d63984311e6ae117677e6c2417ae24b6bc \ No newline at end of file +18af74abc8ceae47ab9fbee3e3e5bb37db8fcba5 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index c469b463e2..70f7326935 100644 --- a/src/expr.c +++ b/src/expr.c @@ -340,7 +340,7 @@ int sqlite3ExprVectorSize(Expr *pExpr){ ** pointer to the i'th returned column value. Otherwise, return a copy ** of the first argument. */ -static Expr *exprVectorField(Expr *pVector, int i){ +Expr *sqlite3ExprVectorField(Expr *pVector, int i){ assert( iop==TK_SELECT ){ @@ -2025,7 +2025,7 @@ int sqlite3FindInIndex( ** comparison is the same as the affinity of each column. If ** it not, it is not possible to use any index. */ for(i=0; ipLeft, i); + Expr *pLhs = sqlite3ExprVectorField(pX->pLeft, i); int iCol = pEList->a[i].pExpr->iColumn; char idxaff = pTab->aCol[iCol].affinity; char cmpaff = sqlite3CompareAffinity(pLhs, idxaff); @@ -2051,7 +2051,7 @@ int sqlite3FindInIndex( } for(i=0; ipLeft, i); + Expr *pLhs = sqlite3ExprVectorField(pX->pLeft, i); Expr *pRhs = pEList->a[i].pExpr; CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs); int j; @@ -2156,7 +2156,7 @@ static char *exprINAffinity(Parse *pParse, Expr *pExpr){ if( zRet ){ int i; for(i=0; ipEList->a[i].pExpr, a); @@ -2308,7 +2308,7 @@ int sqlite3CodeSubselect( assert( pEList->nExpr>0 ); assert( sqlite3KeyInfoIsWriteable(pKeyInfo) ); for(i=0; i1) ? exprVectorField(pLeft, i) : pLeft; + Expr *p = (nVal>1) ? sqlite3ExprVectorField(pLeft, i) : pLeft; pKeyInfo->aColl[i] = sqlite3BinaryCompareCollSeq( pParse, p, pEList->a[i].pExpr ); @@ -2540,7 +2540,7 @@ static void sqlite3ExprCodeIN( } }else{ for(i=0; ipLeft, i); + Expr *p = sqlite3ExprVectorField(pExpr->pLeft, i); if( sqlite3ExprCanBeNull(p) ){ sqlite3VdbeAddOp2(v, OP_IsNull, r1+aiMap[i], destIfNull); } @@ -2638,7 +2638,7 @@ static void sqlite3ExprCodeIN( Expr *p; CollSeq *pColl; int r2 = sqlite3GetTempReg(pParse); - p = exprVectorField(pLeft, i); + p = sqlite3ExprVectorField(pLeft, i); pColl = sqlite3ExprCollSeq(pParse, p); sqlite3VdbeAddOp3(v, OP_Column, iIdx, i, r2); @@ -2656,7 +2656,7 @@ static void sqlite3ExprCodeIN( ** result is 1. */ sqlite3VdbeJumpHere(v, addr); for(i=0; ipLeft, i); + Expr *p = sqlite3ExprVectorField(pExpr->pLeft, i); if( sqlite3ExprCanBeNull(p) ){ sqlite3VdbeAddOp2(v, OP_IsNull, r1+aiMap[i], destIfNull); } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 56da61a2aa..a9061b2833 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4006,10 +4006,12 @@ int sqlite3ExprCheckIN(Parse*, Expr*); #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 void sqlite3AnalyzeFunctions(void); -int sqlite3Stat4ProbeSetValue(Parse*,Index*,UnpackedRecord**,Expr*,u8,int,int*); +int sqlite3Stat4ProbeSetValue( + Parse*,Index*,UnpackedRecord**,Expr*,int,int,int*); int sqlite3Stat4ValueFromExpr(Parse*, Expr*, u8, sqlite3_value**); void sqlite3Stat4ProbeFree(UnpackedRecord*); int sqlite3Stat4Column(sqlite3*, const void*, int, int, sqlite3_value**); +char sqlite3IndexColumnAffinity(sqlite3*, Index*, int); #endif /* @@ -4269,5 +4271,6 @@ int sqlite3DbstatRegister(sqlite3*); int sqlite3ExprVectorSize(Expr *pExpr); int sqlite3ExprIsVector(Expr *pExpr); +Expr *sqlite3ExprVectorField(Expr*, int); #endif /* SQLITEINT_H */ diff --git a/src/vdbemem.c b/src/vdbemem.c index 04cb9c5c63..0709527634 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -1520,9 +1520,9 @@ static int stat4ValueFromExpr( ** structures intended to be compared against sample index keys stored ** in the sqlite_stat4 table. ** -** A single call to this function attempts to populates field iVal (leftmost -** is 0 etc.) of the unpacked record with a value extracted from expression -** pExpr. Extraction of values is possible if: +** A single call to this function populates zero or more fields of the +** record starting with field iVal (fields are numbered from left to +** right starting with 0). A single field is populated if: ** ** * (pExpr==0). In this case the value is assumed to be an SQL NULL, ** @@ -1531,10 +1531,14 @@ static int stat4ValueFromExpr( ** * The sqlite3ValueFromExpr() function is able to extract a value ** from the expression (i.e. the expression is a literal value). ** -** If a value can be extracted, the affinity passed as the 5th argument -** is applied to it before it is copied into the UnpackedRecord. Output -** parameter *pbOk is set to true if a value is extracted, or false -** otherwise. +** Or, if pExpr is a TK_VECTOR, one field is populated for each of the +** vector components that match either of the two latter criteria listed +** above. +** +** Before any value is appended to the record, the affinity of the +** corresponding column within index pIdx is applied to it. Before +** this function returns, output parameter *pnExtract is set to the +** number of values appended to the record. ** ** When this function is called, *ppRec must either point to an object ** allocated by an earlier call to this function, or must be NULL. If it @@ -1550,22 +1554,33 @@ int sqlite3Stat4ProbeSetValue( Index *pIdx, /* Index being probed */ UnpackedRecord **ppRec, /* IN/OUT: Probe record */ Expr *pExpr, /* The expression to extract a value from */ - u8 affinity, /* Affinity to use */ + int nElem, /* Maximum number of values to append */ int iVal, /* Array element to populate */ - int *pbOk /* OUT: True if value was extracted */ + int *pnExtract /* OUT: Values appended to the record */ ){ - int rc; - sqlite3_value *pVal = 0; - struct ValueNewStat4Ctx alloc; + int rc = SQLITE_OK; + int nExtract = 0; - alloc.pParse = pParse; - alloc.pIdx = pIdx; - alloc.ppRec = ppRec; - alloc.iVal = iVal; + if( pExpr==0 || pExpr->op!=TK_SELECT ){ + int i; + struct ValueNewStat4Ctx alloc; - rc = stat4ValueFromExpr(pParse, pExpr, affinity, &alloc, &pVal); - assert( pVal==0 || pVal->db==pParse->db ); - *pbOk = (pVal!=0); + alloc.pParse = pParse; + alloc.pIdx = pIdx; + alloc.ppRec = ppRec; + + for(i=0; idb, pIdx, iVal+i); + alloc.iVal = iVal+i; + rc = stat4ValueFromExpr(pParse, pElem, aff, &alloc, &pVal); + if( !pVal ) break; + nExtract++; + } + } + + *pnExtract = nExtract; return rc; } diff --git a/src/where.c b/src/where.c index 3e8dea70a1..a37ab1bbfe 100644 --- a/src/where.c +++ b/src/where.c @@ -1207,7 +1207,7 @@ static LogEst whereRangeAdjust(WhereTerm *pTerm, LogEst nNew){ /* ** Return the affinity for a single column of an index. */ -static char sqlite3IndexColumnAffinity(sqlite3 *db, Index *pIdx, int iCol){ +char sqlite3IndexColumnAffinity(sqlite3 *db, Index *pIdx, int iCol){ assert( iCol>=0 && iColnColumn ); if( !pIdx->zColAff ){ if( sqlite3IndexAffinityStr(db, pIdx)==0 ) return SQLITE_AFF_BLOB; @@ -1384,7 +1384,8 @@ static int whereRangeScanEst( if( nEq==pBuilder->nRecValid ){ UnpackedRecord *pRec = pBuilder->pRec; tRowcnt a[2]; - u8 aff; + int nBtm = pLoop->u.btree.nBtm; + int nTop = pLoop->u.btree.nTop; /* Variable iLower will be set to the estimate of the number of rows in ** the index that are less than the lower bound of the range query. The @@ -1414,8 +1415,6 @@ static int whereRangeScanEst( testcase( pRec->nField!=pBuilder->nRecValid ); pRec->nField = pBuilder->nRecValid; } - aff = sqlite3IndexColumnAffinity(pParse->db, p, nEq); - assert( nEq!=p->nKeyCol || aff==SQLITE_AFF_INTEGER ); /* Determine iLower and iUpper using ($P) only. */ if( nEq==0 ){ iLower = 0; @@ -1434,17 +1433,20 @@ static int whereRangeScanEst( if( p->aSortOrder[nEq] ){ /* The roles of pLower and pUpper are swapped for a DESC index */ SWAP(WhereTerm*, pLower, pUpper); + SWAP(int, nBtm, nTop); } /* If possible, improve on the iLower estimate using ($P:$L). */ if( pLower ){ - int bOk; /* True if value is extracted from pExpr */ + int n; /* Values extracted from pExpr */ Expr *pExpr = pLower->pExpr->pRight; - rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq, &bOk); - if( rc==SQLITE_OK && bOk ){ + rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, nBtm, nEq, &n); + if( rc==SQLITE_OK && n ){ tRowcnt iNew; + u16 mask = WO_GT|WO_LE; + if( sqlite3ExprVectorSize(pExpr)>n ) mask = (WO_LE|WO_LT); iLwrIdx = whereKeyStats(pParse, p, pRec, 0, a); - iNew = a[0] + ((pLower->eOperator & (WO_GT|WO_LE)) ? a[1] : 0); + iNew = a[0] + ((pLower->eOperator & mask) ? a[1] : 0); if( iNew>iLower ) iLower = iNew; nOut--; pLower = 0; @@ -1453,13 +1455,15 @@ static int whereRangeScanEst( /* If possible, improve on the iUpper estimate using ($P:$U). */ if( pUpper ){ - int bOk; /* True if value is extracted from pExpr */ + int n; /* Values extracted from pExpr */ Expr *pExpr = pUpper->pExpr->pRight; - rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq, &bOk); - if( rc==SQLITE_OK && bOk ){ + rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, nTop, nEq, &n); + if( rc==SQLITE_OK && n ){ tRowcnt iNew; + u16 mask = WO_GT|WO_LE; + if( sqlite3ExprVectorSize(pExpr)>n ) mask = (WO_LE|WO_LT); iUprIdx = whereKeyStats(pParse, p, pRec, 1, a); - iNew = a[0] + ((pUpper->eOperator & (WO_GT|WO_LE)) ? a[1] : 0); + iNew = a[0] + ((pUpper->eOperator & mask) ? a[1] : 0); if( iNewpNew->u.btree.pIndex; int nEq = pBuilder->pNew->u.btree.nEq; UnpackedRecord *pRec = pBuilder->pRec; - u8 aff; /* Column affinity */ int rc; /* Subfunction return code */ tRowcnt a[2]; /* Statistics */ int bOk; @@ -1573,8 +1576,7 @@ static int whereEqualScanEst( return SQLITE_OK; } - aff = sqlite3IndexColumnAffinity(pParse->db, p, nEq-1); - rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq-1, &bOk); + rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, 1, nEq-1, &bOk); pBuilder->pRec = pRec; if( rc!=SQLITE_OK ) return rc; if( bOk==0 ) return SQLITE_NOTFOUND; diff --git a/test/rowvalue4.test b/test/rowvalue4.test index c0e0933ebd..b02ee169f7 100644 --- a/test/rowvalue4.test +++ b/test/rowvalue4.test @@ -143,6 +143,70 @@ foreach {nm idx} { } } +ifcapable stat4 { + do_execsql_test 3.0 { + CREATE TABLE c1(a, b, c, d); + INSERT INTO c1(a, b) VALUES(1, 'a'); + INSERT INTO c1(a, b) VALUES(1, 'b'); + INSERT INTO c1(a, b) VALUES(1, 'c'); + INSERT INTO c1(a, b) VALUES(1, 'd'); + INSERT INTO c1(a, b) VALUES(1, 'e'); + INSERT INTO c1(a, b) VALUES(1, 'f'); + INSERT INTO c1(a, b) VALUES(1, 'g'); + INSERT INTO c1(a, b) VALUES(1, 'h'); + INSERT INTO c1(a, b) VALUES(1, 'i'); + INSERT INTO c1(a, b) VALUES(1, 'j'); + INSERT INTO c1(a, b) VALUES(1, 'k'); + INSERT INTO c1(a, b) VALUES(1, 'l'); + INSERT INTO c1(a, b) VALUES(1, 'm'); + INSERT INTO c1(a, b) VALUES(1, 'n'); + INSERT INTO c1(a, b) VALUES(1, 'o'); + INSERT INTO c1(a, b) VALUES(1, 'p'); + INSERT INTO c1(a, b) VALUES(2, 'a'); + INSERT INTO c1(a, b) VALUES(2, 'b'); + INSERT INTO c1(a, b) VALUES(2, 'c'); + INSERT INTO c1(a, b) VALUES(2, 'd'); + INSERT INTO c1(a, b) VALUES(2, 'e'); + INSERT INTO c1(a, b) VALUES(2, 'f'); + INSERT INTO c1(a, b) VALUES(2, 'g'); + INSERT INTO c1(a, b) VALUES(2, 'h'); + + INSERT INTO c1(c, d) SELECT a, b FROM c1; + + CREATE INDEX c1ab ON c1(a, b); + CREATE INDEX c1cd ON c1(c, d); + ANALYZE; + } + + do_eqp_test 3.1.1 { SELECT * FROM c1 WHERE a=1 AND c=2 } { + 0 0 0 {SEARCH TABLE c1 USING INDEX c1cd (c=?)} + } + do_eqp_test 3.1.2 { SELECT * FROM c1 WHERE a=1 AND b>'d' AND c=2 } { + 0 0 0 {SEARCH TABLE c1 USING INDEX c1cd (c=?)} + } + do_eqp_test 3.1.3 { SELECT * FROM c1 WHERE a=1 AND b>'l' AND c=2 } { + 0 0 0 {SEARCH TABLE c1 USING INDEX c1ab (a=? AND b>?)} + } + + do_eqp_test 3.2.1 { SELECT * FROM c1 WHERE a=1 AND c>1 } { + 0 0 0 {SEARCH TABLE c1 USING INDEX c1cd (c>?)} + } + do_eqp_test 3.2.2 { SELECT * FROM c1 WHERE a=1 AND c>0 } { + 0 0 0 {SEARCH TABLE c1 USING INDEX c1ab (a=?)} + } + do_eqp_test 3.2.3 { SELECT * FROM c1 WHERE a=1 AND c>=1 } { + 0 0 0 {SEARCH TABLE c1 USING INDEX c1ab (a=?)} + } + do_eqp_test 3.2.4 { SELECT * FROM c1 WHERE a=1 AND (c, d)>(1, 'c') } { + 0 0 0 {SEARCH TABLE c1 USING INDEX c1ab (a=?)} + } + do_eqp_test 3.2.5 { SELECT * FROM c1 WHERE a=1 AND (c, d)>(1, 'o') } { + 0 0 0 {SEARCH TABLE c1 USING INDEX c1cd (c>?)} + } + do_eqp_test 3.2.6 { SELECT * FROM c1 WHERE a=1 AND (c, +b)>(1, 'c') } { + 0 0 0 {SEARCH TABLE c1 USING INDEX c1ab (a=?)} + } +} finish_test From 2c628ea9d9c015ad9fd70510ee228c8e43e3aa9c Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 3 Aug 2016 16:39:04 +0000 Subject: [PATCH 18/97] Fix another problem involving vector range constraints and mixed ASC/DESC indexes. FossilOrigin-Name: 1559f4c43473e107f7196eea3ee91c53ede22999 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/where.c | 2 +- test/rowvalue4.test | 4 ++++ 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 6c59839b10..bd4c8f2896 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sstat4-based\scost\sestimates\sfor\svector\srange\sconstraints. -D 2016-08-03T16:14:33.444 +C Fix\sanother\sproblem\sinvolving\svector\srange\sconstraints\sand\smixed\sASC/DESC\sindexes. +D 2016-08-03T16:39:04.109 F Makefile.in 6c20d44f72d4564f11652b26291a214c8367e5db F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 3340e479e5221f06c3d61726f8f7efff885e4233 @@ -463,7 +463,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c 02eeecc265f6ffd0597378f5d8ae9070b62a406a F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354 -F src/where.c 21095414c4bf8d5fdf05f3be790bf8b65e370f94 +F src/where.c 8bd54861755e2ca54bc36b0dbc655834f363c5ec F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 F src/wherecode.c c2392fa30bcb0c555a8ae402d646b357ca428ad6 F src/whereexpr.c 4a8cefc7c122132ac9f3ed125c61629a0e3de094 @@ -1021,7 +1021,7 @@ F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d F test/rowvalue.test 56b34d31d91340a6e922e753b798880170cc1aa7 F test/rowvalue2.test 8d5dfe75b8f4d1868a2f91f0356f20d36cba64ff F test/rowvalue3.test dbe935260851b197dfbbbcb0ac2a15cb5f324fd4 -F test/rowvalue4.test 86a04529ab1da3879d6ef56defe15446e4575b3d +F test/rowvalue4.test b902e053544469e06148f504962abf9afb28be65 F test/rowvaluefault.test 7b16485e3f2b371f3e3d05455b8ded6d0c090244 F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09 @@ -1514,7 +1514,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 e496b2d63984311e6ae117677e6c2417ae24b6bc -R 2ab02159e20122e5dc19c46ffdea23b5 +P 18af74abc8ceae47ab9fbee3e3e5bb37db8fcba5 +R 411ee4bf09cf9ee92d07e741a8c6b68c U dan -Z 1b607e01edb5341321c1446f2f3437dc +Z eb44e2dff5400ed92495955ac9fc17ab diff --git a/manifest.uuid b/manifest.uuid index 61422f33d0..97e702c730 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -18af74abc8ceae47ab9fbee3e3e5bb37db8fcba5 \ No newline at end of file +1559f4c43473e107f7196eea3ee91c53ede22999 \ No newline at end of file diff --git a/src/where.c b/src/where.c index a37ab1bbfe..a80054b1c7 100644 --- a/src/where.c +++ b/src/where.c @@ -2234,7 +2234,7 @@ int whereRangeVectorLen( if( pLhs->op!=TK_COLUMN || pLhs->iTable!=iCur || pLhs->iColumn!=pIdx->aiColumn[i+nEq] - || pIdx->aSortOrder[i]!=pIdx->aSortOrder[0] + || pIdx->aSortOrder[i+nEq]!=pIdx->aSortOrder[nEq] ){ break; } diff --git a/test/rowvalue4.test b/test/rowvalue4.test index b02ee169f7..d615aa8e2e 100644 --- a/test/rowvalue4.test +++ b/test/rowvalue4.test @@ -95,6 +95,7 @@ foreach {nm idx} { idx7 { CREATE INDEX t2abc ON t2(a DESC, b DESC) } idx8 { CREATE INDEX t2abc ON t2(c, b, a); } idx9 { CREATE INDEX t2d ON t2(d); } + idx10 { CREATE INDEX t2abc ON t2(a DESC, b, c DESC); } } { drop_all_indexes execsql $idx @@ -113,6 +114,9 @@ foreach {nm idx} { {1 2 3 4 5 6 7 8 9 10 11 12 13} 10 "(a, b, c) = (SELECT a, b, c FROM t2 WHERE d=14)" 14 + + 11 "a = 2 AND (b, c) > (2, 2)" {15 16 17 18} + 12 "a = 2 AND (b, c) < (3, 3) AND (b, c) > (1, 1)" {11 12 13 14 15 16 17} } { set result [db eval "SELECT d FROM t2 WHERE $where"] do_test 2.1.$nm.$tn { lsort -integer $result } $res From 3d1fb1dd758781bd6bcffbdb0f3738a7e1c0d2cf Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 3 Aug 2016 18:00:49 +0000 Subject: [PATCH 19/97] Fix a problem with estimating the number of rows visited by a query that uses a multi-column IN(SELECT...) constraint. FossilOrigin-Name: 3c2f908f5b7312570cfa74afcf4252a857cb5237 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/where.c | 15 ++++++++++++--- test/rowvalue4.test | 28 ++++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index bd4c8f2896..bcec45f458 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sanother\sproblem\sinvolving\svector\srange\sconstraints\sand\smixed\sASC/DESC\sindexes. -D 2016-08-03T16:39:04.109 +C Fix\sa\sproblem\swith\sestimating\sthe\snumber\sof\srows\svisited\sby\sa\squery\sthat\suses\sa\smulti-column\sIN(SELECT...)\sconstraint. +D 2016-08-03T18:00:49.612 F Makefile.in 6c20d44f72d4564f11652b26291a214c8367e5db F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 3340e479e5221f06c3d61726f8f7efff885e4233 @@ -463,7 +463,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c 02eeecc265f6ffd0597378f5d8ae9070b62a406a F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354 -F src/where.c 8bd54861755e2ca54bc36b0dbc655834f363c5ec +F src/where.c 0a0b8af3920f88467d93019acdf570c4f4192f26 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 F src/wherecode.c c2392fa30bcb0c555a8ae402d646b357ca428ad6 F src/whereexpr.c 4a8cefc7c122132ac9f3ed125c61629a0e3de094 @@ -1021,7 +1021,7 @@ F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d F test/rowvalue.test 56b34d31d91340a6e922e753b798880170cc1aa7 F test/rowvalue2.test 8d5dfe75b8f4d1868a2f91f0356f20d36cba64ff F test/rowvalue3.test dbe935260851b197dfbbbcb0ac2a15cb5f324fd4 -F test/rowvalue4.test b902e053544469e06148f504962abf9afb28be65 +F test/rowvalue4.test 1f0fa2d6e4485c2c35cdb997ba57f572fd9919e0 F test/rowvaluefault.test 7b16485e3f2b371f3e3d05455b8ded6d0c090244 F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09 @@ -1514,7 +1514,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 18af74abc8ceae47ab9fbee3e3e5bb37db8fcba5 -R 411ee4bf09cf9ee92d07e741a8c6b68c +P 1559f4c43473e107f7196eea3ee91c53ede22999 +R f540153e91f079ff5413a490814ad157 U dan -Z eb44e2dff5400ed92495955ac9fc17ab +Z 1ca41c3e8d4f391ec12438826156b785 diff --git a/manifest.uuid b/manifest.uuid index 97e702c730..d136916990 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1559f4c43473e107f7196eea3ee91c53ede22999 \ No newline at end of file +3c2f908f5b7312570cfa74afcf4252a857cb5237 \ No newline at end of file diff --git a/src/where.c b/src/where.c index a80054b1c7..877ca72ad1 100644 --- a/src/where.c +++ b/src/where.c @@ -2376,14 +2376,23 @@ static int whereLoopAddBtreeIndex( pNew->wsFlags |= WHERE_COLUMN_IN; if( ExprHasProperty(pExpr, EP_xIsSelect) ){ /* "x IN (SELECT ...)": TUNING: the SELECT returns 25 rows */ + int i; nIn = 46; assert( 46==sqlite3LogEst(25) ); + + /* The expression may actually be of the form (x, y) IN (SELECT...). + ** In this case there is a separate term for each of (x) and (y). + ** However, the nIn multiplier should only be applied once, not once + ** for each such term. The following loop checks that pTerm is the + ** first such term in use, and sets nIn back to 0 if it is not. */ + for(i=0; inLTerm-1; i++){ + if( pNew->aLTerm[i]->pExpr==pExpr ) nIn = 0; + } }else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){ /* "x IN (value, value, ...)" */ nIn = sqlite3LogEst(pExpr->x.pList->nExpr); + assert( nIn>0 ); /* RHS always has 2 or more terms... The parser + ** changes "x IN (?)" into "x=?". */ } - assert( nIn>0 ); /* RHS always has 2 or more terms... The parser - ** changes "x IN (?)" into "x=?". */ - }else if( eOp & (WO_EQ|WO_IS) ){ int iCol = pProbe->aiColumn[saved_nEq]; pNew->wsFlags |= WHERE_COLUMN_EQ; diff --git a/test/rowvalue4.test b/test/rowvalue4.test index d615aa8e2e..a187063081 100644 --- a/test/rowvalue4.test +++ b/test/rowvalue4.test @@ -212,5 +212,33 @@ ifcapable stat4 { } } +#------------------------------------------------------------------------ + +do_execsql_test 5.0 { + CREATE TABLE d1(x, y); + CREATE TABLE d2(a, b, c); + CREATE INDEX d2ab ON d2(a, b); + CREATE INDEX d2c ON d2(c); + + WITH i(i) AS ( + VALUES(1) UNION ALL SELECT i+1 FROM i WHERE i<1000 + ) + INSERT INTO d2 SELECT i/3, i%3, i/3 FROM i; + ANALYZE; +} + +do_eqp_test 5.1 { + SELECT * FROM d2 WHERE + (a, b) IN (SELECT x, y FROM d1) AND + (c) IN (SELECT y FROM d1) +} { + 0 0 0 {SEARCH TABLE d2 USING INDEX d2ab (a=? AND b=?)} + 0 0 0 {EXECUTE LIST SUBQUERY 1} + 1 0 0 {SCAN TABLE d1} + 0 0 0 {EXECUTE LIST SUBQUERY 2} + 2 0 0 {SCAN TABLE d1} +} + + finish_test From 1d9bc9b7a044eb8a2fccc8aaea108bc84a4e3c00 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 8 Aug 2016 18:42:08 +0000 Subject: [PATCH 20/97] Fix the EXPLAIN QUERY PLAN output for row value range constaints that use an index. FossilOrigin-Name: bb60651163553c5e46bf7b2805490570cea647b8 --- manifest | 14 ++++++------ manifest.uuid | 2 +- src/wherecode.c | 56 ++++++++++++++++++++++++++++++--------------- test/rowvalue4.test | 36 ++++++++++++++++++++++++++++- 4 files changed, 80 insertions(+), 28 deletions(-) diff --git a/manifest b/manifest index 3a589e9726..eaaf4038b5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\strunk\schanges\swith\sthis\sbranch. -D 2016-08-08T16:52:11.472 +C Fix\sthe\sEXPLAIN\sQUERY\sPLAN\soutput\sfor\srow\svalue\srange\sconstaints\sthat\suse\san\sindex. +D 2016-08-08T18:42:08.741 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -465,7 +465,7 @@ F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354 F src/where.c f60310d9fa2dd275698f3a768d2c63917353f22d F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 -F src/wherecode.c c2392fa30bcb0c555a8ae402d646b357ca428ad6 +F src/wherecode.c c24645572eba538c04c5ff8b8f6e9c0278107563 F src/whereexpr.c 4a8cefc7c122132ac9f3ed125c61629a0e3de094 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd @@ -1021,7 +1021,7 @@ F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d F test/rowvalue.test 56b34d31d91340a6e922e753b798880170cc1aa7 F test/rowvalue2.test 8d5dfe75b8f4d1868a2f91f0356f20d36cba64ff F test/rowvalue3.test dbe935260851b197dfbbbcb0ac2a15cb5f324fd4 -F test/rowvalue4.test 1f0fa2d6e4485c2c35cdb997ba57f572fd9919e0 +F test/rowvalue4.test 8d3b26c7ab26314b625cd2b113d782b011b91851 F test/rowvaluefault.test 7b16485e3f2b371f3e3d05455b8ded6d0c090244 F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09 @@ -1514,7 +1514,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 3c2f908f5b7312570cfa74afcf4252a857cb5237 d5e98057028abcf7217d0d2b2e29bbbcdf09d6de -R e29053a139f99493b32e4ec805317c91 +P 0e927a7e0250a65fd8e97b322cd69e93fadd13f0 +R c87cf26a8b83e76d6d485dba242f3437 U dan -Z c05c70937976285652f33ed04f5e8c75 +Z eb833a77a96d41ac0d9eabbd9e7d8788 diff --git a/manifest.uuid b/manifest.uuid index d3debee4c2..460e690f37 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0e927a7e0250a65fd8e97b322cd69e93fadd13f0 \ No newline at end of file +bb60651163553c5e46bf7b2805490570cea647b8 \ No newline at end of file diff --git a/src/wherecode.c b/src/wherecode.c index 9aafa83665..901fafecaa 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -21,6 +21,17 @@ #include "whereInt.h" #ifndef SQLITE_OMIT_EXPLAIN + +/* +** Return the name of the i-th column of the pIdx index. +*/ +static const char *explainIndexColumnName(Index *pIdx, int i){ + i = pIdx->aiColumn[i]; + if( i==XN_EXPR ) return ""; + if( i==XN_ROWID ) return "rowid"; + return pIdx->pTable->aCol[i].zName; +} + /* ** This routine is a helper for explainIndexRange() below ** @@ -31,24 +42,32 @@ */ static void explainAppendTerm( StrAccum *pStr, /* The text expression being built */ - int iTerm, /* Index of this term. First is zero */ - const char *zColumn, /* Name of the column */ + Index *pIdx, /* Index to read column names from */ + int nTerm, /* Number of terms */ + int iTerm, /* Zero-based index of first term. */ + int bAnd, /* Non-zero to append " AND " */ const char *zOp /* Name of the operator */ ){ - if( iTerm ) sqlite3StrAccumAppend(pStr, " AND ", 5); - sqlite3StrAccumAppendAll(pStr, zColumn); - sqlite3StrAccumAppend(pStr, zOp, 1); - sqlite3StrAccumAppend(pStr, "?", 1); -} + int i; -/* -** Return the name of the i-th column of the pIdx index. -*/ -static const char *explainIndexColumnName(Index *pIdx, int i){ - i = pIdx->aiColumn[i]; - if( i==XN_EXPR ) return ""; - if( i==XN_ROWID ) return "rowid"; - return pIdx->pTable->aCol[i].zName; + assert( nTerm>=1 ); + if( bAnd ) sqlite3StrAccumAppend(pStr, " AND ", 5); + + if( nTerm>1 ) sqlite3StrAccumAppend(pStr, "(", 1); + for(i=0; i1 ) sqlite3StrAccumAppend(pStr, ")", 1); + + sqlite3StrAccumAppend(pStr, zOp, 1); + + if( nTerm>1 ) sqlite3StrAccumAppend(pStr, "(", 1); + for(i=0; i1 ) sqlite3StrAccumAppend(pStr, ")", 1); } /* @@ -81,12 +100,11 @@ static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop){ j = i; if( pLoop->wsFlags&WHERE_BTM_LIMIT ){ - const char *z = explainIndexColumnName(pIndex, i); - explainAppendTerm(pStr, i++, z, ">"); + explainAppendTerm(pStr, pIndex, pLoop->u.btree.nBtm, j, i, ">"); + i = 1; } if( pLoop->wsFlags&WHERE_TOP_LIMIT ){ - const char *z = explainIndexColumnName(pIndex, j); - explainAppendTerm(pStr, i, z, "<"); + explainAppendTerm(pStr, pIndex, pLoop->u.btree.nTop, j, i, "<"); } sqlite3StrAccumAppend(pStr, ")", 1); } diff --git a/test/rowvalue4.test b/test/rowvalue4.test index a187063081..5530f8f5ca 100644 --- a/test/rowvalue4.test +++ b/test/rowvalue4.test @@ -205,7 +205,7 @@ ifcapable stat4 { 0 0 0 {SEARCH TABLE c1 USING INDEX c1ab (a=?)} } do_eqp_test 3.2.5 { SELECT * FROM c1 WHERE a=1 AND (c, d)>(1, 'o') } { - 0 0 0 {SEARCH TABLE c1 USING INDEX c1cd (c>?)} + 0 0 0 {SEARCH TABLE c1 USING INDEX c1cd ((c,d)>(?,?))} } do_eqp_test 3.2.6 { SELECT * FROM c1 WHERE a=1 AND (c, +b)>(1, 'c') } { 0 0 0 {SEARCH TABLE c1 USING INDEX c1ab (a=?)} @@ -239,6 +239,40 @@ do_eqp_test 5.1 { 2 0 0 {SCAN TABLE d1} } +do_execsql_test 6.0 { + CREATE TABLE e1(a, b, c, d, e); + CREATE INDEX e1ab ON e1(a, b); + CREATE INDEX e1cde ON e1(c, d, e); +} + +do_eqp_test 6.1 { + SELECT * FROM e1 WHERE (a, b) > (?, ?) +} { + 0 0 0 {SEARCH TABLE e1 USING INDEX e1ab ((a,b)>(?,?))} +} +do_eqp_test 6.2 { + SELECT * FROM e1 WHERE (a, b) < (?, ?) +} { + 0 0 0 {SEARCH TABLE e1 USING INDEX e1ab ((a,b)<(?,?))} +} +do_eqp_test 6.3 { + SELECT * FROM e1 WHERE c = ? AND (d, e) > (?, ?) +} { + 0 0 0 {SEARCH TABLE e1 USING INDEX e1cde (c=? AND (d,e)>(?,?))} +} +do_eqp_test 6.4 { + SELECT * FROM e1 WHERE c = ? AND (d, e) < (?, ?) +} { + 0 0 0 {SEARCH TABLE e1 USING INDEX e1cde (c=? AND (d,e)<(?,?))} +} + +do_eqp_test 6.5 { + SELECT * FROM e1 WHERE (d, e) BETWEEN (?, ?) AND (?, ?) AND c = ? +} { + 0 0 0 + {SEARCH TABLE e1 USING INDEX e1cde (c=? AND (d,e)>(?,?) AND (d,e)<(?,?))} +} + finish_test From 6256c1c2427a4f9867589a7f4204c0b2fe8028c9 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 8 Aug 2016 20:15:41 +0000 Subject: [PATCH 21/97] Fix some cases involving row values and virtual tables. FossilOrigin-Name: 156a41f30a0afd9a70e6c26470dcc468a11bd402 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/where.c | 34 +++++++++++++++++++++++++++------- src/wherecode.c | 7 ++++++- 4 files changed, 41 insertions(+), 16 deletions(-) diff --git a/manifest b/manifest index eaaf4038b5..b187cc2dd9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sthe\sEXPLAIN\sQUERY\sPLAN\soutput\sfor\srow\svalue\srange\sconstaints\sthat\suse\san\sindex. -D 2016-08-08T18:42:08.741 +C Fix\ssome\scases\sinvolving\srow\svalues\sand\svirtual\stables. +D 2016-08-08T20:15:41.766 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -463,9 +463,9 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c 02eeecc265f6ffd0597378f5d8ae9070b62a406a F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354 -F src/where.c f60310d9fa2dd275698f3a768d2c63917353f22d +F src/where.c 457a2c8cd94a1dbe8d2e5113f6078f2e6b9067d5 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 -F src/wherecode.c c24645572eba538c04c5ff8b8f6e9c0278107563 +F src/wherecode.c 92202261a6e41f897a595417c5b0c75c8acf713d F src/whereexpr.c 4a8cefc7c122132ac9f3ed125c61629a0e3de094 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd @@ -1514,7 +1514,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 0e927a7e0250a65fd8e97b322cd69e93fadd13f0 -R c87cf26a8b83e76d6d485dba242f3437 +P bb60651163553c5e46bf7b2805490570cea647b8 +R 874f48b6764b2a2fd58ccae5d488cbf6 U dan -Z eb833a77a96d41ac0d9eabbd9e7d8788 +Z 25cd4c4731603d2ae10da74afc2be17b diff --git a/manifest.uuid b/manifest.uuid index 460e690f37..0e8e885da4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bb60651163553c5e46bf7b2805490570cea647b8 \ No newline at end of file +156a41f30a0afd9a70e6c26470dcc468a11bd402 \ No newline at end of file diff --git a/src/where.c b/src/where.c index 577a1147cb..9faec64bc1 100644 --- a/src/where.c +++ b/src/where.c @@ -826,7 +826,8 @@ static sqlite3_index_info *allocateIndexInfo( WhereClause *pWC, Bitmask mUnusable, /* Ignore terms with these prereqs */ struct SrcList_item *pSrc, - ExprList *pOrderBy + ExprList *pOrderBy, + u16 *pmNoOmit /* Mask of terms not to omit */ ){ int i, j; int nTerm; @@ -836,6 +837,7 @@ static sqlite3_index_info *allocateIndexInfo( WhereTerm *pTerm; int nOrderBy; sqlite3_index_info *pIdxInfo; + u16 mNoOmit = 0; /* Count the number of possible WHERE clause constraints referring ** to this virtual table */ @@ -924,6 +926,15 @@ static sqlite3_index_info *allocateIndexInfo( assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE ); assert( WO_MATCH==SQLITE_INDEX_CONSTRAINT_MATCH ); assert( pTerm->eOperator & (WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_MATCH) ); + + if( op & (WO_LT|WO_LE|WO_GT|WO_GE) + && sqlite3ExprIsVector(pTerm->pExpr->pRight) + ){ + if( i<16 ) mNoOmit |= (1 << i); + if( op==WO_LT ) pIdxCons[j].op = WO_LE; + if( op==WO_GT ) pIdxCons[j].op = WO_GE; + } + j++; } for(i=0; ia[i].sortOrder; } + *pmNoOmit = mNoOmit; return pIdxInfo; } @@ -2943,6 +2955,7 @@ static int whereLoopAddVirtualOne( Bitmask mUsable, /* Mask of usable tables */ u16 mExclude, /* Exclude terms using these operators */ sqlite3_index_info *pIdxInfo, /* Populated object for xBestIndex */ + u16 mNoOmit, /* Do not omit these constraints */ int *pbIn /* OUT: True if plan uses an IN(...) op */ ){ WhereClause *pWC = pBuilder->pWC; @@ -3031,6 +3044,7 @@ static int whereLoopAddVirtualOne( } } } + pNew->u.vtab.omitMask &= ~mNoOmit; pNew->nLTerm = mxTerm+1; assert( pNew->nLTerm<=pNew->nLSlot ); @@ -3104,6 +3118,7 @@ static int whereLoopAddVirtual( int bIn; /* True if plan uses IN(...) operator */ WhereLoop *pNew; Bitmask mBest; /* Tables used by best possible plan */ + u16 mNoOmit; assert( (mPrereq & mUnusable)==0 ); pWInfo = pBuilder->pWInfo; @@ -3112,7 +3127,8 @@ static int whereLoopAddVirtual( pNew = pBuilder->pNew; pSrc = &pWInfo->pTabList->a[pNew->iTab]; assert( IsVirtual(pSrc->pTab) ); - p = allocateIndexInfo(pParse, pWC, mUnusable, pSrc, pBuilder->pOrderBy); + p = allocateIndexInfo(pParse, pWC, mUnusable, pSrc, pBuilder->pOrderBy, + &mNoOmit); if( p==0 ) return SQLITE_NOMEM_BKPT; pNew->rSetup = 0; pNew->wsFlags = WHERE_VIRTUALTABLE; @@ -3126,7 +3142,7 @@ static int whereLoopAddVirtual( /* First call xBestIndex() with all constraints usable. */ WHERETRACE(0x40, (" VirtualOne: all usable\n")); - rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, &bIn); + rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn); /* If the call to xBestIndex() with all terms enabled produced a plan ** that does not require any source tables (IOW: a plan with mBest==0), @@ -3143,7 +3159,8 @@ static int whereLoopAddVirtual( ** xBestIndex again, this time with IN(...) terms disabled. */ if( bIn ){ WHERETRACE(0x40, (" VirtualOne: all usable w/o IN\n")); - rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, WO_IN, p, &bIn); + rc = whereLoopAddVirtualOne( + pBuilder, mPrereq, ALLBITS, WO_IN, p, mNoOmit, &bIn); assert( bIn==0 ); mBestNoIn = pNew->prereq & ~mPrereq; if( mBestNoIn==0 ){ @@ -3169,7 +3186,8 @@ static int whereLoopAddVirtual( if( mNext==mBest || mNext==mBestNoIn ) continue; WHERETRACE(0x40, (" VirtualOne: mPrev=%04llx mNext=%04llx\n", (sqlite3_uint64)mPrev, (sqlite3_uint64)mNext)); - rc = whereLoopAddVirtualOne(pBuilder, mPrereq, mNext|mPrereq, 0, p, &bIn); + rc = whereLoopAddVirtualOne( + pBuilder, mPrereq, mNext|mPrereq, 0, p, mNoOmit, &bIn); if( pNew->prereq==mPrereq ){ seenZero = 1; if( bIn==0 ) seenZeroNoIN = 1; @@ -3181,7 +3199,8 @@ static int whereLoopAddVirtual( ** usable), make a call here with all source tables disabled */ if( rc==SQLITE_OK && seenZero==0 ){ WHERETRACE(0x40, (" VirtualOne: all disabled\n")); - rc = whereLoopAddVirtualOne(pBuilder, mPrereq, mPrereq, 0, p, &bIn); + rc = whereLoopAddVirtualOne( + pBuilder, mPrereq, mPrereq, 0, p, mNoOmit, &bIn); if( bIn==0 ) seenZeroNoIN = 1; } @@ -3190,7 +3209,8 @@ static int whereLoopAddVirtual( ** operator, make a final call to obtain one here. */ if( rc==SQLITE_OK && seenZeroNoIN==0 ){ WHERETRACE(0x40, (" VirtualOne: all disabled and w/o IN\n")); - rc = whereLoopAddVirtualOne(pBuilder, mPrereq, mPrereq, WO_IN, p, &bIn); + rc = whereLoopAddVirtualOne( + pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn); } } diff --git a/src/wherecode.c b/src/wherecode.c index 901fafecaa..80de8a7e54 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -1099,7 +1099,12 @@ Bitmask sqlite3WhereCodeOneLoopStart( codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget); addrNotFound = pLevel->addrNxt; }else{ - sqlite3ExprCode(pParse, pTerm->pExpr->pRight, iTarget); + Expr *pRight = pTerm->pExpr->pRight; + if( pRight->op==TK_SELECT_COLUMN ){ + codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget); + }else{ + codeExprOrVector(pParse, pRight, iTarget, 1); + } } } sqlite3VdbeAddOp2(v, OP_Integer, pLoop->u.vtab.idxNum, iReg); From 9cd4933ec11db313936e1627c83fdc2d9465acf7 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 9 Aug 2016 05:48:40 +0000 Subject: [PATCH 22/97] Add rowvalue5.test, which should have been part of the previous commit on this branch. FossilOrigin-Name: ea03e219ced87777f0c3c6bbb0274078dbb829e2 --- manifest | 11 +++-- manifest.uuid | 2 +- test/rowvalue5.test | 113 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 6 deletions(-) create mode 100644 test/rowvalue5.test diff --git a/manifest b/manifest index b187cc2dd9..db8399f339 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\ssome\scases\sinvolving\srow\svalues\sand\svirtual\stables. -D 2016-08-08T20:15:41.766 +C Add\srowvalue5.test,\swhich\sshould\shave\sbeen\spart\sof\sthe\sprevious\scommit\son\sthis\sbranch. +D 2016-08-09T05:48:40.637 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -1022,6 +1022,7 @@ F test/rowvalue.test 56b34d31d91340a6e922e753b798880170cc1aa7 F test/rowvalue2.test 8d5dfe75b8f4d1868a2f91f0356f20d36cba64ff F test/rowvalue3.test dbe935260851b197dfbbbcb0ac2a15cb5f324fd4 F test/rowvalue4.test 8d3b26c7ab26314b625cd2b113d782b011b91851 +F test/rowvalue5.test 01c7e0bc4048f30b58e6eb27ecd26e5bd312635e F test/rowvaluefault.test 7b16485e3f2b371f3e3d05455b8ded6d0c090244 F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09 @@ -1514,7 +1515,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 bb60651163553c5e46bf7b2805490570cea647b8 -R 874f48b6764b2a2fd58ccae5d488cbf6 +P 156a41f30a0afd9a70e6c26470dcc468a11bd402 +R c38c81cb8ebb8c1f60f796251d669e34 U dan -Z 25cd4c4731603d2ae10da74afc2be17b +Z f2cc02b61092cf3b9735f66f5f90cbf0 diff --git a/manifest.uuid b/manifest.uuid index 0e8e885da4..293e4dd8a1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -156a41f30a0afd9a70e6c26470dcc468a11bd402 \ No newline at end of file +ea03e219ced87777f0c3c6bbb0274078dbb829e2 \ No newline at end of file diff --git a/test/rowvalue5.test b/test/rowvalue5.test new file mode 100644 index 0000000000..c961cf7424 --- /dev/null +++ b/test/rowvalue5.test @@ -0,0 +1,113 @@ +# 2016 July 29 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is syntax errors involving row-values and +# virtual tables. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix rowvalue5 + +proc vtab_command {method args} { + switch -- $method { + xConnect { + return "CREATE TABLE t1(a, b, c, d, expr)" + } + + xBestIndex { + set COL(0) a + set COL(1) b + set COL(2) c + set COL(3) d + set COL(4) expr + + set OP(eq) = + set OP(ne) != + set OP(gt) > + set OP(le) <= + set OP(lt) < + set OP(ge) >= + set OP(match) MATCH + set OP(like) LIKE + set OP(glob) GLOB + set OP(regexp) REGEXP + + set clist [lindex $args 0] + set ret [list] + set elist [list] + set i 0 + foreach c $clist { + array set C $c + if {$C(usable)} { + lappend ret omit $i + lappend elist "$COL($C(column)) $OP($C(op)) %$i%" + } + incr i + } + + lappend ret idxstr [join $elist " AND "] + #puts "xBestIndex: $ret" + return $ret + } + + xFilter { + foreach {idxnum idxstr arglist} $args {} + set i 0 + set ee $idxstr + foreach a $arglist { + if {[string is double $a]==0} { + set a "'[string map {' ''} $a]'" + } + set ee [string map [list "%$i%" $a] $ee] + incr i + } + set ee [string map [list "'" "''"] $ee] + + set ret [list sql "SELECT 1, 'a', 'b', 'c', 'd', '$ee'"] + #puts "xFilter: $ret" + return $ret + } + } + + return {} +} + +register_tcl_module db +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE x1 USING tcl(vtab_command); +} {} + + +foreach {tn where res} { + 1 "1" {{}} + 2 "a=1" {{a = 1}} + 3 "a=1 AND 4 = b" {{a = 1 AND b = 4}} + 4 "c>'hello'" {{c > 'hello'}} + 5 "c<='hel''lo'" {{c <= 'hel''lo'}} + 6 "(a, b) = (SELECT 9, 10)" {{a = 9 AND b = 10}} + 7 "(+a, b) = (SELECT 'a', 'b')" {{b = 'b'}} + 8 "(a, +b) = (SELECT 'a', 'b')" {{a = 'a'}} + 10 "(a, b) IN (SELECT 9, 10 UNION SELECT 11, 12)" + {{a = 9 AND b = 10} {a = 11 AND b = 12}} + 11 "(+a, b) IN (SELECT 'a', 'b')" {{b = 'b'}} + 12 "(a, +b) IN (SELECT 'a', 'b')" {{a = 'a'}} + + 13 "(a, b) < ('d', 'e')" {{a <= 'd'}} + 14 "(a, b) < ('a', 'c')" {{a <= 'a'}} + 15 "(a, b) <= ('a', 'b')" {{a <= 'a'}} + 16 "(a, b) < ('a', 'b')" {} +} { + do_execsql_test 1.$tn "SELECT expr FROM x1 WHERE $where" $res +} + +finish_test + From 17994e3bcabc6a277566b43c927f38769cef8856 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 11 Aug 2016 12:01:52 +0000 Subject: [PATCH 23/97] Fix some problems with handling "no such collation sequence" errors. FossilOrigin-Name: 8278be06fa69e3266866220bdaf5be45a5fcfa23 --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/expr.c | 3 +++ src/where.c | 2 +- test/rowvalue4.test | 18 ++++++++++++++++++ 5 files changed, 31 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index db8399f339..96a9786372 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\srowvalue5.test,\swhich\sshould\shave\sbeen\spart\sof\sthe\sprevious\scommit\son\sthis\sbranch. -D 2016-08-09T05:48:40.637 +C Fix\ssome\sproblems\swith\shandling\s"no\ssuch\scollation\ssequence"\serrors. +D 2016-08-11T12:01:52.255 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -337,7 +337,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 4f6f7f52b49beb9636ffbd517cfe44a402ba4ad0 F src/delete.c 4aba4214a377ce8ddde2d2e609777bcc8235200f -F src/expr.c 94b3d94696e86840f2d0d038c638eefde57c2a40 +F src/expr.c dfc2275726e0a4916f10fe6b5b4f45fff835e790 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c bc4145347595b7770f9a598cff1c848302cf5413 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -463,7 +463,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c 02eeecc265f6ffd0597378f5d8ae9070b62a406a F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354 -F src/where.c 457a2c8cd94a1dbe8d2e5113f6078f2e6b9067d5 +F src/where.c 5bee250c8233c43bd7f53897d12b8468004f63db F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 F src/wherecode.c 92202261a6e41f897a595417c5b0c75c8acf713d F src/whereexpr.c 4a8cefc7c122132ac9f3ed125c61629a0e3de094 @@ -1021,7 +1021,7 @@ F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d F test/rowvalue.test 56b34d31d91340a6e922e753b798880170cc1aa7 F test/rowvalue2.test 8d5dfe75b8f4d1868a2f91f0356f20d36cba64ff F test/rowvalue3.test dbe935260851b197dfbbbcb0ac2a15cb5f324fd4 -F test/rowvalue4.test 8d3b26c7ab26314b625cd2b113d782b011b91851 +F test/rowvalue4.test ed3f7974099f0fc508c9f95df55d37c49f43be65 F test/rowvalue5.test 01c7e0bc4048f30b58e6eb27ecd26e5bd312635e F test/rowvaluefault.test 7b16485e3f2b371f3e3d05455b8ded6d0c090244 F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 @@ -1515,7 +1515,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 156a41f30a0afd9a70e6c26470dcc468a11bd402 -R c38c81cb8ebb8c1f60f796251d669e34 +P ea03e219ced87777f0c3c6bbb0274078dbb829e2 +R 3327e29017db82c17326a5b9067bf8a5 U dan -Z f2cc02b61092cf3b9735f66f5f90cbf0 +Z 2191ed49fb7f045b414ebf34ab584005 diff --git a/manifest.uuid b/manifest.uuid index 293e4dd8a1..b4dbc8b833 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ea03e219ced87777f0c3c6bbb0274078dbb829e2 \ No newline at end of file +8278be06fa69e3266866220bdaf5be45a5fcfa23 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index a0e4e5cb18..8303c5f784 100644 --- a/src/expr.c +++ b/src/expr.c @@ -2056,6 +2056,9 @@ int sqlite3FindInIndex( CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs); int j; + assert( pReq || pParse->nErr ); + if( pReq==0 ) break; + for(j=0; jaiColumn[j]!=pRhs->iColumn ) continue; assert( pIdx->azColl[j] ); diff --git a/src/where.c b/src/where.c index 9faec64bc1..f084b93701 100644 --- a/src/where.c +++ b/src/where.c @@ -2256,7 +2256,7 @@ int whereRangeVectorLen( if( aff!=idxaff ) break; pColl = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs); - if( sqlite3StrICmp(pColl->zName, pIdx->azColl[i+nEq]) ) break; + if( pColl==0 || sqlite3StrICmp(pColl->zName, pIdx->azColl[i+nEq]) ) break; } return i; } diff --git a/test/rowvalue4.test b/test/rowvalue4.test index 5530f8f5ca..55401aa8a3 100644 --- a/test/rowvalue4.test +++ b/test/rowvalue4.test @@ -273,6 +273,24 @@ do_eqp_test 6.5 { {SEARCH TABLE e1 USING INDEX e1cde (c=? AND (d,e)>(?,?) AND (d,e)<(?,?))} } +#------------------------------------------------------------------------- + +do_execsql_test 7.1 { + CREATE TABLE f1(a, b, c); + CREATE INDEX f1ab ON f1(a, b); +} + +do_catchsql_test 7.2 { + SELECT (a COLLATE nocase, b) IN (SELECT a, b FROM f1) FROM f1; +} {0 {}} + +do_catchsql_test 7.3 { + SELECT (a COLLATE nose, b) IN (SELECT a, b FROM f1) FROM f1; +} {1 {no such collation sequence: nose}} + +do_catchsql_test 7.4 { + SELECT * FROM f1 WHERE (?, ? COLLATE nose) > (a, b); +} {1 {no such collation sequence: nose}} finish_test From a48d7e77496b1463c2bea902b331b049bbcaeb01 Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 12 Aug 2016 11:01:20 +0000 Subject: [PATCH 24/97] Fix a post-OOM memory leak. FossilOrigin-Name: 14009b32b955b42cfd5f0c2ce7d4b3ce19ce201e --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/expr.c | 5 +++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 37624e38f2..47aec35253 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\srecent\schanges\sfrom\strunk. -D 2016-08-11T12:31:21.178 +C Fix\sa\spost-OOM\smemory\sleak. +D 2016-08-12T11:01:20.692 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 4f6f7f52b49beb9636ffbd517cfe44a402ba4ad0 F src/delete.c 4aba4214a377ce8ddde2d2e609777bcc8235200f -F src/expr.c dfc2275726e0a4916f10fe6b5b4f45fff835e790 +F src/expr.c 04b320c93f61ddc24ee032dd83317179c5f78a8b F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c bc4145347595b7770f9a598cff1c848302cf5413 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1516,7 +1516,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 8278be06fa69e3266866220bdaf5be45a5fcfa23 ab83d7077da80ddbcf399d0797d79e964dc64f0e -R 8a0bc19d70d02f930372e2439d9171fd +P 959677b97ba15f786936c762e2e68df1151d7ed0 +R a5cbb9bafc9fbcda9f99cc259b487bcb U drh -Z 019cf0a70c3da3c6f133d37b0001aac6 +Z b67a6933488c39df334f580c3bf48fca diff --git a/manifest.uuid b/manifest.uuid index 8c6670f9ff..847f607ac3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -959677b97ba15f786936c762e2e68df1151d7ed0 \ No newline at end of file +14009b32b955b42cfd5f0c2ce7d4b3ce19ce201e \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 8303c5f784..5ef692a440 100644 --- a/src/expr.c +++ b/src/expr.c @@ -2506,12 +2506,13 @@ static void sqlite3ExprCodeIN( if( sqlite3ExprCheckIN(pParse, pExpr) ) return; zAff = exprINAffinity(pParse, pExpr); + if( zAff==0 ) return; nVector = sqlite3ExprVectorSize(pExpr->pLeft); aiMap = (int*)sqlite3DbMallocZero( pParse->db, nVector*(sizeof(int) + sizeof(char)) + 1 ); - if( !zAff || !aiMap ){ - sqlite3DbFree(pParse->db, aiMap); + if( aiMap==0 ){ + sqlite3DbFree(pParse->db, zAff); return; } From 471b4b92bdb2a203d048f2c87fb53d8d83381c0b Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 12 Aug 2016 11:25:49 +0000 Subject: [PATCH 25/97] Add VdbeCoverage() macros on newly added VDBE branch operations. FossilOrigin-Name: 381aa73141db8ec59adbcb09e71af660ee4ae5ce --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/expr.c | 6 ++++++ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 47aec35253..ca1fac3357 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\spost-OOM\smemory\sleak. -D 2016-08-12T11:01:20.692 +C Add\sVdbeCoverage()\smacros\son\snewly\sadded\sVDBE\sbranch\soperations. +D 2016-08-12T11:25:49.567 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 4f6f7f52b49beb9636ffbd517cfe44a402ba4ad0 F src/delete.c 4aba4214a377ce8ddde2d2e609777bcc8235200f -F src/expr.c 04b320c93f61ddc24ee032dd83317179c5f78a8b +F src/expr.c d05cc249f8615bd4655f839ee57c24d11d005dde F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c bc4145347595b7770f9a598cff1c848302cf5413 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1516,7 +1516,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 959677b97ba15f786936c762e2e68df1151d7ed0 -R a5cbb9bafc9fbcda9f99cc259b487bcb +P 14009b32b955b42cfd5f0c2ce7d4b3ce19ce201e +R 57be00fa8f04c7e5888f286343bb13cf U drh -Z b67a6933488c39df334f580c3bf48fca +Z c05ca00f4c6d3d32267dd4171ca3bdf0 diff --git a/manifest.uuid b/manifest.uuid index 847f607ac3..e0b19abe4c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -14009b32b955b42cfd5f0c2ce7d4b3ce19ce201e \ No newline at end of file +381aa73141db8ec59adbcb09e71af660ee4ae5ce \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 5ef692a440..3faeae79d2 100644 --- a/src/expr.c +++ b/src/expr.c @@ -2606,6 +2606,7 @@ static void sqlite3ExprCodeIN( Expr *p = sqlite3ExprVectorField(pExpr->pLeft, i); if( sqlite3ExprCanBeNull(p) ){ sqlite3VdbeAddOp2(v, OP_IsNull, r1+aiMap[i], destIfNull); + VdbeCoverage(v); } } }else if( nVector==1 && sqlite3ExprCanBeNull(pExpr->pLeft) ){ @@ -2632,11 +2633,13 @@ static void sqlite3ExprCodeIN( /* Search the index for the key. */ addr = sqlite3VdbeAddOp4Int(v, OP_Found, iIdx, 0, r1, nVector); + VdbeCoverage(v); /* At this point the specified key is not present in the index, ** so the result of the IN(..) operator must be either NULL or ** 0. The vdbe code generated below figures out which. */ addrNext = 1+sqlite3VdbeAddOp2(v, OP_Rewind, iIdx, destIfFalse); + VdbeCoverage(v); for(i=0; ipLeft, i); if( sqlite3ExprCanBeNull(p) ){ sqlite3VdbeAddOp2(v, OP_IsNull, r1+aiMap[i], destIfNull); + VdbeCoverage(v); } } From 79752b6e63c03a18fd93817c6e9580d7445a7ce9 Mon Sep 17 00:00:00 2001 From: drh Date: Sat, 13 Aug 2016 10:02:17 +0000 Subject: [PATCH 26/97] Attempt to simplify the logic and generated code for vector comparisons. Basic comparison operators are working, but there are many indexing test failures still to be worked through. FossilOrigin-Name: dfc028cfbe7657d20727a2670ecadb1575eb8cbb --- manifest | 19 +++-- manifest.uuid | 2 +- src/expr.c | 109 ++++++++++++++------------- src/sqliteInt.h | 1 + src/vdbe.c | 195 ++++++++++++++++++++---------------------------- 5 files changed, 150 insertions(+), 176 deletions(-) diff --git a/manifest b/manifest index ca1fac3357..1a0b48fa4a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sVdbeCoverage()\smacros\son\snewly\sadded\sVDBE\sbranch\soperations. -D 2016-08-12T11:25:49.567 +C Attempt\sto\ssimplify\sthe\slogic\sand\sgenerated\scode\sfor\svector\scomparisons.\nBasic\scomparison\soperators\sare\sworking,\sbut\sthere\sare\smany\sindexing\stest\nfailures\sstill\sto\sbe\sworked\sthrough. +D 2016-08-13T10:02:17.801 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 4f6f7f52b49beb9636ffbd517cfe44a402ba4ad0 F src/delete.c 4aba4214a377ce8ddde2d2e609777bcc8235200f -F src/expr.c d05cc249f8615bd4655f839ee57c24d11d005dde +F src/expr.c 375de68ad2daf3bd339f79074ced5a6db77e2f62 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c bc4145347595b7770f9a598cff1c848302cf5413 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -389,7 +389,7 @@ F src/shell.c 79dda477be6c96eba6e952a934957ad36f87acc7 F src/sqlite.h.in 0f7580280d1b009b507d8beec1ff0f197ba0cc99 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 8648034aa702469afb553231677306cc6492a1ae -F src/sqliteInt.h a1cf00afd6a5666a160e81c7a600418a3b59a8a6 +F src/sqliteInt.h 98d9ccfa30c0d4b1b886ed61f409dc6b307e9b0f F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247 F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1 F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9 @@ -450,7 +450,7 @@ F src/update.c 4f05ea8cddfa367d045e03589756c02199e8f9bd F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c F src/util.c 810ec3f22e2d1b62e66c30fe3621ebdedd23584d F src/vacuum.c 9dd2f5d276bc6094d8f1d85ecd41b30c1a002a43 -F src/vdbe.c 9f15129214a55044f918a983e1560fd87b0130a8 +F src/vdbe.c 9816bc4f89e4d58340f3cf3354fd062e2da11f8a F src/vdbe.h 67bc551f7faf04c33493892e4b378aada823ed10 F src/vdbeInt.h c59381049af5c7751a83456c39b80d1a6fde1f9d F src/vdbeapi.c c3f6715a99995c11748ecad91d25e93fd9fc390b @@ -1516,7 +1516,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 14009b32b955b42cfd5f0c2ce7d4b3ce19ce201e -R 57be00fa8f04c7e5888f286343bb13cf +P 381aa73141db8ec59adbcb09e71af660ee4ae5ce +R b082d1240e853643b05ab20adb3ffc1b +T *branch * vector-compare +T *sym-vector-compare * +T -sym-rowvalue * U drh -Z c05ca00f4c6d3d32267dd4171ca3bdf0 +Z 873e26b27a2fd221371ad3338313919b diff --git a/manifest.uuid b/manifest.uuid index e0b19abe4c..617135c700 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -381aa73141db8ec59adbcb09e71af660ee4ae5ce \ No newline at end of file +dfc028cfbe7657d20727a2670ecadb1575eb8cbb \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 3faeae79d2..54bf0dfaba 100644 --- a/src/expr.c +++ b/src/expr.c @@ -409,37 +409,52 @@ static int exprVectorRegister( /* ** Expression pExpr is a comparison between two vector values. Compute -** the result of the comparison and write it to register dest. +** the result of the comparison (1, 0, or NULL) and write that +** result into register dest. +** +** The caller must satisfy the following preconditions: +** +** if pExpr->op==TK_IS: op==TK_EQ and p5==SQLITE_NULLEQ +** if pExpr->op==TK_ISNOT: op==TK_NE and p5==SQLITE_NULLEQ +** otherwise: op==pExpr->op and p5==0 */ -static void codeVectorCompare(Parse *pParse, Expr *pExpr, int dest){ +static void codeVectorCompare( + Parse *pParse, /* Code generator context */ + Expr *pExpr, /* The comparison operation */ + int dest, /* Write results into this register */ + u8 op, /* Comparison operator */ + u8 p5 /* SQLITE_NULLEQ or zero */ +){ Vdbe *v = pParse->pVdbe; Expr *pLeft = pExpr->pLeft; Expr *pRight = pExpr->pRight; int nLeft = sqlite3ExprVectorSize(pLeft); int nRight = sqlite3ExprVectorSize(pRight); - int addr = sqlite3VdbeMakeLabel(v); /* Check that both sides of the comparison are vectors, and that ** both are the same length. */ if( nLeft!=nRight ){ sqlite3ErrorMsg(pParse, "invalid use of row value"); }else{ - int p5 = (pExpr->op==TK_IS || pExpr->op==TK_ISNOT) ? SQLITE_NULLEQ : 0; int i; int regLeft = 0; int regRight = 0; - int regTmp = 0; + u8 opx = op; + int addrDone = sqlite3VdbeMakeLabel(v); assert( pExpr->op==TK_EQ || pExpr->op==TK_NE || pExpr->op==TK_IS || pExpr->op==TK_ISNOT || pExpr->op==TK_LT || pExpr->op==TK_GT || pExpr->op==TK_LE || pExpr->op==TK_GE ); + assert( pExpr->op==op || (pExpr->op==TK_IS && op==TK_EQ) + || (pExpr->op==TK_ISNOT && op==TK_NE) ); + assert( p5==0 || pExpr->op!=op ); + assert( p5==SQLITE_NULLEQ || pExpr->op==op ); - if( pExpr->op==TK_EQ || pExpr->op==TK_NE ){ - regTmp = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp2(v, OP_Integer, (pExpr->op==TK_EQ), dest); - } + p5 |= SQLITE_STOREP2; + if( opx==TK_LE ) opx = TK_LT; + if( opx==TK_GE ) opx = TK_GT; regLeft = exprCodeSubselect(pParse, pLeft); regRight = exprCodeSubselect(pParse, pRight); @@ -448,55 +463,43 @@ static void codeVectorCompare(Parse *pParse, Expr *pExpr, int dest){ int regFree1 = 0, regFree2 = 0; Expr *pL, *pR; int r1, r2; - if( i ) sqlite3ExprCachePush(pParse); + if( i>0 ) sqlite3ExprCachePush(pParse); r1 = exprVectorRegister(pParse, pLeft, i, regLeft, &pL, ®Free1); r2 = exprVectorRegister(pParse, pRight, i, regRight, &pR, ®Free2); - - switch( pExpr->op ){ - case TK_IS: - codeCompare( - pParse, pL, pR, OP_Eq, r1, r2, dest, SQLITE_STOREP2|SQLITE_NULLEQ - ); - sqlite3VdbeAddOp3(v, OP_IfNot, dest, addr, 1); - VdbeCoverage(v); - break; - - case TK_ISNOT: - codeCompare( - pParse, pL, pR, OP_Ne, r1, r2, dest, SQLITE_STOREP2|SQLITE_NULLEQ - ); - sqlite3VdbeAddOp3(v, OP_If, dest, addr, 1); - VdbeCoverage(v); - break; - - case TK_EQ: - case TK_NE: - codeCompare(pParse, pL, pR, OP_Cmp, r1, r2, regTmp,SQLITE_STOREP2|p5); - sqlite3VdbeAddOp4Int( - v, OP_CmpTest, regTmp, addr, dest, pExpr->op==TK_NE - ); - VdbeCoverage(v); - break; - - case TK_LT: - case TK_LE: - case TK_GT: - case TK_GE: - codeCompare(pParse, pL, pR, OP_Cmp, r1, r2, dest, SQLITE_STOREP2|p5); - sqlite3VdbeAddOp4Int(v, OP_CmpTest, dest, addr, 0, pExpr->op); - VdbeCoverage(v); - break; - } - + codeCompare(pParse, pL, pR, opx, r1, r2, dest, p5); + testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt); + testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le); + testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt); + testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge); + testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq); + testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne); sqlite3ReleaseTempReg(pParse, regFree1); sqlite3ReleaseTempReg(pParse, regFree2); - if( i ) sqlite3ExprCachePop(pParse); + if( i>0 ) sqlite3ExprCachePop(pParse); + if( i==nLeft-1 ){ + break; + } + if( opx==TK_EQ ){ + sqlite3VdbeAddOp2(v, OP_IfNot, dest, addrDone); VdbeCoverage(v); + p5 |= SQLITE_KEEPNULL; + }else if( opx==TK_NE ){ + sqlite3VdbeAddOp2(v, OP_If, dest, addrDone); VdbeCoverage(v); + p5 |= SQLITE_KEEPNULL; + }else if( opx==op ){ + assert( op==TK_LT || op==TK_GT ); + sqlite3VdbeAddOp3(v, OP_If, dest, addrDone, 1); + VdbeCoverageIf(v, op==TK_LT); + VdbeCoverageIf(v, op==TK_GT); + }else{ + assert( op==TK_LE || op==TK_GE ); + sqlite3VdbeAddOp2(v, OP_ElseNotEq, 0, addrDone); + VdbeCoverageIf(v, op==TK_LE); + VdbeCoverageIf(v, op==TK_GE); + if( i==nLeft-2 ) opx = op; + } } - - sqlite3ReleaseTempReg(pParse, regTmp); + sqlite3VdbeResolveLabel(v, addrDone); } - - sqlite3VdbeResolveLabel(v, addr); } #if SQLITE_MAX_EXPR_DEPTH>0 @@ -3251,7 +3254,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ case TK_EQ: { Expr *pLeft = pExpr->pLeft; if( sqlite3ExprIsVector(pLeft) ){ - codeVectorCompare(pParse, pExpr, target); + codeVectorCompare(pParse, pExpr, target, op, p5); }else{ r1 = sqlite3ExprCodeTemp(pParse, pLeft, ®Free1); r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index a9061b2833..4c6554367f 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1733,6 +1733,7 @@ struct CollSeq { ** operator is NULL. It is added to certain comparison operators to ** prove that the operands are always NOT NULL. */ +#define SQLITE_KEEPNULL 0x08 /* Used by vector == or <> */ #define SQLITE_JUMPIFNULL 0x10 /* jumps if either operand is NULL */ #define SQLITE_STOREP2 0x20 /* Store result in reg[P2] rather than jump */ #define SQLITE_NULLEQ 0x80 /* NULL=NULL */ diff --git a/src/vdbe.c b/src/vdbe.c index 054f1997f3..be5ca79ed5 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -585,6 +585,7 @@ int sqlite3VdbeExec( Mem *pOut = 0; /* Output operand */ int *aPermute = 0; /* Permutation of columns for OP_Compare */ i64 lastRowid = db->lastRowid; /* Saved value of the last insert ROWID */ + int cmpRes; /* Result of last comparison operation */ #ifdef VDBE_PROFILE u64 start; /* CPU clock count at start of opcode */ #endif @@ -1880,14 +1881,59 @@ case OP_Cast: { /* in1 */ } #endif /* SQLITE_OMIT_CAST */ +/* Opcode: Eq P1 P2 P3 P4 P5 +** Synopsis: if r[P1]==r[P3] goto P2 +** +** Compare the values in register P1 and P3. If reg(P3)==reg(P1) then +** jump to address P2. Or if the SQLITE_STOREP2 flag is set in P5, then +** store the result of comparison in register P2. +** +** The SQLITE_AFF_MASK portion of P5 must be an affinity character - +** SQLITE_AFF_TEXT, SQLITE_AFF_INTEGER, and so forth. An attempt is made +** to coerce both inputs according to this affinity before the +** comparison is made. If the SQLITE_AFF_MASK is 0x00, then numeric +** affinity is used. Note that the affinity conversions are stored +** back into the input registers P1 and P3. So this opcode can cause +** persistent changes to registers P1 and P3. +** +** Once any conversions have taken place, and neither value is NULL, +** the values are compared. If both values are blobs then memcmp() is +** used to determine the results of the comparison. If both values +** are text, then the appropriate collating function specified in +** P4 is used to do the comparison. If P4 is not specified then +** memcmp() is used to compare text string. If both values are +** numeric, then a numeric comparison is used. If the two values +** are of different types, then numbers are considered less than +** strings and strings are considered less than blobs. +** +** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either +** true or false and is never NULL. If both operands are NULL then the result +** of comparison is true. If either operand is NULL then the result is false. +** If neither operand is NULL the result is the same as it would be if +** the SQLITE_NULLEQ flag were omitted from P5. +** +** If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the +** content of r[P2] is only set to 1 (true) if it was not previously NULL. +*/ +/* Opcode: Ne P1 P2 P3 P4 P5 +** Synopsis: if r[P1]!=r[P3] goto P2 +** +** This works just like the Eq opcode except that the jump is taken if +** the operands in registers P1 and P3 are not equal. See the Eq opcode for +** additional information. +** +** If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the +** content of r[P2] is only set to 0 (false) if it was not previously NULL. +*/ /* Opcode: Lt P1 P2 P3 P4 P5 ** Synopsis: if r[P1]flags */ u16 flags3; /* Copy of initial value of pIn3->flags */ - assert( pOp->opcode!=OP_Cmp || (pOp->p5 & SQLITE_STOREP2) ); pIn1 = &aMem[pOp->p1]; pIn3 = &aMem[pOp->p3]; flags1 = pIn1->flags; @@ -2002,15 +2004,16 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ && (flags3&MEM_Null)!=0 && (flags3&MEM_Cleared)==0 ){ - res = 0; /* Results are equal */ + cmpRes = 0; /* Results are equal */ }else{ - res = 1; /* Results are not equal */ + cmpRes = 1; /* Results are not equal */ } }else{ /* SQLITE_NULLEQ is clear and at least one operand is NULL, ** then the result is always NULL. ** The jump is taken if the SQLITE_JUMPIFNULL bit is set. */ + cmpRes = 1; if( pOp->p5 & SQLITE_STOREP2 ){ pOut = &aMem[pOp->p2]; memAboutToChange(p, pOut); @@ -2063,16 +2066,15 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ sqlite3VdbeMemExpandBlob(pIn3); flags3 &= ~MEM_Zero; } - res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); + cmpRes = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); } switch( pOp->opcode ){ - case OP_Eq: res = res==0; break; - case OP_Ne: res = res!=0; break; - case OP_Lt: res = res<0; break; - case OP_Le: res = res<=0; break; - case OP_Gt: res = res>0; break; - case OP_Ge: res = res>=0; break; - default: assert( pOp->opcode==OP_Cmp ); break; + case OP_Eq: res = cmpRes==0; break; + case OP_Ne: res = cmpRes!=0; break; + case OP_Lt: res = cmpRes<0; break; + case OP_Le: res = cmpRes<=0; break; + case OP_Gt: res = cmpRes>0; break; + case OP_Ge: res = cmpRes>=0; break; } /* Undo any changes made by applyAffinity() to the input registers. */ @@ -2083,12 +2085,18 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ if( pOp->p5 & SQLITE_STOREP2 ){ pOut = &aMem[pOp->p2]; + if( (pOp->p5 & SQLITE_KEEPNULL)!=0 && (pOut->flags & MEM_Null)!=0 ){ + /* The KEEPNULL flag prevents OP_Eq from overwriting a NULL with 1 + ** and prevents OP_Ne from overwriting NULL with 0. */ + assert( pOp->opcode==OP_Ne || pOp->opcode==OP_Eq ); + assert( res==0 || res==1 ); + if( (pOp->opcode==OP_Eq)==res ) break; + } memAboutToChange(p, pOut); MemSetTypeFlag(pOut, MEM_Int); pOut->u.i = res; REGISTER_TRACE(pOp->p2, pOut); }else{ - assert( pOp->opcode!=OP_Cmp ); VdbeBranchTaken(res!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3); if( res ){ goto jump_to_p2; @@ -2097,6 +2105,22 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ break; } +/* Opcode: ElseNotEq * P2 * * * +** +** This opcode must immediately follow an Lt or Gt comparison operator. +** If the operands in that previous comparison are not equal (possibly +** because one or the other is NULL) then jump to P2. If the two operands +** of the prior comparison are equal, fall through. +*/ +case OP_ElseNotEq: { /* same as TK_ESCAPE, jump */ + assert( pOp>aOp ); + assert( pOp[-1].opcode==OP_Lt || pOp[-1].opcode==OP_Gt ); + VdbeBranchTaken(cmpRes!=0, 2); + if( cmpRes!=0 ) goto jump_to_p2; + break; +} + + /* Opcode: Permutation * * * P4 * ** ** Set the permutation used by the OP_Compare operator to be the array @@ -3885,63 +3909,6 @@ seek_not_found: break; } -/* Opcode: CmpTest P1 P2 P3 P4 * -** -** P2 is a jump destination. Register P1 is guaranteed to contain either -** an integer value or a NULL. -** -** If P3 is non-zero, it identifies an output register. In this case, if -** P1 is NULL, P3 is also set to NULL. Or, if P1 is any integer value -** other than 0, P3 is set to the value of P4 and a jump to P2 is taken. -** -** If P3 is 0, the jump is taken if P1 contains any value other than 0 (i.e. -** NULL does cause a jump). Additionally, if P1 is not NULL, its value is -** modified to integer value 0 or 1 according to the value of the P4 integer -** operand: -** -** P4 modification -** -------------------------- -** OP_Lt (P1 = (P1 < 0)) -** OP_Le (P1 = (P1 <= 0)) -** OP_Gt (P1 = (P1 > 0)) -** OP_Ge (P1 = (P1 >= 0)) -*/ -case OP_CmpTest: { /* in1, jump */ - int bJump; - pIn1 = &aMem[pOp->p1]; - - if( pOp->p3 ){ - bJump = 0; - if( pIn1->flags & MEM_Null ){ - memAboutToChange(p, &aMem[pOp->p3]); - MemSetTypeFlag(&aMem[pOp->p3], MEM_Null); - }else if( pIn1->u.i!=0 ){ - memAboutToChange(p, &aMem[pOp->p3]); - MemSetTypeFlag(&aMem[pOp->p3], MEM_Int); - aMem[pOp->p3].u.i = pOp->p4.i; - bJump = 1; - } - }else{ - if( (pIn1->flags & MEM_Int) ){ - bJump = (pIn1->u.i!=0); - switch( pOp->p4.i ){ - case OP_Lt: pIn1->u.i = (pIn1->u.i < 0); break; - case OP_Le: pIn1->u.i = (pIn1->u.i <= 0); break; - case OP_Gt: pIn1->u.i = (pIn1->u.i > 0); break; - default: - assert( pOp->p4.i==OP_Ge ); - pIn1->u.i = (pIn1->u.i >= 0); - break; - } - }else{ - bJump = 1; - } - } - - if( bJump ) goto jump_to_p2; - break; -} - /* Opcode: Found P1 P2 P3 P4 * ** Synopsis: key=r[P3@P4] ** From a2f629251f764e86120dfd0bf800f8e237605a64 Mon Sep 17 00:00:00 2001 From: drh Date: Sat, 13 Aug 2016 12:37:47 +0000 Subject: [PATCH 27/97] Fix to the vector less-than operator. All legacy tests passing now. FossilOrigin-Name: ec70a67ebc997f457be4d52d8affc37e142dc3ff --- manifest | 17 +++++++---------- manifest.uuid | 2 +- src/expr.c | 9 +++------ test/rowvalue2.test | 1 - 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/manifest b/manifest index 1a0b48fa4a..b2cbce9bc1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Attempt\sto\ssimplify\sthe\slogic\sand\sgenerated\scode\sfor\svector\scomparisons.\nBasic\scomparison\soperators\sare\sworking,\sbut\sthere\sare\smany\sindexing\stest\nfailures\sstill\sto\sbe\sworked\sthrough. -D 2016-08-13T10:02:17.801 +C Fix\sto\sthe\svector\sless-than\soperator.\s\sAll\slegacy\stests\spassing\snow. +D 2016-08-13T12:37:47.521 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 4f6f7f52b49beb9636ffbd517cfe44a402ba4ad0 F src/delete.c 4aba4214a377ce8ddde2d2e609777bcc8235200f -F src/expr.c 375de68ad2daf3bd339f79074ced5a6db77e2f62 +F src/expr.c d79a02ba0f5b44c3319154641247979c48eec637 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c bc4145347595b7770f9a598cff1c848302cf5413 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1020,7 +1020,7 @@ F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81 F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d F test/rowvalue.test 56b34d31d91340a6e922e753b798880170cc1aa7 -F test/rowvalue2.test 8d5dfe75b8f4d1868a2f91f0356f20d36cba64ff +F test/rowvalue2.test 875068299fd4dd50ef0a47786462c8e1f4065f9a F test/rowvalue3.test dbe935260851b197dfbbbcb0ac2a15cb5f324fd4 F test/rowvalue4.test ed3f7974099f0fc508c9f95df55d37c49f43be65 F test/rowvalue5.test 01c7e0bc4048f30b58e6eb27ecd26e5bd312635e @@ -1516,10 +1516,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 381aa73141db8ec59adbcb09e71af660ee4ae5ce -R b082d1240e853643b05ab20adb3ffc1b -T *branch * vector-compare -T *sym-vector-compare * -T -sym-rowvalue * +P dfc028cfbe7657d20727a2670ecadb1575eb8cbb +R 051307c8175981bed144504907ca38ff U drh -Z 873e26b27a2fd221371ad3338313919b +Z bf986d41b4b86a5837f36c1caf70f646 diff --git a/manifest.uuid b/manifest.uuid index 617135c700..b233b1a33d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -dfc028cfbe7657d20727a2670ecadb1575eb8cbb \ No newline at end of file +ec70a67ebc997f457be4d52d8affc37e142dc3ff \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 54bf0dfaba..4f02c98c45 100644 --- a/src/expr.c +++ b/src/expr.c @@ -485,14 +485,11 @@ static void codeVectorCompare( }else if( opx==TK_NE ){ sqlite3VdbeAddOp2(v, OP_If, dest, addrDone); VdbeCoverage(v); p5 |= SQLITE_KEEPNULL; - }else if( opx==op ){ - assert( op==TK_LT || op==TK_GT ); - sqlite3VdbeAddOp3(v, OP_If, dest, addrDone, 1); + }else{ + assert( op==TK_LT || op==TK_GT || op==TK_LE || op==TK_GE ); + sqlite3VdbeAddOp2(v, OP_ElseNotEq, 0, addrDone); VdbeCoverageIf(v, op==TK_LT); VdbeCoverageIf(v, op==TK_GT); - }else{ - assert( op==TK_LE || op==TK_GE ); - sqlite3VdbeAddOp2(v, OP_ElseNotEq, 0, addrDone); VdbeCoverageIf(v, op==TK_LE); VdbeCoverageIf(v, op==TK_GE); if( i==nLeft-2 ) opx = op; diff --git a/test/rowvalue2.test b/test/rowvalue2.test index c7d061e3e2..b385b23ce2 100644 --- a/test/rowvalue2.test +++ b/test/rowvalue2.test @@ -249,4 +249,3 @@ foreach {tn idx} { finish_test - From af69eda63efbb5461a86cfddd871a39134eef8e4 Mon Sep 17 00:00:00 2001 From: drh Date: Sat, 13 Aug 2016 13:03:46 +0000 Subject: [PATCH 28/97] Improvements to commits. No code changes. FossilOrigin-Name: 18f5a3bee4f870be4644a6042a20081c46edb7d0 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/vdbe.c | 13 +++++++------ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/manifest b/manifest index b2cbce9bc1..23cd1d16e7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sto\sthe\svector\sless-than\soperator.\s\sAll\slegacy\stests\spassing\snow. -D 2016-08-13T12:37:47.521 +C Improvements\sto\scommits.\s\sNo\scode\schanges. +D 2016-08-13T13:03:46.411 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -450,7 +450,7 @@ F src/update.c 4f05ea8cddfa367d045e03589756c02199e8f9bd F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c F src/util.c 810ec3f22e2d1b62e66c30fe3621ebdedd23584d F src/vacuum.c 9dd2f5d276bc6094d8f1d85ecd41b30c1a002a43 -F src/vdbe.c 9816bc4f89e4d58340f3cf3354fd062e2da11f8a +F src/vdbe.c 3961408d1e4507b468b6297b79307bd3eee51988 F src/vdbe.h 67bc551f7faf04c33493892e4b378aada823ed10 F src/vdbeInt.h c59381049af5c7751a83456c39b80d1a6fde1f9d F src/vdbeapi.c c3f6715a99995c11748ecad91d25e93fd9fc390b @@ -1516,7 +1516,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 dfc028cfbe7657d20727a2670ecadb1575eb8cbb -R 051307c8175981bed144504907ca38ff +P ec70a67ebc997f457be4d52d8affc37e142dc3ff +R 247af002c746a42b304e23bb96c4f14d U drh -Z bf986d41b4b86a5837f36c1caf70f646 +Z 092630ac00e90be639ed64dcd2fc60ce diff --git a/manifest.uuid b/manifest.uuid index b233b1a33d..efc7c5409f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ec70a67ebc997f457be4d52d8affc37e142dc3ff \ No newline at end of file +18f5a3bee4f870be4644a6042a20081c46edb7d0 \ No newline at end of file diff --git a/src/vdbe.c b/src/vdbe.c index be5ca79ed5..f401f65bcd 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -2004,16 +2004,16 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ && (flags3&MEM_Null)!=0 && (flags3&MEM_Cleared)==0 ){ - cmpRes = 0; /* Results are equal */ + cmpRes = 0; /* Operands are equal */ }else{ - cmpRes = 1; /* Results are not equal */ + cmpRes = 1; /* Operands are not equal */ } }else{ /* SQLITE_NULLEQ is clear and at least one operand is NULL, ** then the result is always NULL. ** The jump is taken if the SQLITE_JUMPIFNULL bit is set. */ - cmpRes = 1; + cmpRes = 1; /* Operands are not equal */ if( pOp->p5 & SQLITE_STOREP2 ){ pOut = &aMem[pOp->p2]; memAboutToChange(p, pOut); @@ -2108,9 +2108,10 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ /* Opcode: ElseNotEq * P2 * * * ** ** This opcode must immediately follow an Lt or Gt comparison operator. -** If the operands in that previous comparison are not equal (possibly -** because one or the other is NULL) then jump to P2. If the two operands -** of the prior comparison are equal, fall through. +** If the operands in that previous comparison had been used with an Eq +** operator and if the result of that Eq would be NULL or false (0), then +** then jump to P2. If the result of comparing the two previous operands +** using Eq would have been true (1), then fall through. */ case OP_ElseNotEq: { /* same as TK_ESCAPE, jump */ assert( pOp>aOp ); From 0f825a7e264f8c058d7865dce67203c033d3098b Mon Sep 17 00:00:00 2001 From: drh Date: Sat, 13 Aug 2016 14:17:02 +0000 Subject: [PATCH 29/97] Remove an unnecessary stack variable from sqlite3VdbeExec(). FossilOrigin-Name: c54bd9c82dd34951dc87848c0b19fcccaef928db --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/vdbe.c | 27 +++++++++++++-------------- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/manifest b/manifest index 23cd1d16e7..032853f761 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improvements\sto\scommits.\s\sNo\scode\schanges. -D 2016-08-13T13:03:46.411 +C Remove\san\sunnecessary\sstack\svariable\sfrom\ssqlite3VdbeExec(). +D 2016-08-13T14:17:02.223 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -450,7 +450,7 @@ F src/update.c 4f05ea8cddfa367d045e03589756c02199e8f9bd F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c F src/util.c 810ec3f22e2d1b62e66c30fe3621ebdedd23584d F src/vacuum.c 9dd2f5d276bc6094d8f1d85ecd41b30c1a002a43 -F src/vdbe.c 3961408d1e4507b468b6297b79307bd3eee51988 +F src/vdbe.c 1b66646c30ae83db67edce52f62801e6204a6e47 F src/vdbe.h 67bc551f7faf04c33493892e4b378aada823ed10 F src/vdbeInt.h c59381049af5c7751a83456c39b80d1a6fde1f9d F src/vdbeapi.c c3f6715a99995c11748ecad91d25e93fd9fc390b @@ -1516,7 +1516,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 ec70a67ebc997f457be4d52d8affc37e142dc3ff -R 247af002c746a42b304e23bb96c4f14d +P 18f5a3bee4f870be4644a6042a20081c46edb7d0 +R 4db42b6f0c7885d63148768f55fefe94 U drh -Z 092630ac00e90be639ed64dcd2fc60ce +Z 3f3cd455e86203369782a9bcddee8cc7 diff --git a/manifest.uuid b/manifest.uuid index efc7c5409f..c50c6baf38 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -18f5a3bee4f870be4644a6042a20081c46edb7d0 \ No newline at end of file +c54bd9c82dd34951dc87848c0b19fcccaef928db \ No newline at end of file diff --git a/src/vdbe.c b/src/vdbe.c index f401f65bcd..1578d2d87e 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -573,7 +573,7 @@ int sqlite3VdbeExec( sqlite3 *db = p->db; /* The database */ u8 resetSchemaOnFault = 0; /* Reset schema after an error if positive */ u8 encoding = ENC(db); /* The database encoding */ - int iCompare = 0; /* Result of last OP_Compare operation */ + int iCompare = 0; /* Result of last comparison */ unsigned nVmStep = 0; /* Number of virtual machine steps */ #ifndef SQLITE_OMIT_PROGRESS_CALLBACK unsigned nProgressLimit = 0;/* Invoke xProgress() when nVmStep reaches this */ @@ -585,7 +585,6 @@ int sqlite3VdbeExec( Mem *pOut = 0; /* Output operand */ int *aPermute = 0; /* Permutation of columns for OP_Compare */ i64 lastRowid = db->lastRowid; /* Saved value of the last insert ROWID */ - int cmpRes; /* Result of last comparison operation */ #ifdef VDBE_PROFILE u64 start; /* CPU clock count at start of opcode */ #endif @@ -2004,16 +2003,16 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ && (flags3&MEM_Null)!=0 && (flags3&MEM_Cleared)==0 ){ - cmpRes = 0; /* Operands are equal */ + iCompare = 0; /* Operands are equal */ }else{ - cmpRes = 1; /* Operands are not equal */ + iCompare = 1; /* Operands are not equal */ } }else{ /* SQLITE_NULLEQ is clear and at least one operand is NULL, ** then the result is always NULL. ** The jump is taken if the SQLITE_JUMPIFNULL bit is set. */ - cmpRes = 1; /* Operands are not equal */ + iCompare = 1; /* Operands are not equal */ if( pOp->p5 & SQLITE_STOREP2 ){ pOut = &aMem[pOp->p2]; memAboutToChange(p, pOut); @@ -2066,15 +2065,15 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ sqlite3VdbeMemExpandBlob(pIn3); flags3 &= ~MEM_Zero; } - cmpRes = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); + iCompare = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); } switch( pOp->opcode ){ - case OP_Eq: res = cmpRes==0; break; - case OP_Ne: res = cmpRes!=0; break; - case OP_Lt: res = cmpRes<0; break; - case OP_Le: res = cmpRes<=0; break; - case OP_Gt: res = cmpRes>0; break; - case OP_Ge: res = cmpRes>=0; break; + case OP_Eq: res = iCompare==0; break; + case OP_Ne: res = iCompare!=0; break; + case OP_Lt: res = iCompare<0; break; + case OP_Le: res = iCompare<=0; break; + case OP_Gt: res = iCompare>0; break; + default: res = iCompare>=0; break; } /* Undo any changes made by applyAffinity() to the input registers. */ @@ -2116,8 +2115,8 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ case OP_ElseNotEq: { /* same as TK_ESCAPE, jump */ assert( pOp>aOp ); assert( pOp[-1].opcode==OP_Lt || pOp[-1].opcode==OP_Gt ); - VdbeBranchTaken(cmpRes!=0, 2); - if( cmpRes!=0 ) goto jump_to_p2; + VdbeBranchTaken(iCompare!=0, 2); + if( iCompare!=0 ) goto jump_to_p2; break; } From c8d0b0b9d46a374a4859bffe230b658656524a53 Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 18 Aug 2016 15:36:03 +0000 Subject: [PATCH 30/97] Since the TK_VECTOR token is not actually used by the parser, make it an extra token code added by the tool/addopcodes.tcl script during parser build. FossilOrigin-Name: b40b202c1f82808d2240e87b22f1e78b2208cb26 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/parse.y | 1 - tool/addopcodes.tcl | 1 + 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 22c5f502bf..5dd783a497 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\srecent\senhancements\sfrom\strunk. -D 2016-08-18T15:21:16.759 +C Since\sthe\sTK_VECTOR\stoken\sis\snot\sactually\sused\sby\sthe\sparser,\smake\sit\san\nextra\stoken\scode\sadded\sby\sthe\stool/addopcodes.tcl\sscript\sduring\sparser\sbuild. +D 2016-08-18T15:36:03.992 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -373,7 +373,7 @@ F src/os_win.c 520f23475f1de530c435d30b67b7b15fe90874b0 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c 40928c450320da78bb4bd3ae82818f4239e19b7e F src/pager.h 966d2769e76ae347c8a32c4165faf6e6cb64546d -F src/parse.y a7402dff6fe8238795f15ca194e1f1b734d169f4 +F src/parse.y d240b1518fa9c5cb02b222e1fac8e07d950c9ed9 F src/pcache.c 5583c8ade4b05075a60ba953ef471d1c1a9c05df F src/pcache.h 2cedcd8407eb23017d92790b112186886e179490 F src/pcache1.c 4bb7a6a5300c67d0b033d25adb509c120c03e812 @@ -1430,7 +1430,7 @@ F test/zerodamage.test e59a56443d6298ecf7435f618f0b27654f0c849e F tool/GetFile.cs a15e08acb5dd7539b75ba23501581d7c2b462cb5 F tool/GetTclKit.bat 629d87562e0487c386db630033931d12d62e6372 F tool/Replace.cs 02c67258801c2fb5f63231e0ac0f220b4b36ba91 -F tool/addopcodes.tcl 7d4954564d7d4bfde5ba139ebced25542696fa52 +F tool/addopcodes.tcl 10c889c4a65ec6c5604e4a47306fa77ff57ae189 F tool/build-all-msvc.bat 3e4e4043b53f1aede4308e0d2567bbd773614630 x F tool/build-shell.sh 950f47c6174f1eea171319438b93ba67ff5bf367 F tool/cg_anno.tcl 692ce4b8693d59e3a3de77ca97f4139ecfa641b0 x @@ -1516,7 +1516,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 e2ad0b5d8e1e83118c12889150aca2f2a6b2bdde 92a22f01343a898455fd61c3b8e7d7c954f5b569 -R a3d6c4bcdda8d18e3de85b224695f9f6 +P 4768a1066cb9c7627064d7efec44188d6755cb03 +R ad89bbe968192719b16d3d08af2d0d65 U drh -Z 554fed64519eb39711e06c16c9610431 +Z f8279b18447d0a73aa27340aaf051f41 diff --git a/manifest.uuid b/manifest.uuid index a8a1479273..790d0fe829 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4768a1066cb9c7627064d7efec44188d6755cb03 \ No newline at end of file +b40b202c1f82808d2240e87b22f1e78b2208cb26 \ No newline at end of file diff --git a/src/parse.y b/src/parse.y index fffc7fa352..752a4ef599 100644 --- a/src/parse.y +++ b/src/parse.y @@ -217,7 +217,6 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);} %left CONCAT. %left COLLATE. %right BITNOT. -%right VECTOR. // An IDENTIFIER can be a generic identifier, or one of several // keywords. Any non-standard keyword can also be an identifier. diff --git a/tool/addopcodes.tcl b/tool/addopcodes.tcl index 29409a4900..a6c58f1a25 100644 --- a/tool/addopcodes.tcl +++ b/tool/addopcodes.tcl @@ -37,6 +37,7 @@ set extras { UMINUS UPLUS REGISTER + VECTOR SELECT_COLUMN ASTERISK SPAN From db97e56217e4bf10ffd115088002ab73a949b498 Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 18 Aug 2016 17:55:57 +0000 Subject: [PATCH 31/97] Display VECTOR expressions in .wheretrace and .selecttrace debugging output. FossilOrigin-Name: 157347e2580e5078c4081d602e9d1a82d194e719 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/sqliteInt.h | 1 + src/treeview.c | 20 ++++++++++++++++---- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index 5dd783a497..0491eac610 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Since\sthe\sTK_VECTOR\stoken\sis\snot\sactually\sused\sby\sthe\sparser,\smake\sit\san\nextra\stoken\scode\sadded\sby\sthe\stool/addopcodes.tcl\sscript\sduring\sparser\sbuild. -D 2016-08-18T15:36:03.992 +C Display\sVECTOR\sexpressions\sin\s.wheretrace\sand\s.selecttrace\sdebugging\soutput. +D 2016-08-18T17:55:57.954 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -389,7 +389,7 @@ F src/shell.c 79dda477be6c96eba6e952a934957ad36f87acc7 F src/sqlite.h.in 0f7580280d1b009b507d8beec1ff0f197ba0cc99 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 8648034aa702469afb553231677306cc6492a1ae -F src/sqliteInt.h 1b51d9e1ae63476fbe69423d531afdefab8aa18f +F src/sqliteInt.h f4079dcc83ec216d34531792626206a1a35463bc F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247 F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1 F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9 @@ -444,7 +444,7 @@ F src/test_windirent.h b12055cab6227f7be10f5c19296f67c60cc5e2a5 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c F src/tokenize.c 78c8085bc7af1922aa687f0f4bbd716821330de5 -F src/treeview.c c56d6ddbed564efda746236b35bcbb8238daac4b +F src/treeview.c 0ec6101f55829005fae5f83d33c666224a275e1b F src/trigger.c 11e20b3b12c847b3b9055594c0f1631266bb53fc F src/update.c 8179e699dbd45b92934fd02d3d8e3732e8da8802 F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c @@ -1516,7 +1516,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 4768a1066cb9c7627064d7efec44188d6755cb03 -R ad89bbe968192719b16d3d08af2d0d65 +P b40b202c1f82808d2240e87b22f1e78b2208cb26 +R 977869bee47fcdc239ba588f810a1009 U drh -Z f8279b18447d0a73aa27340aaf051f41 +Z 61690ef4bbd764846f05c0b7918f3dcb diff --git a/manifest.uuid b/manifest.uuid index 790d0fe829..051933d3ad 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b40b202c1f82808d2240e87b22f1e78b2208cb26 \ No newline at end of file +157347e2580e5078c4081d602e9d1a82d194e719 \ No newline at end of file diff --git a/src/sqliteInt.h b/src/sqliteInt.h index f1d29c643e..0c5fb9ce86 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3513,6 +3513,7 @@ char *sqlite3VMPrintf(sqlite3*,const char*, va_list); #if defined(SQLITE_DEBUG) void sqlite3TreeViewExpr(TreeView*, const Expr*, u8); + void sqlite3TreeViewBareExprList(TreeView*, const ExprList*, const char*); void sqlite3TreeViewExprList(TreeView*, const ExprList*, u8, const char*); void sqlite3TreeViewSelect(TreeView*, const Select*, u8); void sqlite3TreeViewWith(TreeView*, const With*, u8); diff --git a/src/treeview.c b/src/treeview.c index 27996d46d7..6101af5771 100644 --- a/src/treeview.c +++ b/src/treeview.c @@ -451,6 +451,10 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){ sqlite3TreeViewExpr(pView, pExpr->pRight, 0); break; } + case TK_VECTOR: { + sqlite3TreeViewBareExprList(pView, pExpr->x.pList, "VECTOR"); + break; + } default: { sqlite3TreeViewLine(pView, "op=%d", pExpr->op); break; @@ -467,21 +471,20 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){ sqlite3TreeViewPop(pView); } + /* ** Generate a human-readable explanation of an expression list. */ -void sqlite3TreeViewExprList( +void sqlite3TreeViewBareExprList( TreeView *pView, const ExprList *pList, - u8 moreToFollow, const char *zLabel ){ - int i; - pView = sqlite3TreeViewPush(pView, moreToFollow); if( zLabel==0 || zLabel[0]==0 ) zLabel = "LIST"; if( pList==0 ){ sqlite3TreeViewLine(pView, "%s (empty)", zLabel); }else{ + int i; sqlite3TreeViewLine(pView, "%s", zLabel); for(i=0; inExpr; i++){ int j = pList->a[i].u.x.iOrderByCol; @@ -493,6 +496,15 @@ void sqlite3TreeViewExprList( if( j ) sqlite3TreeViewPop(pView); } } +} +void sqlite3TreeViewExprList( + TreeView *pView, + const ExprList *pList, + u8 moreToFollow, + const char *zLabel +){ + pView = sqlite3TreeViewPush(pView, moreToFollow); + sqlite3TreeViewBareExprList(pView, pList, zLabel); sqlite3TreeViewPop(pView); } From 48cb3a76004bab66c4228bb66c83b15fd05f1663 Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 18 Aug 2016 18:09:10 +0000 Subject: [PATCH 32/97] Display SELECT_COLUMN expressions in the .wheretrace debugging output. FossilOrigin-Name: 3b27a5da100037f75a4efc15e0354a6aa94194f8 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/treeview.c | 5 +++++ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 0491eac610..8253bd4b1d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Display\sVECTOR\sexpressions\sin\s.wheretrace\sand\s.selecttrace\sdebugging\soutput. -D 2016-08-18T17:55:57.954 +C Display\sSELECT_COLUMN\sexpressions\sin\sthe\s.wheretrace\sdebugging\soutput. +D 2016-08-18T18:09:10.155 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -444,7 +444,7 @@ F src/test_windirent.h b12055cab6227f7be10f5c19296f67c60cc5e2a5 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c F src/tokenize.c 78c8085bc7af1922aa687f0f4bbd716821330de5 -F src/treeview.c 0ec6101f55829005fae5f83d33c666224a275e1b +F src/treeview.c 15406fc49bd7fb1616b4c19b8d1d9fd85211ca8b F src/trigger.c 11e20b3b12c847b3b9055594c0f1631266bb53fc F src/update.c 8179e699dbd45b92934fd02d3d8e3732e8da8802 F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c @@ -1516,7 +1516,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 b40b202c1f82808d2240e87b22f1e78b2208cb26 -R 977869bee47fcdc239ba588f810a1009 +P 157347e2580e5078c4081d602e9d1a82d194e719 +R c8603389dbe05aeaf19e13f4475b9214 U drh -Z 61690ef4bbd764846f05c0b7918f3dcb +Z 94f649f5f1dae565622eac65874f963e diff --git a/manifest.uuid b/manifest.uuid index 051933d3ad..8229b6d241 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -157347e2580e5078c4081d602e9d1a82d194e719 \ No newline at end of file +3b27a5da100037f75a4efc15e0354a6aa94194f8 \ No newline at end of file diff --git a/src/treeview.c b/src/treeview.c index 6101af5771..2311957184 100644 --- a/src/treeview.c +++ b/src/treeview.c @@ -455,6 +455,11 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){ sqlite3TreeViewBareExprList(pView, pExpr->x.pList, "VECTOR"); break; } + case TK_SELECT_COLUMN: { + sqlite3TreeViewLine(pView, "SELECT-COLUMN %d", pExpr->iColumn); + sqlite3TreeViewSelect(pView, pExpr->pLeft->x.pSelect, 0); + break; + } default: { sqlite3TreeViewLine(pView, "op=%d", pExpr->op); break; From 03181c8c272003b3680096077737f41ba0f1041d Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 18 Aug 2016 19:04:57 +0000 Subject: [PATCH 33/97] Fix a SQL NULL handling bug in the vector IN operator code generation. FossilOrigin-Name: 936146b12e27784f15a68fe65732c6d92c3a12f3 --- manifest | 13 +++++++------ manifest.uuid | 2 +- src/wherecode.c | 5 +++-- test/rowvalue6.test | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 9 deletions(-) create mode 100644 test/rowvalue6.test diff --git a/manifest b/manifest index 8253bd4b1d..edfe85439e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Display\sSELECT_COLUMN\sexpressions\sin\sthe\s.wheretrace\sdebugging\soutput. -D 2016-08-18T18:09:10.155 +C Fix\sa\sSQL\sNULL\shandling\sbug\sin\sthe\svector\sIN\soperator\scode\sgeneration. +D 2016-08-18T19:04:57.401 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -466,7 +466,7 @@ F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 F src/where.c 5bee250c8233c43bd7f53897d12b8468004f63db F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 -F src/wherecode.c 92202261a6e41f897a595417c5b0c75c8acf713d +F src/wherecode.c 916b451003afef5f3a0265e62a22d7ca527656fe F src/whereexpr.c 8d9903d16ae45d15736745f7b75df2340c729782 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd @@ -1024,6 +1024,7 @@ F test/rowvalue2.test 875068299fd4dd50ef0a47786462c8e1f4065f9a F test/rowvalue3.test dbe935260851b197dfbbbcb0ac2a15cb5f324fd4 F test/rowvalue4.test ed3f7974099f0fc508c9f95df55d37c49f43be65 F test/rowvalue5.test 01c7e0bc4048f30b58e6eb27ecd26e5bd312635e +F test/rowvalue6.test d19b54feb604d5601f8614b15e214e0774c01087 F test/rowvaluefault.test 7b16485e3f2b371f3e3d05455b8ded6d0c090244 F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09 @@ -1516,7 +1517,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 157347e2580e5078c4081d602e9d1a82d194e719 -R c8603389dbe05aeaf19e13f4475b9214 +P 3b27a5da100037f75a4efc15e0354a6aa94194f8 +R cb7673431cbb14ee2064b1a631b51486 U drh -Z 94f649f5f1dae565622eac65874f963e +Z e68c3ca3548a25a2524e065bf2465c19 diff --git a/manifest.uuid b/manifest.uuid index 8229b6d241..105e0106d8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3b27a5da100037f75a4efc15e0354a6aa94194f8 \ No newline at end of file +936146b12e27784f15a68fe65732c6d92c3a12f3 \ No newline at end of file diff --git a/src/wherecode.c b/src/wherecode.c index 80de8a7e54..d84ace1922 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -499,15 +499,17 @@ static int codeEqualityTerm( int iMap = 0; /* Index in aiMap[] */ pIn += i; for(i=iEq;inLTerm; i++, pIn++){ + int iOut = iReg; 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; + iOut = iReg + i - iEq; pIn->addrInTop = sqlite3VdbeAddOp3(v,OP_Column,iTab, iCol, iOut); } + sqlite3VdbeAddOp1(v, OP_IsNull, iOut); VdbeCoverage(v); if( i==iEq ){ pIn->iCur = iTab; pIn->eEndLoopOp = bRev ? OP_PrevIfOpen : OP_NextIfOpen; @@ -515,7 +517,6 @@ static int codeEqualityTerm( pIn->eEndLoopOp = OP_Noop; } } - sqlite3VdbeAddOp1(v, OP_IsNull, iReg); VdbeCoverage(v); } }else{ pLevel->u.in.nIn = 0; diff --git a/test/rowvalue6.test b/test/rowvalue6.test new file mode 100644 index 0000000000..d90e61c811 --- /dev/null +++ b/test/rowvalue6.test @@ -0,0 +1,36 @@ +# 2016-08-18 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# The focus of this file is handling of NULL values in row-value IN +# expressions. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix rowvalue6 + +do_execsql_test 1.1 { + CREATE TABLE t1(a,b,c); + CREATE INDEX t1x1 ON t1(a,b); + INSERT INTO t1 VALUES(1,NULL,200); + + CREATE TABLE t2(x,y,z); + INSERT INTO t2 VALUES(1,NULL,55); + + SELECT c FROM t1 WHERE (a,b) IN (SELECT x,y FROM t2 WHERE z==55); +} {} +do_execsql_test 1.2 { + INSERT INTO t1 VALUES(2,3,400); + INSERT INTO t2 VALUES(2,3,55); + + SELECT c FROM t1 WHERE (a,b) IN (SELECT x,y FROM t2 WHERE z==55); +} {400} + +finish_test From 4602b8e89d6949617f01f8e1f0cc0f2fb1d6bd18 Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 19 Aug 2016 18:28:00 +0000 Subject: [PATCH 34/97] Fix an obsolete comment on codeEqualityTerm(). No changes to code. FossilOrigin-Name: d07be5afb0a915769382dfd815403f8832cb3eec --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/wherecode.c | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 23b276d0a0..17fe1e24a2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\srecent\senhancements\sfrom\strunk. -D 2016-08-19T15:41:24.228 +C Fix\san\sobsolete\scomment\son\scodeEqualityTerm().\s\sNo\schanges\sto\scode. +D 2016-08-19T18:28:00.523 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -466,7 +466,7 @@ F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 F src/where.c 5bee250c8233c43bd7f53897d12b8468004f63db F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 -F src/wherecode.c 916b451003afef5f3a0265e62a22d7ca527656fe +F src/wherecode.c f3fd16b9b4cef1345330d22cf3f1628f87a56456 F src/whereexpr.c 8d9903d16ae45d15736745f7b75df2340c729782 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd @@ -1518,7 +1518,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 936146b12e27784f15a68fe65732c6d92c3a12f3 083f9e6270fa4faa402b91231271da4f3915c79f -R b0cf551e47740f01d348f132bf9f7791 +P b17872363b60edab05a5d382a44038aad91e4d9f +R 81462375f5cd0ff256194ab35ae61f4d U drh -Z 4f11e98ced958b5a8510961832e75233 +Z 0138a28f257e6cf64f3bd77d61380c55 diff --git a/manifest.uuid b/manifest.uuid index 64c87f47eb..a0b848e9d3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b17872363b60edab05a5d382a44038aad91e4d9f \ No newline at end of file +d07be5afb0a915769382dfd815403f8832cb3eec \ No newline at end of file diff --git a/src/wherecode.c b/src/wherecode.c index d84ace1922..dd15b5893c 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -358,8 +358,8 @@ static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){ ** ** The current value for the constraint is left in register iReg. ** -** For a constraint of the form X=expr, the expression is evaluated and its -** result is left on the stack. For constraints of the form X IN (...) +** For a constraint of the form X=expr, the expression is evaluated in +** straight-line code. For constraints of the form X IN (...) ** this routine sets up a loop that will iterate over all values of X. */ static int codeEqualityTerm( From a15a8bc822a32bb376c1b76e3fc0baafe7933965 Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 19 Aug 2016 18:40:17 +0000 Subject: [PATCH 35/97] Show the WhereTerm.iField value on debugging output, when it is non-zero. FossilOrigin-Name: 931c95358d8bc02f3e3d8ee4e545a07b2d2c97e8 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/where.c | 7 ++++++- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 17fe1e24a2..2d1e3df464 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\san\sobsolete\scomment\son\scodeEqualityTerm().\s\sNo\schanges\sto\scode. -D 2016-08-19T18:28:00.523 +C Show\sthe\sWhereTerm.iField\svalue\son\sdebugging\soutput,\swhen\sit\sis\snon-zero. +D 2016-08-19T18:40:17.138 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -464,7 +464,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c 02eeecc265f6ffd0597378f5d8ae9070b62a406a F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 -F src/where.c 5bee250c8233c43bd7f53897d12b8468004f63db +F src/where.c c7cdfd54f383090bb801cdd50d36de1a24684bb2 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 F src/wherecode.c f3fd16b9b4cef1345330d22cf3f1628f87a56456 F src/whereexpr.c 8d9903d16ae45d15736745f7b75df2340c729782 @@ -1518,7 +1518,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 b17872363b60edab05a5d382a44038aad91e4d9f -R 81462375f5cd0ff256194ab35ae61f4d +P d07be5afb0a915769382dfd815403f8832cb3eec +R 8b996d549f6c06da9085faa1b04f9847 U drh -Z 0138a28f257e6cf64f3bd77d61380c55 +Z 5690c4429160dd9b0cc8734ae0e94c60 diff --git a/manifest.uuid b/manifest.uuid index a0b848e9d3..6105e3743f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d07be5afb0a915769382dfd815403f8832cb3eec \ No newline at end of file +931c95358d8bc02f3e3d8ee4e545a07b2d2c97e8 \ No newline at end of file diff --git a/src/where.c b/src/where.c index f084b93701..30854ee422 100644 --- a/src/where.c +++ b/src/where.c @@ -1677,9 +1677,14 @@ static void whereTermPrint(WhereTerm *pTerm, int iTerm){ sqlite3_snprintf(sizeof(zLeft),zLeft,"left=%d", pTerm->leftCursor); } sqlite3DebugPrintf( - "TERM-%-3d %p %s %-12s prob=%-3d op=0x%03x wtFlags=0x%04x\n", + "TERM-%-3d %p %s %-12s prob=%-3d op=0x%03x wtFlags=0x%04x", iTerm, pTerm, zType, zLeft, pTerm->truthProb, pTerm->eOperator, pTerm->wtFlags); + if( pTerm->iField ){ + sqlite3DebugPrintf(" iField=%d\n", pTerm->iField); + }else{ + sqlite3DebugPrintf("\n"); + } sqlite3TreeViewExpr(0, pTerm->pExpr, 0); } } From 39a11819f3fc48e0e85e95dadb7ebabfafaa96f5 Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 19 Aug 2016 19:12:58 +0000 Subject: [PATCH 36/97] Improved comments on sqlite3CodeSubquery(). No changes to code. FossilOrigin-Name: acea4ee136def4815d22eec240c5903a72bde9bd --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/expr.c | 27 ++++++++++++++++++--------- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/manifest b/manifest index 2d1e3df464..75bb629cc6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Show\sthe\sWhereTerm.iField\svalue\son\sdebugging\soutput,\swhen\sit\sis\snon-zero. -D 2016-08-19T18:40:17.138 +C Improved\scomments\son\ssqlite3CodeSubquery().\s\sNo\schanges\sto\scode. +D 2016-08-19T19:12:58.298 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c d79a02ba0f5b44c3319154641247979c48eec637 +F src/expr.c e139851835a8532e3974ca4bc8f6b0e5aef0e06f F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1518,7 +1518,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 d07be5afb0a915769382dfd815403f8832cb3eec -R 8b996d549f6c06da9085faa1b04f9847 +P 931c95358d8bc02f3e3d8ee4e545a07b2d2c97e8 +R 041e4a08027e633fe8c3ceee8cf4a385 U drh -Z 5690c4429160dd9b0cc8734ae0e94c60 +Z b93c8a041c20867db97a84e58d7255a6 diff --git a/manifest.uuid b/manifest.uuid index 6105e3743f..715e015f26 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -931c95358d8bc02f3e3d8ee4e545a07b2d2c97e8 \ No newline at end of file +acea4ee136def4815d22eec240c5903a72bde9bd \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 4f02c98c45..1949be50af 100644 --- a/src/expr.c +++ b/src/expr.c @@ -2211,7 +2211,9 @@ void sqlite3SubselectError(Parse *pParse, int nActual, int nExpect){ ** value to non-NULL if the RHS is NULL-free. ** ** For a SELECT or EXISTS operator, return the register that holds the -** result. For IN operators or if an error occurs, the return value is 0. +** result. For a multi-column SELECT, the result is stored in a contiguous +** array of registers and the return value is the register of the left-most +** result column. Return 0 for IN operators or if an error occurs. */ #ifndef SQLITE_OMIT_SUBQUERY int sqlite3CodeSubselect( @@ -2226,8 +2228,8 @@ int sqlite3CodeSubselect( if( NEVER(v==0) ) return 0; sqlite3ExprCachePush(pParse); - /* This code must be run in its entirety every time it is encountered - ** if any of the following is true: + /* The evaluation of the IN/EXISTS/SELECT must be repeated every time it + ** is encountered if any of the following is true: ** ** * The right-hand side is a correlated subquery ** * The right-hand side is an expression list containing variables @@ -2387,14 +2389,21 @@ int sqlite3CodeSubselect( case TK_EXISTS: case TK_SELECT: default: { - /* If this has to be a scalar SELECT. Generate code to put the - ** value of this select in a memory cell and record the number - ** of the memory cell in iColumn. If this is an EXISTS, write - ** an integer 0 (not exists) or 1 (exists) into a memory cell - ** and record that memory cell in iColumn. + /* Case 3: (SELECT ... FROM ...) + ** or: EXISTS(SELECT ... FROM ...) + ** + ** For a SELECT, generate code to put the values for all columns of + ** the first row into an array of registers and return the index of + ** the first register. + ** + ** If this is an EXISTS, write an integer 0 (not exists) or 1 (exists) + ** into a register and return that register number. + ** + ** In both cases, the query is augmented with "LIMIT 1". Any + ** preexisting limit is discarded in place of the new LIMIT 1. */ Select *pSel; /* SELECT statement to encode */ - SelectDest dest; /* How to deal with SELECt result */ + SelectDest dest; /* How to deal with SELECT result */ int nReg; /* Registers to allocate */ testcase( pExpr->op==TK_EXISTS ); From 8d25cb90cdc01b79eb368299c24846388c4e7866 Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 19 Aug 2016 19:58:06 +0000 Subject: [PATCH 37/97] Replace the magic number (-2) with its symbol XN_EXPR in the exprMightBeIndexed() routine. No logic changes. FossilOrigin-Name: d4a5af69cc3bde6f8c02075c07b4d47a0252392d --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/whereexpr.c | 7 ++++--- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 75bb629cc6..760f93473c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improved\scomments\son\ssqlite3CodeSubquery().\s\sNo\schanges\sto\scode. -D 2016-08-19T19:12:58.298 +C Replace\sthe\smagic\snumber\s(-2)\swith\sits\ssymbol\sXN_EXPR\sin\sthe\nexprMightBeIndexed()\sroutine.\s\sNo\slogic\schanges. +D 2016-08-19T19:58:06.626 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -467,7 +467,7 @@ F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 F src/where.c c7cdfd54f383090bb801cdd50d36de1a24684bb2 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 F src/wherecode.c f3fd16b9b4cef1345330d22cf3f1628f87a56456 -F src/whereexpr.c 8d9903d16ae45d15736745f7b75df2340c729782 +F src/whereexpr.c bb22074cc2712c5013ecacdf7d652e7568a47bc3 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -1518,7 +1518,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 931c95358d8bc02f3e3d8ee4e545a07b2d2c97e8 -R 041e4a08027e633fe8c3ceee8cf4a385 +P acea4ee136def4815d22eec240c5903a72bde9bd +R 223c403e8b392fa16a54efbf6c58e1ba U drh -Z b93c8a041c20867db97a84e58d7255a6 +Z b5c0cabb5688d369770dffa65d09a7da diff --git a/manifest.uuid b/manifest.uuid index 715e015f26..95b5affc2d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -acea4ee136def4815d22eec240c5903a72bde9bd \ No newline at end of file +d4a5af69cc3bde6f8c02075c07b4d47a0252392d \ No newline at end of file diff --git a/src/whereexpr.c b/src/whereexpr.c index 23efed9761..2fc903ce6e 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -823,7 +823,8 @@ static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){ ** in any index. Return TRUE (1) if pExpr is an indexed term and return ** FALSE (0) if not. If TRUE is returned, also set *piCur to the cursor ** number of the table that is indexed and *piColumn to the column number -** of the column that is indexed, or -2 if an expression is being indexed. +** of the column that is indexed, or XN_EXPR (-2) if an expression is being +** indexed. ** ** If pExpr is a TK_COLUMN column reference, then this routine always returns ** true even if that particular column is not indexed, because the column @@ -861,10 +862,10 @@ static int exprMightBeIndexed( for(pIdx=pFrom->a[i].pTab->pIndex; pIdx; pIdx=pIdx->pNext){ if( pIdx->aColExpr==0 ) continue; for(i=0; inKeyCol; i++){ - if( pIdx->aiColumn[i]!=(-2) ) continue; + if( pIdx->aiColumn[i]!=XN_EXPR ) continue; if( sqlite3ExprCompare(pExpr, pIdx->aColExpr->a[i].pExpr, iCur)==0 ){ *piCur = iCur; - *piColumn = -2; + *piColumn = XN_EXPR; return 1; } } From fc7f27b9d2e4083faba332a9a15d182549f68cb2 Mon Sep 17 00:00:00 2001 From: drh Date: Sat, 20 Aug 2016 00:07:01 +0000 Subject: [PATCH 38/97] Change the way TK_SELECT_COLUMN is handled so that the subquery is only generated once even if part of the vector comparison is used for indexing and the other part is now. This change also is a pathway to vector assignment in UPDATE statements. FossilOrigin-Name: d8feea7dcde83179bff303072426561cfe825e58 --- manifest | 20 +++++------ manifest.uuid | 2 +- src/expr.c | 95 +++++++++++++++++++++++++++++++++++++++++-------- src/sqliteInt.h | 9 +++-- src/vdbemem.c | 2 +- src/wherecode.c | 35 ++---------------- src/whereexpr.c | 32 ++++------------- 7 files changed, 106 insertions(+), 89 deletions(-) diff --git a/manifest b/manifest index 760f93473c..52c2870784 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Replace\sthe\smagic\snumber\s(-2)\swith\sits\ssymbol\sXN_EXPR\sin\sthe\nexprMightBeIndexed()\sroutine.\s\sNo\slogic\schanges. -D 2016-08-19T19:58:06.626 +C Change\sthe\sway\sTK_SELECT_COLUMN\sis\shandled\sso\sthat\sthe\ssubquery\sis\sonly\ngenerated\sonce\seven\sif\spart\sof\sthe\svector\scomparison\sis\sused\sfor\sindexing\nand\sthe\sother\spart\sis\snow.\s\sThis\schange\salso\sis\sa\spathway\sto\svector\sassignment\nin\sUPDATE\sstatements. +D 2016-08-20T00:07:01.602 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c e139851835a8532e3974ca4bc8f6b0e5aef0e06f +F src/expr.c ff73eb6cae76c2423928d46ceff6f2fdc379a568 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -389,7 +389,7 @@ F src/shell.c 79dda477be6c96eba6e952a934957ad36f87acc7 F src/sqlite.h.in 0f7580280d1b009b507d8beec1ff0f197ba0cc99 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 8648034aa702469afb553231677306cc6492a1ae -F src/sqliteInt.h 02b7f4bd9f945fbc46b05f7914b9c43d89517c59 +F src/sqliteInt.h 84f32ad3a5bfaf698b82bf5a9c6939f3e3d79ee0 F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247 F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1 F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9 @@ -456,7 +456,7 @@ F src/vdbeInt.h c59381049af5c7751a83456c39b80d1a6fde1f9d F src/vdbeapi.c a32d61b7dd05e6890d8fd44d2805f55e2f5ba9f3 F src/vdbeaux.c a32d79aeaa88dc2b97c261172d952d395254a055 F src/vdbeblob.c 3e82a797b60c3b9fed7b8de8c539ca7607874937 -F src/vdbemem.c 77d6505956bf4e45c328ab3ebef6b461334cab5d +F src/vdbemem.c e67dc6d8177fd1830efb5d15e17793408251a187 F src/vdbesort.c 91fda3909326860382b0ca8aa251e609c6a9d62c F src/vdbetrace.c 41963d5376f0349842b5fc4aaaaacd7d9cdc0834 F src/vtab.c 5ca4fa8b028f9e2ed4793ee1670911a83cfcefd8 @@ -466,8 +466,8 @@ F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 F src/where.c c7cdfd54f383090bb801cdd50d36de1a24684bb2 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 -F src/wherecode.c f3fd16b9b4cef1345330d22cf3f1628f87a56456 -F src/whereexpr.c bb22074cc2712c5013ecacdf7d652e7568a47bc3 +F src/wherecode.c 0c790c422ea5ecb6c83d6a78f2c4ebc036eea90e +F src/whereexpr.c 021469f11930cc705a26f7f31ec37640588df1d4 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -1518,7 +1518,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 acea4ee136def4815d22eec240c5903a72bde9bd -R 223c403e8b392fa16a54efbf6c58e1ba +P d4a5af69cc3bde6f8c02075c07b4d47a0252392d +R 8a9288690d2190168e2ddd79f5078350 U drh -Z b5c0cabb5688d369770dffa65d09a7da +Z 663c48e63b0d59d8fc6b403d2678eeeb diff --git a/manifest.uuid b/manifest.uuid index 95b5affc2d..a8cf8cf3bc 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d4a5af69cc3bde6f8c02075c07b4d47a0252392d \ No newline at end of file +d8feea7dcde83179bff303072426561cfe825e58 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 1949be50af..de2c7a6a26 100644 --- a/src/expr.c +++ b/src/expr.c @@ -334,13 +334,21 @@ int sqlite3ExprVectorSize(Expr *pExpr){ #ifndef SQLITE_OMIT_SUBQUERY /* -** If the expression passed as the first argument is a TK_VECTOR, return -** a pointer to the i'th field of the vector. Or, if the first argument -** points to a sub-select that returns more than one column, return a -** pointer to the i'th returned column value. Otherwise, return a copy -** of the first argument. +** Interpret the pVector input as a vector expression. If pVector is +** an ordinary scalar expression, treat it as a vector of size 1. +** +** Return a pointer to a subexpression of pVector that is the i-th +** column of the vector (numbered starting with 0). The caller must +** ensure that i is within range. +** +** pVector retains ownership of the returned subexpression. +** +** If the vector is a (SELECT ...) then the expression returned is +** just the expression for the i-th term of the result set, and is +** necessarily ready to be evaluated because the table cursor might +** not have been positioned yet. */ -Expr *sqlite3ExprVectorField(Expr *pVector, int i){ +Expr *sqlite3VectorFieldSubexpr(Expr *pVector, int i){ assert( iop==TK_SELECT ){ @@ -351,7 +359,57 @@ Expr *sqlite3ExprVectorField(Expr *pVector, int i){ } return pVector; } -#endif +#endif /* !defined(SQLITE_OMIT_SUBQUERY) */ + +#ifndef SQLITE_OMIT_SUBQUERY +/* +** Compute and return a new Expr object which when passed to +** sqlite3ExprCode() will generate all necessary code to compute +** the iField-th column of the vector expression pVector. +** +** The caller owns the returned Expr object and is responsible for +** ensuring that the returned value eventually gets freed. +** +** Ownership of pVector is controlled by the takeOwnership parameter. If +** takeOwnership is true, this routine takes responsibility for freeing +** pVector, and may do so before returning, hence the caller must not reference +** pVector again. If takeOwnership is false, then the caller takes +** responsibility for freeing pVector and must ensure the pVector remains +** valid as long as the returned value remains in use. +*/ +Expr *sqlite3ExprForVectorField( + Parse *pParse, /* Parsing context */ + Expr *pVector, /* The vector. List of expressions or a sub-SELECT */ + int iField, /* Which column of the vector to return */ + int takeOwnership /* True to take ownership of pVector before returning */ +){ + Expr *pRet; + assert( sqlite3ExprIsVector(pVector) ); + /* FIXME: Add support for takeOwnership!=0 */ assert( takeOwnership==0 ); + if( pVector->flags & EP_xIsSelect ){ + /* The TK_SELECT_COLUMN Expr node: + ** + ** pLeft: pVector containing TK_SELECT + ** pRight: pVector if ownership taken + ** iColumn: Index of a column in pVector + ** pLeft->iTable: First in an array of register holding result, or 0 + ** if the result is not yet computed. + ** + ** sqlite3ExprDelete() specifically skips the recursive delete of + ** pLeft on TK_SELECT_COLUMN nodes. But pRight is followed, so pVector + ** is included on pRight if ownership is taken. Typically there will + ** be multiple TK_SELECT_COLUMN nodes with the same pLeft pointer to + ** the pVector, but only one of them will own the pVector. + */ + pRet = sqlite3PExpr(pParse, TK_SELECT_COLUMN, pVector, 0, 0); + if( pRet ) pRet->iColumn = iField; + assert( pRet==0 || pRet->iTable==0 ); + }else{ + pRet = sqlite3ExprDup(pParse->db, pVector->x.pList->a[iField].pExpr, 0); + } + return pRet; +} +#endif /* !define(SQLITE_OMIT_SUBQUERY) */ /* ** If expression pExpr is of type TK_SELECT, generate code to evaluate @@ -2025,7 +2083,7 @@ int sqlite3FindInIndex( ** comparison is the same as the affinity of each column. If ** it not, it is not possible to use any index. */ for(i=0; ipLeft, i); + Expr *pLhs = sqlite3VectorFieldSubexpr(pX->pLeft, i); int iCol = pEList->a[i].pExpr->iColumn; char idxaff = pTab->aCol[iCol].affinity; char cmpaff = sqlite3CompareAffinity(pLhs, idxaff); @@ -2051,7 +2109,7 @@ int sqlite3FindInIndex( } for(i=0; ipLeft, i); + Expr *pLhs = sqlite3VectorFieldSubexpr(pX->pLeft, i); Expr *pRhs = pEList->a[i].pExpr; CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs); int j; @@ -2159,7 +2217,7 @@ static char *exprINAffinity(Parse *pParse, Expr *pExpr){ if( zRet ){ int i; for(i=0; ipEList->a[i].pExpr, a); @@ -2313,7 +2371,7 @@ int sqlite3CodeSubselect( assert( pEList->nExpr>0 ); assert( sqlite3KeyInfoIsWriteable(pKeyInfo) ); for(i=0; i1) ? sqlite3ExprVectorField(pLeft, i) : pLeft; + Expr *p = (nVal>1) ? sqlite3VectorFieldSubexpr(pLeft, i) : pLeft; pKeyInfo->aColl[i] = sqlite3BinaryCompareCollSeq( pParse, p, pEList->a[i].pExpr ); @@ -2553,7 +2611,7 @@ static void sqlite3ExprCodeIN( } }else{ for(i=0; ipLeft, i); + Expr *p = sqlite3VectorFieldSubexpr(pExpr->pLeft, i); if( sqlite3ExprCanBeNull(p) ){ sqlite3VdbeAddOp2(v, OP_IsNull, r1+aiMap[i], destIfNull); VdbeCoverage(v); @@ -2654,7 +2712,7 @@ static void sqlite3ExprCodeIN( Expr *p; CollSeq *pColl; int r2 = sqlite3GetTempReg(pParse); - p = sqlite3ExprVectorField(pLeft, i); + p = sqlite3VectorFieldSubexpr(pLeft, i); pColl = sqlite3ExprCollSeq(pParse, p); sqlite3VdbeAddOp3(v, OP_Column, iIdx, i, r2); @@ -2674,7 +2732,7 @@ static void sqlite3ExprCodeIN( ** result is 1. */ sqlite3VdbeJumpHere(v, addr); for(i=0; ipLeft, i); + Expr *p = sqlite3VectorFieldSubexpr(pExpr->pLeft, i); if( sqlite3ExprCanBeNull(p) ){ sqlite3VdbeAddOp2(v, OP_IsNull, r1+aiMap[i], destIfNull); VdbeCoverage(v); @@ -3510,6 +3568,13 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ } break; } + case TK_SELECT_COLUMN: { + if( pExpr->pLeft->iTable==0 ){ + pExpr->pLeft->iTable = sqlite3CodeSubselect(pParse, pExpr->pLeft, 0, 0); + } + inReg = pExpr->pLeft->iTable + pExpr->iColumn; + break; + } case TK_IN: { int destIfFalse = sqlite3VdbeMakeLabel(v); int destIfNull = sqlite3VdbeMakeLabel(v); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 7b37b030cd..7651ff69e4 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2298,9 +2298,11 @@ struct Expr { int iTable; /* TK_COLUMN: cursor number of table holding column ** TK_REGISTER: register number ** TK_TRIGGER: 1 -> new, 0 -> old - ** EP_Unlikely: 134217728 times likelihood */ + ** EP_Unlikely: 134217728 times likelihood + ** TK_SELECT: 1st register of result vector */ ynVar iColumn; /* TK_COLUMN: column index. -1 for rowid. - ** TK_VARIABLE: variable number (always >= 1). */ + ** TK_VARIABLE: variable number (always >= 1). + ** TK_SELECT_COLUMN: column of the result vector */ i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */ i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */ u8 op2; /* TK_REGISTER: original value of Expr.op @@ -4273,6 +4275,7 @@ int sqlite3DbstatRegister(sqlite3*); int sqlite3ExprVectorSize(Expr *pExpr); int sqlite3ExprIsVector(Expr *pExpr); -Expr *sqlite3ExprVectorField(Expr*, int); +Expr *sqlite3VectorFieldSubexpr(Expr*, int); +Expr *sqlite3ExprForVectorField(Parse*,Expr*,int,int); #endif /* SQLITEINT_H */ diff --git a/src/vdbemem.c b/src/vdbemem.c index 0709527634..bc08042000 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -1571,7 +1571,7 @@ int sqlite3Stat4ProbeSetValue( for(i=0; idb, pIdx, iVal+i); alloc.iVal = iVal+i; rc = stat4ValueFromExpr(pParse, pElem, aff, &alloc, &pVal); diff --git a/src/wherecode.c b/src/wherecode.c index dd15b5893c..2ef9a46b5d 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -377,34 +377,7 @@ static int codeEqualityTerm( assert( pLevel->pWLoop->aLTerm[iEq]==pTerm ); assert( iTarget>0 ); if( pX->op==TK_EQ || pX->op==TK_IS ){ - Expr *pRight = pX->pRight; -#ifndef SQLITE_OMIT_SUBQUERY - if( pRight->op==TK_SELECT_COLUMN ){ - /* This case occurs for expressions like "(a, b) == (SELECT ...)". */ - WhereLoop *pLoop = pLevel->pWLoop; - int i; - Expr *pSub = pRight->pLeft; - assert( pSub->op==TK_SELECT ); - for(i=pLoop->nSkip; iaLTerm[i]->pExpr->pRight; - if( pExpr && pExpr->op==TK_SELECT_COLUMN && pExpr->pLeft==pSub ) break; - } - - if( i==iEq ){ - iReg = sqlite3CodeSubselect(pParse, pSub, 0, 0); - for(/*no-op*/; inLTerm; i++){ - Expr *pExpr = pLoop->aLTerm[i]->pExpr->pRight; - if( pExpr && pExpr->op==TK_SELECT_COLUMN && pExpr->pLeft==pSub ){ - sqlite3VdbeAddOp2(v, OP_Copy, iReg+pExpr->iColumn, iTarget-iEq+i); - } - } - } - iReg = iTarget; - }else -#endif - { - iReg = sqlite3ExprCodeTarget(pParse, pRight, iTarget); - } + iReg = sqlite3ExprCodeTarget(pParse, pX->pRight, iTarget); }else if( pX->op==TK_ISNULL ){ iReg = iTarget; sqlite3VdbeAddOp2(v, OP_Null, 0, iReg); @@ -1101,11 +1074,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( addrNotFound = pLevel->addrNxt; }else{ Expr *pRight = pTerm->pExpr->pRight; - if( pRight->op==TK_SELECT_COLUMN ){ - codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget); - }else{ - codeExprOrVector(pParse, pRight, iTarget, 1); - } + codeExprOrVector(pParse, pRight, iTarget, 1); } } sqlite3VdbeAddOp2(v, OP_Integer, pLoop->u.vtab.idxNum, iReg); diff --git a/src/whereexpr.c b/src/whereexpr.c index 2fc903ce6e..55a4cf568a 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -873,29 +873,6 @@ static int exprMightBeIndexed( return 0; } -/* -** The expression passed as the second argument is a vector (either a -** TK_VECTOR node or a TK_SELECT that returns more than one column). This -** function returns a pointer to a new expression object representing -** field iField of the vector. -** -** If pVector is of type TK_VECTOR, the returned object is just a copy of -** the iField'th element of the vector. Or, if pVector is of type TK_SELECT, -** the return value points to a new expression object of type -** TK_SELECT_COLUMN. -*/ -static Expr *exprExtractVectorField(Parse *pParse, Expr *pVector, int iField){ - Expr *pRet; - assert( sqlite3ExprIsVector(pVector) ); - if( pVector->flags & EP_xIsSelect ){ - pRet = sqlite3PExpr(pParse, TK_SELECT_COLUMN, pVector, 0, 0); - if( pRet ) pRet->iColumn = iField; - }else{ - pRet = sqlite3ExprDup(pParse->db, pVector->x.pList->a[iField].pExpr, 0); - } - return pRet; -} - /* ** The input to this routine is an WhereTerm structure with only the ** "pExpr" field filled in. The job of this routine is to analyze the @@ -1211,14 +1188,17 @@ static void exprAnalyze( for(i=0; ipLeft); i++){ int idxNew; Expr *pNew; - Expr *pLeft = exprExtractVectorField(pParse, pExpr->pLeft, i); - Expr *pRight = exprExtractVectorField(pParse, pExpr->pRight, i); + Expr *pLeft = sqlite3ExprForVectorField(pParse, pExpr->pLeft, i, 0); + Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i, 0); pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight, 0); - idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC); + idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC); exprAnalyze(pSrc, pWC, idxNew); markTermAsChild(pWC, idxNew, idxTerm); } + pTerm = &pWC->a[idxTerm]; + pTerm->wtFlags = TERM_CODED; + pTerm->eOperator = 0; } } From a1251bc40d7f825a764e558717a8c348eb94ca1b Mon Sep 17 00:00:00 2001 From: drh Date: Sat, 20 Aug 2016 00:51:37 +0000 Subject: [PATCH 39/97] Add support for vector assignments in the SET clause of an UPDATE statement. FossilOrigin-Name: f320d47d6b7b08d9552c8444004bc256348bda90 --- manifest | 19 +++++++------ manifest.uuid | 2 +- src/expr.c | 68 +++++++++++++++++++++++++++++++++++++-------- src/parse.y | 6 ++++ src/sqliteInt.h | 3 +- src/whereexpr.c | 4 +-- test/rowvalue7.test | 58 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 135 insertions(+), 25 deletions(-) create mode 100644 test/rowvalue7.test diff --git a/manifest b/manifest index 52c2870784..122bc50e37 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Change\sthe\sway\sTK_SELECT_COLUMN\sis\shandled\sso\sthat\sthe\ssubquery\sis\sonly\ngenerated\sonce\seven\sif\spart\sof\sthe\svector\scomparison\sis\sused\sfor\sindexing\nand\sthe\sother\spart\sis\snow.\s\sThis\schange\salso\sis\sa\spathway\sto\svector\sassignment\nin\sUPDATE\sstatements. -D 2016-08-20T00:07:01.602 +C Add\ssupport\sfor\svector\sassignments\sin\sthe\sSET\sclause\sof\san\sUPDATE\sstatement. +D 2016-08-20T00:51:37.333 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c ff73eb6cae76c2423928d46ceff6f2fdc379a568 +F src/expr.c bdc98d3301358bac9b19e23a22375f5220a77274 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -373,7 +373,7 @@ F src/os_win.c 520f23475f1de530c435d30b67b7b15fe90874b0 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c 40928c450320da78bb4bd3ae82818f4239e19b7e F src/pager.h 966d2769e76ae347c8a32c4165faf6e6cb64546d -F src/parse.y 3d02d64f333c4371c4f08fbd79a64964fa27b576 +F src/parse.y 9895eddbbb03c4a47cd760ff4f51cb8d139884d2 F src/pcache.c 5583c8ade4b05075a60ba953ef471d1c1a9c05df F src/pcache.h 2cedcd8407eb23017d92790b112186886e179490 F src/pcache1.c 4bb7a6a5300c67d0b033d25adb509c120c03e812 @@ -389,7 +389,7 @@ F src/shell.c 79dda477be6c96eba6e952a934957ad36f87acc7 F src/sqlite.h.in 0f7580280d1b009b507d8beec1ff0f197ba0cc99 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 8648034aa702469afb553231677306cc6492a1ae -F src/sqliteInt.h 84f32ad3a5bfaf698b82bf5a9c6939f3e3d79ee0 +F src/sqliteInt.h db8aa0c875b1a3cdb26e1cee47662c30a34882fd F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247 F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1 F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9 @@ -467,7 +467,7 @@ F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 F src/where.c c7cdfd54f383090bb801cdd50d36de1a24684bb2 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 F src/wherecode.c 0c790c422ea5ecb6c83d6a78f2c4ebc036eea90e -F src/whereexpr.c 021469f11930cc705a26f7f31ec37640588df1d4 +F src/whereexpr.c 706b539d9fcc884b1b9d4692a87c78019fb59fd2 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -1025,6 +1025,7 @@ F test/rowvalue3.test dbe935260851b197dfbbbcb0ac2a15cb5f324fd4 F test/rowvalue4.test ed3f7974099f0fc508c9f95df55d37c49f43be65 F test/rowvalue5.test 01c7e0bc4048f30b58e6eb27ecd26e5bd312635e F test/rowvalue6.test d19b54feb604d5601f8614b15e214e0774c01087 +F test/rowvalue7.test 3c9a127954d3da309a271babdfc43dbcc5c4da7f F test/rowvaluefault.test 7b16485e3f2b371f3e3d05455b8ded6d0c090244 F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09 @@ -1518,7 +1519,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 d4a5af69cc3bde6f8c02075c07b4d47a0252392d -R 8a9288690d2190168e2ddd79f5078350 +P d8feea7dcde83179bff303072426561cfe825e58 +R d0cc8110f72472ebd6ad961310b5fb0d U drh -Z 663c48e63b0d59d8fc6b403d2678eeeb +Z 375b2bf2a6add0d9f506b37662f21e95 diff --git a/manifest.uuid b/manifest.uuid index a8cf8cf3bc..653a21c0af 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d8feea7dcde83179bff303072426561cfe825e58 \ No newline at end of file +f320d47d6b7b08d9552c8444004bc256348bda90 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index de2c7a6a26..d8737c075e 100644 --- a/src/expr.c +++ b/src/expr.c @@ -370,23 +370,17 @@ Expr *sqlite3VectorFieldSubexpr(Expr *pVector, int i){ ** The caller owns the returned Expr object and is responsible for ** ensuring that the returned value eventually gets freed. ** -** Ownership of pVector is controlled by the takeOwnership parameter. If -** takeOwnership is true, this routine takes responsibility for freeing -** pVector, and may do so before returning, hence the caller must not reference -** pVector again. If takeOwnership is false, then the caller takes -** responsibility for freeing pVector and must ensure the pVector remains -** valid as long as the returned value remains in use. +** The caller retains ownership of pVector and must ensure that pVector +** remains valid as long as the returned value is in use. */ Expr *sqlite3ExprForVectorField( Parse *pParse, /* Parsing context */ Expr *pVector, /* The vector. List of expressions or a sub-SELECT */ - int iField, /* Which column of the vector to return */ - int takeOwnership /* True to take ownership of pVector before returning */ + int iField /* Which column of the vector to return */ ){ Expr *pRet; - assert( sqlite3ExprIsVector(pVector) ); - /* FIXME: Add support for takeOwnership!=0 */ assert( takeOwnership==0 ); - if( pVector->flags & EP_xIsSelect ){ + if( pVector->op==TK_SELECT ){ + assert( pVector->flags & EP_xIsSelect ); /* The TK_SELECT_COLUMN Expr node: ** ** pLeft: pVector containing TK_SELECT @@ -405,7 +399,8 @@ Expr *sqlite3ExprForVectorField( if( pRet ) pRet->iColumn = iField; assert( pRet==0 || pRet->iTable==0 ); }else{ - pRet = sqlite3ExprDup(pParse->db, pVector->x.pList->a[iField].pExpr, 0); + if( pVector->op==TK_VECTOR ) pVector = pVector->x.pList->a[iField].pExpr; + pRet = sqlite3ExprDup(pParse->db, pVector, 0); } return pRet; } @@ -1440,6 +1435,55 @@ no_mem: return 0; } +/* +** pColumns and pExpr for a vector assignment, like this: +** +** (a,b,c) = (expr1,expr2,expr3) +** Or: (a,b,c) = (SELECT x,y,z FROM ....) +** +** For each term of the vector assignment, append new entries to the +** expression list. In the case of a subquery on the LHS, append +** TK_SELECT_COLUMN expressions. +*/ +ExprList *sqlite3ExprListAppendVector( + Parse *pParse, /* Parsing context */ + ExprList *pList, /* List to which to append. Might be NULL */ + IdList *pColumns, /* List of names of LHS of the assignment */ + Expr *pExpr /* Vector expression to be appended. Might be NULL */ +){ + sqlite3 *db = pParse->db; + int n; + int i; + if( pColumns==0 ) goto vector_append_error; + if( pExpr==0 ) goto vector_append_error; + n = sqlite3ExprVectorSize(pExpr); + if( pColumns->nId!=n ){ + sqlite3ErrorMsg(pParse, "%d columns assigned %d values", + pColumns->nId, n); + goto vector_append_error; + } + for(i=0; ia[pList->nExpr-1].zName = pColumns->a[i].zName; + pColumns->a[i].zName = 0; + } + } + if( pExpr->op==TK_SELECT ){ + if( pList && pList->a[0].pExpr ){ + assert( pList->a[0].pExpr->op==TK_SELECT_COLUMN ); + pList->a[0].pExpr->pRight = pExpr; + pExpr = 0; + } + } + +vector_append_error: + sqlite3ExprDelete(db, pExpr); + sqlite3IdListDelete(db, pColumns); + return pList; +} + /* ** Set the sort order for the last element on the given ExprList. */ diff --git a/src/parse.y b/src/parse.y index bf15d3d5a7..c175b02867 100644 --- a/src/parse.y +++ b/src/parse.y @@ -789,10 +789,16 @@ setlist(A) ::= setlist(A) COMMA nm(X) EQ expr(Y). { A = sqlite3ExprListAppend(pParse, A, Y.pExpr); sqlite3ExprListSetName(pParse, A, &X, 1); } +setlist(A) ::= setlist(A) COMMA LP idlist(X) RP EQ expr(Y). { + A = sqlite3ExprListAppendVector(pParse, A, X, Y.pExpr); +} setlist(A) ::= nm(X) EQ expr(Y). { A = sqlite3ExprListAppend(pParse, 0, Y.pExpr); sqlite3ExprListSetName(pParse, A, &X, 1); } +setlist(A) ::= LP idlist(X) RP EQ expr(Y). { + A = sqlite3ExprListAppendVector(pParse, 0, X, Y.pExpr); +} ////////////////////////// The INSERT command ///////////////////////////////// // diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 7651ff69e4..d5ba007724 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3547,6 +3547,7 @@ Expr *sqlite3ExprFunction(Parse*,ExprList*, Token*); void sqlite3ExprAssignVarNumber(Parse*, Expr*); void sqlite3ExprDelete(sqlite3*, Expr*); ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*); +ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*); void sqlite3ExprListSetSortOrder(ExprList*,int); void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int); void sqlite3ExprListSetSpan(Parse*,ExprList*,ExprSpan*); @@ -4276,6 +4277,6 @@ int sqlite3DbstatRegister(sqlite3*); int sqlite3ExprVectorSize(Expr *pExpr); int sqlite3ExprIsVector(Expr *pExpr); Expr *sqlite3VectorFieldSubexpr(Expr*, int); -Expr *sqlite3ExprForVectorField(Parse*,Expr*,int,int); +Expr *sqlite3ExprForVectorField(Parse*,Expr*,int); #endif /* SQLITEINT_H */ diff --git a/src/whereexpr.c b/src/whereexpr.c index 55a4cf568a..c49ff50f35 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -1188,8 +1188,8 @@ static void exprAnalyze( for(i=0; ipLeft); i++){ int idxNew; Expr *pNew; - Expr *pLeft = sqlite3ExprForVectorField(pParse, pExpr->pLeft, i, 0); - Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i, 0); + Expr *pLeft = sqlite3ExprForVectorField(pParse, pExpr->pLeft, i); + Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i); pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight, 0); idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC); diff --git a/test/rowvalue7.test b/test/rowvalue7.test new file mode 100644 index 0000000000..a90e235ce2 --- /dev/null +++ b/test/rowvalue7.test @@ -0,0 +1,58 @@ +# 2016-08-18 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# The focus of this file is vector assignments in the SET clause of +# an UPDATE statement. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix rowvalue7 + +do_execsql_test 1.1 { + CREATE TABLE t1(a,b,c,d); + CREATE INDEX t1x ON t1(a,b); + INSERT INTO t1(a,b,c,d) VALUES(1,2,0,0),(3,4,0,0),(5,6,0,0); + CREATE TABLE t2(w,x,y,z); + CREATE INDEX t2x ON t2(w,x); + INSERT INTO t2(w,x,y,z) VALUES(1,2,11,22),(8,9,88,99),(3,5,33,55),(5,6,55,66); + + SELECT *,'|' FROM t1 ORDER BY a; +} {1 2 0 0 | 3 4 0 0 | 5 6 0 0 |} + +do_execsql_test 1.2 { + UPDATE t1 SET (c,d) = (SELECT y,z FROM t2 WHERE (w,x)=(a,b)); + SELECT *,'|' FROM t1 ORDER BY a; +} {1 2 11 22 | 3 4 {} {} | 5 6 55 66 |} + +do_execsql_test 1.3 { + UPDATE t1 SET (c,d) = (SELECT y,z FROM t2 WHERE w=a); + SELECT *,'|' FROM t1 ORDER BY a; +} {1 2 11 22 | 3 4 33 55 | 5 6 55 66 |} + +do_execsql_test 1.4 { + UPDATE t1 SET (c) = 99 WHERE a=3; + SELECT *,'|' FROM t1 ORDER BY a; +} {1 2 11 22 | 3 4 99 55 | 5 6 55 66 |} + +do_execsql_test 1.5 { + UPDATE t1 SET b = 8, (c,d) = (123,456) WHERE a=3; + SELECT *,'|' FROM t1 ORDER BY a; +} {1 2 11 22 | 3 8 123 456 | 5 6 55 66 |} + +do_catchsql_test 2.1 { + UPDATE t1 SET (c,d) = (SELECT x,y,z FROM t2 WHERE w=a); +} {1 {2 columns assigned 3 values}} + +do_catchsql_test 2.2 { + UPDATE t1 SET (b,c,d) = (SELECT x,y FROM t2 WHERE w=a); +} {1 {3 columns assigned 2 values}} + +finish_test From 8762ec19356cced5e3f3d3647912ed9f7507ae64 Mon Sep 17 00:00:00 2001 From: drh Date: Sat, 20 Aug 2016 01:06:22 +0000 Subject: [PATCH 40/97] Improvements to comments. No code changes. FossilOrigin-Name: 4165d20f64d778a4e48f511a37cfced8579d4dbe --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/expr.c | 28 ++++++++++++++++++++-------- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/manifest b/manifest index 122bc50e37..c056b2c63e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\ssupport\sfor\svector\sassignments\sin\sthe\sSET\sclause\sof\san\sUPDATE\sstatement. -D 2016-08-20T00:51:37.333 +C Improvements\sto\scomments.\s\sNo\scode\schanges. +D 2016-08-20T01:06:22.412 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c bdc98d3301358bac9b19e23a22375f5220a77274 +F src/expr.c d5cffb307d4e812c41a6fb1f6bc94e6b0801e3e8 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1519,7 +1519,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 d8feea7dcde83179bff303072426561cfe825e58 -R d0cc8110f72472ebd6ad961310b5fb0d +P f320d47d6b7b08d9552c8444004bc256348bda90 +R d44ef1061d1c50d06d327d2ef8a626d9 U drh -Z 375b2bf2a6add0d9f506b37662f21e95 +Z fa86522a92bb8bcf293bd50750ba2b7b diff --git a/manifest.uuid b/manifest.uuid index 653a21c0af..f9c4d10372 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f320d47d6b7b08d9552c8444004bc256348bda90 \ No newline at end of file +4165d20f64d778a4e48f511a37cfced8579d4dbe \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index d8737c075e..5a5afe5ff3 100644 --- a/src/expr.c +++ b/src/expr.c @@ -367,11 +367,21 @@ Expr *sqlite3VectorFieldSubexpr(Expr *pVector, int i){ ** sqlite3ExprCode() will generate all necessary code to compute ** the iField-th column of the vector expression pVector. ** +** It is ok for pVector to be a scalar (as long as iField==0). +** In that case, this routine works like sqlite3ExprDup(). +** ** The caller owns the returned Expr object and is responsible for ** ensuring that the returned value eventually gets freed. ** -** The caller retains ownership of pVector and must ensure that pVector -** remains valid as long as the returned value is in use. +** The caller retains ownership of pVector. If pVector is a TK_SELECT, +** then the return value will reference pVector and so pVector must remain +** valid for the life of the returned object. If pVector is a TK_VECTOR +** or a scalar expression, then it can be deleted as soon as this routine +** routines. +** +** A trick to cause a TK_SELECT pVector to be deleted together with +** the returned Expr object is to attach the pVector to the pRight field +** of the returned TK_SELECT_COLUMN Expr object. */ Expr *sqlite3ExprForVectorField( Parse *pParse, /* Parsing context */ @@ -384,16 +394,17 @@ Expr *sqlite3ExprForVectorField( /* The TK_SELECT_COLUMN Expr node: ** ** pLeft: pVector containing TK_SELECT - ** pRight: pVector if ownership taken + ** pRight: not used. But recursively deleted. ** iColumn: Index of a column in pVector ** pLeft->iTable: First in an array of register holding result, or 0 ** if the result is not yet computed. ** ** sqlite3ExprDelete() specifically skips the recursive delete of ** pLeft on TK_SELECT_COLUMN nodes. But pRight is followed, so pVector - ** is included on pRight if ownership is taken. Typically there will - ** be multiple TK_SELECT_COLUMN nodes with the same pLeft pointer to - ** the pVector, but only one of them will own the pVector. + ** can be attached to pRight to cause this node to take ownership of + ** pVector. Typically there will be multiple TK_SELECT_COLUMN nodes + ** with the same pLeft pointer to the pVector, but only one of them + ** will own the pVector. */ pRet = sqlite3PExpr(pParse, TK_SELECT_COLUMN, pVector, 0, 0); if( pRet ) pRet->iColumn = iField; @@ -1436,13 +1447,14 @@ no_mem: } /* -** pColumns and pExpr for a vector assignment, like this: +** pColumns and pExpr form a vector assignment which is part of the SET +** clause of an UPDATE statement. Like this: ** ** (a,b,c) = (expr1,expr2,expr3) ** Or: (a,b,c) = (SELECT x,y,z FROM ....) ** ** For each term of the vector assignment, append new entries to the -** expression list. In the case of a subquery on the LHS, append +** expression list pList. In the case of a subquery on the LHS, append ** TK_SELECT_COLUMN expressions. */ ExprList *sqlite3ExprListAppendVector( From 9e730f0bb8f06ca83ab6afa54c4224957f673606 Mon Sep 17 00:00:00 2001 From: drh Date: Sat, 20 Aug 2016 12:00:05 +0000 Subject: [PATCH 41/97] Improvements to the vector comparison splitter in exprAnalyze(). FossilOrigin-Name: a3ffd283bc931b04170ef737e56bced33d27f06d --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/whereexpr.c | 8 +++++--- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index c056b2c63e..04b3482aed 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improvements\sto\scomments.\s\sNo\scode\schanges. -D 2016-08-20T01:06:22.412 +C Improvements\sto\sthe\svector\scomparison\ssplitter\sin\sexprAnalyze(). +D 2016-08-20T12:00:05.657 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -467,7 +467,7 @@ F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 F src/where.c c7cdfd54f383090bb801cdd50d36de1a24684bb2 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 F src/wherecode.c 0c790c422ea5ecb6c83d6a78f2c4ebc036eea90e -F src/whereexpr.c 706b539d9fcc884b1b9d4692a87c78019fb59fd2 +F src/whereexpr.c aa54bf11adf6bc7e52f56281f436ab5fd421ce16 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -1519,7 +1519,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 f320d47d6b7b08d9552c8444004bc256348bda90 -R d44ef1061d1c50d06d327d2ef8a626d9 +P 4165d20f64d778a4e48f511a37cfced8579d4dbe +R 29e91d5826d73f7831f4580f57f08037 U drh -Z fa86522a92bb8bcf293bd50750ba2b7b +Z 96272366ac5b1657f908e6772496984c diff --git a/manifest.uuid b/manifest.uuid index f9c4d10372..72cde1cebc 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4165d20f64d778a4e48f511a37cfced8579d4dbe \ No newline at end of file +a3ffd283bc931b04170ef737e56bced33d27f06d \ No newline at end of file diff --git a/src/whereexpr.c b/src/whereexpr.c index c49ff50f35..823a2df235 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -1173,7 +1173,10 @@ static void exprAnalyze( #endif /* SQLITE_OMIT_VIRTUALTABLE */ /* If there is a vector == or IS term - e.g. "(a, b) == (?, ?)" - create - ** a virtual term for each component comparison - "a = ?" and "b = ?". + ** new terms for each component comparison - "a = ?" and "b = ?". The + ** new terms completely replace the original vector comparison, which is + ** no longer used. + ** ** This is only required if at least one side of the comparison operation ** is not a sub-select. */ if( pWC->op==TK_AND @@ -1194,10 +1197,9 @@ static void exprAnalyze( pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight, 0); idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC); exprAnalyze(pSrc, pWC, idxNew); - markTermAsChild(pWC, idxNew, idxTerm); } pTerm = &pWC->a[idxTerm]; - pTerm->wtFlags = TERM_CODED; + pTerm->wtFlags = TERM_CODED|TERM_VIRTUAL; /* Disable the original */ pTerm->eOperator = 0; } } From f358009a93556ba3f2b71de5354168bd65c49452 Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 20 Aug 2016 15:01:24 +0000 Subject: [PATCH 42/97] Fix a segfault that could occur if a query that used a vector comparison contained certain types of syntax errors. FossilOrigin-Name: 203f07c5e140e74cf91d0c1e20135c21236f0fc1 --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/expr.c | 4 ++-- test/rowvalue4.test | 16 ++++++++++++++++ 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index 04b3482aed..9c5692d524 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improvements\sto\sthe\svector\scomparison\ssplitter\sin\sexprAnalyze(). -D 2016-08-20T12:00:05.657 +C Fix\sa\ssegfault\sthat\scould\soccur\sif\sa\squery\sthat\sused\sa\svector\scomparison\scontained\scertain\stypes\sof\ssyntax\serrors. +D 2016-08-20T15:01:24.921 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c d5cffb307d4e812c41a6fb1f6bc94e6b0801e3e8 +F src/expr.c f1d9c4374246955b9a93edddfc3abfd3297d1455 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1022,7 +1022,7 @@ F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d F test/rowvalue.test 56b34d31d91340a6e922e753b798880170cc1aa7 F test/rowvalue2.test 875068299fd4dd50ef0a47786462c8e1f4065f9a F test/rowvalue3.test dbe935260851b197dfbbbcb0ac2a15cb5f324fd4 -F test/rowvalue4.test ed3f7974099f0fc508c9f95df55d37c49f43be65 +F test/rowvalue4.test 318cdd40e66dfae686537eea581ae49cbb01195d F test/rowvalue5.test 01c7e0bc4048f30b58e6eb27ecd26e5bd312635e F test/rowvalue6.test d19b54feb604d5601f8614b15e214e0774c01087 F test/rowvalue7.test 3c9a127954d3da309a271babdfc43dbcc5c4da7f @@ -1519,7 +1519,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 4165d20f64d778a4e48f511a37cfced8579d4dbe -R 29e91d5826d73f7831f4580f57f08037 -U drh -Z 96272366ac5b1657f908e6772496984c +P a3ffd283bc931b04170ef737e56bced33d27f06d +R b7a3d3965778241daed9250312140835 +U dan +Z 9e9f2920cf64cc9378d276416e2ee3f8 diff --git a/manifest.uuid b/manifest.uuid index 72cde1cebc..6e35ee275b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a3ffd283bc931b04170ef737e56bced33d27f06d \ No newline at end of file +203f07c5e140e74cf91d0c1e20135c21236f0fc1 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 5a5afe5ff3..0412919a24 100644 --- a/src/expr.c +++ b/src/expr.c @@ -462,8 +462,8 @@ static int exprVectorRegister( int *pRegFree /* OUT: Temp register to free */ ){ assert( pVector->op==TK_VECTOR || pVector->op==TK_SELECT ); - assert( (pVector->op==TK_VECTOR)==(regSelect==0) ); - if( regSelect ){ + assert( pParse->nErr || (pVector->op==TK_VECTOR)==(regSelect==0) ); + if( pVector->op==TK_SELECT ){ *ppExpr = pVector->x.pSelect->pEList->a[iField].pExpr; return regSelect+iField; } diff --git a/test/rowvalue4.test b/test/rowvalue4.test index 55401aa8a3..c4581d5562 100644 --- a/test/rowvalue4.test +++ b/test/rowvalue4.test @@ -292,5 +292,21 @@ do_catchsql_test 7.4 { SELECT * FROM f1 WHERE (?, ? COLLATE nose) > (a, b); } {1 {no such collation sequence: nose}} +#------------------------------------------------------------------------- +drop_all_tables +do_execsql_test 8.1 { + CREATE TABLE c1(x, y); + CREATE TABLE c2(a, b, c); + CREATE INDEX c2ab ON c2(a, b); + CREATE INDEX c2c ON c2(c); + + CREATE TABLE c3(d); +} +do_catchsql_test 8.2 { + SELECT * FROM c2 CROSS JOIN c3 WHERE + ( (a, b) == (SELECT x, y FROM c1) AND c3.d = c ) OR + ( c == (SELECT x, y FROM c1) AND c3.d = c ) +} {1 {sub-select returns 2 columns - expected 1}} + finish_test From 9854260bcaa2c049e9d323a39a44f7d75365c820 Mon Sep 17 00:00:00 2001 From: drh Date: Sat, 20 Aug 2016 17:00:16 +0000 Subject: [PATCH 43/97] Do not duplicate the Expr.pLeft subtree of a TK_SELECT_COLUMN node. FossilOrigin-Name: 8384c77ebb3f65fbc54c199885926f2066f0b140 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/expr.c | 6 +++++- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 9c5692d524..2f60c39a03 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\ssegfault\sthat\scould\soccur\sif\sa\squery\sthat\sused\sa\svector\scomparison\scontained\scertain\stypes\sof\ssyntax\serrors. -D 2016-08-20T15:01:24.921 +C Do\snot\sduplicate\sthe\sExpr.pLeft\ssubtree\sof\sa\sTK_SELECT_COLUMN\snode. +D 2016-08-20T17:00:16.608 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c f1d9c4374246955b9a93edddfc3abfd3297d1455 +F src/expr.c 8e1288b32f542dd8e8a2e21c30e936112968a4ab F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1519,7 +1519,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 a3ffd283bc931b04170ef737e56bced33d27f06d -R b7a3d3965778241daed9250312140835 -U dan -Z 9e9f2920cf64cc9378d276416e2ee3f8 +P 203f07c5e140e74cf91d0c1e20135c21236f0fc1 +R 753238d9341a2b18b0026dcf708bc835 +U drh +Z d423421ba92880810032946cac8ccbb5 diff --git a/manifest.uuid b/manifest.uuid index 6e35ee275b..3459023db3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -203f07c5e140e74cf91d0c1e20135c21236f0fc1 \ No newline at end of file +8384c77ebb3f65fbc54c199885926f2066f0b140 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 0412919a24..bd2426addb 100644 --- a/src/expr.c +++ b/src/expr.c @@ -1205,7 +1205,11 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){ } }else{ if( !ExprHasProperty(p, EP_TokenOnly) ){ - pNew->pLeft = sqlite3ExprDup(db, p->pLeft, 0); + if( pNew->op==TK_SELECT_COLUMN ){ + pNew->pLeft = p->pLeft; + }else{ + pNew->pLeft = sqlite3ExprDup(db, p->pLeft, 0); + } pNew->pRight = sqlite3ExprDup(db, p->pRight, 0); } } From 8bd0d58e1c5e66c5109435bf86c24f9d0b71130f Mon Sep 17 00:00:00 2001 From: drh Date: Sat, 20 Aug 2016 18:06:14 +0000 Subject: [PATCH 44/97] Fixes for problems following OOM errors. FossilOrigin-Name: 9041ee4a6f0e8389297f887f1431ab5cfe783390 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/expr.c | 10 +++++++--- src/parse.y | 7 +++++-- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/manifest b/manifest index 2f60c39a03..15b48703cd 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Do\snot\sduplicate\sthe\sExpr.pLeft\ssubtree\sof\sa\sTK_SELECT_COLUMN\snode. -D 2016-08-20T17:00:16.608 +C Fixes\sfor\sproblems\sfollowing\sOOM\serrors. +D 2016-08-20T18:06:14.286 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 8e1288b32f542dd8e8a2e21c30e936112968a4ab +F src/expr.c e96f29a02a3927b2afaca1f86fc84e80cac50ca7 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -373,7 +373,7 @@ F src/os_win.c 520f23475f1de530c435d30b67b7b15fe90874b0 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c 40928c450320da78bb4bd3ae82818f4239e19b7e F src/pager.h 966d2769e76ae347c8a32c4165faf6e6cb64546d -F src/parse.y 9895eddbbb03c4a47cd760ff4f51cb8d139884d2 +F src/parse.y 0e0b6d46a990d01e4ca1e9d7e1d2d9b5a98f6bcb F src/pcache.c 5583c8ade4b05075a60ba953ef471d1c1a9c05df F src/pcache.h 2cedcd8407eb23017d92790b112186886e179490 F src/pcache1.c 4bb7a6a5300c67d0b033d25adb509c120c03e812 @@ -1519,7 +1519,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 203f07c5e140e74cf91d0c1e20135c21236f0fc1 -R 753238d9341a2b18b0026dcf708bc835 +P 8384c77ebb3f65fbc54c199885926f2066f0b140 +R d29ab3049f2812696918a926abccb285 U drh -Z d423421ba92880810032946cac8ccbb5 +Z 08f6ec095045e30304bec0161a676b54 diff --git a/manifest.uuid b/manifest.uuid index 3459023db3..be0615edf6 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8384c77ebb3f65fbc54c199885926f2066f0b140 \ No newline at end of file +9041ee4a6f0e8389297f887f1431ab5cfe783390 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index bd2426addb..ac67eca691 100644 --- a/src/expr.c +++ b/src/expr.c @@ -406,8 +406,11 @@ Expr *sqlite3ExprForVectorField( ** with the same pLeft pointer to the pVector, but only one of them ** will own the pVector. */ - pRet = sqlite3PExpr(pParse, TK_SELECT_COLUMN, pVector, 0, 0); - if( pRet ) pRet->iColumn = iField; + pRet = sqlite3PExpr(pParse, TK_SELECT_COLUMN, 0, 0, 0); + if( pRet ){ + pRet->iColumn = iField; + pRet->pLeft = pVector; + } assert( pRet==0 || pRet->iTable==0 ); }else{ if( pVector->op==TK_VECTOR ) pVector = pVector->x.pList->a[iField].pExpr; @@ -462,7 +465,8 @@ static int exprVectorRegister( int *pRegFree /* OUT: Temp register to free */ ){ assert( pVector->op==TK_VECTOR || pVector->op==TK_SELECT ); - assert( pParse->nErr || (pVector->op==TK_VECTOR)==(regSelect==0) ); + assert( pParse->nErr || pParse->db->mallocFailed + || (pVector->op==TK_VECTOR)==(regSelect==0) ); if( pVector->op==TK_SELECT ){ *ppExpr = pVector->x.pSelect->pEList->a[iField].pExpr; return regSelect+iField; diff --git a/src/parse.y b/src/parse.y index c175b02867..51bdcff691 100644 --- a/src/parse.y +++ b/src/parse.y @@ -952,11 +952,14 @@ term(A) ::= CTIME_KW(OP). { } } -expr(A) ::= LP(L) nexprlist(X) COMMA expr(Y) RP(R). { +expr(A) ::= LP(L) nexprlist(X) COMMA expr(Y) RP(R). { + ExprList *pList = sqlite3ExprListAppend(pParse, X, Y.pExpr); A.pExpr = sqlite3PExpr(pParse, TK_VECTOR, 0, 0, 0); if( A.pExpr ){ - A.pExpr->x.pList = sqlite3ExprListAppend(pParse, X, Y.pExpr); + A.pExpr->x.pList = pList; spanSet(&A, &L, &R); + }else{ + sqlite3ExprListDelete(pParse->db, pList); } } From 76dbe7a8d0feac9a600e6425274f65187d378cbd Mon Sep 17 00:00:00 2001 From: drh Date: Sat, 20 Aug 2016 21:02:38 +0000 Subject: [PATCH 45/97] Clarification of code comments in expr.c. Clean up the implementations of sqlite3ExprIsVector() and sqlite3ExprVectorSize() slightly. FossilOrigin-Name: 4fb66d6592b141a4a71359250dbd1ac454569cb9 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/expr.c | 28 ++++++++++++++-------------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/manifest b/manifest index 15b48703cd..63e0795631 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fixes\sfor\sproblems\sfollowing\sOOM\serrors. -D 2016-08-20T18:06:14.286 +C Clarification\sof\scode\scomments\sin\sexpr.c.\s\sClean\sup\sthe\simplementations\nof\ssqlite3ExprIsVector()\sand\ssqlite3ExprVectorSize()\sslightly. +D 2016-08-20T21:02:38.224 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c e96f29a02a3927b2afaca1f86fc84e80cac50ca7 +F src/expr.c 4adf875e1bfc155b107ee8e35e7cb1c5599f955b F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1519,7 +1519,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 8384c77ebb3f65fbc54c199885926f2066f0b140 -R d29ab3049f2812696918a926abccb285 +P 9041ee4a6f0e8389297f887f1431ab5cfe783390 +R bba779342591015bf7eda1fee0ab13af U drh -Z 08f6ec095045e30304bec0161a676b54 +Z 030a18789c82832c6d203bdc6c28c7db diff --git a/manifest.uuid b/manifest.uuid index be0615edf6..d9ffa9fb0c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9041ee4a6f0e8389297f887f1431ab5cfe783390 \ No newline at end of file +4fb66d6592b141a4a71359250dbd1ac454569cb9 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index ac67eca691..69754fada6 100644 --- a/src/expr.c +++ b/src/expr.c @@ -313,9 +313,7 @@ static int codeCompare( ** Return true if expression pExpr is a vector, or false otherwise. */ int sqlite3ExprIsVector(Expr *pExpr){ - return ( (pExpr->op==TK_VECTOR) - || (pExpr->op==TK_SELECT && pExpr->x.pSelect->pEList->nExpr>1) - ); + return sqlite3ExprVectorSize(pExpr)>1; } /* @@ -325,28 +323,30 @@ int sqlite3ExprIsVector(Expr *pExpr){ ** any other type of expression, return 1. */ int sqlite3ExprVectorSize(Expr *pExpr){ - if( sqlite3ExprIsVector(pExpr)==0 ) return 1; - if( pExpr->flags & EP_xIsSelect ){ + if( pExpr->op==TK_VECTOR ){ + return pExpr->x.pList->nExpr; + }else if( pExpr->op==TK_SELECT ){ return pExpr->x.pSelect->pEList->nExpr; + }else{ + return 1; } - return pExpr->x.pList->nExpr; } #ifndef SQLITE_OMIT_SUBQUERY /* -** Interpret the pVector input as a vector expression. If pVector is -** an ordinary scalar expression, treat it as a vector of size 1. -** ** Return a pointer to a subexpression of pVector that is the i-th ** column of the vector (numbered starting with 0). The caller must ** ensure that i is within range. ** +** If pVector is really a scalar (and "scalar" here includes subqueries +** that return a single column!) then return pVector unmodified. +** ** pVector retains ownership of the returned subexpression. ** ** If the vector is a (SELECT ...) then the expression returned is -** just the expression for the i-th term of the result set, and is -** necessarily ready to be evaluated because the table cursor might -** not have been positioned yet. +** just the expression for the i-th term of the result set, and may +** not be ready for evaluation because the table cursor has not yet +** been positioned. */ Expr *sqlite3VectorFieldSubexpr(Expr *pVector, int i){ assert( i Date: Sat, 20 Aug 2016 21:11:25 +0000 Subject: [PATCH 46/97] Further comment enhancements. No changes to code. FossilOrigin-Name: d4562a9e7b1eaff41466210e3a0caaf374ec5a92 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/expr.c | 6 ++++++ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 63e0795631..256fcbf510 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Clarification\sof\scode\scomments\sin\sexpr.c.\s\sClean\sup\sthe\simplementations\nof\ssqlite3ExprIsVector()\sand\ssqlite3ExprVectorSize()\sslightly. -D 2016-08-20T21:02:38.224 +C Further\scomment\senhancements.\s\sNo\schanges\sto\scode. +D 2016-08-20T21:11:25.815 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 4adf875e1bfc155b107ee8e35e7cb1c5599f955b +F src/expr.c b6f0592292944a1700b503d00f588932f5683d67 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1519,7 +1519,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 9041ee4a6f0e8389297f887f1431ab5cfe783390 -R bba779342591015bf7eda1fee0ab13af +P 4fb66d6592b141a4a71359250dbd1ac454569cb9 +R 560b79caaccb19afcc4a2c629096a4bc U drh -Z 030a18789c82832c6d203bdc6c28c7db +Z f5ddde0143384b43283a729a6b1d1670 diff --git a/manifest.uuid b/manifest.uuid index d9ffa9fb0c..1bfd374437 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4fb66d6592b141a4a71359250dbd1ac454569cb9 \ No newline at end of file +d4562a9e7b1eaff41466210e3a0caaf374ec5a92 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 69754fada6..c92179d5e4 100644 --- a/src/expr.c +++ b/src/expr.c @@ -311,6 +311,12 @@ static int codeCompare( /* ** Return true if expression pExpr is a vector, or false otherwise. +** +** A vector is defined as any expression that results in two or more +** columns of result. Every TK_VECTOR node is an vector because the +** parser will not generate a TK_VECTOR with fewer than two entries. +** But a TK_SELECT might be either a vector or a scalar. It is only +** considered a vector if it has two or more result columns. */ int sqlite3ExprIsVector(Expr *pExpr){ return sqlite3ExprVectorSize(pExpr)>1; From 84b19a3da16ebfd784fea1a42ea90c3473444ddb Mon Sep 17 00:00:00 2001 From: drh Date: Sat, 20 Aug 2016 22:49:28 +0000 Subject: [PATCH 47/97] The docs promise the in "x BETWEEN y AND z" the x expression is only evaluated once. That is no longer true, and so some tests are failing. This needs to be fixed before merging to trunk. FossilOrigin-Name: e50d264fdc2f08d19202c68f73f18df301cb233d --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/expr.c | 47 +++++++++++++++++++++++------------------------ 3 files changed, 30 insertions(+), 31 deletions(-) diff --git a/manifest b/manifest index 256fcbf510..826481de0f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Further\scomment\senhancements.\s\sNo\schanges\sto\scode. -D 2016-08-20T21:11:25.815 +C The\sdocs\spromise\sthe\sin\s"x\sBETWEEN\sy\sAND\sz"\sthe\sx\sexpression\sis\sonly\sevaluated\nonce.\s\sThat\sis\sno\slonger\strue,\sand\sso\ssome\stests\sare\sfailing.\s\sThis\sneeds\sto\nbe\sfixed\sbefore\smerging\sto\strunk. +D 2016-08-20T22:49:28.330 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c b6f0592292944a1700b503d00f588932f5683d67 +F src/expr.c 157f2aa7e573c8d354ccfea9955dac2842467b0e F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1519,7 +1519,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 4fb66d6592b141a4a71359250dbd1ac454569cb9 -R 560b79caaccb19afcc4a2c629096a4bc +P d4562a9e7b1eaff41466210e3a0caaf374ec5a92 +R 79bb83cf9bac81ae1bf4f01bc53e554b U drh -Z f5ddde0143384b43283a729a6b1d1670 +Z 762069ce8b935adb1e168884e493813e diff --git a/manifest.uuid b/manifest.uuid index 1bfd374437..d9c935329d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d4562a9e7b1eaff41466210e3a0caaf374ec5a92 \ No newline at end of file +e50d264fdc2f08d19202c68f73f18df301cb233d \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index c92179d5e4..ea8ca32fdb 100644 --- a/src/expr.c +++ b/src/expr.c @@ -4064,55 +4064,54 @@ int sqlite3ExprCodeExprList( ** ** Code it as such, taking care to do the common subexpression ** elimination of x. +** +** The xJumpIf parameter determines details: +** +** NULL: Store the boolean result in reg[dest] +** sqlite3ExprIfTrue: Jump to dest if true +** sqlite3ExprIfFalse: Jump to dest if false +** +** The jumpIfNull parameter is ignored if xJumpIf is NULL. */ static void exprCodeBetween( Parse *pParse, /* Parsing and code generating context */ Expr *pExpr, /* The BETWEEN expression */ - int dest, /* Jump here if the jump is taken */ - void (*xJumpIf)(Parse*,Expr*,int,int), + int dest, /* Jump destination or storage location */ + void (*xJump)(Parse*,Expr*,int,int), /* Action to take */ int jumpIfNull /* Take the jump if the BETWEEN is NULL */ ){ Expr exprAnd; /* The AND operator in x>=y AND x<=z */ Expr compLeft; /* The x>=y term */ Expr compRight; /* The x<=z term */ - Expr exprX; /* The x subexpression */ - int regFree1 = 0; /* Temporary use register */ + + assert( xJump==0 || xJump==sqlite3ExprIfTrue || xJump==sqlite3ExprIfFalse ); + assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); memset(&compLeft, 0, sizeof(Expr)); memset(&compRight, 0, sizeof(Expr)); memset(&exprAnd, 0, sizeof(Expr)); - - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); - exprX = *pExpr->pLeft; exprAnd.op = TK_AND; exprAnd.pLeft = &compLeft; exprAnd.pRight = &compRight; compLeft.op = TK_GE; - compLeft.pLeft = &exprX; + compLeft.pLeft = pExpr->pLeft; compLeft.pRight = pExpr->x.pList->a[0].pExpr; compRight.op = TK_LE; - compRight.pLeft = &exprX; + compRight.pLeft = pExpr->pLeft; compRight.pRight = pExpr->x.pList->a[1].pExpr; - if( sqlite3ExprIsVector(&exprX)==0 ){ - exprToRegister(&exprX, sqlite3ExprCodeTemp(pParse, &exprX, ®Free1)); - } - if( xJumpIf ){ - xJumpIf(pParse, &exprAnd, dest, jumpIfNull); + if( xJump ){ + xJump(pParse, &exprAnd, dest, jumpIfNull); }else{ - exprX.flags |= EP_FromJoin; + /*exprX.flags |= EP_FromJoin;*/ sqlite3ExprCodeTarget(pParse, &exprAnd, dest); } - sqlite3ReleaseTempReg(pParse, regFree1); /* Ensure adequate test coverage */ - testcase( jumpIfTrue==0 && jumpIfNull==0 && regFree1==0 ); - testcase( jumpIfTrue==0 && jumpIfNull==0 && regFree1!=0 ); - testcase( jumpIfTrue==0 && jumpIfNull!=0 && regFree1==0 ); - testcase( jumpIfTrue==0 && jumpIfNull!=0 && regFree1!=0 ); - testcase( jumpIfTrue!=0 && jumpIfNull==0 && regFree1==0 ); - testcase( jumpIfTrue!=0 && jumpIfNull==0 && regFree1!=0 ); - testcase( jumpIfTrue!=0 && jumpIfNull!=0 && regFree1==0 ); - testcase( jumpIfTrue!=0 && jumpIfNull!=0 && regFree1!=0 ); + testcase( xJump==sqlite3ExprIfTrue && jumpIfNull==0 ); + testcase( xJump==sqlite3ExprIfTrue && jumpIfNull!=0 ); + testcase( xJump==sqlite3ExprIfFalse && jumpIfNull==0 ); + testcase( xJump==sqlite3ExprIfFalse && jumpIfNull!=0 ); + testcase( xJump==0 ); } /* From db45bd5e826505fb6f8a02dd28c805a85c349f59 Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 22 Aug 2016 00:48:58 +0000 Subject: [PATCH 48/97] Reinstate the mechanism in BETWEEN that avoids evaluating the first expression more than once, but fix the affinity extractor so that it works with this mechanism. The de-duplication of the first expression still does not work for vector expressions, though. FossilOrigin-Name: 2f39987f21bd6dae8d2be610a1fd5f06f8878e9e --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/expr.c | 38 +++++++++++++++++++++++--------------- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/manifest b/manifest index 826481de0f..ac59fb1a27 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C The\sdocs\spromise\sthe\sin\s"x\sBETWEEN\sy\sAND\sz"\sthe\sx\sexpression\sis\sonly\sevaluated\nonce.\s\sThat\sis\sno\slonger\strue,\sand\sso\ssome\stests\sare\sfailing.\s\sThis\sneeds\sto\nbe\sfixed\sbefore\smerging\sto\strunk. -D 2016-08-20T22:49:28.330 +C Reinstate\sthe\smechanism\sin\sBETWEEN\sthat\savoids\sevaluating\sthe\sfirst\sexpression\nmore\sthan\sonce,\sbut\sfix\sthe\saffinity\sextractor\sso\sthat\sit\sworks\swith\sthis\nmechanism.\s\sThe\sde-duplication\sof\sthe\sfirst\sexpression\sstill\sdoes\snot\swork\nfor\svector\sexpressions,\sthough. +D 2016-08-22T00:48:58.062 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 157f2aa7e573c8d354ccfea9955dac2842467b0e +F src/expr.c 77215e927ab39426e19340b2e109267f62abe398 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1519,7 +1519,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 d4562a9e7b1eaff41466210e3a0caaf374ec5a92 -R 79bb83cf9bac81ae1bf4f01bc53e554b +P e50d264fdc2f08d19202c68f73f18df301cb233d +R 333f2c163d1312925e2511fcd28df52d U drh -Z 762069ce8b935adb1e168884e493813e +Z ca2741b6fe90555773b217749c459f72 diff --git a/manifest.uuid b/manifest.uuid index d9c935329d..20c785fdca 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e50d264fdc2f08d19202c68f73f18df301cb233d \ No newline at end of file +2f39987f21bd6dae8d2be610a1fd5f06f8878e9e \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index ea8ca32fdb..88ff3e40f0 100644 --- a/src/expr.c +++ b/src/expr.c @@ -39,17 +39,14 @@ char sqlite3ExprAffinity(Expr *pExpr){ assert( pExpr->flags&EP_xIsSelect ); return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr); } + if( op==TK_REGISTER ) op = pExpr->op2; #ifndef SQLITE_OMIT_CAST if( op==TK_CAST ){ assert( !ExprHasProperty(pExpr, EP_IntValue) ); return sqlite3AffinityType(pExpr->u.zToken, 0); } #endif - if( (op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_REGISTER) - && pExpr->pTab!=0 - ){ - /* op==TK_REGISTER && pExpr->pTab!=0 happens when pExpr was originally - ** a TK_COLUMN but was previously evaluated and cached in a register */ + if( (op==TK_AGG_COLUMN || op==TK_COLUMN) && pExpr->pTab!=0 ){ int j = pExpr->iColumn; if( j<0 ) return SQLITE_AFF_INTEGER; assert( pExpr->pTab && jpTab->nCol ); @@ -4080,37 +4077,48 @@ static void exprCodeBetween( void (*xJump)(Parse*,Expr*,int,int), /* Action to take */ int jumpIfNull /* Take the jump if the BETWEEN is NULL */ ){ - Expr exprAnd; /* The AND operator in x>=y AND x<=z */ + Expr exprAnd; /* The AND operator in x>=y AND x<=z */ Expr compLeft; /* The x>=y term */ Expr compRight; /* The x<=z term */ + Expr exprX; /* The x subexpression */ + int regFree1 = 0; /* Temporary use register */ - assert( xJump==0 || xJump==sqlite3ExprIfTrue || xJump==sqlite3ExprIfFalse ); - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); memset(&compLeft, 0, sizeof(Expr)); memset(&compRight, 0, sizeof(Expr)); memset(&exprAnd, 0, sizeof(Expr)); + + assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); + exprX = *pExpr->pLeft; exprAnd.op = TK_AND; exprAnd.pLeft = &compLeft; exprAnd.pRight = &compRight; compLeft.op = TK_GE; - compLeft.pLeft = pExpr->pLeft; + compLeft.pLeft = &exprX; compLeft.pRight = pExpr->x.pList->a[0].pExpr; compRight.op = TK_LE; - compRight.pLeft = pExpr->pLeft; + compRight.pLeft = &exprX; compRight.pRight = pExpr->x.pList->a[1].pExpr; + if( sqlite3ExprIsVector(&exprX)==0 ){ + exprToRegister(&exprX, sqlite3ExprCodeTemp(pParse, &exprX, ®Free1)); + } if( xJump ){ xJump(pParse, &exprAnd, dest, jumpIfNull); }else{ - /*exprX.flags |= EP_FromJoin;*/ + exprX.flags |= EP_FromJoin; sqlite3ExprCodeTarget(pParse, &exprAnd, dest); } + sqlite3ReleaseTempReg(pParse, regFree1); /* Ensure adequate test coverage */ - testcase( xJump==sqlite3ExprIfTrue && jumpIfNull==0 ); - testcase( xJump==sqlite3ExprIfTrue && jumpIfNull!=0 ); - testcase( xJump==sqlite3ExprIfFalse && jumpIfNull==0 ); - testcase( xJump==sqlite3ExprIfFalse && jumpIfNull!=0 ); + testcase( xJump==sqlite3ExprIfTrue && jumpIfNull==0 && regFree1==0 ); + testcase( xJump==sqlite3ExprIfTrue && jumpIfNull==0 && regFree1!=0 ); + testcase( xJump==sqlite3ExprIfTrue && jumpIfNull!=0 && regFree1==0 ); + testcase( xJump==sqlite3ExprIfTrue && jumpIfNull!=0 && regFree1!=0 ); + testcase( xJump==sqlite3ExprIfFalse && jumpIfNull==0 && regFree1==0 ); + testcase( xJump==sqlite3ExprIfFalse && jumpIfNull==0 && regFree1!=0 ); + testcase( xJump==sqlite3ExprIfFalse && jumpIfNull!=0 && regFree1==0 ); + testcase( xJump==sqlite3ExprIfFalse && jumpIfNull!=0 && regFree1!=0 ); testcase( xJump==0 ); } From 12abf408ff0f89efce2405c172f130e7b2ea011d Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 22 Aug 2016 14:30:05 +0000 Subject: [PATCH 49/97] Fix the vector BETWEEN operator so that it only evaluates the left-most vector expression once. Add support for vector comparisons in the CASE operator. FossilOrigin-Name: 07e69f43a294d35b5145a2b0242ee42d50adab14 --- manifest | 15 ++++---- manifest.uuid | 2 +- src/expr.c | 84 ++++++++++++++++++++++++++++++++------------- test/e_expr.test | 3 ++ test/rowvalue8.test | 59 +++++++++++++++++++++++++++++++ 5 files changed, 131 insertions(+), 32 deletions(-) create mode 100644 test/rowvalue8.test diff --git a/manifest b/manifest index ac59fb1a27..21dcf79efe 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Reinstate\sthe\smechanism\sin\sBETWEEN\sthat\savoids\sevaluating\sthe\sfirst\sexpression\nmore\sthan\sonce,\sbut\sfix\sthe\saffinity\sextractor\sso\sthat\sit\sworks\swith\sthis\nmechanism.\s\sThe\sde-duplication\sof\sthe\sfirst\sexpression\sstill\sdoes\snot\swork\nfor\svector\sexpressions,\sthough. -D 2016-08-22T00:48:58.062 +C Fix\sthe\svector\sBETWEEN\soperator\sso\sthat\sit\sonly\sevaluates\sthe\sleft-most\nvector\sexpression\sonce.\s\sAdd\ssupport\sfor\svector\scomparisons\sin\sthe\sCASE\noperator. +D 2016-08-22T14:30:05.854 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 77215e927ab39426e19340b2e109267f62abe398 +F src/expr.c 575d6767abc4d38362bb54599dbb72a09946c5be F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -644,7 +644,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 3f9e639b5df18de36c0aa700703589cd65281174 +F test/e_expr.test 1ffa8866d38e7becc76893a8829e9432050e5716 F test/e_fkey.test a1783fe1f759e1990e6a11adfcf0702dac4d0707 F test/e_fts3.test 5c02288842e4f941896fd44afdef564dd5fc1459 F test/e_insert.test 3de217e95094d3d165992a6de1164bbc4bd92dc7 @@ -1026,6 +1026,7 @@ F test/rowvalue4.test 318cdd40e66dfae686537eea581ae49cbb01195d F test/rowvalue5.test 01c7e0bc4048f30b58e6eb27ecd26e5bd312635e F test/rowvalue6.test d19b54feb604d5601f8614b15e214e0774c01087 F test/rowvalue7.test 3c9a127954d3da309a271babdfc43dbcc5c4da7f +F test/rowvalue8.test 5900eddad9e2c3c2e26f1a95f74aafc1232ee5e0 F test/rowvaluefault.test 7b16485e3f2b371f3e3d05455b8ded6d0c090244 F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09 @@ -1519,7 +1520,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 e50d264fdc2f08d19202c68f73f18df301cb233d -R 333f2c163d1312925e2511fcd28df52d +P 2f39987f21bd6dae8d2be610a1fd5f06f8878e9e +R 46b91546c6e852a3d13f61b9c8a6f99f U drh -Z ca2741b6fe90555773b217749c459f72 +Z cdfb95caa6cbc00a53a66a6c4e92b927 diff --git a/manifest.uuid b/manifest.uuid index 20c785fdca..e35c310130 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2f39987f21bd6dae8d2be610a1fd5f06f8878e9e \ No newline at end of file +07e69f43a294d35b5145a2b0242ee42d50adab14 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 88ff3e40f0..b304e27835 100644 --- a/src/expr.c +++ b/src/expr.c @@ -14,6 +14,11 @@ */ #include "sqliteInt.h" +/* Forward declarations */ +static void exprCodeBetween(Parse*,Expr*,int,void(*)(Parse*,Expr*,int,int),int); +static int exprCodeVector(Parse *pParse, Expr *p, int *piToFree); + + /* ** Return the 'affinity' of the expression pExpr if any. ** @@ -326,9 +331,11 @@ int sqlite3ExprIsVector(Expr *pExpr){ ** any other type of expression, return 1. */ int sqlite3ExprVectorSize(Expr *pExpr){ - if( pExpr->op==TK_VECTOR ){ + u8 op = pExpr->op; + if( op==TK_REGISTER ) op = pExpr->op2; + if( op==TK_VECTOR ){ return pExpr->x.pList->nExpr; - }else if( pExpr->op==TK_SELECT ){ + }else if( op==TK_SELECT ){ return pExpr->x.pSelect->pEList->nExpr; }else{ return 1; @@ -354,7 +361,9 @@ int sqlite3ExprVectorSize(Expr *pExpr){ Expr *sqlite3VectorFieldSubexpr(Expr *pVector, int i){ assert( iop==TK_SELECT ){ + if( pVector->op==TK_SELECT + || (pVector->op==TK_REGISTER && pVector->op2==TK_SELECT) + ){ return pVector->x.pSelect->pEList->a[i].pExpr; }else{ return pVector->x.pList->a[i].pExpr; @@ -467,10 +476,13 @@ static int exprVectorRegister( Expr **ppExpr, /* OUT: Expression element */ int *pRegFree /* OUT: Temp register to free */ ){ - assert( pVector->op==TK_VECTOR || pVector->op==TK_SELECT ); - assert( pParse->nErr || pParse->db->mallocFailed - || (pVector->op==TK_VECTOR)==(regSelect==0) ); - if( pVector->op==TK_SELECT ){ + u8 op = pVector->op; + assert( op==TK_VECTOR || op==TK_SELECT || op==TK_REGISTER ); + if( op==TK_REGISTER ){ + *ppExpr = sqlite3VectorFieldSubexpr(pVector, iField); + return pVector->iTable+iField; + } + if( op==TK_SELECT ){ *ppExpr = pVector->x.pSelect->pEList->a[iField].pExpr; return regSelect+iField; } @@ -2630,11 +2642,12 @@ static void sqlite3ExprCodeIN( ){ int rRhsHasNull = 0; /* Register that is true if RHS contains NULL values */ int eType; /* Type of the RHS */ - int r1; /* Temporary use register */ + int r1, r2; /* Temporary use registers */ Vdbe *v; /* Statement under construction */ int *aiMap = 0; /* Map from vector field to index column */ char *zAff = 0; /* Affinity string for comparisons */ int nVector; /* Size of vectors for this IN(...) op */ + int iDummy; /* Dummy parameter to exprCodeVector() */ Expr *pLeft = pExpr->pLeft; int i; @@ -2671,16 +2684,9 @@ static void sqlite3ExprCodeIN( */ r1 = sqlite3GetTempRange(pParse, nVector); sqlite3ExprCachePush(pParse); - if( nVector>1 && (pLeft->flags & EP_xIsSelect) ){ - int regSelect = sqlite3CodeSubselect(pParse, pLeft, 0, 0); - for(i=0; iop2 = p->op; @@ -3237,7 +3245,37 @@ static void exprToRegister(Expr *p, int iReg){ ExprClearProperty(p, EP_Skip); } -static void exprCodeBetween(Parse*,Expr*,int,void(*)(Parse*,Expr*,int,int),int); +/* +** Evaluate an expression (either a vector or a scalar expression) and store +** the result in continguous temporary registers. Return the index of +** the first register used to store the result. +** +** If the returned result register is a temporary scalar, then also write +** that register number into *piFreeable. If the returned result register +** is not a temporary or if the expression is a vector set *piFreeable +** to 0. +*/ +static int exprCodeVector(Parse *pParse, Expr *p, int *piFreeable){ + int iResult; + int nResult = sqlite3ExprVectorSize(p); + if( nResult==1 ){ + iResult = sqlite3ExprCodeTemp(pParse, p, piFreeable); + }else{ + *piFreeable = 0; + if( p->op==TK_SELECT ){ + iResult = sqlite3CodeSubselect(pParse, p, 0, 0); + }else{ + int i; + iResult = pParse->nMem+1; + pParse->nMem += nResult; + for(i=0; ix.pList->a[i].pExpr, i+iResult); + } + } + } + return iResult; +} + /* ** Generate code into the current Vdbe to evaluate the given @@ -3781,7 +3819,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ if( (pX = pExpr->pLeft)!=0 ){ tempX = *pX; testcase( pX->op==TK_COLUMN ); - exprToRegister(&tempX, sqlite3ExprCodeTemp(pParse, pX, ®Free1)); + exprToRegister(&tempX, exprCodeVector(pParse, &tempX, ®Free1)); testcase( regFree1==0 ); opCompare.op = TK_EQ; opCompare.pLeft = &tempX; @@ -4099,9 +4137,7 @@ static void exprCodeBetween( compRight.op = TK_LE; compRight.pLeft = &exprX; compRight.pRight = pExpr->x.pList->a[1].pExpr; - if( sqlite3ExprIsVector(&exprX)==0 ){ - exprToRegister(&exprX, sqlite3ExprCodeTemp(pParse, &exprX, ®Free1)); - } + exprToRegister(&exprX, exprCodeVector(pParse, &exprX, ®Free1)); if( xJump ){ xJump(pParse, &exprAnd, dest, jumpIfNull); }else{ diff --git a/test/e_expr.test b/test/e_expr.test index 6ef55ce8a6..6165aa3588 100644 --- a/test/e_expr.test +++ b/test/e_expr.test @@ -847,6 +847,9 @@ foreach {tn x expr res nEval} { 3 5 "x() >= 5 AND x() <= 5" 1 2 4 5 "x() BETWEEN 5 AND 5" 1 1 + + 5 9 "(x(),8) >= (9,7) AND (x(),8)<=(9,10)" 1 2 + 6 9 "(x(),8) BETWEEN (9,7) AND (9,10)" 1 1 } { do_test e_expr-13.1.$tn { set ::xcount 0 diff --git a/test/rowvalue8.test b/test/rowvalue8.test new file mode 100644 index 0000000000..432dad1278 --- /dev/null +++ b/test/rowvalue8.test @@ -0,0 +1,59 @@ +# 2016-08-22 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# Use of row values in CASE statements. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix rowvalue8 + +do_execsql_test 1.1 { + CREATE TABLE t1(a INTEGER PRIMARY KEY,b,c,d); + INSERT INTO t1(a,b,c,d) VALUES + (1,1,2,3), + (2,2,3,4), + (3,1,2,4), + (4,2,3,5), + (5,3,4,6), + (6,4,5,9); + SELECT a, CASE (b,c) WHEN (1,2) THEN 'aleph' + WHEN (2,3) THEN 'bet' + WHEN (3,4) THEN 'gimel' + ELSE '-' END, + '|' + FROM t1 + ORDER BY a; +} {1 aleph | 2 bet | 3 aleph | 4 bet | 5 gimel | 6 - |} +do_execsql_test 1.2 { + SELECT a, CASE (b,c,d) WHEN (1,2,3) THEN 'aleph' + WHEN (2,3,4) THEN 'bet' + WHEN (3,4,6) THEN 'gimel' + ELSE '-' END, + '|' + FROM t1 + ORDER BY a; +} {1 aleph | 2 bet | 3 - | 4 - | 5 gimel | 6 - |} + +do_execsql_test 2.1 { + CREATE TABLE t2(x INTEGER PRIMARY KEY, y); + INSERT INTO t2(x,y) VALUES(1,6),(2,5),(3,4),(4,3),(5,2),(6,1); + SELECT x, CASE (SELECT b,c FROM t1 WHERE a=y) + WHEN (1,2) THEN 'aleph' + WHEN (2,3) THEN 'bet' + WHEN (3,4) THEN 'gimel' + ELSE '-' END, + '|' + FROM t2 + ORDER BY +x; +} {1 - | 2 gimel | 3 bet | 4 aleph | 5 bet | 6 aleph |} + + +finish_test From abb9d5f18972f48fe9b461c44fc615b2d3366fbe Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 23 Aug 2016 17:30:55 +0000 Subject: [PATCH 50/97] Fix an uninitialized variable in CASE expression code generation. FossilOrigin-Name: c8ffae05e13033ec7425bf0f002df31f550bb7f1 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/expr.c | 1 + 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 21dcf79efe..7a34078a6c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sthe\svector\sBETWEEN\soperator\sso\sthat\sit\sonly\sevaluates\sthe\sleft-most\nvector\sexpression\sonce.\s\sAdd\ssupport\sfor\svector\scomparisons\sin\sthe\sCASE\noperator. -D 2016-08-22T14:30:05.854 +C Fix\san\suninitialized\svariable\sin\sCASE\sexpression\scode\sgeneration. +D 2016-08-23T17:30:55.883 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 575d6767abc4d38362bb54599dbb72a09946c5be +F src/expr.c 511768687886e4e55b586678ab4111e49a4a346e F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1520,7 +1520,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 2f39987f21bd6dae8d2be610a1fd5f06f8878e9e -R 46b91546c6e852a3d13f61b9c8a6f99f +P 07e69f43a294d35b5145a2b0242ee42d50adab14 +R 4aa2c65fcf3b3b47ff0ca17ab554bdbd U drh -Z cdfb95caa6cbc00a53a66a6c4e92b927 +Z 4308f4ee8b9a3cd69479e48fad7b9733 diff --git a/manifest.uuid b/manifest.uuid index e35c310130..c7f5a16ecd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -07e69f43a294d35b5145a2b0242ee42d50adab14 \ No newline at end of file +c8ffae05e13033ec7425bf0f002df31f550bb7f1 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index b304e27835..829afc4cd6 100644 --- a/src/expr.c +++ b/src/expr.c @@ -3821,6 +3821,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ testcase( pX->op==TK_COLUMN ); exprToRegister(&tempX, exprCodeVector(pParse, &tempX, ®Free1)); testcase( regFree1==0 ); + memset(&opCompare, 0, sizeof(opCompare)); opCompare.op = TK_EQ; opCompare.pLeft = &tempX; pTest = &opCompare; From 66860af3dd71fc0242ce5ef89124f3f7deaa0e48 Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 23 Aug 2016 18:30:10 +0000 Subject: [PATCH 51/97] Fix a problem with rowvalue UPDATE when the rowvalue is not the left-most and the RHS is a multi-column subquery. FossilOrigin-Name: e149e6b93a9afb3d574309c0db60e221e24078f7 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/expr.c | 8 +++++--- test/rowvalue7.test | 2 +- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index 7a34078a6c..d4c268de95 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\san\suninitialized\svariable\sin\sCASE\sexpression\scode\sgeneration. -D 2016-08-23T17:30:55.883 +C Fix\sa\sproblem\swith\srowvalue\sUPDATE\swhen\sthe\srowvalue\sis\snot\sthe\sleft-most\sand\nthe\sRHS\sis\sa\smulti-column\ssubquery. +D 2016-08-23T18:30:10.697 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 511768687886e4e55b586678ab4111e49a4a346e +F src/expr.c 3d3cc24386929991ff0c4264a8dde45b298c599e F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1025,7 +1025,7 @@ F test/rowvalue3.test dbe935260851b197dfbbbcb0ac2a15cb5f324fd4 F test/rowvalue4.test 318cdd40e66dfae686537eea581ae49cbb01195d F test/rowvalue5.test 01c7e0bc4048f30b58e6eb27ecd26e5bd312635e F test/rowvalue6.test d19b54feb604d5601f8614b15e214e0774c01087 -F test/rowvalue7.test 3c9a127954d3da309a271babdfc43dbcc5c4da7f +F test/rowvalue7.test 5d06ff19d9e6969e574a2e662a531dd0c67801a8 F test/rowvalue8.test 5900eddad9e2c3c2e26f1a95f74aafc1232ee5e0 F test/rowvaluefault.test 7b16485e3f2b371f3e3d05455b8ded6d0c090244 F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 @@ -1520,7 +1520,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 07e69f43a294d35b5145a2b0242ee42d50adab14 -R 4aa2c65fcf3b3b47ff0ca17ab554bdbd +P c8ffae05e13033ec7425bf0f002df31f550bb7f1 +R 8c42685900faf8a76ebfa8d4883d872e U drh -Z 4308f4ee8b9a3cd69479e48fad7b9733 +Z b8c84bcbf71dd1d1d9c9f62200ebb8ca diff --git a/manifest.uuid b/manifest.uuid index c7f5a16ecd..bd556e03a8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c8ffae05e13033ec7425bf0f002df31f550bb7f1 \ No newline at end of file +e149e6b93a9afb3d574309c0db60e221e24078f7 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 829afc4cd6..2e09d07982 100644 --- a/src/expr.c +++ b/src/expr.c @@ -1489,6 +1489,7 @@ ExprList *sqlite3ExprListAppendVector( sqlite3 *db = pParse->db; int n; int i; + int iFirst = pList ? pList->nExpr : 0; if( pColumns==0 ) goto vector_append_error; if( pExpr==0 ) goto vector_append_error; n = sqlite3ExprVectorSize(pExpr); @@ -1501,14 +1502,15 @@ ExprList *sqlite3ExprListAppendVector( Expr *pSubExpr = sqlite3ExprForVectorField(pParse, pExpr, i); pList = sqlite3ExprListAppend(pParse, pList, pSubExpr); if( pList ){ + assert( pList->nExpr==iFirst+i+1 ); pList->a[pList->nExpr-1].zName = pColumns->a[i].zName; pColumns->a[i].zName = 0; } } if( pExpr->op==TK_SELECT ){ - if( pList && pList->a[0].pExpr ){ - assert( pList->a[0].pExpr->op==TK_SELECT_COLUMN ); - pList->a[0].pExpr->pRight = pExpr; + if( pList && pList->a[iFirst].pExpr ){ + assert( pList->a[iFirst].pExpr->op==TK_SELECT_COLUMN ); + pList->a[iFirst].pExpr->pRight = pExpr; pExpr = 0; } } diff --git a/test/rowvalue7.test b/test/rowvalue7.test index a90e235ce2..f6764f301e 100644 --- a/test/rowvalue7.test +++ b/test/rowvalue7.test @@ -43,7 +43,7 @@ do_execsql_test 1.4 { } {1 2 11 22 | 3 4 99 55 | 5 6 55 66 |} do_execsql_test 1.5 { - UPDATE t1 SET b = 8, (c,d) = (123,456) WHERE a=3; + UPDATE t1 SET b = 8, (c,d) = (SELECT 123,456) WHERE a=3; SELECT *,'|' FROM t1 ORDER BY a; } {1 2 11 22 | 3 8 123 456 | 5 6 55 66 |} From e835bc1285ca3688d8f92c4c972edb876382a2e7 Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 23 Aug 2016 19:02:55 +0000 Subject: [PATCH 52/97] Simplify the row value misuse error message. FossilOrigin-Name: 838c50a5bf46fd0340839d577fa28ba02b4f2034 --- manifest | 20 ++++++++++---------- manifest.uuid | 2 +- src/expr.c | 6 +++--- test/rowvalue.test | 5 ++--- test/rowvalue3.test | 2 -- test/rowvalue4.test | 11 +++++------ test/rowvalue5.test | 1 - 7 files changed, 21 insertions(+), 26 deletions(-) diff --git a/manifest b/manifest index d4c268de95..90d05b40fd 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sproblem\swith\srowvalue\sUPDATE\swhen\sthe\srowvalue\sis\snot\sthe\sleft-most\sand\nthe\sRHS\sis\sa\smulti-column\ssubquery. -D 2016-08-23T18:30:10.697 +C Simplify\sthe\srow\svalue\smisuse\serror\smessage. +D 2016-08-23T19:02:55.746 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 3d3cc24386929991ff0c4264a8dde45b298c599e +F src/expr.c e67849c475435a2d8268a660aaeff7d1df505a2f F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1019,11 +1019,11 @@ F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81 F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d -F test/rowvalue.test 56b34d31d91340a6e922e753b798880170cc1aa7 +F test/rowvalue.test c2b4d043f4253711c8a2c6aa126a3f6d71182969 F test/rowvalue2.test 875068299fd4dd50ef0a47786462c8e1f4065f9a -F test/rowvalue3.test dbe935260851b197dfbbbcb0ac2a15cb5f324fd4 -F test/rowvalue4.test 318cdd40e66dfae686537eea581ae49cbb01195d -F test/rowvalue5.test 01c7e0bc4048f30b58e6eb27ecd26e5bd312635e +F test/rowvalue3.test 01399b7bf150b0d41abce76c18072da777c2500c +F test/rowvalue4.test 9b40c9be9bdde30fc66cddbfdf6a5af37de4ccac +F test/rowvalue5.test a440d490c8c0bf606034c09d5c6bbf7840b98f95 F test/rowvalue6.test d19b54feb604d5601f8614b15e214e0774c01087 F test/rowvalue7.test 5d06ff19d9e6969e574a2e662a531dd0c67801a8 F test/rowvalue8.test 5900eddad9e2c3c2e26f1a95f74aafc1232ee5e0 @@ -1520,7 +1520,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 c8ffae05e13033ec7425bf0f002df31f550bb7f1 -R 8c42685900faf8a76ebfa8d4883d872e +P e149e6b93a9afb3d574309c0db60e221e24078f7 +R 4cb3db3c4cfd8c4158ffeb6116b28260 U drh -Z b8c84bcbf71dd1d1d9c9f62200ebb8ca +Z 62ec580862aeb7b6a1de73caa33d9d9e diff --git a/manifest.uuid b/manifest.uuid index bd556e03a8..f1ac3fff86 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e149e6b93a9afb3d574309c0db60e221e24078f7 \ No newline at end of file +838c50a5bf46fd0340839d577fa28ba02b4f2034 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 2e09d07982..8c0d8b3cce 100644 --- a/src/expr.c +++ b/src/expr.c @@ -517,7 +517,7 @@ static void codeVectorCompare( /* Check that both sides of the comparison are vectors, and that ** both are the same length. */ if( nLeft!=nRight ){ - sqlite3ErrorMsg(pParse, "invalid use of row value"); + sqlite3ErrorMsg(pParse, "row value misused"); }else{ int i; int regLeft = 0; @@ -2610,7 +2610,7 @@ int sqlite3ExprCheckIN(Parse *pParse, Expr *pIn){ if( (pIn->pLeft->flags & EP_xIsSelect) ){ sqlite3SubselectError(pParse, nVector, 1); }else{ - sqlite3ErrorMsg(pParse, "invalid use of row value"); + sqlite3ErrorMsg(pParse, "row value misused"); } return 1; } @@ -3775,7 +3775,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ } case TK_VECTOR: { - sqlite3ErrorMsg(pParse, "invalid use of row value"); + sqlite3ErrorMsg(pParse, "row value misused"); break; } diff --git a/test/rowvalue.test b/test/rowvalue.test index a1faba97ab..642a7843aa 100644 --- a/test/rowvalue.test +++ b/test/rowvalue.test @@ -150,10 +150,10 @@ do_execsql_test 6.5 { } {i ii} do_catchsql_test 6.6 { SELECT c FROM hh WHERE (a, b) = (SELECT 'abc', 1) COLLATE nocase; -} {1 {invalid use of row value}} +} {1 {row value misused}} do_catchsql_test 6.7 { SELECT c FROM hh WHERE (a, b) = 1; -} {1 {invalid use of row value}} +} {1 {row value misused}} do_execsql_test 6.8 { SELECT c FROM hh WHERE (a COLLATE nocase, b) = (SELECT 'def', 2); } {iii iv} @@ -221,4 +221,3 @@ foreach {tn q res} { finish_test - diff --git a/test/rowvalue3.test b/test/rowvalue3.test index 17bf96a0f4..b050e70dde 100644 --- a/test/rowvalue3.test +++ b/test/rowvalue3.test @@ -204,5 +204,3 @@ foreach {tn idx} { finish_test - - diff --git a/test/rowvalue4.test b/test/rowvalue4.test index c4581d5562..a30edcc262 100644 --- a/test/rowvalue4.test +++ b/test/rowvalue4.test @@ -34,14 +34,14 @@ foreach {tn e} { 2 "1 + (1, 2)" 3 "(1,2,3) == (1, 2)" } { - do_catchsql_test 1.$tn "SELECT $e" {1 {invalid use of row value}} + do_catchsql_test 1.$tn "SELECT $e" {1 {row value misused}} } foreach {tn s error} { - 1 "SELECT * FROM t1 WHERE a = (1, 2)" {invalid use of row value} - 2 "SELECT * FROM t1 WHERE b = (1, 2)" {invalid use of row value} - 3 "SELECT * FROM t1 WHERE NOT (b = (1, 2))" {invalid use of row value} - 4 "SELECT * FROM t1 LIMIT (1, 2)" {invalid use of row value} + 1 "SELECT * FROM t1 WHERE a = (1, 2)" {row value misused} + 2 "SELECT * FROM t1 WHERE b = (1, 2)" {row value misused} + 3 "SELECT * FROM t1 WHERE NOT (b = (1, 2))" {row value misused} + 4 "SELECT * FROM t1 LIMIT (1, 2)" {row value misused} 5 "SELECT (a, b) IN (SELECT * FROM t1) FROM t1" {sub-select returns 3 columns - expected 2} @@ -309,4 +309,3 @@ do_catchsql_test 8.2 { } {1 {sub-select returns 2 columns - expected 1}} finish_test - diff --git a/test/rowvalue5.test b/test/rowvalue5.test index c961cf7424..d5976ccb89 100644 --- a/test/rowvalue5.test +++ b/test/rowvalue5.test @@ -110,4 +110,3 @@ foreach {tn where res} { } finish_test - From 894241c8473aa2079a73a82debf3f81eb7cac74b Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 24 Aug 2016 00:25:27 +0000 Subject: [PATCH 53/97] Avoid a potential null-pointer dereference following an OOM. FossilOrigin-Name: 25f6ed8de4df9c9890d4a352a6d11084433e82ea --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/expr.c | 1 + 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 90d05b40fd..ce1c2ee88c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Simplify\sthe\srow\svalue\smisuse\serror\smessage. -D 2016-08-23T19:02:55.746 +C Avoid\sa\spotential\snull-pointer\sdereference\sfollowing\san\sOOM. +D 2016-08-24T00:25:27.492 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c e67849c475435a2d8268a660aaeff7d1df505a2f +F src/expr.c be8772ac5019400096eea179c1bfd5acc420bde9 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1520,7 +1520,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 e149e6b93a9afb3d574309c0db60e221e24078f7 -R 4cb3db3c4cfd8c4158ffeb6116b28260 +P 838c50a5bf46fd0340839d577fa28ba02b4f2034 +R 960a362fd9424a163c5a4d069783cdea U drh -Z 62ec580862aeb7b6a1de73caa33d9d9e +Z 2276dcfeffa9ab927fd9ef765fca4810 diff --git a/manifest.uuid b/manifest.uuid index f1ac3fff86..7ec267fc1e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -838c50a5bf46fd0340839d577fa28ba02b4f2034 \ No newline at end of file +25f6ed8de4df9c9890d4a352a6d11084433e82ea \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 8c0d8b3cce..0f59970311 100644 --- a/src/expr.c +++ b/src/expr.c @@ -2103,6 +2103,7 @@ int sqlite3FindInIndex( int mustBeUnique; /* True if RHS must be unique */ Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */ + if( pParse->db->mallocFailed ) return IN_INDEX_NOOP; assert( pX->op==TK_IN ); mustBeUnique = (inFlags & IN_INDEX_LOOP)!=0; From ac6b47d164a73101d700f244384a2339f31a8e5b Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 24 Aug 2016 00:51:48 +0000 Subject: [PATCH 54/97] The previous OOM fix was bad. Back it out and replace it with a better one. FossilOrigin-Name: 1e3bc3698a4b779e6af8e3c727929c4dbddf3edb --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/expr.c | 2 +- src/wherecode.c | 16 ++++++++-------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/manifest b/manifest index ce1c2ee88c..c7b9d98ca9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Avoid\sa\spotential\snull-pointer\sdereference\sfollowing\san\sOOM. -D 2016-08-24T00:25:27.492 +C The\sprevious\sOOM\sfix\swas\sbad.\s\sBack\sit\sout\sand\sreplace\sit\swith\sa\sbetter\sone. +D 2016-08-24T00:51:48.043 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c be8772ac5019400096eea179c1bfd5acc420bde9 +F src/expr.c f433feeeaa43d52a4e029066a1af02bac9ed5f1a F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -466,7 +466,7 @@ F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 F src/where.c c7cdfd54f383090bb801cdd50d36de1a24684bb2 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 -F src/wherecode.c 0c790c422ea5ecb6c83d6a78f2c4ebc036eea90e +F src/wherecode.c 0c99e2e97c23ec0b0d64071b3590d3a5e6091a96 F src/whereexpr.c aa54bf11adf6bc7e52f56281f436ab5fd421ce16 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd @@ -1520,7 +1520,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 838c50a5bf46fd0340839d577fa28ba02b4f2034 -R 960a362fd9424a163c5a4d069783cdea +P 25f6ed8de4df9c9890d4a352a6d11084433e82ea +R 19a9e7a69bf070f3aad327c389d879a1 U drh -Z 2276dcfeffa9ab927fd9ef765fca4810 +Z c33731cf7b01c5dd25f3f1c4114950f7 diff --git a/manifest.uuid b/manifest.uuid index 7ec267fc1e..6e72ba55fc 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -25f6ed8de4df9c9890d4a352a6d11084433e82ea \ No newline at end of file +1e3bc3698a4b779e6af8e3c727929c4dbddf3edb \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 0f59970311..572edaa541 100644 --- a/src/expr.c +++ b/src/expr.c @@ -1952,6 +1952,7 @@ static Select *isCandidateForInOpt(Expr *pX){ assert( pTab->pSelect==0 ); /* FROM clause is not a view */ if( IsVirtual(pTab) ) return 0; /* FROM clause not a virtual table */ pEList = p->pEList; + assert( pEList!=0 ); /* All SELECT results must be columns. */ for(i=0; inExpr; i++){ @@ -2103,7 +2104,6 @@ int sqlite3FindInIndex( int mustBeUnique; /* True if RHS must be unique */ Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */ - if( pParse->db->mallocFailed ) return IN_INDEX_NOOP; assert( pX->op==TK_IN ); mustBeUnique = (inFlags & IN_INDEX_LOOP)!=0; diff --git a/src/wherecode.c b/src/wherecode.c index 2ef9a46b5d..fbf6ad1946 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -383,7 +383,7 @@ static int codeEqualityTerm( sqlite3VdbeAddOp2(v, OP_Null, 0, iReg); #ifndef SQLITE_OMIT_SUBQUERY }else{ - int eType; + int eType = IN_INDEX_NOOP; int iTab; struct InLoop *pIn; WhereLoop *pLoop = pLevel->pWLoop; @@ -436,13 +436,13 @@ static int codeEqualityTerm( 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; + if( !db->mallocFailed ){ + 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); } From 7887d7f24d176b75a77dd8a86abcdbb70a83b957 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 24 Aug 2016 12:22:17 +0000 Subject: [PATCH 55/97] Fix a buffer overrun in the code for handling IN(...) operators when the LHS of the operator contains indexed columns or expressions. FossilOrigin-Name: f41a0391b732a8c4ad188163f34a0f4a22237bb5 --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/wherecode.c | 3 ++- test/rowvalue.test | 9 +++++++++ 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index c7b9d98ca9..b6792e8020 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C The\sprevious\sOOM\sfix\swas\sbad.\s\sBack\sit\sout\sand\sreplace\sit\swith\sa\sbetter\sone. -D 2016-08-24T00:51:48.043 +C Fix\sa\sbuffer\soverrun\sin\sthe\scode\sfor\shandling\sIN(...)\soperators\swhen\sthe\sLHS\sof\sthe\soperator\scontains\sindexed\scolumns\sor\sexpressions. +D 2016-08-24T12:22:17.962 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -466,7 +466,7 @@ F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 F src/where.c c7cdfd54f383090bb801cdd50d36de1a24684bb2 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 -F src/wherecode.c 0c99e2e97c23ec0b0d64071b3590d3a5e6091a96 +F src/wherecode.c 5a5528c39be09593cada6ae465d7a0f48db0077f F src/whereexpr.c aa54bf11adf6bc7e52f56281f436ab5fd421ce16 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd @@ -1019,7 +1019,7 @@ F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81 F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d -F test/rowvalue.test c2b4d043f4253711c8a2c6aa126a3f6d71182969 +F test/rowvalue.test 7d8482dde9023973615eaaca65647f33d70c1f01 F test/rowvalue2.test 875068299fd4dd50ef0a47786462c8e1f4065f9a F test/rowvalue3.test 01399b7bf150b0d41abce76c18072da777c2500c F test/rowvalue4.test 9b40c9be9bdde30fc66cddbfdf6a5af37de4ccac @@ -1520,7 +1520,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 25f6ed8de4df9c9890d4a352a6d11084433e82ea -R 19a9e7a69bf070f3aad327c389d879a1 -U drh -Z c33731cf7b01c5dd25f3f1c4114950f7 +P 1e3bc3698a4b779e6af8e3c727929c4dbddf3edb +R 66ed27e8c4688d763f7b5bcfa14b1684 +U dan +Z b0da933895eae6df1437a965446c74bb diff --git a/manifest.uuid b/manifest.uuid index 6e72ba55fc..14c9bf29a3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1e3bc3698a4b779e6af8e3c727929c4dbddf3edb \ No newline at end of file +f41a0391b732a8c4ad188163f34a0f4a22237bb5 \ No newline at end of file diff --git a/src/wherecode.c b/src/wherecode.c index fbf6ad1946..65079872e1 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -471,7 +471,7 @@ static int codeEqualityTerm( if( pIn ){ int iMap = 0; /* Index in aiMap[] */ pIn += i; - for(i=iEq;inLTerm; i++, pIn++){ + for(i=iEq;inLTerm; i++){ int iOut = iReg; if( pLoop->aLTerm[i]->pExpr==pX ){ if( eType==IN_INDEX_ROWID ){ @@ -489,6 +489,7 @@ static int codeEqualityTerm( }else{ pIn->eEndLoopOp = OP_Noop; } + pIn++; } } }else{ diff --git a/test/rowvalue.test b/test/rowvalue.test index 642a7843aa..f716c26c8b 100644 --- a/test/rowvalue.test +++ b/test/rowvalue.test @@ -219,5 +219,14 @@ foreach {tn q res} { do_execsql_test 9.$tn "SELECT c FROM t2 WHERE $q" $res } +do_execsql_test 10.0 { + CREATE TABLE dual(dummy); INSERT INTO dual(dummy) VALUES('X'); + CREATE TABLE t3(a TEXT,b TEXT,c TEXT,d TEXT,e TEXT,f TEXT); + CREATE INDEX t3x ON t3(b,c,d,e,f); + + SELECT a FROM t3 + WHERE (c,d) IN (SELECT 'c','d' FROM dual) + AND (a,b,e) IN (SELECT 'a','b','d' FROM dual); +} finish_test From d0b67a8654809493d07bff62e2d9ce480be7b4df Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 24 Aug 2016 15:37:31 +0000 Subject: [PATCH 56/97] Add a NEVER() on an unreachable branch in comparisonAffinity(). FossilOrigin-Name: 505a2f20eac62d4e170f003255c8984e4f3b0918 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/expr.c | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index b6792e8020..9cd230d12f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sbuffer\soverrun\sin\sthe\scode\sfor\shandling\sIN(...)\soperators\swhen\sthe\sLHS\sof\sthe\soperator\scontains\sindexed\scolumns\sor\sexpressions. -D 2016-08-24T12:22:17.962 +C Add\sa\sNEVER()\son\san\sunreachable\sbranch\sin\scomparisonAffinity(). +D 2016-08-24T15:37:31.592 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c f433feeeaa43d52a4e029066a1af02bac9ed5f1a +F src/expr.c c2d3844fb18e7659653e482060ee0c5d3ec5ed10 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1520,7 +1520,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 1e3bc3698a4b779e6af8e3c727929c4dbddf3edb -R 66ed27e8c4688d763f7b5bcfa14b1684 -U dan -Z b0da933895eae6df1437a965446c74bb +P f41a0391b732a8c4ad188163f34a0f4a22237bb5 +R b0541cab67b62c886354064e8f5cfc4d +U drh +Z e7d2fda8d9703a6fa7987d8b8853c395 diff --git a/manifest.uuid b/manifest.uuid index 14c9bf29a3..4a04b24187 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f41a0391b732a8c4ad188163f34a0f4a22237bb5 \ No newline at end of file +505a2f20eac62d4e170f003255c8984e4f3b0918 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 572edaa541..37f778b29e 100644 --- a/src/expr.c +++ b/src/expr.c @@ -221,7 +221,7 @@ static char comparisonAffinity(Expr *pExpr){ aff = sqlite3CompareAffinity(pExpr->pRight, aff); }else if( ExprHasProperty(pExpr, EP_xIsSelect) ){ aff = sqlite3CompareAffinity(pExpr->x.pSelect->pEList->a[0].pExpr, aff); - }else if( !aff ){ + }else if( NEVER(aff==0) ){ aff = SQLITE_AFF_BLOB; } return aff; From 321e828d035d72c80e90d82f06d413658185805a Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 24 Aug 2016 17:49:07 +0000 Subject: [PATCH 57/97] Fix more unreachable branches. FossilOrigin-Name: 6099c180db55396d6307538a5428ae5ef1b82d10 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/expr.c | 7 +++++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 9cd230d12f..51920f87c0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sa\sNEVER()\son\san\sunreachable\sbranch\sin\scomparisonAffinity(). -D 2016-08-24T15:37:31.592 +C Fix\smore\sunreachable\sbranches. +D 2016-08-24T17:49:07.427 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c c2d3844fb18e7659653e482060ee0c5d3ec5ed10 +F src/expr.c 57b6d994d88d390a76ccb0ef81ea7b3bccf06fec F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1520,7 +1520,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 f41a0391b732a8c4ad188163f34a0f4a22237bb5 -R b0541cab67b62c886354064e8f5cfc4d +P 505a2f20eac62d4e170f003255c8984e4f3b0918 +R 3bf744e7d3187456751558defe5b0a1e U drh -Z e7d2fda8d9703a6fa7987d8b8853c395 +Z 0d5e4686e1b28bbfaea275d793f60f16 diff --git a/manifest.uuid b/manifest.uuid index 4a04b24187..55daf21f48 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -505a2f20eac62d4e170f003255c8984e4f3b0918 \ No newline at end of file +6099c180db55396d6307538a5428ae5ef1b82d10 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 37f778b29e..56affdfeaa 100644 --- a/src/expr.c +++ b/src/expr.c @@ -542,10 +542,11 @@ static void codeVectorCompare( regLeft = exprCodeSubselect(pParse, pLeft); regRight = exprCodeSubselect(pParse, pRight); - for(i=0; i=0 && i0 ) sqlite3ExprCachePush(pParse); r1 = exprVectorRegister(pParse, pLeft, i, regLeft, &pL, ®Free1); r2 = exprVectorRegister(pParse, pRight, i, regRight, &pR, ®Free2); @@ -1490,7 +1491,9 @@ ExprList *sqlite3ExprListAppendVector( int n; int i; int iFirst = pList ? pList->nExpr : 0; - if( pColumns==0 ) goto vector_append_error; + /* pColumns can only be NULL due to an OOM but an OOM will cause an + ** exit prior to this routine being invoked */ + if( NEVER(pColumns==0) ) goto vector_append_error; if( pExpr==0 ) goto vector_append_error; n = sqlite3ExprVectorSize(pExpr); if( pColumns->nId!=n ){ From 62659b2a806146dfb91a58b7f9e5f3649caa66c5 Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 24 Aug 2016 18:51:23 +0000 Subject: [PATCH 58/97] In sqlite3FindInIndex(), improve internal comments and avoid an unreachable branch. FossilOrigin-Name: 55945fc12f8157e32e6850e41575c0c6422d29e7 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/expr.c | 16 ++++++++++++---- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index 51920f87c0..daccb4786d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\smore\sunreachable\sbranches. -D 2016-08-24T17:49:07.427 +C In\ssqlite3FindInIndex(),\simprove\sinternal\scomments\sand\savoid\san\nunreachable\sbranch. +D 2016-08-24T18:51:23.484 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 57b6d994d88d390a76ccb0ef81ea7b3bccf06fec +F src/expr.c 3dc226ccd9eebee600253eba07b8d97534e49337 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1520,7 +1520,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 505a2f20eac62d4e170f003255c8984e4f3b0918 -R 3bf744e7d3187456751558defe5b0a1e +P 6099c180db55396d6307538a5428ae5ef1b82d10 +R fa8e14e08c5acfaa4521c63ce095c576 U drh -Z 0d5e4686e1b28bbfaea275d793f60f16 +Z 500a8a58193db1f42f4b49dd37f14aa8 diff --git a/manifest.uuid b/manifest.uuid index 55daf21f48..879b23070a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6099c180db55396d6307538a5428ae5ef1b82d10 \ No newline at end of file +55945fc12f8157e32e6850e41575c0c6422d29e7 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 56affdfeaa..63958b0d25 100644 --- a/src/expr.c +++ b/src/expr.c @@ -2152,6 +2152,7 @@ int sqlite3FindInIndex( */ assert(v); if( nExpr==1 && pEList->a[0].pExpr->iColumn<0 ){ + /* The "x IN (SELECT rowid FROM table)" case */ int iAddr = sqlite3CodeOnce(pParse); VdbeCoverage(v); @@ -2165,18 +2166,25 @@ int sqlite3FindInIndex( int i; /* Check that the affinity that will be used to perform each - ** comparison is the same as the affinity of each column. If - ** it not, it is not possible to use any index. */ + ** comparison is the same as the affinity of each column in table + ** on the RHS of the IN operator. If it not, it is not possible to + ** use any index of the RHS table. */ for(i=0; ipLeft, i); int iCol = pEList->a[i].pExpr->iColumn; - char idxaff = pTab->aCol[iCol].affinity; + char idxaff = pTab->aCol[iCol].affinity; /* RHS table affinity */ char cmpaff = sqlite3CompareAffinity(pLhs, idxaff); + testcase( cmpaff==SQLITE_AFF_BLOB ); + testcase( cmpaff==SQLITE_AFF_TEXT ); switch( cmpaff ){ case SQLITE_AFF_BLOB: break; case SQLITE_AFF_TEXT: - affinity_ok = (idxaff==SQLITE_AFF_TEXT); + /* sqlite3CompareAffinity() only returns TEXT if one side or the + ** other has no affinity and the other side is TEXT. Hence, + ** the only way for cmpaff to be TEXT is for idxaff to be TEXT + ** and for the term on the LHS of the IN to have no affinity. */ + assert( idxaff==SQLITE_AFF_TEXT ); break; default: affinity_ok = sqlite3IsNumericAffinity(idxaff); From 72e26dec860e739ba6be30fcfb438dd0c7fa878e Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 24 Aug 2016 21:24:04 +0000 Subject: [PATCH 59/97] Improved extended comments of comparison operators when the SQLITE_STOREP2 flags is set on P5. No changes to non-debug code. FossilOrigin-Name: bbc1b016164ed0793e07302614384d52119463e0 --- manifest | 14 ++++++------- manifest.uuid | 2 +- src/vdbe.c | 58 +++++++++++++++++++++++++-------------------------- src/vdbeaux.c | 9 ++++++++ 4 files changed, 46 insertions(+), 37 deletions(-) diff --git a/manifest b/manifest index daccb4786d..f6f42159d3 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C In\ssqlite3FindInIndex(),\simprove\sinternal\scomments\sand\savoid\san\nunreachable\sbranch. -D 2016-08-24T18:51:23.484 +C Improved\sextended\scomments\sof\scomparison\soperators\swhen\sthe\sSQLITE_STOREP2\nflags\sis\sset\son\sP5.\s\sNo\schanges\sto\snon-debug\scode. +D 2016-08-24T21:24:04.787 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -450,11 +450,11 @@ F src/update.c 8179e699dbd45b92934fd02d3d8e3732e8da8802 F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c F src/util.c 810ec3f22e2d1b62e66c30fe3621ebdedd23584d F src/vacuum.c 913970b9d86dd6c2b8063ef1af421880f1464ec3 -F src/vdbe.c 766d363ef6520c671eef165098a24c0c6ef36d89 +F src/vdbe.c 68d56c11d5c5e704dde6d2d8748750a2f6352a09 F src/vdbe.h 67bc551f7faf04c33493892e4b378aada823ed10 F src/vdbeInt.h c59381049af5c7751a83456c39b80d1a6fde1f9d F src/vdbeapi.c a32d61b7dd05e6890d8fd44d2805f55e2f5ba9f3 -F src/vdbeaux.c a32d79aeaa88dc2b97c261172d952d395254a055 +F src/vdbeaux.c 83458783d241cfd6691141c8a105832ee50258e5 F src/vdbeblob.c 3e82a797b60c3b9fed7b8de8c539ca7607874937 F src/vdbemem.c e67dc6d8177fd1830efb5d15e17793408251a187 F src/vdbesort.c 91fda3909326860382b0ca8aa251e609c6a9d62c @@ -1520,7 +1520,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 6099c180db55396d6307538a5428ae5ef1b82d10 -R fa8e14e08c5acfaa4521c63ce095c576 +P 55945fc12f8157e32e6850e41575c0c6422d29e7 +R d8287dc165c2512db536a01f0275f112 U drh -Z 500a8a58193db1f42f4b49dd37f14aa8 +Z f51d93eb7238381398d31778d751851b diff --git a/manifest.uuid b/manifest.uuid index 879b23070a..dc4716aac1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -55945fc12f8157e32e6850e41575c0c6422d29e7 \ No newline at end of file +bbc1b016164ed0793e07302614384d52119463e0 \ No newline at end of file diff --git a/src/vdbe.c b/src/vdbe.c index 4477f6dfb9..24828ace3b 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -905,7 +905,7 @@ case OP_Yield: { /* in1, jump */ } /* Opcode: HaltIfNull P1 P2 P3 P4 P5 -** Synopsis: if r[P3]=null halt +** Synopsis: if r[P3]=null halt ** ** Check the value in register P3. If it is NULL then Halt using ** parameter P1, P2, and P4 as if this were a Halt instruction. If the @@ -1118,7 +1118,7 @@ case OP_String: { /* out2 */ } /* Opcode: Null P1 P2 P3 * * -** Synopsis: r[P2..P3]=NULL +** Synopsis: r[P2..P3]=NULL ** ** Write a NULL into registers P2. If P3 greater than P2, then also write ** NULL into register P3 and every register in between P2 and P3. If P3 @@ -1147,7 +1147,7 @@ case OP_Null: { /* out2 */ } /* Opcode: SoftNull P1 * * * * -** Synopsis: r[P1]=NULL +** Synopsis: r[P1]=NULL ** ** Set register P1 to have the value NULL as seen by the OP_MakeRecord ** instruction, but do not free any string or blob memory associated with @@ -1200,7 +1200,7 @@ case OP_Variable: { /* out2 */ } /* Opcode: Move P1 P2 P3 * * -** Synopsis: r[P2@P3]=r[P1@P3] +** Synopsis: r[P2@P3]=r[P1@P3] ** ** Move the P3 values in register P1..P1+P3-1 over into ** registers P2..P2+P3-1. Registers P1..P1+P3-1 are @@ -1310,7 +1310,7 @@ case OP_IntCopy: { /* out2 */ } /* Opcode: ResultRow P1 P2 * * * -** Synopsis: output=r[P1@P2] +** Synopsis: output=r[P1@P2] ** ** The registers P1 through P1+P2-1 contain a single row of ** results. This opcode causes the sqlite3_step() call to terminate @@ -1443,14 +1443,14 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ } /* Opcode: Add P1 P2 P3 * * -** Synopsis: r[P3]=r[P1]+r[P2] +** Synopsis: r[P3]=r[P1]+r[P2] ** ** Add the value in register P1 to the value in register P2 ** and store the result in register P3. ** If either input is NULL, the result is NULL. */ /* Opcode: Multiply P1 P2 P3 * * -** Synopsis: r[P3]=r[P1]*r[P2] +** Synopsis: r[P3]=r[P1]*r[P2] ** ** ** Multiply the value in register P1 by the value in register P2 @@ -1458,14 +1458,14 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ ** If either input is NULL, the result is NULL. */ /* Opcode: Subtract P1 P2 P3 * * -** Synopsis: r[P3]=r[P2]-r[P1] +** Synopsis: r[P3]=r[P2]-r[P1] ** ** Subtract the value in register P1 from the value in register P2 ** and store the result in register P3. ** If either input is NULL, the result is NULL. */ /* Opcode: Divide P1 P2 P3 * * -** Synopsis: r[P3]=r[P2]/r[P1] +** Synopsis: r[P3]=r[P2]/r[P1] ** ** Divide the value in register P1 by the value in register P2 ** and store the result in register P3 (P3=P2/P1). If the value in @@ -1473,7 +1473,7 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ ** NULL, the result is NULL. */ /* Opcode: Remainder P1 P2 P3 * * -** Synopsis: r[P3]=r[P2]%r[P1] +** Synopsis: r[P3]=r[P2]%r[P1] ** ** Compute the remainder after integer register P2 is divided by ** register P1 and store the result in register P3. @@ -1706,21 +1706,21 @@ case OP_Function: { } /* Opcode: BitAnd P1 P2 P3 * * -** Synopsis: r[P3]=r[P1]&r[P2] +** Synopsis: r[P3]=r[P1]&r[P2] ** ** Take the bit-wise AND of the values in register P1 and P2 and ** store the result in register P3. ** If either input is NULL, the result is NULL. */ /* Opcode: BitOr P1 P2 P3 * * -** Synopsis: r[P3]=r[P1]|r[P2] +** Synopsis: r[P3]=r[P1]|r[P2] ** ** Take the bit-wise OR of the values in register P1 and P2 and ** store the result in register P3. ** If either input is NULL, the result is NULL. */ /* Opcode: ShiftLeft P1 P2 P3 * * -** Synopsis: r[P3]=r[P2]<>r[P1] +** Synopsis: r[P3]=r[P2]>>r[P1] ** ** Shift the integer value in register P2 to the right by the ** number of bits specified by the integer in register P1. @@ -1788,7 +1788,7 @@ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */ } /* Opcode: AddImm P1 P2 * * * -** Synopsis: r[P1]=r[P1]+P2 +** Synopsis: r[P1]=r[P1]+P2 ** ** Add the constant P2 to the value in register P1. ** The result is always an integer. @@ -1881,7 +1881,7 @@ case OP_Cast: { /* in1 */ #endif /* SQLITE_OMIT_CAST */ /* Opcode: Eq P1 P2 P3 P4 P5 -** Synopsis: if r[P1]==r[P3] goto P2 +** Synopsis: IF r[P1]==r[P3] ** ** Compare the values in register P1 and P3. If reg(P3)==reg(P1) then ** jump to address P2. Or if the SQLITE_STOREP2 flag is set in P5, then @@ -1915,7 +1915,7 @@ case OP_Cast: { /* in1 */ ** content of r[P2] is only set to 1 (true) if it was not previously NULL. */ /* Opcode: Ne P1 P2 P3 P4 P5 -** Synopsis: if r[P1]!=r[P3] goto P2 +** Synopsis: IF r[P1]!=r[P3] ** ** This works just like the Eq opcode except that the jump is taken if ** the operands in registers P1 and P3 are not equal. See the Eq opcode for @@ -1925,7 +1925,7 @@ case OP_Cast: { /* in1 */ ** content of r[P2] is only set to 0 (false) if it was not previously NULL. */ /* Opcode: Lt P1 P2 P3 P4 P5 -** Synopsis: if r[P1]r[P3] goto P2 +** Synopsis: IF r[P1]>r[P3] ** ** This works just like the Lt opcode except that the jump is taken if ** the content of register P3 is greater than the content of ** register P1. See the Lt opcode for additional information. */ /* Opcode: Ge P1 P2 P3 P4 P5 -** Synopsis: if r[P1]>=r[P3] goto P2 +** Synopsis: IF r[P1]>=r[P3] ** ** This works just like the Lt opcode except that the jump is taken if ** the content of register P3 is greater than or equal to the content of @@ -2370,7 +2370,7 @@ case OP_IfNot: { /* jump, in1 */ } /* Opcode: IsNull P1 P2 * * * -** Synopsis: if r[P1]==NULL goto P2 +** Synopsis: if r[P1]==NULL goto P2 ** ** Jump to P2 if the value in register P1 is NULL. */ @@ -2398,7 +2398,7 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */ } /* Opcode: Column P1 P2 P3 P4 P5 -** Synopsis: r[P3]=PX +** Synopsis: r[P3]=PX ** ** Interpret the data that cursor P1 points to as a structure built using ** the MakeRecord instruction. (See the MakeRecord opcode for additional @@ -4340,7 +4340,7 @@ case OP_NewRowid: { /* out2 */ ** for indices is OP_IdxInsert. */ /* Opcode: InsertInt P1 P2 P3 P4 P5 -** Synopsis: intkey=P3 data=r[P2] +** Synopsis: intkey=P3 data=r[P2] ** ** This works exactly like OP_Insert except that the key is the ** integer value P3, not the value of the integer stored in register P3. @@ -4571,7 +4571,7 @@ case OP_ResetCount: { } /* Opcode: SorterCompare P1 P2 P3 P4 -** Synopsis: if key(P1)!=trim(r[P3],P4) goto P2 +** Synopsis: if key(P1)!=trim(r[P3],P4) goto P2 ** ** P1 is a sorter cursor. This instruction compares a prefix of the ** record blob in register P3 against a prefix of the entry that @@ -5098,7 +5098,7 @@ case OP_IdxDelete: { } /* Opcode: Seek P1 * P3 P4 * -** Synopsis: Move P3 to P1.rowid +** Synopsis: Move P3 to P1.rowid ** ** P1 is an open index cursor and P3 is a cursor on the corresponding ** table. This opcode does a deferred seek of the P3 table cursor @@ -5605,7 +5605,7 @@ case OP_IntegrityCk: { #endif /* SQLITE_OMIT_INTEGRITY_CHECK */ /* Opcode: RowSetAdd P1 P2 * * * -** Synopsis: rowset(P1)=r[P2] +** Synopsis: rowset(P1)=r[P2] ** ** Insert the integer value held by register P2 into a boolean index ** held in register P1. @@ -5625,7 +5625,7 @@ case OP_RowSetAdd: { /* in1, in2 */ } /* Opcode: RowSetRead P1 P2 P3 * * -** Synopsis: r[P3]=rowset(P1) +** Synopsis: r[P3]=rowset(P1) ** ** Extract the smallest value from boolean index P1 and put that value into ** register P3. Or, if boolean index P1 is initially empty, leave P3 @@ -6804,7 +6804,7 @@ case OP_MaxPgcnt: { /* out2 */ /* Opcode: Init * P2 * P4 * -** Synopsis: Start at P2 +** Synopsis: Start at P2 ** ** Programs contain a single instance of this opcode as the very first ** opcode. diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 992fa4db9e..963833cce5 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -1094,12 +1094,21 @@ static int displayComment( const char *zSynopsis; int nOpName; int ii, jj; + char zAlt[50]; zOpName = sqlite3OpcodeName(pOp->opcode); nOpName = sqlite3Strlen30(zOpName); if( zOpName[nOpName+1] ){ int seenCom = 0; char c; zSynopsis = zOpName += nOpName + 1; + if( strncmp(zSynopsis,"IF ",3)==0 ){ + if( pOp->p5 & SQLITE_STOREP2 ){ + sqlite3_snprintf(sizeof(zAlt), zAlt, "r[P2] = (%s)", zSynopsis+3); + }else{ + sqlite3_snprintf(sizeof(zAlt), zAlt, "if %s goto P2", zSynopsis+3); + } + zSynopsis = zAlt; + } for(ii=jj=0; jj Date: Wed, 24 Aug 2016 21:54:47 +0000 Subject: [PATCH 60/97] Simplified VDBE code for the vector NOT IN null-scanning loop. FossilOrigin-Name: 7ae504e62e9bbbbd85a676f3c3922b7fd0cc73d2 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/expr.c | 22 ++++++++++++---------- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/manifest b/manifest index f6f42159d3..105c9bfbb2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improved\sextended\scomments\sof\scomparison\soperators\swhen\sthe\sSQLITE_STOREP2\nflags\sis\sset\son\sP5.\s\sNo\schanges\sto\snon-debug\scode. -D 2016-08-24T21:24:04.787 +C Simplified\sVDBE\scode\sfor\sthe\svector\sNOT\sIN\snull-scanning\sloop. +D 2016-08-24T21:54:47.055 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 3dc226ccd9eebee600253eba07b8d97534e49337 +F src/expr.c 866bcb6e85806beb0f96e651e9d17811e1ecbda5 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1520,7 +1520,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 55945fc12f8157e32e6850e41575c0c6422d29e7 -R d8287dc165c2512db536a01f0275f112 +P bbc1b016164ed0793e07302614384d52119463e0 +R 5a565b60b3cfcc2e549e66fdc0e5eedc U drh -Z f51d93eb7238381398d31778d751851b +Z 08a1319918b046fa6beb913b7de3f092 diff --git a/manifest.uuid b/manifest.uuid index dc4716aac1..0459a673e1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bbc1b016164ed0793e07302614384d52119463e0 \ No newline at end of file +7ae504e62e9bbbbd85a676f3c3922b7fd0cc73d2 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 63958b0d25..16f74d785e 100644 --- a/src/expr.c +++ b/src/expr.c @@ -2782,18 +2782,20 @@ static void sqlite3ExprCodeIN( if( nVector>1 && destIfNull!=destIfFalse ){ int iIdx = pExpr->iTable; - int addr; + int addrTop; int addrNext; + int addrFound; /* Search the index for the key. */ - addr = sqlite3VdbeAddOp4Int(v, OP_Found, iIdx, 0, r1, nVector); + addrFound = sqlite3VdbeAddOp4Int(v, OP_Found, iIdx, 0, r1, nVector); VdbeCoverage(v); /* At this point the specified key is not present in the index, ** so the result of the IN(..) operator must be either NULL or ** 0. The vdbe code generated below figures out which. */ - addrNext = 1+sqlite3VdbeAddOp2(v, OP_Rewind, iIdx, destIfFalse); + addrTop = 1+sqlite3VdbeAddOp2(v, OP_Rewind, iIdx, destIfFalse); VdbeCoverage(v); + addrNext = sqlite3VdbeMakeLabel(v); for(i=0; ipLeft, i); if( sqlite3ExprCanBeNull(p) ){ From 539f2fef03004a11476235a942615fb49f1ce48a Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 25 Aug 2016 14:00:15 +0000 Subject: [PATCH 61/97] Add notes on the implementation of the IN operator. FossilOrigin-Name: d256b2caeb9e3eb5dd88bb569ec71f91e9991c81 --- manifest | 11 ++--- manifest.uuid | 2 +- src/in-operator.md | 105 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 src/in-operator.md diff --git a/manifest b/manifest index 105c9bfbb2..b69115a5fc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Simplified\sVDBE\scode\sfor\sthe\svector\sNOT\sIN\snull-scanning\sloop. -D 2016-08-24T21:54:47.055 +C Add\snotes\son\sthe\simplementation\sof\sthe\sIN\soperator. +D 2016-08-25T14:00:15.437 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -346,6 +346,7 @@ F src/global.c c45ea22aff29334f6a9ec549235ac3357c970015 F src/hash.c 55b5fb474100cee0b901edaf203e26c970940f36 F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4 F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da +F src/in-operator.md 8176075ceca5b1a0564d4aff1e415ff6f9037280 F src/insert.c a255eb795cf475e7a0659297144fc80f70eb4e30 F src/legacy.c 75d3023be8f0d2b99d60f905090341a03358c58e F src/loadext.c dd7a2b77902cc66c22555aef02e1a682554b7aec @@ -1520,7 +1521,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 bbc1b016164ed0793e07302614384d52119463e0 -R 5a565b60b3cfcc2e549e66fdc0e5eedc +P 7ae504e62e9bbbbd85a676f3c3922b7fd0cc73d2 +R 85a14e909442e83b4fd5e48b00047186 U drh -Z 08a1319918b046fa6beb913b7de3f092 +Z fb24d479cf53b7630adc4dea69c62760 diff --git a/manifest.uuid b/manifest.uuid index 0459a673e1..0fc9cd5be3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7ae504e62e9bbbbd85a676f3c3922b7fd0cc73d2 \ No newline at end of file +d256b2caeb9e3eb5dd88bb569ec71f91e9991c81 \ No newline at end of file diff --git a/src/in-operator.md b/src/in-operator.md new file mode 100644 index 0000000000..76002b8597 --- /dev/null +++ b/src/in-operator.md @@ -0,0 +1,105 @@ +IN-Operator Implementation Notes +================================ + +## Definitions: + +An IN operator has one of the following formats: + +> + x IN (list) + x IN (subquery) + +The "x" is referred to as the LHS (left-hand side). The list or subquery +on the right is called the RHS (right-hand side). If the RHS is a list +it must be a non-empty list. But if the RHS is a subquery, it can be an +empty set. + +Both the LHS and RHS can be scalars or vectors. The two must match. +In other words, they must both be scalar or else they must both be +vectors of the same length. + +NULL values can occur in either or both of the LHS and RHS. +If the LHS contains only +NULL values then we say that it is a "total-NULL". If the LHS contains +some NULL values and some non-NULL values, then it is a "partial-NULL". +For a scalar, there is no difference between a partial-NULL and a total-NULL. +The RHS is a partial-NULL if any row contains a NULL value. The RHS is +a total-NULL if it contains one or more rows that contain only NULL values. +The LHS is called "non-NULL" if it contains no NULL values. The RHS is +called "non-NULL" if it contains no NULL values in any row. + +The result of an IN operator is one of TRUE, FALSE, or NULL. A NULL result +means that it cannot be determined if the LHS is contained in the RHS due +to the presence of NULL values. In some contexts (for example, when the IN +operator occurs in a WHERE clause) +the system only needs a binary result: TRUE or NOT-TRUE. One can also +to define a binary result of FALSE and NOT-FALSE, but +it turns out that no extra optimizations are possible in that case, so if +the FALSE/NOT-FALSE binary is needed, we have to compute the three-state +TRUE/FALSE/NULL result and then combine the TRUE and NULL values into +NOT-FALSE. + +A "NOT IN" operator is computed by first computing the equivalent IN +operator, then interchanging the TRUE and FALSE results. + +## Simple Full-Scan Algorithm + +The following algorithm always compute the correct answer. However, this +algorithm is suboptimal, especially if there are many rows on the RHS. + + 1. Set the null-flag to false + 2. For each row in the RHS: +
    +
  1. Compare the LHS against the RHS +
  2. If the LHS exactly matches the RHS, immediately return TRUE +
  3. If the comparison result is NULL, set the null-flag to true +
+ 3. If the null-flag is true, return NULL. + 4. Return FALSE + +## Optimized Algorithm + +The following procedure computes the same answer as the simple full-scan +algorithm, though it does so with less work in the common case. This +is the algorithm that is implemented in SQLite. The steps must occur +in the order specified. Except for the INDEX_NOOP optimization of step 1, +none of the steps can be skipped. + + 1. If the RHS is a constant list of length 1 or 2, then rewrite the + IN operator as a simple expression. Implement + + x IN (y1,y2) + + as if it were + + x=y1 OR x=y2 + + This is the INDEX_NOOP optimization and is only undertaken if the + IN operator is used for membership testing. If the IN operator is + driving a loop, then skip this step entirely. + + 2. If the RHS is empty, return FALSE. + + 3. If the LHS is a total-NULL or if the RHS contains a total-NULL, + then return NULL. + + 4. If the LHS is non-NULL, then use the LHS as a probe in a binary + search of the RHS + +
    +
  1. If the binary search finds an exact match, return TRUE + +
  2. If the RHS is known to be not-null, return FALSE +
+ + 5. At this point, it is known that the result cannot be TRUE. All + that remains is to distinguish between NULL and FALSE. + If a NOT-TRUE result is acceptable, then return NOT-TRUE now. + + 6. For each row in the RHS, compare that row against the LHS and + if the result is NULL, immediately return NULL. This step is + essentially the "Simple Full-scan Algorithm" above with the + tests for TRUE removed, since we know that the result cannot be + TRUE at this point. + + 7. Return FALSE. From ee0f22fd3b273a852f5ed32f0f624f369845e6fa Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 25 Aug 2016 14:23:59 +0000 Subject: [PATCH 62/97] Corrections to the IN-operator notes. FossilOrigin-Name: 25033ee94538289ba7e0147da30a18300047123f --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/in-operator.md | 13 +++++-------- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/manifest b/manifest index b69115a5fc..2c01a256ff 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\snotes\son\sthe\simplementation\sof\sthe\sIN\soperator. -D 2016-08-25T14:00:15.437 +C Corrections\sto\sthe\sIN-operator\snotes. +D 2016-08-25T14:23:59.673 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -346,7 +346,7 @@ F src/global.c c45ea22aff29334f6a9ec549235ac3357c970015 F src/hash.c 55b5fb474100cee0b901edaf203e26c970940f36 F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4 F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da -F src/in-operator.md 8176075ceca5b1a0564d4aff1e415ff6f9037280 +F src/in-operator.md 973fe4522b871fb8cf683d8453760f4f19ac68c8 F src/insert.c a255eb795cf475e7a0659297144fc80f70eb4e30 F src/legacy.c 75d3023be8f0d2b99d60f905090341a03358c58e F src/loadext.c dd7a2b77902cc66c22555aef02e1a682554b7aec @@ -1521,7 +1521,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 7ae504e62e9bbbbd85a676f3c3922b7fd0cc73d2 -R 85a14e909442e83b4fd5e48b00047186 +P d256b2caeb9e3eb5dd88bb569ec71f91e9991c81 +R e83420368d9fa99d84d7c5701ecba45b U drh -Z fb24d479cf53b7630adc4dea69c62760 +Z a8948efc181e8ffaf5af6c9f4ff55b46 diff --git a/manifest.uuid b/manifest.uuid index 0fc9cd5be3..b1c505cee1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d256b2caeb9e3eb5dd88bb569ec71f91e9991c81 \ No newline at end of file +25033ee94538289ba7e0147da30a18300047123f \ No newline at end of file diff --git a/src/in-operator.md b/src/in-operator.md index 76002b8597..0c9480234d 100644 --- a/src/in-operator.md +++ b/src/in-operator.md @@ -62,8 +62,8 @@ algorithm is suboptimal, especially if there are many rows on the RHS. The following procedure computes the same answer as the simple full-scan algorithm, though it does so with less work in the common case. This is the algorithm that is implemented in SQLite. The steps must occur -in the order specified. Except for the INDEX_NOOP optimization of step 1, -none of the steps can be skipped. +in the order specified. Steps 1 and 3 are optional. All other steps +are required for correctness. 1. If the RHS is a constant list of length 1 or 2, then rewrite the IN operator as a simple expression. Implement @@ -80,17 +80,14 @@ none of the steps can be skipped. 2. If the RHS is empty, return FALSE. - 3. If the LHS is a total-NULL or if the RHS contains a total-NULL, - then return NULL. + 3. If the LHS is a total-NULL, then return NULL. 4. If the LHS is non-NULL, then use the LHS as a probe in a binary search of the RHS -
    -
  1. If the binary search finds an exact match, return TRUE + 4-A. If the binary search finds an exact match, return TRUE -
  2. If the RHS is known to be not-null, return FALSE -
+ 4-B. If the RHS is known to be not-null, return FALSE 5. At this point, it is known that the result cannot be TRUE. All that remains is to distinguish between NULL and FALSE. From ecb87ac88d6cac19eb68cab7c5944fcb1d2a372e Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 25 Aug 2016 15:46:25 +0000 Subject: [PATCH 63/97] Improvements to IN operator code generator comments. Avoid unnecessary Copy operations on the LHS of the IN operator. FossilOrigin-Name: b6344298783a1207cba3f635939ddc9ba922ab67 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/expr.c | 54 +++++++++++++++++++++++++++++++++------------------ 3 files changed, 42 insertions(+), 26 deletions(-) diff --git a/manifest b/manifest index 2c01a256ff..cd1f8ff071 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Corrections\sto\sthe\sIN-operator\snotes. -D 2016-08-25T14:23:59.673 +C Improvements\sto\sIN\soperator\scode\sgenerator\scomments.\s\sAvoid\sunnecessary\nCopy\soperations\son\sthe\sLHS\sof\sthe\sIN\soperator. +D 2016-08-25T15:46:25.026 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 866bcb6e85806beb0f96e651e9d17811e1ecbda5 +F src/expr.c 1c003fcb9c5a6f8c54f6392378225d578be2a086 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1521,7 +1521,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 d256b2caeb9e3eb5dd88bb569ec71f91e9991c81 -R e83420368d9fa99d84d7c5701ecba45b +P 25033ee94538289ba7e0147da30a18300047123f +R e1cedb5c8edd4d27c5fcda8527491fbe U drh -Z a8948efc181e8ffaf5af6c9f4ff55b46 +Z c3029179ef3a13ce81541f0730bc01a8 diff --git a/manifest.uuid b/manifest.uuid index b1c505cee1..f23c36e283 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -25033ee94538289ba7e0147da30a18300047123f \ No newline at end of file +b6344298783a1207cba3f635939ddc9ba922ab67 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 16f74d785e..385e808917 100644 --- a/src/expr.c +++ b/src/expr.c @@ -2637,16 +2637,19 @@ int sqlite3ExprCheckIN(Parse *pParse, Expr *pIn){ ** x IN (SELECT ...) ** x IN (value, value, ...) ** -** The left-hand side (LHS) is a scalar expression. The right-hand side (RHS) -** is an array of zero or more values. The expression is true if the LHS is -** contained within the RHS. The value of the expression is unknown (NULL) -** if the LHS is NULL or if the LHS is not contained within the RHS and the -** RHS contains one or more NULL values. +** The left-hand side (LHS) is a scalar or vector expression. The +** right-hand side (RHS) is an array of zero or more values. The IN operator +** is true if the LHS is contained within the RHS. The result is false +** if the LHS is definitely not in the RHS. The result is NULL if the presence +** of the LHS in the RHS cannot be determined due to NULLs. ** ** This routine generates code that jumps to destIfFalse if the LHS is not ** contained within the RHS. If due to NULLs we cannot determine if the LHS ** is contained in the RHS then jump to destIfNull. If the LHS is contained ** within the RHS then fall through. +** +** See the separate in-operator.md documentation file in the canonical +** SQLite source tree for additional information. */ static void sqlite3ExprCodeIN( Parse *pParse, /* Parsing and code generating context */ @@ -2660,22 +2663,18 @@ static void sqlite3ExprCodeIN( Vdbe *v; /* Statement under construction */ int *aiMap = 0; /* Map from vector field to index column */ char *zAff = 0; /* Affinity string for comparisons */ - int nVector; /* Size of vectors for this IN(...) op */ - int iDummy; /* Dummy parameter to exprCodeVector() */ - Expr *pLeft = pExpr->pLeft; - int i; + int nVector; /* Size of vectors for this IN operator */ + int iDummy; /* Dummy parameter to exprCodeVector() */ + Expr *pLeft = pExpr->pLeft; /* The LHS of the IN operator */ + int i; /* loop counter */ if( sqlite3ExprCheckIN(pParse, pExpr) ) return; zAff = exprINAffinity(pParse, pExpr); - if( zAff==0 ) return; nVector = sqlite3ExprVectorSize(pExpr->pLeft); aiMap = (int*)sqlite3DbMallocZero( pParse->db, nVector*(sizeof(int) + sizeof(char)) + 1 ); - if( aiMap==0 ){ - sqlite3DbFree(pParse->db, zAff); - return; - } + if( pParse->db->mallocFailed ) goto end_code_IN_op; /* Attempt to compute the RHS. After this step, if anything other than ** IN_INDEX_NOOP is returned, the table opened ith cursor pExpr->iTable @@ -2691,16 +2690,32 @@ static void sqlite3ExprCodeIN( assert( pParse->nErr || nVector==1 || eType==IN_INDEX_EPH || eType==IN_INDEX_INDEX_ASC || eType==IN_INDEX_INDEX_DESC ); +#ifdef SQLITE_DEBUG + /* Confirm that aiMap[] contains nVector integer values between 0 and + ** nVector-1. */ + for(i=0; i from " IN (...)". If the LHS is a ** vector, then it is stored in an array of nVector registers starting ** at r1. */ - r1 = sqlite3GetTempRange(pParse, nVector); sqlite3ExprCachePush(pParse); r2 = exprCodeVector(pParse, pLeft, &iDummy); - for(i=0; idb, aiMap); sqlite3DbFree(pParse->db, zAff); - VdbeComment((v, "end IN expr")); } #endif /* SQLITE_OMIT_SUBQUERY */ From 1373c3a8c52ea8f2ba2877f223476cbe61c729b7 Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 25 Aug 2016 17:40:32 +0000 Subject: [PATCH 64/97] Further refinement of the in-operator.md documentation. FossilOrigin-Name: df0648373a50006ca18d692e12552d1d53d549e3 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/in-operator.md | 30 ++++++++++++------------------ 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/manifest b/manifest index cd1f8ff071..9f1b129892 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improvements\sto\sIN\soperator\scode\sgenerator\scomments.\s\sAvoid\sunnecessary\nCopy\soperations\son\sthe\sLHS\sof\sthe\sIN\soperator. -D 2016-08-25T15:46:25.026 +C Further\srefinement\sof\sthe\sin-operator.md\sdocumentation. +D 2016-08-25T17:40:32.626 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -346,7 +346,7 @@ F src/global.c c45ea22aff29334f6a9ec549235ac3357c970015 F src/hash.c 55b5fb474100cee0b901edaf203e26c970940f36 F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4 F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da -F src/in-operator.md 973fe4522b871fb8cf683d8453760f4f19ac68c8 +F src/in-operator.md 9627b332c40228c0cb756eff1d84471b397539be F src/insert.c a255eb795cf475e7a0659297144fc80f70eb4e30 F src/legacy.c 75d3023be8f0d2b99d60f905090341a03358c58e F src/loadext.c dd7a2b77902cc66c22555aef02e1a682554b7aec @@ -1521,7 +1521,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 25033ee94538289ba7e0147da30a18300047123f -R e1cedb5c8edd4d27c5fcda8527491fbe +P b6344298783a1207cba3f635939ddc9ba922ab67 +R 0bc7dc0041a27b5ec53385d0f7163e25 U drh -Z c3029179ef3a13ce81541f0730bc01a8 +Z 0df057355417cbdb73d372028cbbe733 diff --git a/manifest.uuid b/manifest.uuid index f23c36e283..fa403130a1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b6344298783a1207cba3f635939ddc9ba922ab67 \ No newline at end of file +df0648373a50006ca18d692e12552d1d53d549e3 \ No newline at end of file diff --git a/src/in-operator.md b/src/in-operator.md index 0c9480234d..ecd0b97e3a 100644 --- a/src/in-operator.md +++ b/src/in-operator.md @@ -61,9 +61,7 @@ algorithm is suboptimal, especially if there are many rows on the RHS. The following procedure computes the same answer as the simple full-scan algorithm, though it does so with less work in the common case. This -is the algorithm that is implemented in SQLite. The steps must occur -in the order specified. Steps 1 and 3 are optional. All other steps -are required for correctness. +is the algorithm that is implemented in SQLite. 1. If the RHS is a constant list of length 1 or 2, then rewrite the IN operator as a simple expression. Implement @@ -78,25 +76,21 @@ are required for correctness. IN operator is used for membership testing. If the IN operator is driving a loop, then skip this step entirely. - 2. If the RHS is empty, return FALSE. + 2. Check the LHS to see if it is a partial-NULL and if it is, jump + ahead to step 4. - 3. If the LHS is a total-NULL, then return NULL. + 3. Do a binary search for the RHS using the LHS as a probe. If + an exact match is found, return TRUE. - 4. If the LHS is non-NULL, then use the LHS as a probe in a binary - search of the RHS + 4. If we do not need to distingish between FALSE and NULL, + then return FALSE. - 4-A. If the binary search finds an exact match, return TRUE - - 4-B. If the RHS is known to be not-null, return FALSE - - 5. At this point, it is known that the result cannot be TRUE. All - that remains is to distinguish between NULL and FALSE. - If a NOT-TRUE result is acceptable, then return NOT-TRUE now. + 5. If the RHS is non-NULL then return FALSE. 6. For each row in the RHS, compare that row against the LHS and - if the result is NULL, immediately return NULL. This step is - essentially the "Simple Full-scan Algorithm" above with the - tests for TRUE removed, since we know that the result cannot be - TRUE at this point. + if the result is NULL, immediately return NULL. In the case + of a scalar IN operator, we only need to look at the very first + row the RHS because for a scalar RHS, all NULLs will always come + first. If the RHS is empty, this step is a no-op. 7. Return FALSE. From 0a1082aec2a46e1189a6ccf8ca1edfc10c6681be Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 25 Aug 2016 17:47:36 +0000 Subject: [PATCH 65/97] Another fix in the IN-operator algorithm description. FossilOrigin-Name: f474aeac4fa62d87e73189868d7c7a295ffb7265 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/in-operator.md | 12 ++++++------ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/manifest b/manifest index 9f1b129892..6410765471 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Further\srefinement\sof\sthe\sin-operator.md\sdocumentation. -D 2016-08-25T17:40:32.626 +C Another\sfix\sin\sthe\sIN-operator\salgorithm\sdescription. +D 2016-08-25T17:47:36.711 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -346,7 +346,7 @@ F src/global.c c45ea22aff29334f6a9ec549235ac3357c970015 F src/hash.c 55b5fb474100cee0b901edaf203e26c970940f36 F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4 F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da -F src/in-operator.md 9627b332c40228c0cb756eff1d84471b397539be +F src/in-operator.md e74c3dbd32b765c22c0bfc023f3b867e841a292b F src/insert.c a255eb795cf475e7a0659297144fc80f70eb4e30 F src/legacy.c 75d3023be8f0d2b99d60f905090341a03358c58e F src/loadext.c dd7a2b77902cc66c22555aef02e1a682554b7aec @@ -1521,7 +1521,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 b6344298783a1207cba3f635939ddc9ba922ab67 -R 0bc7dc0041a27b5ec53385d0f7163e25 +P df0648373a50006ca18d692e12552d1d53d549e3 +R 5563c2646a49e89461278dffc729d682 U drh -Z 0df057355417cbdb73d372028cbbe733 +Z f4cbbe9a35f7af77488dc4e4d3d948a7 diff --git a/manifest.uuid b/manifest.uuid index fa403130a1..53efcbce1d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -df0648373a50006ca18d692e12552d1d53d549e3 \ No newline at end of file +f474aeac4fa62d87e73189868d7c7a295ffb7265 \ No newline at end of file diff --git a/src/in-operator.md b/src/in-operator.md index ecd0b97e3a..ff691864b0 100644 --- a/src/in-operator.md +++ b/src/in-operator.md @@ -77,16 +77,16 @@ is the algorithm that is implemented in SQLite. driving a loop, then skip this step entirely. 2. Check the LHS to see if it is a partial-NULL and if it is, jump - ahead to step 4. + ahead to step 5. - 3. Do a binary search for the RHS using the LHS as a probe. If + 3. Do a binary search of the RHS using the LHS as a probe. If an exact match is found, return TRUE. - 4. If we do not need to distingish between FALSE and NULL, + 4. If the RHS is non-NULL then return FALSE. + + 5. If we do not need to distingish between FALSE and NULL, then return FALSE. - - 5. If the RHS is non-NULL then return FALSE. - + 6. For each row in the RHS, compare that row against the LHS and if the result is NULL, immediately return NULL. In the case of a scalar IN operator, we only need to look at the very first From e347d3e8132c56ef59b12889605a4f3f0ad082ec Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 25 Aug 2016 21:14:34 +0000 Subject: [PATCH 66/97] Refactor the sqlite3ExprCodeIN() routine for improved maintainability. FossilOrigin-Name: b56705ae6374db9db82613ef89faa1a1e6b00a18 --- manifest | 14 +-- manifest.uuid | 2 +- src/expr.c | 271 +++++++++++++++++++++++---------------------- src/in-operator.md | 21 +++- 4 files changed, 161 insertions(+), 147 deletions(-) diff --git a/manifest b/manifest index 6410765471..5805cde823 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Another\sfix\sin\sthe\sIN-operator\salgorithm\sdescription. -D 2016-08-25T17:47:36.711 +C Refactor\sthe\ssqlite3ExprCodeIN()\sroutine\sfor\simproved\smaintainability. +D 2016-08-25T21:14:34.728 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 1c003fcb9c5a6f8c54f6392378225d578be2a086 +F src/expr.c 45f814e52d2279914bd7a9141d92a26cb71b1533 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -346,7 +346,7 @@ F src/global.c c45ea22aff29334f6a9ec549235ac3357c970015 F src/hash.c 55b5fb474100cee0b901edaf203e26c970940f36 F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4 F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da -F src/in-operator.md e74c3dbd32b765c22c0bfc023f3b867e841a292b +F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c a255eb795cf475e7a0659297144fc80f70eb4e30 F src/legacy.c 75d3023be8f0d2b99d60f905090341a03358c58e F src/loadext.c dd7a2b77902cc66c22555aef02e1a682554b7aec @@ -1521,7 +1521,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 df0648373a50006ca18d692e12552d1d53d549e3 -R 5563c2646a49e89461278dffc729d682 +P f474aeac4fa62d87e73189868d7c7a295ffb7265 +R 015c67366edd8d396f4d5202834c4796 U drh -Z f4cbbe9a35f7af77488dc4e4d3d948a7 +Z ec6ee7d7de4c92376a0d667aef9d24cc diff --git a/manifest.uuid b/manifest.uuid index 53efcbce1d..b8c92ac025 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f474aeac4fa62d87e73189868d7c7a295ffb7265 \ No newline at end of file +b56705ae6374db9db82613ef89faa1a1e6b00a18 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 385e808917..ab0349dcb7 100644 --- a/src/expr.c +++ b/src/expr.c @@ -2638,10 +2638,15 @@ int sqlite3ExprCheckIN(Parse *pParse, Expr *pIn){ ** x IN (value, value, ...) ** ** The left-hand side (LHS) is a scalar or vector expression. The -** right-hand side (RHS) is an array of zero or more values. The IN operator -** is true if the LHS is contained within the RHS. The result is false -** if the LHS is definitely not in the RHS. The result is NULL if the presence -** of the LHS in the RHS cannot be determined due to NULLs. +** right-hand side (RHS) is an array of zero or more scalar values, or a +** subquery. If the RHS is a subquery, the number of result columns must +** match the number of columns in the vector on the LHS. If the RHS is +** a list of values, the LHS must be a scalar. +** +** The IN operator is true if the LHS value is contained within the RHS. +** The result is false if the LHS is definitely not in the RHS. The +** result is NULL if the presence of the LHS in the RHS cannot be +** determined due to NULLs. ** ** This routine generates code that jumps to destIfFalse if the LHS is not ** contained within the RHS. If due to NULLs we cannot determine if the LHS @@ -2659,22 +2664,29 @@ static void sqlite3ExprCodeIN( ){ int rRhsHasNull = 0; /* Register that is true if RHS contains NULL values */ int eType; /* Type of the RHS */ - int r1, r2; /* Temporary use registers */ + int rLhs; /* Register(s) holding the LHS values */ + int rLhsOrig; /* LHS values prior to reordering by aiMap[] */ Vdbe *v; /* Statement under construction */ int *aiMap = 0; /* Map from vector field to index column */ char *zAff = 0; /* Affinity string for comparisons */ - int nVector; /* Size of vectors for this IN operator */ - int iDummy; /* Dummy parameter to exprCodeVector() */ - Expr *pLeft = pExpr->pLeft; /* The LHS of the IN operator */ - int i; /* loop counter */ + int nVector; /* Size of vectors for this IN operator */ + int iDummy; /* Dummy parameter to exprCodeVector() */ + Expr *pLeft; /* The LHS of the IN operator */ + int i; /* loop counter */ + int destStep2; /* Where to jump when NULLs seen in step 2 */ + int destStep6 = 0; /* Start of code for Step 6 */ + int addrTruthOp; /* Address of opcode that determines the IN is true */ + int destNotNull; /* Jump here if a comparison is not true in step 6 */ + int addrTop; /* Top of the step-6 loop */ + pLeft = pExpr->pLeft; if( sqlite3ExprCheckIN(pParse, pExpr) ) return; zAff = exprINAffinity(pParse, pExpr); nVector = sqlite3ExprVectorSize(pExpr->pLeft); aiMap = (int*)sqlite3DbMallocZero( pParse->db, nVector*(sizeof(int) + sizeof(char)) + 1 ); - if( pParse->db->mallocFailed ) goto end_code_IN_op; + if( pParse->db->mallocFailed ) goto sqlite3ExprCodeIN_oom_error; /* Attempt to compute the RHS. After this step, if anything other than ** IN_INDEX_NOOP is returned, the table opened ith cursor pExpr->iTable @@ -2703,24 +2715,31 @@ static void sqlite3ExprCodeIN( /* Code the LHS, the from " IN (...)". If the LHS is a ** vector, then it is stored in an array of nVector registers starting ** at r1. + ** + ** sqlite3FindInIndex() might have reordered the fields of the LHS vector + ** so that the fields are in the same order as an existing index. The + ** aiMap[] array contains a mapping from the original LHS field order to + ** the field order that matches the RHS index. */ sqlite3ExprCachePush(pParse); - r2 = exprCodeVector(pParse, pLeft, &iDummy); - for(i=0; ix.pList; @@ -2732,7 +2751,7 @@ static void sqlite3ExprCodeIN( assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); if( destIfNull!=destIfFalse ){ regCkNull = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_BitAnd, r1, r1, regCkNull); + sqlite3VdbeAddOp3(v, OP_BitAnd, rLhs, rLhs, regCkNull); } for(ii=0; iinExpr; ii++){ r2 = sqlite3ExprCodeTemp(pParse, pList->a[ii].pExpr, ®ToFree); @@ -2740,14 +2759,14 @@ static void sqlite3ExprCodeIN( sqlite3VdbeAddOp3(v, OP_BitAnd, regCkNull, r2, regCkNull); } if( iinExpr-1 || destIfNull!=destIfFalse ){ - sqlite3VdbeAddOp4(v, OP_Eq, r1, labelOk, r2, + sqlite3VdbeAddOp4(v, OP_Eq, rLhs, labelOk, r2, (void*)pColl, P4_COLLSEQ); VdbeCoverageIf(v, iinExpr-1); VdbeCoverageIf(v, ii==pList->nExpr-1); sqlite3VdbeChangeP5(v, zAff[0]); }else{ assert( destIfNull==destIfFalse ); - sqlite3VdbeAddOp4(v, OP_Ne, r1, destIfFalse, r2, + sqlite3VdbeAddOp4(v, OP_Ne, rLhs, destIfFalse, r2, (void*)pColl, P4_COLLSEQ); VdbeCoverage(v); sqlite3VdbeChangeP5(v, zAff[0] | SQLITE_JUMPIFNULL); } @@ -2759,127 +2778,111 @@ static void sqlite3ExprCodeIN( } sqlite3VdbeResolveLabel(v, labelOk); sqlite3ReleaseTempReg(pParse, regCkNull); + goto sqlite3ExprCodeIN_finished; + } + + /* Step 2: Check to see if the LHS contains any NULL columns. If the + ** LHS does contain NULLs then the result must be either FALSE or NULL. + ** We will then skip the binary search of the RHS. + */ + if( destIfNull==destIfFalse ){ + destStep2 = destIfFalse; }else{ - - /* If any value on the LHS is NULL, the result of the IN(...) operator - ** must be either false or NULL. If these two are handled identically, - ** test the LHS for NULLs and jump directly to destIfNull if any are - ** found. - ** - ** Otherwise, if NULL and false are handled differently, and the - ** IN(...) operation is not a vector operation, and the LHS of the - ** operator is NULL, then the result is false if the index is - ** completely empty, or NULL otherwise. */ - if( destIfNull==destIfFalse ){ - for(i=0; ipLeft, i); - if( sqlite3ExprCanBeNull(p) ){ - sqlite3VdbeAddOp2(v, OP_IsNull, r1+aiMap[i], destIfNull); - VdbeCoverage(v); - } - } - }else if( nVector==1 && sqlite3ExprCanBeNull(pExpr->pLeft) ){ - int addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, r1); VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Rewind, pExpr->iTable, destIfFalse); + destStep2 = destStep6 = sqlite3VdbeMakeLabel(v); + } + for(i=0; ipLeft, i); + if( sqlite3ExprCanBeNull(p) ){ + sqlite3VdbeAddOp2(v, OP_IsNull, rLhs+i, destStep2); VdbeCoverage(v); - sqlite3VdbeGoto(v, destIfNull); - sqlite3VdbeJumpHere(v, addr1); - } - - if( eType==IN_INDEX_ROWID ){ - /* In this case, the RHS is the ROWID of table b-tree */ - sqlite3VdbeAddOp3(v, OP_SeekRowid, pExpr->iTable, destIfFalse, r1); - VdbeCoverage(v); - }else{ - /* In this case, the RHS is an index b-tree. Apply the comparison - ** affinities to each value on the LHS of the operator. */ - sqlite3VdbeAddOp4(v, OP_Affinity, r1, nVector, 0, zAff, nVector); - - if( nVector>1 && destIfNull!=destIfFalse ){ - int iIdx = pExpr->iTable; - int addrTop; - int addrNext; - int addrFound; - - /* Search the index for the key. */ - addrFound = sqlite3VdbeAddOp4Int(v, OP_Found, iIdx, 0, r1, nVector); - VdbeCoverage(v); - - /* At this point the specified key is not present in the index, - ** so the result of the IN(..) operator must be either NULL or - ** 0. The vdbe code generated below figures out which. */ - addrTop = 1+sqlite3VdbeAddOp2(v, OP_Rewind, iIdx, destIfFalse); - VdbeCoverage(v); - addrNext = sqlite3VdbeMakeLabel(v); - - for(i=0; ipLeft, i); - if( sqlite3ExprCanBeNull(p) ){ - sqlite3VdbeAddOp2(v, OP_IsNull, r1+aiMap[i], destIfNull); - VdbeCoverage(v); - } - } - - }else if( rRhsHasNull==0 ){ - /* This branch runs if it is known at compile time that the RHS - ** cannot contain NULL values. This happens as a result - ** of "NOT NULL" constraints in the database schema. - ** - ** Also run this branch if NULL is equivalent to FALSE - ** for this particular IN operator. */ - sqlite3VdbeAddOp4Int( - v, OP_NotFound, pExpr->iTable, destIfFalse, r1, nVector - ); - VdbeCoverage(v); - }else{ - /* In this branch, the RHS of the IN might contain a NULL and - ** the presence of a NULL on the RHS makes a difference in the - ** outcome. - */ - int addr1; - - /* First check to see if the LHS is contained in the RHS. If so, - ** then the answer is TRUE the presence of NULLs in the RHS does - ** not matter. If the LHS is not contained in the RHS, then the - ** answer is NULL if the RHS contains NULLs and the answer is - ** FALSE if the RHS is NULL-free. - */ - addr1 = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, r1, 1); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_IsNull, rRhsHasNull, destIfNull); - VdbeCoverage(v); - sqlite3VdbeGoto(v, destIfFalse); - sqlite3VdbeJumpHere(v, addr1); - } } } - if( r2!=r1 ) sqlite3ReleaseTempReg(pParse, r1); + + /* Step 3. The LHS is now known to be non-NULL. Do the binary search + ** of the RHS using the LHS as a probe. If found, the result is + ** true. + */ + if( eType==IN_INDEX_ROWID ){ + /* In this case, the RHS is the ROWID of table b-tree and so we also + ** know that the RHS is non-NULL. Hence, we combine steps 3 and 4 + ** into a single opcode. */ + sqlite3VdbeAddOp3(v, OP_SeekRowid, pExpr->iTable, destIfFalse, rLhs); + VdbeCoverage(v); + addrTruthOp = sqlite3VdbeAddOp0(v, OP_Goto); /* Return True */ + }else{ + sqlite3VdbeAddOp4(v, OP_Affinity, rLhs, nVector, 0, zAff, nVector); + if( destIfFalse==destIfNull ){ + /* Combine Step 3 and Step 5 into a single opcode */ + sqlite3VdbeAddOp4Int(v, OP_NotFound, pExpr->iTable, destIfFalse, + rLhs, nVector); VdbeCoverage(v); + goto sqlite3ExprCodeIN_finished; + } + /* Ordinary Step 3, for the case where FALSE and NULL are distinct */ + addrTruthOp = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, + rLhs, nVector); VdbeCoverage(v); + } + + /* Step 4. If the RHS is known to be non-NULL and we did not find + ** an match on the search above, then the result must be FALSE. + */ + if( rRhsHasNull && nVector==1 ){ + sqlite3VdbeAddOp2(v, OP_NotNull, rRhsHasNull, destIfFalse); + VdbeCoverage(v); + } + + /* Step 5. If we do not care about the difference between NULL and + ** FALSE, then just return false. + */ + if( destIfFalse==destIfNull ) sqlite3VdbeGoto(v, destIfFalse); + + /* Step 6: Loop through rows of the RHS. Compare each row to the LHS. + ** If any comparison is NULL, then the result is NULL. If all + ** comparisons are FALSE then the final result is FALSE. + ** + ** For a scalar LHS, it is sufficient to check just the first row + ** of the RHS. + */ + if( destStep6 ) sqlite3VdbeResolveLabel(v, destStep6); + addrTop = sqlite3VdbeAddOp2(v, OP_Rewind, pExpr->iTable, destIfFalse); + VdbeCoverage(v); + if( nVector>1 ){ + destNotNull = sqlite3VdbeMakeLabel(v); + }else{ + /* For nVector==1, combine steps 6 and 7 by immediately returning + ** FALSE if the first comparison is not NULL */ + destNotNull = destIfFalse; + } + for(i=0; iiTable, i, r3); + sqlite3VdbeAddOp4(v, OP_Ne, rLhs+i, destNotNull, r3, + (void*)pColl, P4_COLLSEQ); + VdbeCoverage(v); + sqlite3ReleaseTempReg(pParse, r3); + } + sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfNull); + if( nVector>1 ){ + sqlite3VdbeResolveLabel(v, destNotNull); + sqlite3VdbeAddOp2(v, OP_Next, pExpr->iTable, addrTop+1); + VdbeCoverage(v); + + /* Step 7: If we reach this point, we know that the result must + ** be false. */ + sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfFalse); + } + + /* Jumps here in order to return true. */ + sqlite3VdbeJumpHere(v, addrTruthOp); + +sqlite3ExprCodeIN_finished: + if( rLhs!=rLhsOrig ) sqlite3ReleaseTempReg(pParse, rLhs); sqlite3ExprCachePop(pParse); VdbeComment((v, "end IN expr")); -end_code_IN_op: +sqlite3ExprCodeIN_oom_error: sqlite3DbFree(pParse->db, aiMap); sqlite3DbFree(pParse->db, zAff); } diff --git a/src/in-operator.md b/src/in-operator.md index ff691864b0..e9ad2101aa 100644 --- a/src/in-operator.md +++ b/src/in-operator.md @@ -6,7 +6,7 @@ IN-Operator Implementation Notes An IN operator has one of the following formats: > - x IN (list) + x IN (y1,y2,y3,...,yN) x IN (subquery) The "x" is referred to as the LHS (left-hand side). The list or subquery @@ -14,9 +14,20 @@ on the right is called the RHS (right-hand side). If the RHS is a list it must be a non-empty list. But if the RHS is a subquery, it can be an empty set. -Both the LHS and RHS can be scalars or vectors. The two must match. -In other words, they must both be scalar or else they must both be -vectors of the same length. +The LHS can be a scalar (a single quantity) or a vector (a list of +two or or more values) or a subquery that returns one or more columns. +We use the term "vector" to mean an actually list of values or a +subquery that returns two or more columns. An isolated value or +a subquery that returns a single columns is called a scalar. + +The RHS can be a subquery that returns a single column, a subquery +that returns two or more columns, or a list of scalars. It is not +currently support for the RHS to be a list of vectors. + +The number of columns for LHS must match the number of columns for +the RHS. If the RHS is a list of values, then the LHS must be a +scalar. If the RHS is a subquery returning N columns, then the LHS +must be a vector of size N. NULL values can occur in either or both of the LHS and RHS. If the LHS contains only @@ -84,7 +95,7 @@ is the algorithm that is implemented in SQLite. 4. If the RHS is non-NULL then return FALSE. - 5. If we do not need to distingish between FALSE and NULL, + 5. If we do not need to distinguish between FALSE and NULL, then return FALSE. 6. For each row in the RHS, compare that row against the LHS and From a28f85b076be5ca68ef53b59291feab0153dc735 Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 26 Aug 2016 01:02:09 +0000 Subject: [PATCH 67/97] Remove an unreachable branch from sqlite3ExprAffinity() FossilOrigin-Name: 9d96f61481704e5ec399ee425f0ebb246902ecc5 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/expr.c | 3 ++- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 5713d84932..ceeeb74f30 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\srecent\schanges\sfrom\strunk. -D 2016-08-25T22:31:44.503 +C Remove\san\sunreachable\sbranch\sfrom\ssqlite3ExprAffinity() +D 2016-08-26T01:02:09.972 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 45f814e52d2279914bd7a9141d92a26cb71b1533 +F src/expr.c 2fe35bc5e93ac4c692b0b8c563ed6fc022247ed4 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1521,7 +1521,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 b56705ae6374db9db82613ef89faa1a1e6b00a18 37e6c54b1afc634844026b3fe874dd2d550c6558 -R 2a905e6de196a1adbfb5e3026f93f057 +P 5789aab8ef59ae1cdfdae123b078ee03da6e08ed +R 9ebda3b476f4e068ca78a071831449eb U drh -Z 71128db26f4e518aa8ba75a034627d25 +Z ef957217ed40f2345a57bc50f3f65b90 diff --git a/manifest.uuid b/manifest.uuid index d27311b40f..d803bb5d8a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5789aab8ef59ae1cdfdae123b078ee03da6e08ed \ No newline at end of file +9d96f61481704e5ec399ee425f0ebb246902ecc5 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index ab0349dcb7..982605d196 100644 --- a/src/expr.c +++ b/src/expr.c @@ -51,8 +51,9 @@ char sqlite3ExprAffinity(Expr *pExpr){ return sqlite3AffinityType(pExpr->u.zToken, 0); } #endif - if( (op==TK_AGG_COLUMN || op==TK_COLUMN) && pExpr->pTab!=0 ){ + if( op==TK_AGG_COLUMN || op==TK_COLUMN ){ int j = pExpr->iColumn; + assert( pExpr->pTab!=0 ); if( j<0 ) return SQLITE_AFF_INTEGER; assert( pExpr->pTab && jpTab->nCol ); return pExpr->pTab->aCol[j].affinity; From 64bcb8cfbb6898d4554de7d88c1ed42dca2c2709 Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 26 Aug 2016 03:42:57 +0000 Subject: [PATCH 68/97] Comment improvements. Put ALWAYS and NEVER macros on three unreachable branches. FossilOrigin-Name: 397617009e07004596476d6f5644fdf84c376f54 --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/expr.c | 6 +++--- src/where.c | 15 ++++++++++++--- src/whereexpr.c | 4 +++- 5 files changed, 27 insertions(+), 16 deletions(-) diff --git a/manifest b/manifest index ceeeb74f30..0ab7cba655 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\san\sunreachable\sbranch\sfrom\ssqlite3ExprAffinity() -D 2016-08-26T01:02:09.972 +C Comment\simprovements.\s\sPut\sALWAYS\sand\sNEVER\smacros\son\sthree\sunreachable\nbranches. +D 2016-08-26T03:42:57.323 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 2fe35bc5e93ac4c692b0b8c563ed6fc022247ed4 +F src/expr.c bca7140549a34c8a71a4cf0605666403af390477 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -465,10 +465,10 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c 02eeecc265f6ffd0597378f5d8ae9070b62a406a F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 -F src/where.c c7cdfd54f383090bb801cdd50d36de1a24684bb2 +F src/where.c 2f60a506364b22c48371b8b94089c4028a694a0a F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 F src/wherecode.c 5a5528c39be09593cada6ae465d7a0f48db0077f -F src/whereexpr.c aa54bf11adf6bc7e52f56281f436ab5fd421ce16 +F src/whereexpr.c 7f9ada866d48d15d09754ae819c1c40efe3b2aff F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -1521,7 +1521,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 5789aab8ef59ae1cdfdae123b078ee03da6e08ed -R 9ebda3b476f4e068ca78a071831449eb +P 9d96f61481704e5ec399ee425f0ebb246902ecc5 +R 0b547643c3844e6bc7fc81ff0189aeeb U drh -Z ef957217ed40f2345a57bc50f3f65b90 +Z e947ea7de839ffcabb8c6e9f9aa97cfb diff --git a/manifest.uuid b/manifest.uuid index d803bb5d8a..559f8ba56e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9d96f61481704e5ec399ee425f0ebb246902ecc5 \ No newline at end of file +397617009e07004596476d6f5644fdf84c376f54 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 982605d196..f0670799e8 100644 --- a/src/expr.c +++ b/src/expr.c @@ -2443,9 +2443,9 @@ int sqlite3CodeSubselect( ExprList *pEList = pSelect->pEList; assert( !isRowid ); - if( pEList->nExpr!=nVal ){ - sqlite3SubselectError(pParse, pEList->nExpr, nVal); - }else{ + /* If the LHS and RHS of the IN operator do not match, that + ** error will have been caught long before we reach this point. */ + if( ALWAYS(pEList->nExpr==nVal) ){ SelectDest dest; int i; sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable); diff --git a/src/where.c b/src/where.c index 30854ee422..74da4089a8 100644 --- a/src/where.c +++ b/src/where.c @@ -2224,7 +2224,11 @@ static void whereLoopOutputAdjust( ** this case is 3. */ int whereRangeVectorLen( - Parse *pParse, int iCur, Index *pIdx, int nEq, WhereTerm *pTerm + Parse *pParse, /* Parsing context */ + int iCur, /* Cursor open on pIdx */ + Index *pIdx, /* The index to be used for a inequality constraint */ + int nEq, /* Number of prior equality constraints on same index */ + WhereTerm *pTerm /* The vector inequality constraint */ ){ int nCmp = sqlite3ExprVectorSize(pTerm->pExpr->pLeft); int i; @@ -2261,7 +2265,8 @@ int whereRangeVectorLen( if( aff!=idxaff ) break; pColl = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs); - if( pColl==0 || sqlite3StrICmp(pColl->zName, pIdx->azColl[i+nEq]) ) break; + if( NEVER(pColl==0) ) break; + if( sqlite3StrICmp(pColl->zName, pIdx->azColl[i+nEq]) ) break; } return i; } @@ -3573,7 +3578,11 @@ static i8 wherePathSatisfiesOrderBy( isOrderDistinct = 0; } continue; - }else if( eOp & WO_IN ){ + }else if( ALWAYS(eOp & WO_IN) ){ + /* ALWAYS() justification: eOp is an equality operator due to the + ** ju.btree.nEq constraint above. Any equality other + ** than WO_IN is captured by the previous "if". So this one + ** always has to be WO_IN. */ Expr *pX = pLoop->aLTerm[j]->pExpr; for(i=j+1; iu.btree.nEq; i++){ if( pLoop->aLTerm[i]->pExpr==pX ){ diff --git a/src/whereexpr.c b/src/whereexpr.c index 823a2df235..00328989e7 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -846,7 +846,9 @@ static int exprMightBeIndexed( ** inequality constraint (>, <, >= or <=), perform the processing ** on the first element of the vector. */ assert( TK_GT+1==TK_LE && TK_GT+2==TK_LT && TK_GT+3==TK_GE ); - if( pExpr->op==TK_VECTOR && (op>=TK_GT && op<=TK_GE) ){ + assert( TK_ISop==TK_VECTOR && (op>=TK_GT && ALWAYS(op<=TK_GE)) ){ pExpr = pExpr->x.pList->a[0].pExpr; } From 0dfa4f6fcc8b442797dccda5c3409fec20c91b87 Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 26 Aug 2016 13:19:49 +0000 Subject: [PATCH 69/97] Allow ROWID values in indexed vector comparisons. FossilOrigin-Name: b0cc6be4eb81f21b11796e1f14d4412bf21dea6e --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/expr.c | 16 +++++++++------- src/sqliteInt.h | 1 + src/where.c | 4 ++-- 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/manifest b/manifest index 0ab7cba655..1bca06079a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Comment\simprovements.\s\sPut\sALWAYS\sand\sNEVER\smacros\son\sthree\sunreachable\nbranches. -D 2016-08-26T03:42:57.323 +C Allow\sROWID\svalues\sin\sindexed\svector\scomparisons. +D 2016-08-26T13:19:49.916 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c bca7140549a34c8a71a4cf0605666403af390477 +F src/expr.c 1f2ddbec8bf6de323cc0da3ff73e80b0ad21769d F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -390,7 +390,7 @@ F src/shell.c 79dda477be6c96eba6e952a934957ad36f87acc7 F src/sqlite.h.in 4a030e254e204570444b34bf7d40fb4a5416089e F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 8648034aa702469afb553231677306cc6492a1ae -F src/sqliteInt.h db8aa0c875b1a3cdb26e1cee47662c30a34882fd +F src/sqliteInt.h c9e010a79ab4ed7bdc910a24d8f08f3c6d5f822c F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247 F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1 F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9 @@ -465,7 +465,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c 02eeecc265f6ffd0597378f5d8ae9070b62a406a F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 -F src/where.c 2f60a506364b22c48371b8b94089c4028a694a0a +F src/where.c bad93f9bc5e62c38d2e0d2f572dd01d359c8d4cb F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 F src/wherecode.c 5a5528c39be09593cada6ae465d7a0f48db0077f F src/whereexpr.c 7f9ada866d48d15d09754ae819c1c40efe3b2aff @@ -1521,7 +1521,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 9d96f61481704e5ec399ee425f0ebb246902ecc5 -R 0b547643c3844e6bc7fc81ff0189aeeb +P 397617009e07004596476d6f5644fdf84c376f54 +R bebe23f740f385ed54a0790b48a77a67 U drh -Z e947ea7de839ffcabb8c6e9f9aa97cfb +Z b30339a1eb9e7f2b74488db2fab0ab4b diff --git a/manifest.uuid b/manifest.uuid index 559f8ba56e..f2f928f54a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -397617009e07004596476d6f5644fdf84c376f54 \ No newline at end of file +b0cc6be4eb81f21b11796e1f14d4412bf21dea6e \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index f0670799e8..a62538c556 100644 --- a/src/expr.c +++ b/src/expr.c @@ -18,6 +18,13 @@ static void exprCodeBetween(Parse*,Expr*,int,void(*)(Parse*,Expr*,int,int),int); static int exprCodeVector(Parse *pParse, Expr *p, int *piToFree); +/* +** Return the affinity character for a single column of a table. +*/ +char sqlite3TableColumnAffinity(Table *pTab, int iCol){ + assert( iColnCol ); + return iCol>=0 ? pTab->aCol[iCol].affinity : SQLITE_AFF_INTEGER; +} /* ** Return the 'affinity' of the expression pExpr if any. @@ -52,11 +59,7 @@ char sqlite3ExprAffinity(Expr *pExpr){ } #endif if( op==TK_AGG_COLUMN || op==TK_COLUMN ){ - int j = pExpr->iColumn; - assert( pExpr->pTab!=0 ); - if( j<0 ) return SQLITE_AFF_INTEGER; - assert( pExpr->pTab && jpTab->nCol ); - return pExpr->pTab->aCol[j].affinity; + return sqlite3TableColumnAffinity(pExpr->pTab, pExpr->iColumn); } return pExpr->affinity; } @@ -2173,7 +2176,7 @@ int sqlite3FindInIndex( for(i=0; ipLeft, i); int iCol = pEList->a[i].pExpr->iColumn; - char idxaff = pTab->aCol[iCol].affinity; /* RHS table affinity */ + char idxaff = sqlite3TableColumnAffinity(pTab,iCol); /* RHS table */ char cmpaff = sqlite3CompareAffinity(pLhs, idxaff); testcase( cmpaff==SQLITE_AFF_BLOB ); testcase( cmpaff==SQLITE_AFF_TEXT ); @@ -2208,7 +2211,6 @@ int sqlite3FindInIndex( CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs); int j; - assert( pReq || pParse->nErr ); if( pReq==0 ) break; for(j=0; jpTable->aCol[pLhs->iColumn].affinity; + idxaff = sqlite3TableColumnAffinity(pIdx->pTable, pLhs->iColumn); if( aff!=idxaff ) break; pColl = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs); - if( NEVER(pColl==0) ) break; + if( pColl==0 ) break; if( sqlite3StrICmp(pColl->zName, pIdx->azColl[i+nEq]) ) break; } return i; From b7ca2177d7ddcf5dbf06d1b210fa92750324249b Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 26 Aug 2016 17:54:46 +0000 Subject: [PATCH 70/97] Fix a problem with affinity changes and vector range comparisons. FossilOrigin-Name: b34413ac7e34369b4420e57b0132249dca68a7b0 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/wherecode.c | 50 +++++++++++++++++++++++++++++++++---------------- 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/manifest b/manifest index 1bca06079a..cb2c5f28b5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Allow\sROWID\svalues\sin\sindexed\svector\scomparisons. -D 2016-08-26T13:19:49.916 +C Fix\sa\sproblem\swith\saffinity\schanges\sand\svector\srange\scomparisons. +D 2016-08-26T17:54:46.350 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -467,7 +467,7 @@ F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 F src/where.c bad93f9bc5e62c38d2e0d2f572dd01d359c8d4cb F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 -F src/wherecode.c 5a5528c39be09593cada6ae465d7a0f48db0077f +F src/wherecode.c 71de4d2d36fa3afe6160e98334f1a717c226ee86 F src/whereexpr.c 7f9ada866d48d15d09754ae819c1c40efe3b2aff F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd @@ -1521,7 +1521,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 397617009e07004596476d6f5644fdf84c376f54 -R bebe23f740f385ed54a0790b48a77a67 -U drh -Z b30339a1eb9e7f2b74488db2fab0ab4b +P b0cc6be4eb81f21b11796e1f14d4412bf21dea6e +R 0bd9bba299be3b1c19235baf0c5a546b +U dan +Z 7b1a4a5a11db26063e955544de8c89fc diff --git a/manifest.uuid b/manifest.uuid index f2f928f54a..ab39b9341f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b0cc6be4eb81f21b11796e1f14d4412bf21dea6e \ No newline at end of file +b34413ac7e34369b4420e57b0132249dca68a7b0 \ No newline at end of file diff --git a/src/wherecode.c b/src/wherecode.c index 65079872e1..34894fe1bb 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -350,6 +350,32 @@ static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){ } } +/* +** Expression pRight, which is the RHS of a comparison operation, is +** either a vector of n elements or, if n==1, a scalar expression. +** Before the comparison operation, affinity zAff is to be applied +** to the pRight values. This function modifies characters within the +** affinity string to SQLITE_AFF_BLOB if either: +** +** * the comparison will be performed with no affinity, or +** * the affinity change in zAff is guaranteed not to change the value. +*/ +static void updateRangeAffinityStr( + Parse *pParse, /* Parse context */ + Expr *pRight, /* RHS of comparison */ + int n, /* Number of vector elements in comparison */ + char *zAff /* Affinity string to modify */ +){ + int i; + for(i=0; i=nEq ); - if( zStartAff ) cEndAff = zStartAff[nEq]; + if( zStartAff && nTop ){ + zEndAff = sqlite3DbStrDup(db, &zStartAff[nEq]); + } addrNxt = pLevel->addrNxt; testcase( pRangeStart && (pRangeStart->eOperator & WO_LE)!=0 ); @@ -1441,15 +1469,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( VdbeCoverage(v); } if( zStartAff ){ - if( sqlite3CompareAffinity(pRight, zStartAff[nEq])==SQLITE_AFF_BLOB){ - /* Since the comparison is to be performed with no conversions - ** applied to the operands, set the affinity to apply to pRight to - ** SQLITE_AFF_BLOB. */ - zStartAff[nEq] = SQLITE_AFF_BLOB; - } - if( sqlite3ExprNeedsNoAffinityChange(pRight, zStartAff[nEq]) ){ - zStartAff[nEq] = SQLITE_AFF_BLOB; - } + updateRangeAffinityStr(pParse, pRight, nBtm, &zStartAff[nEq]); } nConstraint += nBtm; testcase( pRangeStart->wtFlags & TERM_VIRTUAL ); @@ -1498,11 +1518,8 @@ Bitmask sqlite3WhereCodeOneLoopStart( sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt); VdbeCoverage(v); } - if( sqlite3CompareAffinity(pRight, cEndAff)!=SQLITE_AFF_BLOB - && !sqlite3ExprNeedsNoAffinityChange(pRight, cEndAff) - ){ - codeApplyAffinity(pParse, regBase+nEq, 1, &cEndAff); - } + updateRangeAffinityStr(pParse, pRight, nTop, zEndAff); + codeApplyAffinity(pParse, regBase+nEq, nTop, zEndAff); nConstraint += nTop; testcase( pRangeEnd->wtFlags & TERM_VIRTUAL ); @@ -1517,6 +1534,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( nConstraint++; } sqlite3DbFree(db, zStartAff); + sqlite3DbFree(db, zEndAff); /* Top of the loop body */ pLevel->p2 = sqlite3VdbeCurrentAddr(v); From 0c36fca004ee51ef626a3df0e74d191fbdc609fe Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 26 Aug 2016 18:17:08 +0000 Subject: [PATCH 71/97] Fix a post-OOM crash in updateRangeAffinityStr(). Add several ALWAYS() macros on unreachable branches. FossilOrigin-Name: 87d40195ae5cc2abd9bae45073a615db81263285 --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/where.c | 1 + src/wherecode.c | 12 ++++++++---- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/manifest b/manifest index cb2c5f28b5..59c570e2f8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sproblem\swith\saffinity\schanges\sand\svector\srange\scomparisons. -D 2016-08-26T17:54:46.350 +C Fix\sa\spost-OOM\scrash\sin\supdateRangeAffinityStr().\s\sAdd\sseveral\sALWAYS()\nmacros\son\sunreachable\sbranches. +D 2016-08-26T18:17:08.713 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -465,9 +465,9 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c 02eeecc265f6ffd0597378f5d8ae9070b62a406a F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 -F src/where.c bad93f9bc5e62c38d2e0d2f572dd01d359c8d4cb +F src/where.c 48d705e5196a0611a7be90698eade455ee238536 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 -F src/wherecode.c 71de4d2d36fa3afe6160e98334f1a717c226ee86 +F src/wherecode.c ee7b5353ff5f63548c206e3ecb4423ff0857d766 F src/whereexpr.c 7f9ada866d48d15d09754ae819c1c40efe3b2aff F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd @@ -1521,7 +1521,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 b0cc6be4eb81f21b11796e1f14d4412bf21dea6e -R 0bd9bba299be3b1c19235baf0c5a546b -U dan -Z 7b1a4a5a11db26063e955544de8c89fc +P b34413ac7e34369b4420e57b0132249dca68a7b0 +R e94f28a1c30d92b873747a9f1cb33b94 +U drh +Z a3a8a387b6b2455df60af55b1e2df964 diff --git a/manifest.uuid b/manifest.uuid index ab39b9341f..1ac92a55f8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b34413ac7e34369b4420e57b0132249dca68a7b0 \ No newline at end of file +87d40195ae5cc2abd9bae45073a615db81263285 \ No newline at end of file diff --git a/src/where.c b/src/where.c index 5c004ca0c2..f60d5be6da 100644 --- a/src/where.c +++ b/src/where.c @@ -2260,6 +2260,7 @@ int whereRangeVectorLen( break; } + testcase( pLhs->iColumn==XN_ROWID ); aff = sqlite3CompareAffinity(pRhs, sqlite3ExprAffinity(pLhs)); idxaff = sqlite3TableColumnAffinity(pIdx->pTable, pLhs->iColumn); if( aff!=idxaff ) break; diff --git a/src/wherecode.c b/src/wherecode.c index 34894fe1bb..16ffb89db1 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -294,7 +294,7 @@ void sqlite3WhereAddScanStatus( */ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){ int nLoop = 0; - while( pTerm + while( ALWAYS(pTerm!=0) && (pTerm->wtFlags & TERM_CODED)==0 && (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin)) && (pLevel->notReady & pTerm->prereqAll)==0 @@ -435,7 +435,7 @@ static int codeEqualityTerm( } } for(i=iEq;inLTerm; i++){ - if( pLoop->aLTerm[i] && pLoop->aLTerm[i]->pExpr==pX ) nEq++; + if( ALWAYS(pLoop->aLTerm[i]) && pLoop->aLTerm[i]->pExpr==pX ) nEq++; } if( nEq>1 ){ @@ -1518,8 +1518,12 @@ Bitmask sqlite3WhereCodeOneLoopStart( sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt); VdbeCoverage(v); } - updateRangeAffinityStr(pParse, pRight, nTop, zEndAff); - codeApplyAffinity(pParse, regBase+nEq, nTop, zEndAff); + if( zEndAff ){ + updateRangeAffinityStr(pParse, pRight, nTop, zEndAff); + codeApplyAffinity(pParse, regBase+nEq, nTop, zEndAff); + }else{ + assert( pParse->db->mallocFailed ); + } nConstraint += nTop; testcase( pRangeEnd->wtFlags & TERM_VIRTUAL ); From 6fc8f364908faf289d19ebd349c0a4c2995bcd95 Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 26 Aug 2016 19:31:29 +0000 Subject: [PATCH 72/97] Enhance sqlite3FindInIndex() so that it is able to make use of the primary keys at the end of an index. FossilOrigin-Name: 4b589fbfcc4265902de0f552961d2df497a184da --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/expr.c | 24 ++++++++++++++---------- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/manifest b/manifest index 59c570e2f8..55a6113c35 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\spost-OOM\scrash\sin\supdateRangeAffinityStr().\s\sAdd\sseveral\sALWAYS()\nmacros\son\sunreachable\sbranches. -D 2016-08-26T18:17:08.713 +C Enhance\ssqlite3FindInIndex()\sso\sthat\sit\sis\sable\sto\smake\suse\sof\sthe\nprimary\skeys\sat\sthe\send\sof\san\sindex. +D 2016-08-26T19:31:29.011 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 1f2ddbec8bf6de323cc0da3ff73e80b0ad21769d +F src/expr.c 192dac6da7ff380fde35861d3f9d40bab39fab2c F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1521,7 +1521,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 b34413ac7e34369b4420e57b0132249dca68a7b0 -R e94f28a1c30d92b873747a9f1cb33b94 +P 87d40195ae5cc2abd9bae45073a615db81263285 +R c0ed51d5af8253a23448fcfb693a50e8 U drh -Z a3a8a387b6b2455df60af55b1e2df964 +Z 3b9104524847b85ed5a08921e98408a8 diff --git a/manifest.uuid b/manifest.uuid index 1ac92a55f8..3562568838 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -87d40195ae5cc2abd9bae45073a615db81263285 \ No newline at end of file +4b589fbfcc4265902de0f552961d2df497a184da \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index a62538c556..3a5075719e 100644 --- a/src/expr.c +++ b/src/expr.c @@ -2099,11 +2099,11 @@ static int sqlite3InRhsIsConstant(Expr *pIn){ */ #ifndef SQLITE_OMIT_SUBQUERY int sqlite3FindInIndex( - Parse *pParse, - Expr *pX, - u32 inFlags, - int *prRhsHasNull, - int *aiMap + Parse *pParse, /* Parsing context */ + Expr *pX, /* The right-hand side (RHS) of the IN operator */ + u32 inFlags, /* IN_INDEX_LOOP, _MEMBERSHIP, and/or _NOOP_OK */ + int *prRhsHasNull, /* Register holding NULL status. See notes */ + int *aiMap /* Mapping from Index fields to RHS fields */ ){ Select *p; /* SELECT to the right of IN operator */ int eType = 0; /* Type of RHS table. IN_INDEX_* */ @@ -2200,9 +2200,13 @@ int sqlite3FindInIndex( ** to this collation sequence. */ for(pIdx=pTab->pIndex; pIdx && eType==0 && affinity_ok; pIdx=pIdx->pNext){ - if( pIdx->nKeyColnKeyCol!=nExpr || !IsUniqueIndex(pIdx)) ){ - continue; + if( pIdx->nColumnnKeyCol>nExpr + ||(pIdx->nColumn>nExpr && !IsUniqueIndex(pIdx)) + ){ + continue; + } } for(i=0; iiColumn==XN_ROWID || pParse->nErr ); for(j=0; jaiColumn[j]!=pRhs->iColumn ) continue; assert( pIdx->azColl[j] ); + if( pReq==0 ) continue; if( sqlite3StrICmp(pReq->zName, pIdx->azColl[j])!=0 ) continue; break; } From 4b4f51148ae48c263027f2c269ff53e6bea1b8d9 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 26 Aug 2016 19:47:30 +0000 Subject: [PATCH 73/97] Add test cases to rowvalue2.test. FossilOrigin-Name: 078bb69e99891ba3b76a39ac974990714c43908f --- manifest | 14 +++++++------- manifest.uuid | 2 +- test/rowvalue2.test | 28 ++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 55a6113c35..8bf9f54ae7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Enhance\ssqlite3FindInIndex()\sso\sthat\sit\sis\sable\sto\smake\suse\sof\sthe\nprimary\skeys\sat\sthe\send\sof\san\sindex. -D 2016-08-26T19:31:29.011 +C Add\stest\scases\sto\srowvalue2.test. +D 2016-08-26T19:47:30.838 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -1021,7 +1021,7 @@ F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81 F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d F test/rowvalue.test 7d8482dde9023973615eaaca65647f33d70c1f01 -F test/rowvalue2.test 875068299fd4dd50ef0a47786462c8e1f4065f9a +F test/rowvalue2.test 060d238b7e5639a7c5630cb5e63e311b44efef2b F test/rowvalue3.test 01399b7bf150b0d41abce76c18072da777c2500c F test/rowvalue4.test 9b40c9be9bdde30fc66cddbfdf6a5af37de4ccac F test/rowvalue5.test a440d490c8c0bf606034c09d5c6bbf7840b98f95 @@ -1521,7 +1521,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 87d40195ae5cc2abd9bae45073a615db81263285 -R c0ed51d5af8253a23448fcfb693a50e8 -U drh -Z 3b9104524847b85ed5a08921e98408a8 +P 4b589fbfcc4265902de0f552961d2df497a184da +R 2639cfd9d35718211068678a56d30d2c +U dan +Z 1bf9a10eeccbff62da890e6c842f5df6 diff --git a/manifest.uuid b/manifest.uuid index 3562568838..54b588e6c5 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4b589fbfcc4265902de0f552961d2df497a184da \ No newline at end of file +078bb69e99891ba3b76a39ac974990714c43908f \ No newline at end of file diff --git a/test/rowvalue2.test b/test/rowvalue2.test index b385b23ce2..1502e31a54 100644 --- a/test/rowvalue2.test +++ b/test/rowvalue2.test @@ -247,5 +247,33 @@ foreach {tn idx} { } } +do_execsql_test 5.0 { + CREATE TABLE r1(a TEXT, iB TEXT); + CREATE TABLE r2(x TEXT, zY INTEGER); + CREATE INDEX r1ab ON r1(a, iB); + + INSERT INTO r1 VALUES(35, 35); + INSERT INTO r2 VALUES(35, 36); + INSERT INTO r2 VALUES(35, 4); + INSERT INTO r2 VALUES(35, 35); +} {} + +foreach {tn lhs rhs} { + 1 {x +zY} {a iB} + 2 {x zY} {a iB} + 3 {x zY} {a +iB} + 4 {+x zY} {a iB} + 5 {x zY} {+a iB} +} { + foreach op { IS == < <= > >= } { + set e1 [make_expr1 $lhs $rhs $op] + set e2 [make_expr2 $lhs $rhs $op] + do_execsql_test 5.$tn.$op \ + "SELECT * FROM r1, r2 WHERE $e2 ORDER BY iB" [db eval \ + "SELECT * FROM r1, r2 WHERE $e1 ORDER BY iB" + ] + } +} + finish_test From 363fb95bc38825f49317532fe90241a172e1cf45 Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 26 Aug 2016 19:54:12 +0000 Subject: [PATCH 74/97] Add an EXPLAIN QUERY PLAN line for when a index is used to implement an IN operator. FossilOrigin-Name: 171aa833a2e1650c3d9cf9bd6438ae46f6c35871 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/expr.c | 5 +++++ 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 8bf9f54ae7..e112f421dc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\stest\scases\sto\srowvalue2.test. -D 2016-08-26T19:47:30.838 +C Add\san\sEXPLAIN\sQUERY\sPLAN\sline\sfor\swhen\sa\sindex\sis\sused\sto\simplement\nan\sIN\soperator. +D 2016-08-26T19:54:12.433 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 192dac6da7ff380fde35861d3f9d40bab39fab2c +F src/expr.c 935366a02ad0c33e7cd19ff382ef9bcdfa1aa7a9 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1521,7 +1521,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 4b589fbfcc4265902de0f552961d2df497a184da -R 2639cfd9d35718211068678a56d30d2c -U dan -Z 1bf9a10eeccbff62da890e6c842f5df6 +P 078bb69e99891ba3b76a39ac974990714c43908f +R 155df167db2a04d38d0a374ab683c0e3 +U drh +Z d9ed9721b1e3c6348d8b59f53fc4268d diff --git a/manifest.uuid b/manifest.uuid index 54b588e6c5..5d43906475 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -078bb69e99891ba3b76a39ac974990714c43908f \ No newline at end of file +171aa833a2e1650c3d9cf9bd6438ae46f6c35871 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 3a5075719e..08b72fed80 100644 --- a/src/expr.c +++ b/src/expr.c @@ -2229,6 +2229,11 @@ int sqlite3FindInIndex( if( i==nExpr ){ int iAddr = sqlite3CodeOnce(pParse); VdbeCoverage(v); +#ifndef SQLITE_OMIT_EXPLAIN + sqlite3VdbeAddOp4(v, OP_Explain, 0, 0, 0, + sqlite3MPrintf(db, "USING INDEX %s FOR IN-OPERATOR", pIdx->zName), + P4_DYNAMIC); +#endif sqlite3VdbeAddOp3(v, OP_OpenRead, iTab, pIdx->tnum, iDb); sqlite3VdbeSetP4KeyInfo(pParse, pIdx); VdbeComment((v, "%s", pIdx->zName)); From a84a283d180a4ffa54b1fb615572709dfe0f7b25 Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 26 Aug 2016 21:15:35 +0000 Subject: [PATCH 75/97] Fix the sqlite3FindInIndex() to ensure that it always uses a prefix of the index and uses no repeated columns. Enhanced comments. FossilOrigin-Name: b9fc89e432fbe4e5b41959a42797641907e075e3 --- manifest | 12 ++--- manifest.uuid | 2 +- src/expr.c | 137 ++++++++++++++++++++++++++------------------------ 3 files changed, 79 insertions(+), 72 deletions(-) diff --git a/manifest b/manifest index e112f421dc..05accaa53d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\san\sEXPLAIN\sQUERY\sPLAN\sline\sfor\swhen\sa\sindex\sis\sused\sto\simplement\nan\sIN\soperator. -D 2016-08-26T19:54:12.433 +C Fix\sthe\ssqlite3FindInIndex()\sto\sensure\sthat\sit\salways\suses\sa\sprefix\sof\nthe\sindex\sand\suses\sno\srepeated\scolumns.\s\sEnhanced\scomments. +D 2016-08-26T21:15:35.199 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 935366a02ad0c33e7cd19ff382ef9bcdfa1aa7a9 +F src/expr.c 4c80148f83127862f69ff509fb0aa261396df24e F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1521,7 +1521,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 078bb69e99891ba3b76a39ac974990714c43908f -R 155df167db2a04d38d0a374ab683c0e3 +P 171aa833a2e1650c3d9cf9bd6438ae46f6c35871 +R 94dfe8764b4b5e69457ee17a8ecdc781 U drh -Z d9ed9721b1e3c6348d8b59f53fc4268d +Z ef81f77fdd077e1b395585c253a1bf6f diff --git a/manifest.uuid b/manifest.uuid index 5d43906475..e5a0ab8485 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -171aa833a2e1650c3d9cf9bd6438ae46f6c35871 \ No newline at end of file +b9fc89e432fbe4e5b41959a42797641907e075e3 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 08b72fed80..8ede47731f 100644 --- a/src/expr.c +++ b/src/expr.c @@ -1960,7 +1960,6 @@ static Select *isCandidateForInOpt(Expr *pX){ if( IsVirtual(pTab) ) return 0; /* FROM clause not a virtual table */ pEList = p->pEList; assert( pEList!=0 ); - /* All SELECT results must be columns. */ for(i=0; inExpr; i++){ Expr *pRes = pEList->a[i].pExpr; @@ -2150,11 +2149,7 @@ int sqlite3FindInIndex( sqlite3CodeVerifySchema(pParse, iDb); sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); - /* This function is only called from two places. In both cases the vdbe - ** has already been allocated. So assume sqlite3GetVdbe() is always - ** successful here. - */ - assert(v); + assert(v); /* sqlite3GetVdbe() has always been previously called */ if( nExpr==1 && pEList->a[0].pExpr->iColumn<0 ){ /* The "x IN (SELECT rowid FROM table)" case */ int iAddr = sqlite3CodeOnce(pParse); @@ -2195,67 +2190,79 @@ int sqlite3FindInIndex( } } - /* The collation sequence used by the comparison. If an index is to - ** be used in place of a temp-table, it must be ordered according - ** to this collation sequence. */ - - for(pIdx=pTab->pIndex; pIdx && eType==0 && affinity_ok; pIdx=pIdx->pNext){ - if( pIdx->nColumnnKeyCol>nExpr - ||(pIdx->nColumn>nExpr && !IsUniqueIndex(pIdx)) - ){ - continue; - } - } - - for(i=0; ipLeft, i); - Expr *pRhs = pEList->a[i].pExpr; - CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs); - int j; - - assert( pReq!=0 || pRhs->iColumn==XN_ROWID || pParse->nErr ); - for(j=0; jaiColumn[j]!=pRhs->iColumn ) continue; - assert( pIdx->azColl[j] ); - if( pReq==0 ) continue; - if( sqlite3StrICmp(pReq->zName, pIdx->azColl[j])!=0 ) continue; - break; - } - if( j==nExpr ) break; - if( aiMap ) aiMap[i] = j; - } - - if( i==nExpr ){ - int iAddr = sqlite3CodeOnce(pParse); VdbeCoverage(v); -#ifndef SQLITE_OMIT_EXPLAIN - sqlite3VdbeAddOp4(v, OP_Explain, 0, 0, 0, - sqlite3MPrintf(db, "USING INDEX %s FOR IN-OPERATOR", pIdx->zName), - P4_DYNAMIC); -#endif - sqlite3VdbeAddOp3(v, OP_OpenRead, iTab, pIdx->tnum, iDb); - sqlite3VdbeSetP4KeyInfo(pParse, pIdx); - VdbeComment((v, "%s", pIdx->zName)); - assert( IN_INDEX_INDEX_DESC == IN_INDEX_INDEX_ASC+1 ); - eType = IN_INDEX_INDEX_ASC + pIdx->aSortOrder[0]; - - if( prRhsHasNull ){ - *prRhsHasNull = ++pParse->nMem; -#ifdef SQLITE_ENABLE_COLUMN_USED_MASK - i64 mask = (1<pIndex; pIdx && eType==0; pIdx=pIdx->pNext){ + Bitmask colUsed; /* Columns of the index used */ + Bitmask mCol; /* Mask for the current column */ + if( pIdx->nColumnnColumn==BMS-2 ); + testcase( pIdx->nColumn==BMS-1 ); + if( pIdx->nColumn>=BMS-1 ) continue; + if( mustBeUnique ){ + if( pIdx->nKeyCol>nExpr + ||(pIdx->nColumn>nExpr && !IsUniqueIndex(pIdx)) + ){ + continue; /* This index is not unique over the IN RHS columns */ } } - sqlite3VdbeJumpHere(v, iAddr); - } - } - } - } + + colUsed = 0; /* Columns of index used so far */ + for(i=0; ipLeft, i); + Expr *pRhs = pEList->a[i].pExpr; + CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs); + int j; + + assert( pReq!=0 || pRhs->iColumn==XN_ROWID || pParse->nErr ); + for(j=0; jaiColumn[j]!=pRhs->iColumn ) continue; + assert( pIdx->azColl[j] ); + if( pReq==0 ) continue; + if( sqlite3StrICmp(pReq->zName, pIdx->azColl[j])!=0 ) continue; + break; + } + if( j==nExpr ) break; + mCol = MASKBIT(j); + if( mCol & colUsed ) break; /* Each column used only once */ + colUsed |= mCol; + if( aiMap ) aiMap[i] = j; + } + + assert( i==nExpr || colUsed!=(MASKBIT(nExpr)-1) ); + if( colUsed==(MASKBIT(nExpr)-1) ){ + /* If we reach this point, that means the index pIdx is usable */ + int iAddr = sqlite3CodeOnce(pParse); VdbeCoverage(v); + #ifndef SQLITE_OMIT_EXPLAIN + sqlite3VdbeAddOp4(v, OP_Explain, 0, 0, 0, + sqlite3MPrintf(db, "USING INDEX %s FOR IN-OPERATOR",pIdx->zName), + P4_DYNAMIC); + #endif + sqlite3VdbeAddOp3(v, OP_OpenRead, iTab, pIdx->tnum, iDb); + sqlite3VdbeSetP4KeyInfo(pParse, pIdx); + VdbeComment((v, "%s", pIdx->zName)); + assert( IN_INDEX_INDEX_DESC == IN_INDEX_INDEX_ASC+1 ); + eType = IN_INDEX_INDEX_ASC + pIdx->aSortOrder[0]; + + if( prRhsHasNull ){ + *prRhsHasNull = ++pParse->nMem; + #ifdef SQLITE_ENABLE_COLUMN_USED_MASK + i64 mask = (1< Date: Fri, 26 Aug 2016 22:09:01 +0000 Subject: [PATCH 76/97] Fix a minor problem in sqlite3FindInIndex() related to rowids being used as part of the index. FossilOrigin-Name: 829f802be7d4647dd815b739bbc9e1d1ac6a224d --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/expr.c | 5 +++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 05accaa53d..f0a6286386 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sthe\ssqlite3FindInIndex()\sto\sensure\sthat\sit\salways\suses\sa\sprefix\sof\nthe\sindex\sand\suses\sno\srepeated\scolumns.\s\sEnhanced\scomments. -D 2016-08-26T21:15:35.199 +C Fix\sa\sminor\sproblem\sin\ssqlite3FindInIndex()\srelated\sto\srowids\sbeing\sused\nas\spart\sof\sthe\sindex. +D 2016-08-26T22:09:01.711 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 4c80148f83127862f69ff509fb0aa261396df24e +F src/expr.c 6ac2955b85fb32934b5505904da3d4be39d5d877 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1521,7 +1521,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 171aa833a2e1650c3d9cf9bd6438ae46f6c35871 -R 94dfe8764b4b5e69457ee17a8ecdc781 +P b9fc89e432fbe4e5b41959a42797641907e075e3 +R 4de3e6777e68cb197039afe2926927d7 U drh -Z ef81f77fdd077e1b395585c253a1bf6f +Z 2cb9a82dfc67b60efaaa214dda6ee177 diff --git a/manifest.uuid b/manifest.uuid index e5a0ab8485..407491af35 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b9fc89e432fbe4e5b41959a42797641907e075e3 \ No newline at end of file +829f802be7d4647dd815b739bbc9e1d1ac6a224d \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 8ede47731f..8e74fff61b 100644 --- a/src/expr.c +++ b/src/expr.c @@ -2220,8 +2220,9 @@ int sqlite3FindInIndex( for(j=0; jaiColumn[j]!=pRhs->iColumn ) continue; assert( pIdx->azColl[j] ); - if( pReq==0 ) continue; - if( sqlite3StrICmp(pReq->zName, pIdx->azColl[j])!=0 ) continue; + if( pReq!=0 && sqlite3StrICmp(pReq->zName, pIdx->azColl[j])!=0 ){ + continue; + } break; } if( j==nExpr ) break; From 88e665fd0bf40251472a6349faf9e6fbf04b1511 Mon Sep 17 00:00:00 2001 From: drh Date: Sat, 27 Aug 2016 01:41:53 +0000 Subject: [PATCH 77/97] Fix the "Synopsis" on the OP_Lt, OP_Le, OP_Gt, and OP_Ge opcodes, which has been backwards for time out of mind. FossilOrigin-Name: f51248dcfa22e989dacde41021e96a65f12c5ca8 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/vdbe.c | 12 ++++++------ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/manifest b/manifest index f0a6286386..ec178d85bc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sminor\sproblem\sin\ssqlite3FindInIndex()\srelated\sto\srowids\sbeing\sused\nas\spart\sof\sthe\sindex. -D 2016-08-26T22:09:01.711 +C Fix\sthe\s"Synopsis"\son\sthe\sOP_Lt,\sOP_Le,\sOP_Gt,\sand\sOP_Ge\sopcodes,\swhich\nhas\sbeen\sbackwards\sfor\stime\sout\sof\smind. +D 2016-08-27T01:41:53.133 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -451,7 +451,7 @@ F src/update.c 8179e699dbd45b92934fd02d3d8e3732e8da8802 F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c F src/util.c 810ec3f22e2d1b62e66c30fe3621ebdedd23584d F src/vacuum.c 913970b9d86dd6c2b8063ef1af421880f1464ec3 -F src/vdbe.c 68d56c11d5c5e704dde6d2d8748750a2f6352a09 +F src/vdbe.c 4be39068ea1412a21a2566c71007c87b67928d67 F src/vdbe.h 67bc551f7faf04c33493892e4b378aada823ed10 F src/vdbeInt.h c59381049af5c7751a83456c39b80d1a6fde1f9d F src/vdbeapi.c a32d61b7dd05e6890d8fd44d2805f55e2f5ba9f3 @@ -1521,7 +1521,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 b9fc89e432fbe4e5b41959a42797641907e075e3 -R 4de3e6777e68cb197039afe2926927d7 +P 829f802be7d4647dd815b739bbc9e1d1ac6a224d +R 90bb9a2e80849dad7e38b09045c0c98d U drh -Z 2cb9a82dfc67b60efaaa214dda6ee177 +Z ce5881477602b96772cc87ff99cdf3de diff --git a/manifest.uuid b/manifest.uuid index 407491af35..cc0a3022ca 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -829f802be7d4647dd815b739bbc9e1d1ac6a224d \ No newline at end of file +f51248dcfa22e989dacde41021e96a65f12c5ca8 \ No newline at end of file diff --git a/src/vdbe.c b/src/vdbe.c index 24828ace3b..b640e0b4f5 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -1881,7 +1881,7 @@ case OP_Cast: { /* in1 */ #endif /* SQLITE_OMIT_CAST */ /* Opcode: Eq P1 P2 P3 P4 P5 -** Synopsis: IF r[P1]==r[P3] +** Synopsis: IF r[P3]==r[P1] ** ** Compare the values in register P1 and P3. If reg(P3)==reg(P1) then ** jump to address P2. Or if the SQLITE_STOREP2 flag is set in P5, then @@ -1915,7 +1915,7 @@ case OP_Cast: { /* in1 */ ** content of r[P2] is only set to 1 (true) if it was not previously NULL. */ /* Opcode: Ne P1 P2 P3 P4 P5 -** Synopsis: IF r[P1]!=r[P3] +** Synopsis: IF r[P3]!=r[P1] ** ** This works just like the Eq opcode except that the jump is taken if ** the operands in registers P1 and P3 are not equal. See the Eq opcode for @@ -1925,7 +1925,7 @@ case OP_Cast: { /* in1 */ ** content of r[P2] is only set to 0 (false) if it was not previously NULL. */ /* Opcode: Lt P1 P2 P3 P4 P5 -** Synopsis: IF r[P1]r[P3] +** Synopsis: IF r[P3]>r[P1] ** ** This works just like the Lt opcode except that the jump is taken if ** the content of register P3 is greater than the content of ** register P1. See the Lt opcode for additional information. */ /* Opcode: Ge P1 P2 P3 P4 P5 -** Synopsis: IF r[P1]>=r[P3] +** Synopsis: IF r[P3]>=r[P1] ** ** This works just like the Lt opcode except that the jump is taken if ** the content of register P3 is greater than or equal to the content of From 4910a76d4501b11fdc639c01e531d5100401b444 Mon Sep 17 00:00:00 2001 From: drh Date: Sat, 3 Sep 2016 01:46:15 +0000 Subject: [PATCH 78/97] Performance optimizations. FossilOrigin-Name: f1d06c49ba0d814dc7ffb538aac3f4e6251fd8f0 --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/expr.c | 2 +- src/select.c | 2 +- src/vdbe.c | 37 ++++++++++++++++++++----------------- 5 files changed, 31 insertions(+), 28 deletions(-) diff --git a/manifest b/manifest index 0d346f639b..e6005476d0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\srecent\schanges\sfrom\strunk. -D 2016-09-02T23:56:32.342 +C Performance\soptimizations. +D 2016-09-03T01:46:15.276 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 6ac2955b85fb32934b5505904da3d4be39d5d877 +F src/expr.c 7e0470e2b82af8d6544132d81da18990d9b9c09d F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -385,7 +385,7 @@ F src/printf.c a5f0ca08ddede803c241266abb46356ec748ded1 F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 F src/resolve.c d67b9a5cc33339256e2088c5a722745fc2ff5219 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac -F src/select.c cf90abce32567023c940ecabf5707c4714c61c92 +F src/select.c 38216d0b2d42a0f475abf86a84c3499e6421ba29 F src/shell.c 79dda477be6c96eba6e952a934957ad36f87acc7 F src/sqlite.h.in 4a030e254e204570444b34bf7d40fb4a5416089e F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 @@ -451,7 +451,7 @@ F src/update.c 8179e699dbd45b92934fd02d3d8e3732e8da8802 F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c F src/util.c 810ec3f22e2d1b62e66c30fe3621ebdedd23584d F src/vacuum.c 913970b9d86dd6c2b8063ef1af421880f1464ec3 -F src/vdbe.c 4be39068ea1412a21a2566c71007c87b67928d67 +F src/vdbe.c d8fb59a3ef10636df2447f55c1e82be7b8ad1604 F src/vdbe.h 67bc551f7faf04c33493892e4b378aada823ed10 F src/vdbeInt.h c59381049af5c7751a83456c39b80d1a6fde1f9d F src/vdbeapi.c a32d61b7dd05e6890d8fd44d2805f55e2f5ba9f3 @@ -1521,7 +1521,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 082fd5f8ac227dbb983da0a772485268af40a484 9bdf7ca1b317fe0ba7efea38fb395bf6130ac89a -R 5ef4ae610db43c409abf4a916ff574b9 +P c7271fbde1aebb15daaedb7f1fa75fe410fd46f6 +R 617c8c8681735b3b6f061557d07c266d U drh -Z 8a1867c9ed1b748f87443a20db67a6e2 +Z dbe31dcf0712b654c7c10b1471579b28 diff --git a/manifest.uuid b/manifest.uuid index f54ff68f8f..eb5bf868a4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c7271fbde1aebb15daaedb7f1fa75fe410fd46f6 \ No newline at end of file +f1d06c49ba0d814dc7ffb538aac3f4e6251fd8f0 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 8e74fff61b..31df95c057 100644 --- a/src/expr.c +++ b/src/expr.c @@ -1021,7 +1021,7 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){ if( !ExprHasProperty(p, EP_TokenOnly) ){ /* The Expr.x union is never used at the same time as Expr.pRight */ assert( p->x.pList==0 || p->pRight==0 ); - if( p->op!=TK_SELECT_COLUMN ) sqlite3ExprDelete(db, p->pLeft); + if( p->pLeft && p->op!=TK_SELECT_COLUMN ) sqlite3ExprDeleteNN(db, p->pLeft); sqlite3ExprDelete(db, p->pRight); if( ExprHasProperty(p, EP_MemToken) ) sqlite3DbFree(db, p->u.zToken); if( ExprHasProperty(p, EP_xIsSelect) ){ diff --git a/src/select.c b/src/select.c index 6e51494f77..809285ea89 100644 --- a/src/select.c +++ b/src/select.c @@ -1796,7 +1796,7 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){ */ static SQLITE_NOINLINE Vdbe *allocVdbe(Parse *pParse){ Vdbe *v = pParse->pVdbe = sqlite3VdbeCreate(pParse); - if( v ) sqlite3VdbeAddOp0(v, OP_Init); + if( v ) sqlite3VdbeAddOp2(v, OP_Init, 0, 1); if( pParse->pToplevel==0 && OptimizationEnabled(pParse->db,SQLITE_FactorOutConst) ){ diff --git a/src/vdbe.c b/src/vdbe.c index b640e0b4f5..443ecbce1d 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -1980,7 +1980,7 @@ case OP_Lt: /* same as TK_LT, jump, in1, in3 */ case OP_Le: /* same as TK_LE, jump, in1, in3 */ case OP_Gt: /* same as TK_GT, jump, in1, in3 */ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ - int res; /* Result of the comparison of pIn1 against pIn3 */ + int res, res2; /* Result of the comparison of pIn1 against pIn3 */ char affinity; /* Affinity to use for comparison */ u16 flags1; /* Copy of initial value of pIn1->flags */ u16 flags3; /* Copy of initial value of pIn3->flags */ @@ -2003,18 +2003,18 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ && (flags3&MEM_Null)!=0 && (flags3&MEM_Cleared)==0 ){ - iCompare = 0; /* Operands are equal */ + res = 0; /* Operands are equal */ }else{ - iCompare = 1; /* Operands are not equal */ + res = 1; /* Operands are not equal */ } }else{ /* SQLITE_NULLEQ is clear and at least one operand is NULL, ** then the result is always NULL. ** The jump is taken if the SQLITE_JUMPIFNULL bit is set. */ - iCompare = 1; /* Operands are not equal */ if( pOp->p5 & SQLITE_STOREP2 ){ pOut = &aMem[pOp->p2]; + iCompare = 1; /* Operands are not equal */ memAboutToChange(p, pOut); MemSetTypeFlag(pOut, MEM_Null); REGISTER_TRACE(pOp->p2, pOut); @@ -2065,15 +2065,15 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ sqlite3VdbeMemExpandBlob(pIn3); flags3 &= ~MEM_Zero; } - iCompare = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); + res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); } switch( pOp->opcode ){ - case OP_Eq: res = iCompare==0; break; - case OP_Ne: res = iCompare!=0; break; - case OP_Lt: res = iCompare<0; break; - case OP_Le: res = iCompare<=0; break; - case OP_Gt: res = iCompare>0; break; - default: res = iCompare>=0; break; + case OP_Eq: res2 = res==0; break; + case OP_Ne: res2 = res; break; + case OP_Lt: res2 = res<0; break; + case OP_Le: res2 = res<=0; break; + case OP_Gt: res2 = res>0; break; + default: res2 = res>=0; break; } /* Undo any changes made by applyAffinity() to the input registers. */ @@ -2084,20 +2084,22 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ if( pOp->p5 & SQLITE_STOREP2 ){ pOut = &aMem[pOp->p2]; + iCompare = res; + res2 = res2!=0; /* For this path res2 must be exactly 0 or 1 */ if( (pOp->p5 & SQLITE_KEEPNULL)!=0 && (pOut->flags & MEM_Null)!=0 ){ /* The KEEPNULL flag prevents OP_Eq from overwriting a NULL with 1 ** and prevents OP_Ne from overwriting NULL with 0. */ assert( pOp->opcode==OP_Ne || pOp->opcode==OP_Eq ); - assert( res==0 || res==1 ); - if( (pOp->opcode==OP_Eq)==res ) break; + assert( res2==0 || res2==1 ); + if( (pOp->opcode==OP_Eq)==res2 ) break; } memAboutToChange(p, pOut); MemSetTypeFlag(pOut, MEM_Int); - pOut->u.i = res; + pOut->u.i = res2; REGISTER_TRACE(pOp->p2, pOut); }else{ VdbeBranchTaken(res!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3); - if( res ){ + if( res2 ){ goto jump_to_p2; } } @@ -2115,6 +2117,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ case OP_ElseNotEq: { /* same as TK_ESCAPE, jump */ assert( pOp>aOp ); assert( pOp[-1].opcode==OP_Lt || pOp[-1].opcode==OP_Gt ); + assert( pOp[-1].p5 & SQLITE_STOREP2 ); VdbeBranchTaken(iCompare!=0, 2); if( iCompare!=0 ) goto jump_to_p2; break; @@ -6864,8 +6867,8 @@ case OP_Init: { /* jump */ } #endif /* SQLITE_DEBUG */ #endif /* SQLITE_OMIT_TRACE */ - if( pOp->p2 ) goto jump_to_p2; - break; + assert( pOp->p2>0 ); + goto jump_to_p2; } #ifdef SQLITE_ENABLE_CURSOR_HINTS From 271896031628a161bc0c3937dc90af01029e8bdf Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 3 Sep 2016 15:31:20 +0000 Subject: [PATCH 79/97] Consider the affinity of "b" when using an "a IN (SELECT b ...)" expression with an index on "a". Fix for [199df416]. FossilOrigin-Name: f5e49855412e389a8a410db5d7ffb2e3634c5fa3 --- manifest | 15 ++--- manifest.uuid | 2 +- src/wherecode.c | 20 ++++-- test/rowvalue9.test | 144 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 167 insertions(+), 14 deletions(-) create mode 100644 test/rowvalue9.test diff --git a/manifest b/manifest index e6005476d0..fdd02d717d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Performance\soptimizations. -D 2016-09-03T01:46:15.276 +C Consider\sthe\saffinity\sof\s"b"\swhen\susing\san\s"a\sIN\s(SELECT\sb\s...)"\sexpression\swith\san\sindex\son\s"a".\sFix\sfor\s[199df416]. +D 2016-09-03T15:31:20.197 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -467,7 +467,7 @@ F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 F src/where.c 48d705e5196a0611a7be90698eade455ee238536 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 -F src/wherecode.c ee7b5353ff5f63548c206e3ecb4423ff0857d766 +F src/wherecode.c 8a9a53cb52dd8a75e07c85e3bc12c1604c735954 F src/whereexpr.c 7f9ada866d48d15d09754ae819c1c40efe3b2aff F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd @@ -1028,6 +1028,7 @@ F test/rowvalue5.test a440d490c8c0bf606034c09d5c6bbf7840b98f95 F test/rowvalue6.test d19b54feb604d5601f8614b15e214e0774c01087 F test/rowvalue7.test 5d06ff19d9e6969e574a2e662a531dd0c67801a8 F test/rowvalue8.test 5900eddad9e2c3c2e26f1a95f74aafc1232ee5e0 +F test/rowvalue9.test ca4e07da993e455e2c34a11800cd5eecfa25dbea F test/rowvaluefault.test 7b16485e3f2b371f3e3d05455b8ded6d0c090244 F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09 @@ -1521,7 +1522,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 c7271fbde1aebb15daaedb7f1fa75fe410fd46f6 -R 617c8c8681735b3b6f061557d07c266d -U drh -Z dbe31dcf0712b654c7c10b1471579b28 +P f1d06c49ba0d814dc7ffb538aac3f4e6251fd8f0 +R 7e510355146cbd1276c808b956f1d4a6 +U dan +Z 2c951e6f5da67796ee0d56ffce2dd993 diff --git a/manifest.uuid b/manifest.uuid index eb5bf868a4..2759fbda66 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f1d06c49ba0d814dc7ffb538aac3f4e6251fd8f0 \ No newline at end of file +f5e49855412e389a8a410db5d7ffb2e3634c5fa3 \ No newline at end of file diff --git a/src/wherecode.c b/src/wherecode.c index 16ffb89db1..896509bc32 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -649,13 +649,21 @@ static int codeAllEqualityTerms( } testcase( pTerm->eOperator & WO_ISNULL ); testcase( pTerm->eOperator & WO_IN ); - if( (pTerm->eOperator & (WO_ISNULL|WO_IN))==0 ){ - Expr *pRight = pTerm->pExpr->pRight; - if( (pTerm->wtFlags & TERM_IS)==0 && sqlite3ExprCanBeNull(pRight) ){ - sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk); - VdbeCoverage(v); + if( (pTerm->eOperator & WO_ISNULL)==0 ){ + Expr *pRight = 0; + if( pTerm->eOperator & WO_IN ){ + if( pTerm->pExpr->flags & EP_xIsSelect ){ + int iField = pTerm->iField ? pTerm->iField-1 : 0; + pRight = pTerm->pExpr->x.pSelect->pEList->a[iField].pExpr; + } + }else{ + pRight = pTerm->pExpr->pRight; + if( (pTerm->wtFlags & TERM_IS)==0 && sqlite3ExprCanBeNull(pRight) ){ + sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk); + VdbeCoverage(v); + } } - if( zAff ){ + if( pRight && zAff ){ if( sqlite3CompareAffinity(pRight, zAff[j])==SQLITE_AFF_BLOB ){ zAff[j] = SQLITE_AFF_BLOB; } diff --git a/test/rowvalue9.test b/test/rowvalue9.test new file mode 100644 index 0000000000..9e01e08932 --- /dev/null +++ b/test/rowvalue9.test @@ -0,0 +1,144 @@ +# 2016 September 3 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing SQL statements that use row value +# constructors. +# + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix rowvalue9 + +do_execsql_test 1.0.1 { + CREATE TABLE a1(c, b INTEGER, a TEXT, PRIMARY KEY(a, b)); + + INSERT INTO a1 (rowid, c, b, a) VALUES(3, '0x03', 1, 1); + INSERT INTO a1 (rowid, c, b, a) VALUES(14, '0x0E', 2, 2); + INSERT INTO a1 (rowid, c, b, a) VALUES(15, '0x0F', 3, 3); + INSERT INTO a1 (rowid, c, b, a) VALUES(92, '0x5C', 4, 4); + + CREATE TABLE a2(x BLOB, y BLOB); + INSERT INTO a2(x, y) VALUES(1, 1); + INSERT INTO a2(x, y) VALUES(2, '2'); + INSERT INTO a2(x, y) VALUES('3', 3); + INSERT INTO a2(x, y) VALUES('4', '4'); +} + +do_execsql_test 1.0.2 { + SELECT x, typeof(x), y, typeof(y) FROM a2 ORDER BY rowid +} { + 1 integer 1 integer + 2 integer 2 text + 3 text 3 integer + 4 text 4 text +} + +do_execsql_test 1.1.1 { + SELECT (SELECT rowid FROM a1 WHERE a=x AND b=y) FROM a2 +} {{} {} 15 92} +do_execsql_test 1.1.2 { + SELECT (SELECT rowid FROM a1 WHERE (a, b) = (x, y)) FROM a2 +} {{} {} 15 92} + +do_execsql_test 1.2.3 { + SELECT a1.rowid FROM a1, a2 WHERE a=x AND b=y; +} {15 92} +do_execsql_test 1.2.4 { + SELECT a1.rowid FROM a1, a2 WHERE (a, b) = (x, y) +} {15 92} + + +do_execsql_test 1.3.1 { + SELECT a1.rowid FROM a1, a2 WHERE coalesce(NULL,x)=a AND coalesce(NULL,y)=b +} {3 14 15 92} +do_execsql_test 1.3.2 { + SELECT a1.rowid FROM a1, a2 + WHERE (coalesce(NULL,x), coalesce(NULL,y)) = (a, b) +} {3 14 15 92} + +do_execsql_test 1.4.1 { + SELECT a1.rowid FROM a1, a2 WHERE +x=a AND +y=b +} {3 14 15 92} +do_execsql_test 1.4.2 { + SELECT a1.rowid FROM a1, a2 WHERE (+x, +y) = (a, b) +} {3 14 15 92} + +do_execsql_test 1.5.1 { + SELECT (SELECT rowid FROM a1 WHERE a=+x AND b=+y) FROM a2 +} {3 14 15 92} +do_execsql_test 1.5.2 { + SELECT (SELECT rowid FROM a1 WHERE (a, b) = (+x, +y)) FROM a2 +} {3 14 15 92} +do_execsql_test 1.5.3 { + SELECT (SELECT rowid FROM a1 WHERE (+x, +y) = (a, b)) FROM a2 +} {3 14 15 92} + +do_execsql_test 1.6.1 { + SELECT a1.rowid FROM a1 WHERE (a, b) IN (SELECT x, y FROM a2) +} {15 92} + +do_execsql_test 1.6.2 { + SELECT a1.rowid FROM a1, a2 WHERE EXISTS ( + SELECT 1 FROM a1 WHERE a=x AND b=y + ) +} {3 14 15 92 3 14 15 92} + +do_execsql_test 2.1 { + CREATE TABLE b1(a TEXT); + CREATE TABLE b2(x BLOB); + + INSERT INTO b1 VALUES(1); + INSERT INTO b2 VALUES(1); +} + +do_execsql_test 2.2 { SELECT * FROM b1, b2 WHERE a=x; } {} +do_execsql_test 2.3 { SELECT * FROM b1 WHERE a IN (SELECT x FROM b2) } {} + +do_execsql_test 2.4 { + CREATE UNIQUE INDEX b1a ON b1(a); +} +do_execsql_test 2.5 { SELECT * FROM b1 WHERE a IN (SELECT x FROM b2) } {} + +do_execsql_test 3.1 { + CREATE TABLE c1(a INTEGER, b TEXT); + INSERT INTO c1 VALUES(1, 1); + + CREATE TABLE c2(x BLOB, y BLOB); + INSERT INTO c2 VALUES(1, 1); +} +do_execsql_test 3.2 { + SELECT * FROM c1 WHERE (a, b) IN (SELECT x, y FROM c2) +} {} +do_execsql_test 3.3 { + CREATE UNIQUE INDEX c1ab ON c1(a, b); + SELECT * FROM c1 WHERE (a, b) IN (SELECT x, y FROM c2) +} {} + +do_execsql_test 4.0 { + CREATE TABLE d1(a TEXT); + CREATE TABLE d2(x BLOB); + INSERT INTO d1 VALUES(1); + INSERT INTO d2 VALUES(1); +} +do_execsql_test 4.1 { + SELECT * FROM d1 WHERE a IN (SELECT x FROM b2) +} {} +do_execsql_test 4.2 { + CREATE UNIQUE INDEX d1a ON d1(a); +} +do_execsql_test 4.3 { + SELECT * FROM d1 WHERE a IN (SELECT x FROM d2) +} {} + + +finish_test + From 80aa545337b65289a0db4e73c97d763c357aad53 Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 3 Sep 2016 19:52:12 +0000 Subject: [PATCH 80/97] Fix a problem causing the affinity of sub-select row-value elements to be ignored in some contextes. FossilOrigin-Name: 7d9bd22c0715ede2592ee1fa7ebc215aded1ca1b --- manifest | 18 ++++---- manifest.uuid | 2 +- src/expr.c | 6 +++ test/rowvalue9.test | 110 +++++++++++++++++++++++++++++++++++--------- test/types2.test | 2 +- 5 files changed, 105 insertions(+), 33 deletions(-) diff --git a/manifest b/manifest index 8837f64701..045ff79cbf 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sthe\sfuzzershell\senhancement\sfrom\strunk. -D 2016-09-03T16:24:43.721 +C Fix\sa\sproblem\scausing\sthe\saffinity\sof\ssub-select\srow-value\selements\sto\sbe\signored\sin\ssome\scontextes. +D 2016-09-03T19:52:12.432 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 7e0470e2b82af8d6544132d81da18990d9b9c09d +F src/expr.c 23b595cc6e2b609d53c72343b3fe16ce8b5e446d F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1028,7 +1028,7 @@ F test/rowvalue5.test a440d490c8c0bf606034c09d5c6bbf7840b98f95 F test/rowvalue6.test d19b54feb604d5601f8614b15e214e0774c01087 F test/rowvalue7.test 5d06ff19d9e6969e574a2e662a531dd0c67801a8 F test/rowvalue8.test 5900eddad9e2c3c2e26f1a95f74aafc1232ee5e0 -F test/rowvalue9.test ca4e07da993e455e2c34a11800cd5eecfa25dbea +F test/rowvalue9.test 749a471f237f3fcfdfbd902f850adff2d9963560 F test/rowvaluefault.test 7b16485e3f2b371f3e3d05455b8ded6d0c090244 F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09 @@ -1324,7 +1324,7 @@ F test/tt3_lookaside1.c 0377e202c3c2a50d688cb65ba203afeda6fafeb9 F test/tt3_stress.c c57d804716165811d979d4a719e05baccd79277f F test/tt3_vacuum.c 1753f45917699c9c1f66b64c717a717c9379f776 F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff -F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84 +F test/types2.test 1aeb81976841a91eef292723649b5c4fe3bc3cac F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264 F test/unique2.test 3674e9f2a3f1fbbfd4772ac74b7a97090d0f77d2 @@ -1522,7 +1522,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 f5e49855412e389a8a410db5d7ffb2e3634c5fa3 672c21bcf09c5bfb67e061456a56be45409c4f34 -R 66aa92e29e4f871901eff534f575b0fe -U drh -Z af3091527994ed297383eb80433196df +P ed206048484667e4fe6aaeadd65882537d74bd35 +R ebdd3ea482059f46220cc36bb6b2e41e +U dan +Z 13aac770e03afb07d71bee955ff12a59 diff --git a/manifest.uuid b/manifest.uuid index 1b53160d31..7dd209be18 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ed206048484667e4fe6aaeadd65882537d74bd35 \ No newline at end of file +7d9bd22c0715ede2592ee1fa7ebc215aded1ca1b \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 31df95c057..76f0d49081 100644 --- a/src/expr.c +++ b/src/expr.c @@ -61,6 +61,12 @@ char sqlite3ExprAffinity(Expr *pExpr){ if( op==TK_AGG_COLUMN || op==TK_COLUMN ){ return sqlite3TableColumnAffinity(pExpr->pTab, pExpr->iColumn); } + if( op==TK_SELECT_COLUMN ){ + assert( pExpr->pLeft->flags&EP_xIsSelect ); + return sqlite3ExprAffinity( + pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr + ); + } return pExpr->affinity; } diff --git a/test/rowvalue9.test b/test/rowvalue9.test index 9e01e08932..214eaccb7e 100644 --- a/test/rowvalue9.test +++ b/test/rowvalue9.test @@ -85,33 +85,31 @@ do_execsql_test 1.5.3 { do_execsql_test 1.6.1 { SELECT a1.rowid FROM a1 WHERE (a, b) IN (SELECT x, y FROM a2) } {15 92} - do_execsql_test 1.6.2 { SELECT a1.rowid FROM a1, a2 WHERE EXISTS ( SELECT 1 FROM a1 WHERE a=x AND b=y ) } {3 14 15 92 3 14 15 92} +# Test that [199df416] is fixed. +# do_execsql_test 2.1 { CREATE TABLE b1(a TEXT); CREATE TABLE b2(x BLOB); - INSERT INTO b1 VALUES(1); INSERT INTO b2 VALUES(1); } - do_execsql_test 2.2 { SELECT * FROM b1, b2 WHERE a=x; } {} do_execsql_test 2.3 { SELECT * FROM b1 WHERE a IN (SELECT x FROM b2) } {} - -do_execsql_test 2.4 { - CREATE UNIQUE INDEX b1a ON b1(a); -} +do_execsql_test 2.4 { CREATE UNIQUE INDEX b1a ON b1(a); } do_execsql_test 2.5 { SELECT * FROM b1 WHERE a IN (SELECT x FROM b2) } {} +# Test that a multi-column version of the query that revealed problem +# [199df416] also works. +# do_execsql_test 3.1 { CREATE TABLE c1(a INTEGER, b TEXT); INSERT INTO c1 VALUES(1, 1); - CREATE TABLE c2(x BLOB, y BLOB); INSERT INTO c2 VALUES(1, 1); } @@ -122,23 +120,91 @@ do_execsql_test 3.3 { CREATE UNIQUE INDEX c1ab ON c1(a, b); SELECT * FROM c1 WHERE (a, b) IN (SELECT x, y FROM c2) } {} +do_execsql_test 3.4 { + SELECT * FROM c1 WHERE (a, +b) IN (SELECT x, y FROM c2) +} {} +do_execsql_test 3.5 { + SELECT c1.rowid FROM c1 WHERE b = (SELECT y FROM c2); +} {} +do_execsql_test 3.6 { + SELECT c1.rowid FROM c1 WHERE (a, b) = (SELECT x, y FROM c2); +} {} + + +#------------------------------------------------------------------------- +# do_execsql_test 4.0 { - CREATE TABLE d1(a TEXT); - CREATE TABLE d2(x BLOB); - INSERT INTO d1 VALUES(1); - INSERT INTO d2 VALUES(1); -} -do_execsql_test 4.1 { - SELECT * FROM d1 WHERE a IN (SELECT x FROM b2) -} {} -do_execsql_test 4.2 { - CREATE UNIQUE INDEX d1a ON d1(a); -} -do_execsql_test 4.3 { - SELECT * FROM d1 WHERE a IN (SELECT x FROM d2) -} {} + CREATE TABLE d1(a TEXT, b INTEGER, c NUMERIC); + CREATE TABLE d2(x BLOB, y BLOB); + INSERT INTO d1 VALUES(1, 1, 1); + INSERT INTO d1 VALUES(2, 2, 2); + INSERT INTO d1 VALUES(3, 3, 3); + INSERT INTO d1 VALUES(4, 4, 4); + + INSERT INTO d2 VALUES (1, 1); + INSERT INTO d2 VALUES (2, '2'); + INSERT INTO d2 VALUES ('3', 3); + INSERT INTO d2 VALUES ('4', '4'); +} + +foreach {tn idx} { + 1 {} + 2 { CREATE INDEX idx ON d1(a) } + 3 { CREATE INDEX idx ON d1(a, c) } + 4 { CREATE INDEX idx ON d1(c) } + 5 { CREATE INDEX idx ON d1(c, a) } + + 6 { + CREATE INDEX idx ON d1(c, a) ; + CREATE INDEX idx1 ON d2(x, y); + } + + 7 { + CREATE INDEX idx ON d1(c, a) ; + CREATE UNIQUE INDEX idx2 ON d2(x, y) ; + } + + 8 { + CREATE INDEX idx ON d1(c) ; + CREATE UNIQUE INDEX idx2 ON d2(x); + } + +} { + execsql { DROP INDEX IF EXISTS idx } + execsql { DROP INDEX IF EXISTS idx2 } + execsql { DROP INDEX IF EXISTS idx3 } + execsql $idx + + do_execsql_test 4.$tn.1 { + SELECT rowid FROM d1 WHERE (a, c) IN (SELECT x, y FROM d2); + } {3 4} + + do_execsql_test 4.$tn.2 { + SELECT rowid FROM d1 WHERE (c, a) IN (SELECT x, y FROM d2); + } {2 4} + + do_execsql_test 4.$tn.3 { + SELECT rowid FROM d1 WHERE (+c, a) IN (SELECT x, y FROM d2); + } {2} + + do_execsql_test 4.$tn.4 { + SELECT rowid FROM d1 WHERE (c, a) = ( + SELECT x, y FROM d2 WHERE d2.rowid=d1.rowid + ); + } {2 4} + + do_execsql_test 4.$tn.5 { + SELECT d1.rowid FROM d1, d2 WHERE a = y; + } {2 4} + + do_execsql_test 4.$tn.6 { + SELECT d1.rowid FROM d1 WHERE a = ( + SELECT y FROM d2 where d2.rowid=d1.rowid + ); + } {2 4} +} finish_test diff --git a/test/types2.test b/test/types2.test index 4a70aa5fe2..d6d84e65b5 100644 --- a/test/types2.test +++ b/test/types2.test @@ -333,7 +333,7 @@ ifcapable subquery { test_boolset types2-8.8 {o IN (SELECT t FROM t4)} {7} test_boolset types2-8.9 {i IN (SELECT o FROM t4)} {9 10 11 12} test_boolset types2-8.6 {n IN (SELECT o FROM t4)} {9 10 11 12} - test_boolset types2-8.7 {t IN (SELECT o FROM t4)} {9 11} + test_boolset types2-8.7 {t IN (SELECT o FROM t4)} {} test_boolset types2-8.8 {o IN (SELECT o FROM t4)} {9 10} } From 5c3340bd4781d346c7f401ea800f68c6ecce1885 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 5 Sep 2016 09:44:45 +0000 Subject: [PATCH 81/97] Fix a crash that could occur under certain circumstances if the vectors on either side of a comparison operator were of a different size. FossilOrigin-Name: 42670935aba152ba774c2a8bdcbe72b943309d68 --- manifest | 18 +++++++++--------- manifest.uuid | 2 +- src/expr.c | 32 +++++++++++++++++++++++++------- src/sqliteInt.h | 1 + src/whereexpr.c | 4 ++++ test/rowvalue4.test | 2 ++ 6 files changed, 42 insertions(+), 17 deletions(-) diff --git a/manifest b/manifest index 045ff79cbf..a3cbab4faf 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sproblem\scausing\sthe\saffinity\sof\ssub-select\srow-value\selements\sto\sbe\signored\sin\ssome\scontextes. -D 2016-09-03T19:52:12.432 +C Fix\sa\scrash\sthat\scould\soccur\sunder\scertain\scircumstances\sif\sthe\svectors\son\seither\sside\sof\sa\scomparison\soperator\swere\sof\sa\sdifferent\ssize. +D 2016-09-05T09:44:45.878 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 23b595cc6e2b609d53c72343b3fe16ce8b5e446d +F src/expr.c 750ed16649d655fef117c6d983c7382acd625bfd F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -390,7 +390,7 @@ F src/shell.c 79dda477be6c96eba6e952a934957ad36f87acc7 F src/sqlite.h.in 4a030e254e204570444b34bf7d40fb4a5416089e F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 8648034aa702469afb553231677306cc6492a1ae -F src/sqliteInt.h c9e010a79ab4ed7bdc910a24d8f08f3c6d5f822c +F src/sqliteInt.h e7afbfaa4033a4c1cf2aa7bbdd0eb27e4663ab3a F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247 F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1 F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9 @@ -468,7 +468,7 @@ F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 F src/where.c 48d705e5196a0611a7be90698eade455ee238536 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 F src/wherecode.c 8a9a53cb52dd8a75e07c85e3bc12c1604c735954 -F src/whereexpr.c 7f9ada866d48d15d09754ae819c1c40efe3b2aff +F src/whereexpr.c 571597ac8d761505f626e597884b2a03494053cd F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -1023,7 +1023,7 @@ F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d F test/rowvalue.test 7d8482dde9023973615eaaca65647f33d70c1f01 F test/rowvalue2.test 060d238b7e5639a7c5630cb5e63e311b44efef2b F test/rowvalue3.test 01399b7bf150b0d41abce76c18072da777c2500c -F test/rowvalue4.test 9b40c9be9bdde30fc66cddbfdf6a5af37de4ccac +F test/rowvalue4.test 29494f1c66e73329d90027619ee6d22e7ffe0ebf F test/rowvalue5.test a440d490c8c0bf606034c09d5c6bbf7840b98f95 F test/rowvalue6.test d19b54feb604d5601f8614b15e214e0774c01087 F test/rowvalue7.test 5d06ff19d9e6969e574a2e662a531dd0c67801a8 @@ -1522,7 +1522,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 ed206048484667e4fe6aaeadd65882537d74bd35 -R ebdd3ea482059f46220cc36bb6b2e41e +P 7d9bd22c0715ede2592ee1fa7ebc215aded1ca1b +R 26e65ef1f9dc6647fee64756ced37096 U dan -Z 13aac770e03afb07d71bee955ff12a59 +Z c125d50595ea7cb48721a8bf5a5398c8 diff --git a/manifest.uuid b/manifest.uuid index 7dd209be18..d05b3e11b2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7d9bd22c0715ede2592ee1fa7ebc215aded1ca1b \ No newline at end of file +42670935aba152ba774c2a8bdcbe72b943309d68 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 76f0d49081..cba90b8757 100644 --- a/src/expr.c +++ b/src/expr.c @@ -521,14 +521,9 @@ static void codeVectorCompare( Vdbe *v = pParse->pVdbe; Expr *pLeft = pExpr->pLeft; Expr *pRight = pExpr->pRight; - int nLeft = sqlite3ExprVectorSize(pLeft); - int nRight = sqlite3ExprVectorSize(pRight); - /* Check that both sides of the comparison are vectors, and that - ** both are the same length. */ - if( nLeft!=nRight ){ - sqlite3ErrorMsg(pParse, "row value misused"); - }else{ + if( sqlite3ExprCheckComparison(pParse, pLeft, pRight)==0 ){ + int nLeft = sqlite3ExprVectorSize(pLeft); int i; int regLeft = 0; int regRight = 0; @@ -2656,6 +2651,29 @@ int sqlite3ExprCheckIN(Parse *pParse, Expr *pIn){ } #endif +/* +** Expressions pLeft and pRight are the left and right sides of a comparison +** operator. If either pLeft or pRight is a vector and the other is not, or +** if they are both vectors but of a different size, leave an error message +** in the Parse object and return non-zero. Or, if there is no problem, +** return 0. +*/ +int sqlite3ExprCheckComparison(Parse *pParse, Expr *pLeft, Expr *pRight){ + int nLeft = sqlite3ExprVectorSize(pLeft); + int nRight = sqlite3ExprVectorSize(pRight); + if( nLeft!=nRight ){ + if( (pRight->flags & EP_xIsSelect) ){ + sqlite3SubselectError(pParse, nRight, nLeft); + }else if( pLeft->flags & EP_xIsSelect ){ + sqlite3SubselectError(pParse, nLeft, nRight); + }else{ + sqlite3ErrorMsg(pParse, "row value misused"); + } + return 1; + } + return 0; +} + #ifndef SQLITE_OMIT_SUBQUERY /* ** Generate code for an IN expression. diff --git a/src/sqliteInt.h b/src/sqliteInt.h index dbb18a7458..c2a49b08e3 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4009,6 +4009,7 @@ int sqlite3ExprCheckIN(Parse*, Expr*); #else # define sqlite3ExprCheckIN(x,y) SQLITE_OK #endif +int sqlite3ExprCheckComparison(Parse*, Expr*, Expr*); #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 void sqlite3AnalyzeFunctions(void); diff --git a/src/whereexpr.c b/src/whereexpr.c index 00328989e7..582b42b0cf 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -952,6 +952,10 @@ static void exprAnalyze( Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight); u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV; + if( pRight && sqlite3ExprCheckComparison(pParse, pLeft, pRight) ){ + return; + } + if( pTerm->iField>0 ){ assert( op==TK_IN ); assert( pLeft->op==TK_VECTOR ); diff --git a/test/rowvalue4.test b/test/rowvalue4.test index a30edcc262..f6a34f7260 100644 --- a/test/rowvalue4.test +++ b/test/rowvalue4.test @@ -47,6 +47,8 @@ foreach {tn s error} { 6 "SELECT * FROM t1 WHERE (a, b) IN (SELECT * FROM t1)" {sub-select returns 3 columns - expected 2} + 7 "SELECT * FROM t1 WHERE (c, c) <= 1" {row value misused} + 8 "SELECT * FROM t1 WHERE (b, b) <= 1" {row value misused} } { do_catchsql_test 2.$tn "$s" [list 1 $error] } From b29e60c4487b6abc55acd8e0fe1694c590f18412 Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 5 Sep 2016 12:02:34 +0000 Subject: [PATCH 82/97] Catch vector size mismatch problems during name resolution to avoid later problems. FossilOrigin-Name: 56562a0346170cf7b72445976864b058437a8ac3 --- manifest | 27 +++++----- manifest.uuid | 2 +- src/expr.c | 119 +++++++++++++++++++++----------------------- src/resolve.c | 27 ++++++++++ src/whereexpr.c | 27 +++++----- test/rowvalue.test | 27 ++++++++++ test/rowvalue4.test | 2 +- test/subselect.test | 2 +- 8 files changed, 141 insertions(+), 92 deletions(-) diff --git a/manifest b/manifest index 045ff79cbf..e3076e225f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sproblem\scausing\sthe\saffinity\sof\ssub-select\srow-value\selements\sto\sbe\signored\sin\ssome\scontextes. -D 2016-09-03T19:52:12.432 +C Catch\svector\ssize\smismatch\sproblems\sduring\sname\sresolution\sto\savoid\slater\nproblems. +D 2016-09-05T12:02:34.649 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 23b595cc6e2b609d53c72343b3fe16ce8b5e446d +F src/expr.c 942c417e84e835ce51c6b732afa0b4572110e1c1 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -383,7 +383,7 @@ F src/pragma.h 64c78a648751b9f4f297276c4eb7507b14b4628c F src/prepare.c 0fcf16eaacc90c1059055519a76b75b516a59a88 F src/printf.c a5f0ca08ddede803c241266abb46356ec748ded1 F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 -F src/resolve.c d67b9a5cc33339256e2088c5a722745fc2ff5219 +F src/resolve.c 24f40fd0c3475821d1ad762a3f2c3455cc839b42 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac F src/select.c 38216d0b2d42a0f475abf86a84c3499e6421ba29 F src/shell.c 79dda477be6c96eba6e952a934957ad36f87acc7 @@ -468,7 +468,7 @@ F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 F src/where.c 48d705e5196a0611a7be90698eade455ee238536 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 F src/wherecode.c 8a9a53cb52dd8a75e07c85e3bc12c1604c735954 -F src/whereexpr.c 7f9ada866d48d15d09754ae819c1c40efe3b2aff +F src/whereexpr.c c5ec87e234faf62ac2d4e7f7ce18fb1f4bd475ff F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -1020,10 +1020,10 @@ F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81 F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d -F test/rowvalue.test 7d8482dde9023973615eaaca65647f33d70c1f01 +F test/rowvalue.test 753eb744b7efeb5ac643d35d6e1e5066452ccf79 F test/rowvalue2.test 060d238b7e5639a7c5630cb5e63e311b44efef2b F test/rowvalue3.test 01399b7bf150b0d41abce76c18072da777c2500c -F test/rowvalue4.test 9b40c9be9bdde30fc66cddbfdf6a5af37de4ccac +F test/rowvalue4.test 9b60544dcd0231a9fcbe1ec095edb16cdaa7ae13 F test/rowvalue5.test a440d490c8c0bf606034c09d5c6bbf7840b98f95 F test/rowvalue6.test d19b54feb604d5601f8614b15e214e0774c01087 F test/rowvalue7.test 5d06ff19d9e6969e574a2e662a531dd0c67801a8 @@ -1121,7 +1121,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 ccec43f85d488c6c4b6f98ea2dfa95b6086871c0 +F test/subselect.test 0966aa8e720224dbd6a5e769a3ec2a723e332303 F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a F test/subtype1.test 7fe09496352f97053af1437150751be2d0a0cae8 F test/superlock.test ec94f0556b6488d97f71c79f9061ae08d9ab8f12 @@ -1522,7 +1522,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P ed206048484667e4fe6aaeadd65882537d74bd35 -R ebdd3ea482059f46220cc36bb6b2e41e -U dan -Z 13aac770e03afb07d71bee955ff12a59 +P 7d9bd22c0715ede2592ee1fa7ebc215aded1ca1b +R 9a7c982f4a8ba0874174ff495f80018e +T *branch * early-vector-size-check +T *sym-early-vector-size-check * +T -sym-rowvalue * +U drh +Z 17a06585da7241edb65fdad418303946 diff --git a/manifest.uuid b/manifest.uuid index 7dd209be18..474117a10b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7d9bd22c0715ede2592ee1fa7ebc215aded1ca1b \ No newline at end of file +56562a0346170cf7b72445976864b058437a8ac3 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 76f0d49081..f604ae4fe2 100644 --- a/src/expr.c +++ b/src/expr.c @@ -522,75 +522,68 @@ static void codeVectorCompare( Expr *pLeft = pExpr->pLeft; Expr *pRight = pExpr->pRight; int nLeft = sqlite3ExprVectorSize(pLeft); - int nRight = sqlite3ExprVectorSize(pRight); + int i; + int regLeft = 0; + int regRight = 0; + u8 opx = op; + int addrDone = sqlite3VdbeMakeLabel(v); - /* Check that both sides of the comparison are vectors, and that - ** both are the same length. */ - if( nLeft!=nRight ){ - sqlite3ErrorMsg(pParse, "row value misused"); - }else{ - int i; - int regLeft = 0; - int regRight = 0; - u8 opx = op; - int addrDone = sqlite3VdbeMakeLabel(v); + assert( nLeft==sqlite3ExprVectorSize(pRight) ); + assert( pExpr->op==TK_EQ || pExpr->op==TK_NE + || pExpr->op==TK_IS || pExpr->op==TK_ISNOT + || pExpr->op==TK_LT || pExpr->op==TK_GT + || pExpr->op==TK_LE || pExpr->op==TK_GE + ); + assert( pExpr->op==op || (pExpr->op==TK_IS && op==TK_EQ) + || (pExpr->op==TK_ISNOT && op==TK_NE) ); + assert( p5==0 || pExpr->op!=op ); + assert( p5==SQLITE_NULLEQ || pExpr->op==op ); - assert( pExpr->op==TK_EQ || pExpr->op==TK_NE - || pExpr->op==TK_IS || pExpr->op==TK_ISNOT - || pExpr->op==TK_LT || pExpr->op==TK_GT - || pExpr->op==TK_LE || pExpr->op==TK_GE - ); - assert( pExpr->op==op || (pExpr->op==TK_IS && op==TK_EQ) - || (pExpr->op==TK_ISNOT && op==TK_NE) ); - assert( p5==0 || pExpr->op!=op ); - assert( p5==SQLITE_NULLEQ || pExpr->op==op ); + p5 |= SQLITE_STOREP2; + if( opx==TK_LE ) opx = TK_LT; + if( opx==TK_GE ) opx = TK_GT; - p5 |= SQLITE_STOREP2; - if( opx==TK_LE ) opx = TK_LT; - if( opx==TK_GE ) opx = TK_GT; + regLeft = exprCodeSubselect(pParse, pLeft); + regRight = exprCodeSubselect(pParse, pRight); - regLeft = exprCodeSubselect(pParse, pLeft); - regRight = exprCodeSubselect(pParse, pRight); - - for(i=0; 1 /*Loop exits by "break"*/; i++){ - int regFree1 = 0, regFree2 = 0; - Expr *pL, *pR; - int r1, r2; - assert( i>=0 && i0 ) sqlite3ExprCachePush(pParse); - r1 = exprVectorRegister(pParse, pLeft, i, regLeft, &pL, ®Free1); - r2 = exprVectorRegister(pParse, pRight, i, regRight, &pR, ®Free2); - codeCompare(pParse, pL, pR, opx, r1, r2, dest, p5); - testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt); - testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le); - testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt); - testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge); - testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq); - testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne); - sqlite3ReleaseTempReg(pParse, regFree1); - sqlite3ReleaseTempReg(pParse, regFree2); - if( i>0 ) sqlite3ExprCachePop(pParse); - if( i==nLeft-1 ){ - break; - } - if( opx==TK_EQ ){ - sqlite3VdbeAddOp2(v, OP_IfNot, dest, addrDone); VdbeCoverage(v); - p5 |= SQLITE_KEEPNULL; - }else if( opx==TK_NE ){ - sqlite3VdbeAddOp2(v, OP_If, dest, addrDone); VdbeCoverage(v); - p5 |= SQLITE_KEEPNULL; - }else{ - assert( op==TK_LT || op==TK_GT || op==TK_LE || op==TK_GE ); - sqlite3VdbeAddOp2(v, OP_ElseNotEq, 0, addrDone); - VdbeCoverageIf(v, op==TK_LT); - VdbeCoverageIf(v, op==TK_GT); - VdbeCoverageIf(v, op==TK_LE); - VdbeCoverageIf(v, op==TK_GE); - if( i==nLeft-2 ) opx = op; - } + for(i=0; 1 /*Loop exits by "break"*/; i++){ + int regFree1 = 0, regFree2 = 0; + Expr *pL, *pR; + int r1, r2; + assert( i>=0 && i0 ) sqlite3ExprCachePush(pParse); + r1 = exprVectorRegister(pParse, pLeft, i, regLeft, &pL, ®Free1); + r2 = exprVectorRegister(pParse, pRight, i, regRight, &pR, ®Free2); + codeCompare(pParse, pL, pR, opx, r1, r2, dest, p5); + testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt); + testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le); + testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt); + testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge); + testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq); + testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne); + sqlite3ReleaseTempReg(pParse, regFree1); + sqlite3ReleaseTempReg(pParse, regFree2); + if( i>0 ) sqlite3ExprCachePop(pParse); + if( i==nLeft-1 ){ + break; + } + if( opx==TK_EQ ){ + sqlite3VdbeAddOp2(v, OP_IfNot, dest, addrDone); VdbeCoverage(v); + p5 |= SQLITE_KEEPNULL; + }else if( opx==TK_NE ){ + sqlite3VdbeAddOp2(v, OP_If, dest, addrDone); VdbeCoverage(v); + p5 |= SQLITE_KEEPNULL; + }else{ + assert( op==TK_LT || op==TK_GT || op==TK_LE || op==TK_GE ); + sqlite3VdbeAddOp2(v, OP_ElseNotEq, 0, addrDone); + VdbeCoverageIf(v, op==TK_LT); + VdbeCoverageIf(v, op==TK_GT); + VdbeCoverageIf(v, op==TK_LE); + VdbeCoverageIf(v, op==TK_GE); + if( i==nLeft-2 ) opx = op; } - sqlite3VdbeResolveLabel(v, addrDone); } + sqlite3VdbeResolveLabel(v, addrDone); } #if SQLITE_MAX_EXPR_DEPTH>0 diff --git a/src/resolve.c b/src/resolve.c index 206015fcfa..a5cc06b919 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -776,6 +776,33 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ notValid(pParse, pNC, "parameters", NC_IsCheck|NC_PartIdx|NC_IdxExpr); break; } + case TK_EQ: + case TK_NE: + case TK_LT: + case TK_LE: + case TK_GT: + case TK_GE: + case TK_IS: + case TK_ISNOT: { + int nLeft, nRight; + if( pParse->db->mallocFailed ) break; + assert( pExpr->pRight!=0 ); + assert( pExpr->pLeft!=0 ); + nLeft = sqlite3ExprVectorSize(pExpr->pLeft); + nRight = sqlite3ExprVectorSize(pExpr->pRight); + if( nLeft!=nRight ){ + testcase( pExpr->op==TK_EQ ); + testcase( pExpr->op==TK_NE ); + testcase( pExpr->op==TK_LT ); + testcase( pExpr->op==TK_LE ); + testcase( pExpr->op==TK_GT ); + testcase( pExpr->op==TK_GE ); + testcase( pExpr->op==TK_IS ); + testcase( pExpr->op==TK_ISNOT ); + sqlite3ErrorMsg(pParse, "row value misused"); + } + break; + } } return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue; } diff --git a/src/whereexpr.c b/src/whereexpr.c index 00328989e7..92aed29bbc 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -1188,22 +1188,21 @@ static void exprAnalyze( || (pExpr->pRight->flags & EP_xIsSelect)==0 )){ int nLeft = sqlite3ExprVectorSize(pExpr->pLeft); - if( nLeft==sqlite3ExprVectorSize(pExpr->pRight) ){ - int i; - for(i=0; ipLeft); i++){ - int idxNew; - Expr *pNew; - Expr *pLeft = sqlite3ExprForVectorField(pParse, pExpr->pLeft, i); - Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i); + int i; + assert( nLeft==sqlite3ExprVectorSize(pExpr->pRight) ); + for(i=0; ipLeft, i); + Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i); - pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight, 0); - idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC); - exprAnalyze(pSrc, pWC, idxNew); - } - pTerm = &pWC->a[idxTerm]; - pTerm->wtFlags = TERM_CODED|TERM_VIRTUAL; /* Disable the original */ - pTerm->eOperator = 0; + pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight, 0); + idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC); + exprAnalyze(pSrc, pWC, idxNew); } + pTerm = &pWC->a[idxTerm]; + pTerm->wtFlags = TERM_CODED|TERM_VIRTUAL; /* Disable the original */ + pTerm->eOperator = 0; } /* If there is a vector IN term - e.g. "(a, b) IN (SELECT ...)" - create diff --git a/test/rowvalue.test b/test/rowvalue.test index f716c26c8b..f53d5328ca 100644 --- a/test/rowvalue.test +++ b/test/rowvalue.test @@ -229,4 +229,31 @@ do_execsql_test 10.0 { AND (a,b,e) IN (SELECT 'a','b','d' FROM dual); } +do_catchsql_test 11.1 { + CREATE TABLE t11(a); + SELECT * FROM t11 WHERE (a,a)<=1; +} {1 {row value misused}} +do_catchsql_test 11.2 { + SELECT * FROM t11 WHERE (a,a)<1; +} {1 {row value misused}} +do_catchsql_test 11.3 { + SELECT * FROM t11 WHERE (a,a)>=1; +} {1 {row value misused}} +do_catchsql_test 11.4 { + SELECT * FROM t11 WHERE (a,a)>1; +} {1 {row value misused}} +do_catchsql_test 11.5 { + SELECT * FROM t11 WHERE (a,a)==1; +} {1 {row value misused}} +do_catchsql_test 11.6 { + SELECT * FROM t11 WHERE (a,a)<>1; +} {1 {row value misused}} +do_catchsql_test 11.7 { + SELECT * FROM t11 WHERE (a,a) IS 1; +} {1 {row value misused}} +do_catchsql_test 11.8 { + SELECT * FROM t11 WHERE (a,a) IS NOT 1; +} {1 {row value misused}} + + finish_test diff --git a/test/rowvalue4.test b/test/rowvalue4.test index a30edcc262..d3d5e46c7e 100644 --- a/test/rowvalue4.test +++ b/test/rowvalue4.test @@ -306,6 +306,6 @@ do_catchsql_test 8.2 { SELECT * FROM c2 CROSS JOIN c3 WHERE ( (a, b) == (SELECT x, y FROM c1) AND c3.d = c ) OR ( c == (SELECT x, y FROM c1) AND c3.d = c ) -} {1 {sub-select returns 2 columns - expected 1}} +} {1 {row value misused}} finish_test diff --git a/test/subselect.test b/test/subselect.test index fc1c103085..4d0efde47a 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 {sub-select returns 2 columns - expected 1}} +} {1 {row value misused}} # A subselect without an aggregate. # From 3fffbf932719916b9a6564280b1254ce4f6c5012 Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 5 Sep 2016 15:02:41 +0000 Subject: [PATCH 83/97] Simplifications to the SQLITE_KEEPNULL flag on VDBE comparison operators. FossilOrigin-Name: 96269f0179a7d8fa44ee278cef362962368c59ef --- manifest | 13 ++++++------- manifest.uuid | 2 +- src/vdbe.c | 19 +++++++++++++++---- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index 22dc90908e..050f3253f6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Do\svector\scomparison\ssize\schecking\searly\s-\sat\sname\sresolution\stime\s-\sto\nforestall\sfuture\sproblems. -D 2016-09-05T12:12:56.221 +C Simplifications\sto\sthe\sSQLITE_KEEPNULL\sflag\son\sVDBE\scomparison\soperators. +D 2016-09-05T15:02:41.820 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -451,7 +451,7 @@ F src/update.c 8179e699dbd45b92934fd02d3d8e3732e8da8802 F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c F src/util.c 810ec3f22e2d1b62e66c30fe3621ebdedd23584d F src/vacuum.c 913970b9d86dd6c2b8063ef1af421880f1464ec3 -F src/vdbe.c d8fb59a3ef10636df2447f55c1e82be7b8ad1604 +F src/vdbe.c 3148d5d47816c5ad2ed3c62beb3086cbbcaab107 F src/vdbe.h 67bc551f7faf04c33493892e4b378aada823ed10 F src/vdbeInt.h c59381049af5c7751a83456c39b80d1a6fde1f9d F src/vdbeapi.c a32d61b7dd05e6890d8fd44d2805f55e2f5ba9f3 @@ -1522,8 +1522,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 42670935aba152ba774c2a8bdcbe72b943309d68 56562a0346170cf7b72445976864b058437a8ac3 -R 9ea9c6d7e55a2e73634a9a6491229b09 -T +closed 56562a0346170cf7b72445976864b058437a8ac3 +P ae127bcc0a5827786853f47b229021435d6098d7 +R b6639bee17b9273a45c7e5e2da3019f4 U drh -Z c545a18fc0ce61b83efad3cc467f4a6a +Z a8418630f7d7128d87f9b291fb48ec92 diff --git a/manifest.uuid b/manifest.uuid index a64a078eae..1973c3697e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ae127bcc0a5827786853f47b229021435d6098d7 \ No newline at end of file +96269f0179a7d8fa44ee278cef362962368c59ef \ No newline at end of file diff --git a/src/vdbe.c b/src/vdbe.c index 443ecbce1d..c3c9c607cb 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -1912,7 +1912,8 @@ case OP_Cast: { /* in1 */ ** the SQLITE_NULLEQ flag were omitted from P5. ** ** If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the -** content of r[P2] is only set to 1 (true) if it was not previously NULL. +** content of r[P2] is only changed if the new value is NULL or 0 (false). +** In other words, a prior r[P2] value will not be overwritten by 1 (true). */ /* Opcode: Ne P1 P2 P3 P4 P5 ** Synopsis: IF r[P3]!=r[P1] @@ -1922,7 +1923,8 @@ case OP_Cast: { /* in1 */ ** additional information. ** ** If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the -** content of r[P2] is only set to 0 (false) if it was not previously NULL. +** content of r[P2] is only changed if the new value is NULL or 1 (true). +** In other words, a prior r[P2] value will not be overwritten by 0 (false). */ /* Opcode: Lt P1 P2 P3 P4 P5 ** Synopsis: IF r[P3]p2]; iCompare = res; res2 = res2!=0; /* For this path res2 must be exactly 0 or 1 */ - if( (pOp->p5 & SQLITE_KEEPNULL)!=0 && (pOut->flags & MEM_Null)!=0 ){ + if( (pOp->p5 & SQLITE_KEEPNULL)!=0 ){ /* The KEEPNULL flag prevents OP_Eq from overwriting a NULL with 1 - ** and prevents OP_Ne from overwriting NULL with 0. */ + ** and prevents OP_Ne from overwriting NULL with 0. This flag + ** is only used in contexts where either: + ** (1) op==OP_Eq && (r[P2]==NULL || r[P2]==0) + ** (2) op==OP_Ne && (r[P2]==NULL || r[P2]==1) + ** Therefore it is not necessary to check the content of r[P2] for + ** NULL. */ assert( pOp->opcode==OP_Ne || pOp->opcode==OP_Eq ); assert( res2==0 || res2==1 ); + testcase( res2==0 && pOp->opcode==OP_Eq ); + testcase( res2==1 && pOp->opcode==OP_Eq ); + testcase( res2==0 && pOp->opcode==OP_Ne ); + testcase( res2==1 && pOp->opcode==OP_Ne ); if( (pOp->opcode==OP_Eq)==res2 ) break; } memAboutToChange(p, pOut); From c1bcd9cc3e9913acec4d8b04178d52826f82eb2d Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 5 Sep 2016 19:57:46 +0000 Subject: [PATCH 84/97] Fix an assert() so that it does C-compiler does not combine an assert() conditional with a production code conditional and thereby confuse the mutation testing script. FossilOrigin-Name: 2fa5288a7ef43f1fb26037c1b5e84b7b90400623 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/expr.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 050f3253f6..a7e3009364 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Simplifications\sto\sthe\sSQLITE_KEEPNULL\sflag\son\sVDBE\scomparison\soperators. -D 2016-09-05T15:02:41.820 +C Fix\san\sassert()\sso\sthat\sit\sdoes\sC-compiler\sdoes\snot\scombine\san\sassert()\nconditional\swith\sa\sproduction\scode\sconditional\sand\sthereby\sconfuse\sthe\smutation\ntesting\sscript. +D 2016-09-05T19:57:46.769 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 942c417e84e835ce51c6b732afa0b4572110e1c1 +F src/expr.c 48b18962e32c22be92eb00ef941499f4c80efd2c F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1522,7 +1522,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 ae127bcc0a5827786853f47b229021435d6098d7 -R b6639bee17b9273a45c7e5e2da3019f4 +P 96269f0179a7d8fa44ee278cef362962368c59ef +R 2715120448e06410de8fb9d7dcf49e0c U drh -Z a8418630f7d7128d87f9b291fb48ec92 +Z 8e9c25e5732d609d10738e0bb4ef6b76 diff --git a/manifest.uuid b/manifest.uuid index 1973c3697e..9849952ab4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -96269f0179a7d8fa44ee278cef362962368c59ef \ No newline at end of file +2fa5288a7ef43f1fb26037c1b5e84b7b90400623 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index f604ae4fe2..517034ca39 100644 --- a/src/expr.c +++ b/src/expr.c @@ -487,7 +487,7 @@ static int exprVectorRegister( int *pRegFree /* OUT: Temp register to free */ ){ u8 op = pVector->op; - assert( op==TK_VECTOR || op==TK_SELECT || op==TK_REGISTER ); + assert( op==TK_VECTOR || op==TK_REGISTER || op==TK_SELECT ); if( op==TK_REGISTER ){ *ppExpr = sqlite3VectorFieldSubexpr(pVector, iField); return pVector->iTable+iField; From 9f24b53dcccf022ee9290f7c38872e288b1eaaa3 Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 5 Sep 2016 22:50:48 +0000 Subject: [PATCH 85/97] Simplified logic to extract a column from a row value. FossilOrigin-Name: e8f105c3009e9b667db2afc0088b020a2b94d370 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/expr.c | 5 ++--- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index a7e3009364..dbde222f9f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\san\sassert()\sso\sthat\sit\sdoes\sC-compiler\sdoes\snot\scombine\san\sassert()\nconditional\swith\sa\sproduction\scode\sconditional\sand\sthereby\sconfuse\sthe\smutation\ntesting\sscript. -D 2016-09-05T19:57:46.769 +C Simplified\slogic\sto\sextract\sa\scolumn\sfrom\sa\srow\svalue. +D 2016-09-05T22:50:48.311 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 48b18962e32c22be92eb00ef941499f4c80efd2c +F src/expr.c 32bbbf7ef8d0e49e346612b1832a51a3dcf41818 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1522,7 +1522,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 96269f0179a7d8fa44ee278cef362962368c59ef -R 2715120448e06410de8fb9d7dcf49e0c +P 2fa5288a7ef43f1fb26037c1b5e84b7b90400623 +R 7603658287e131d43381ef690aa890bc U drh -Z 8e9c25e5732d609d10738e0bb4ef6b76 +Z 751e7d4fc1004371a10bd2fdff652b35 diff --git a/manifest.uuid b/manifest.uuid index 9849952ab4..b0fd78e535 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2fa5288a7ef43f1fb26037c1b5e84b7b90400623 \ No newline at end of file +e8f105c3009e9b667db2afc0088b020a2b94d370 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 517034ca39..a21ca05d51 100644 --- a/src/expr.c +++ b/src/expr.c @@ -371,9 +371,8 @@ int sqlite3ExprVectorSize(Expr *pExpr){ Expr *sqlite3VectorFieldSubexpr(Expr *pVector, int i){ assert( iop==TK_SELECT - || (pVector->op==TK_REGISTER && pVector->op2==TK_SELECT) - ){ + assert( pVector->op2==0 || pVector->op==TK_REGISTER ); + if( pVector->op==TK_SELECT || pVector->op2==TK_SELECT ){ return pVector->x.pSelect->pEList->a[i].pExpr; }else{ return pVector->x.pList->a[i].pExpr; From fad0e70c637cd210cd1696693e5c64ed64ce2cd3 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 6 Sep 2016 12:04:50 +0000 Subject: [PATCH 86/97] Fix a typo in a comment in expr.c. FossilOrigin-Name: 288e934f356ff6276b3e7581ac0f998ca6e93610 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/expr.c | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index dbde222f9f..8e1a6947e5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Simplified\slogic\sto\sextract\sa\scolumn\sfrom\sa\srow\svalue. -D 2016-09-05T22:50:48.311 +C Fix\sa\stypo\sin\sa\scomment\sin\sexpr.c. +D 2016-09-06T12:04:50.217 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 32bbbf7ef8d0e49e346612b1832a51a3dcf41818 +F src/expr.c 17af5dc7326543ce395503adc1b88b0a722fcee3 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1522,7 +1522,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 2fa5288a7ef43f1fb26037c1b5e84b7b90400623 -R 7603658287e131d43381ef690aa890bc -U drh -Z 751e7d4fc1004371a10bd2fdff652b35 +P e8f105c3009e9b667db2afc0088b020a2b94d370 +R 48ea3338c1b6c2a2aac4971ca7c78062 +U dan +Z 231ae31b564ec961477419ce2ce3ecda diff --git a/manifest.uuid b/manifest.uuid index b0fd78e535..ea64ef4497 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e8f105c3009e9b667db2afc0088b020a2b94d370 \ No newline at end of file +288e934f356ff6276b3e7581ac0f998ca6e93610 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index a21ca05d51..f0bfdb0a7a 100644 --- a/src/expr.c +++ b/src/expr.c @@ -395,7 +395,7 @@ Expr *sqlite3VectorFieldSubexpr(Expr *pVector, int i){ ** ensuring that the returned value eventually gets freed. ** ** The caller retains ownership of pVector. If pVector is a TK_SELECT, -** then the returne object will reference pVector and so pVector must remain +** then the returned object will reference pVector and so pVector must remain ** valid for the life of the returned object. If pVector is a TK_VECTOR ** or a scalar expression, then it can be deleted as soon as this routine ** returns. From ed24da4b16fe235e0a5e9c41f92bfb45b5f3448e Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 6 Sep 2016 14:37:05 +0000 Subject: [PATCH 87/97] Enhance the sqlite3GetTempRange() and sqlite3ReleaseTempRange() internal routines so that they use sqlite3GetTempReg() and sqlite3ReleaseTempReg() when nReg==1. FossilOrigin-Name: 4071da2f87a2f24a279ac3bced8c794ad374b47c --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/expr.c | 7 ++++++- src/select.c | 6 +----- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/manifest b/manifest index 8e1a6947e5..3f3f165fd4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\stypo\sin\sa\scomment\sin\sexpr.c. -D 2016-09-06T12:04:50.217 +C Enhance\sthe\ssqlite3GetTempRange()\sand\ssqlite3ReleaseTempRange()\sinternal\nroutines\sso\sthat\sthey\suse\ssqlite3GetTempReg()\sand\ssqlite3ReleaseTempReg()\nwhen\snReg==1. +D 2016-09-06T14:37:05.419 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 17af5dc7326543ce395503adc1b88b0a722fcee3 +F src/expr.c 4c2b9886927fb6628560ad40d14b04946d71abbd F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -385,7 +385,7 @@ F src/printf.c a5f0ca08ddede803c241266abb46356ec748ded1 F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 F src/resolve.c 24f40fd0c3475821d1ad762a3f2c3455cc839b42 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac -F src/select.c 38216d0b2d42a0f475abf86a84c3499e6421ba29 +F src/select.c 4ccfc554a2d2313b0ac364120b1db9b74c41b248 F src/shell.c 79dda477be6c96eba6e952a934957ad36f87acc7 F src/sqlite.h.in 4a030e254e204570444b34bf7d40fb4a5416089e F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 @@ -1522,7 +1522,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 e8f105c3009e9b667db2afc0088b020a2b94d370 -R 48ea3338c1b6c2a2aac4971ca7c78062 -U dan -Z 231ae31b564ec961477419ce2ce3ecda +P 288e934f356ff6276b3e7581ac0f998ca6e93610 +R 373bd5ec1c7378267f0ce69533be2f50 +U drh +Z c12fde59fd4c6cc2e37d73169853128b diff --git a/manifest.uuid b/manifest.uuid index ea64ef4497..aeeeb90ec5 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -288e934f356ff6276b3e7581ac0f998ca6e93610 \ No newline at end of file +4071da2f87a2f24a279ac3bced8c794ad374b47c \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index f0bfdb0a7a..a83ede6cb5 100644 --- a/src/expr.c +++ b/src/expr.c @@ -4991,10 +4991,11 @@ void sqlite3ReleaseTempReg(Parse *pParse, int iReg){ } /* -** Allocate or deallocate a block of nReg consecutive registers +** Allocate or deallocate a block of nReg consecutive registers. */ int sqlite3GetTempRange(Parse *pParse, int nReg){ int i, n; + if( nReg==1 ) return sqlite3GetTempReg(pParse); i = pParse->iRangeReg; n = pParse->nRangeReg; if( nReg<=n ){ @@ -5008,6 +5009,10 @@ int sqlite3GetTempRange(Parse *pParse, int nReg){ return i; } void sqlite3ReleaseTempRange(Parse *pParse, int iReg, int nReg){ + if( nReg==1 ){ + sqlite3ReleaseTempReg(pParse, iReg); + return; + } sqlite3ExprCacheRemove(pParse, iReg, nReg); if( nReg>pParse->nRangeReg ){ pParse->nRangeReg = nReg; diff --git a/src/select.c b/src/select.c index 809285ea89..e5bb4a748a 100644 --- a/src/select.c +++ b/src/select.c @@ -1222,14 +1222,10 @@ static void generateSortTail( regRowid = 0; regRow = pDest->iSdst; nSortData = nColumn; - }else if( eDest==SRT_Set ){ + }else{ regRowid = sqlite3GetTempReg(pParse); regRow = sqlite3GetTempRange(pParse, nColumn); nSortData = nColumn; - }else{ - regRowid = sqlite3GetTempReg(pParse); - regRow = sqlite3GetTempReg(pParse); - nSortData = 1; } nKey = pOrderBy->nExpr - pSort->nOBSat; if( pSort->sortFlags & SORTFLAG_UseSorter ){ From 83c434e68d61531902d32a42d28a8a0d240c47f8 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 6 Sep 2016 14:58:15 +0000 Subject: [PATCH 88/97] Fix a problem handling (a, b) IN (SELECT ...) expressions when there is an index on just one of "a" or "b". FossilOrigin-Name: 231c72d9f651f3a70d5c8af080f3ff181b89d939 --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/wherecode.c | 13 +++++++++++-- test/rowvalue9.test | 30 ++++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index 3f3f165fd4..63b1c19384 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Enhance\sthe\ssqlite3GetTempRange()\sand\ssqlite3ReleaseTempRange()\sinternal\nroutines\sso\sthat\sthey\suse\ssqlite3GetTempReg()\sand\ssqlite3ReleaseTempReg()\nwhen\snReg==1. -D 2016-09-06T14:37:05.419 +C Fix\sa\sproblem\shandling\s(a,\sb)\sIN\s(SELECT\s...)\sexpressions\swhen\sthere\sis\san\sindex\son\sjust\sone\sof\s"a"\sor\s"b". +D 2016-09-06T14:58:15.417 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -467,7 +467,7 @@ F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 F src/where.c 48d705e5196a0611a7be90698eade455ee238536 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 -F src/wherecode.c 8a9a53cb52dd8a75e07c85e3bc12c1604c735954 +F src/wherecode.c 7279bb68e2f947ac5fe4c1fc655757b24878d541 F src/whereexpr.c c5ec87e234faf62ac2d4e7f7ce18fb1f4bd475ff F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd @@ -1028,7 +1028,7 @@ F test/rowvalue5.test a440d490c8c0bf606034c09d5c6bbf7840b98f95 F test/rowvalue6.test d19b54feb604d5601f8614b15e214e0774c01087 F test/rowvalue7.test 5d06ff19d9e6969e574a2e662a531dd0c67801a8 F test/rowvalue8.test 5900eddad9e2c3c2e26f1a95f74aafc1232ee5e0 -F test/rowvalue9.test 749a471f237f3fcfdfbd902f850adff2d9963560 +F test/rowvalue9.test 480c60654941bebdc2d0caa0104df7ca358cbed2 F test/rowvaluefault.test 7b16485e3f2b371f3e3d05455b8ded6d0c090244 F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09 @@ -1522,7 +1522,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 288e934f356ff6276b3e7581ac0f998ca6e93610 -R 373bd5ec1c7378267f0ce69533be2f50 -U drh -Z c12fde59fd4c6cc2e37d73169853128b +P 4071da2f87a2f24a279ac3bced8c794ad374b47c +R 57b76f412decde1968bde74dac3d47dc +U dan +Z 9ca614bd00458fc036a54c6c73063696 diff --git a/manifest.uuid b/manifest.uuid index aeeeb90ec5..81e40c5b8e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4071da2f87a2f24a279ac3bced8c794ad374b47c \ No newline at end of file +231c72d9f651f3a70d5c8af080f3ff181b89d939 \ No newline at end of file diff --git a/src/wherecode.c b/src/wherecode.c index 896509bc32..2de1d3ca96 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -463,11 +463,20 @@ static int codeEqualityTerm( } } if( !db->mallocFailed ){ + Expr *pLeft = pX->pLeft; + /* Take care here not to generate a TK_VECTOR containing only a + ** single value. Since the parser never creates such a vector, some + ** of the subroutines do not handle this case. */ + if( pLhs->nExpr==1 ){ + pX->pLeft = pLhs->a[0].pExpr; + }else{ + pLeft->x.pList = pLhs; + } 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; + pLeft->x.pList = pOrigLhs; + pX->pLeft = pLeft; } sqlite3ExprListDelete(pParse->db, pLhs); sqlite3ExprListDelete(pParse->db, pRhs); diff --git a/test/rowvalue9.test b/test/rowvalue9.test index 214eaccb7e..a906c4e67c 100644 --- a/test/rowvalue9.test +++ b/test/rowvalue9.test @@ -18,6 +18,19 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl set ::testprefix rowvalue9 +# Tests: +# +# 1.*: Test that affinities are handled correctly by various row-value +# operations without indexes. +# +# 2.*: Test an affinity bug that came up during testing. +# +# 3.*: Test a row-value version of the bug tested by 2.*. +# +# 4.*: Test that affinities are handled correctly by various row-value +# operations with assorted indexes. +# + do_execsql_test 1.0.1 { CREATE TABLE a1(c, b INTEGER, a TEXT, PRIMARY KEY(a, b)); @@ -206,5 +219,22 @@ foreach {tn idx} { } {2 4} } +do_execsql_test 5.0 { + CREATE TABLE e1(a TEXT, c NUMERIC); + CREATE TABLE e2(x BLOB, y BLOB); + + INSERT INTO e1 VALUES(2, 2); + + INSERT INTO e2 VALUES ('2', 2); + INSERT INTO e2 VALUES ('2', '2'); + INSERT INTO e2 VALUES ('2', '2.0'); + + CREATE INDEX e1c ON e1(c); +} + +do_execsql_test 5.1 { + SELECT rowid FROM e1 WHERE (a, c) IN (SELECT x, y FROM e2); +} {1} + finish_test From 099a0f5f54ee8108b4b131fecc5f20a0005cdd58 Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 6 Sep 2016 15:25:53 +0000 Subject: [PATCH 89/97] Fix the header comment on codeEqualityTerm(). FossilOrigin-Name: b7e710e406ed22bcc316099b5e200b6bb2d9c872 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/wherecode.c | 6 +++++- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 63b1c19384..756ea13b46 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sproblem\shandling\s(a,\sb)\sIN\s(SELECT\s...)\sexpressions\swhen\sthere\sis\san\sindex\son\sjust\sone\sof\s"a"\sor\s"b". -D 2016-09-06T14:58:15.417 +C Fix\sthe\sheader\scomment\son\scodeEqualityTerm(). +D 2016-09-06T15:25:53.108 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -467,7 +467,7 @@ F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 F src/where.c 48d705e5196a0611a7be90698eade455ee238536 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 -F src/wherecode.c 7279bb68e2f947ac5fe4c1fc655757b24878d541 +F src/wherecode.c 49125c4db8fbbf1a7013aa261d0d55230ae1b71a F src/whereexpr.c c5ec87e234faf62ac2d4e7f7ce18fb1f4bd475ff F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd @@ -1522,7 +1522,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 4071da2f87a2f24a279ac3bced8c794ad374b47c -R 57b76f412decde1968bde74dac3d47dc -U dan -Z 9ca614bd00458fc036a54c6c73063696 +P 231c72d9f651f3a70d5c8af080f3ff181b89d939 +R f1724dce64a0910d56689218d8ab18df +U drh +Z ba82e808b5a71f302b7f5c7627019f3d diff --git a/manifest.uuid b/manifest.uuid index 81e40c5b8e..b81d39b24f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -231c72d9f651f3a70d5c8af080f3ff181b89d939 \ No newline at end of file +b7e710e406ed22bcc316099b5e200b6bb2d9c872 \ No newline at end of file diff --git a/src/wherecode.c b/src/wherecode.c index 2de1d3ca96..03d1ac16df 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -382,7 +382,11 @@ static void updateRangeAffinityStr( ** term can be either X=expr or X IN (...). pTerm is the term to be ** coded. ** -** The current value for the constraint is left in register iReg. +** The current value for the constraint is left in a register, the index +** of which is returned. An attempt is made store the result in iTarget but +** this is only guaranteed for TK_ISNULL and TK_IN constraints. If the +** constraint is a TK_EQ or TK_IS, then the current value might be left in +** some other register and it is the caller's responsibility to compensate. ** ** For a constraint of the form X=expr, the expression is evaluated in ** straight-line code. For constraints of the form X IN (...) From 1c12657f9a5527a72986ce4fc0487ea5f5915401 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 6 Sep 2016 16:33:24 +0000 Subject: [PATCH 90/97] Simplify the fix in commit [7d9bd22c]. FossilOrigin-Name: bd5a342008575bf66f63881a0bebf43741f2a67b --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/wherecode.c | 24 +++++++++++++----------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/manifest b/manifest index 756ea13b46..97b5702545 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sthe\sheader\scomment\son\scodeEqualityTerm(). -D 2016-09-06T15:25:53.108 +C Simplify\sthe\sfix\sin\scommit\s[7d9bd22c]. +D 2016-09-06T16:33:24.850 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -467,7 +467,7 @@ F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 F src/where.c 48d705e5196a0611a7be90698eade455ee238536 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 -F src/wherecode.c 49125c4db8fbbf1a7013aa261d0d55230ae1b71a +F src/wherecode.c ea92c232f9de5bf8a719356f8a5b028e7a287d93 F src/whereexpr.c c5ec87e234faf62ac2d4e7f7ce18fb1f4bd475ff F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd @@ -1522,7 +1522,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 231c72d9f651f3a70d5c8af080f3ff181b89d939 -R f1724dce64a0910d56689218d8ab18df -U drh -Z ba82e808b5a71f302b7f5c7627019f3d +P b7e710e406ed22bcc316099b5e200b6bb2d9c872 +R 7abdcc30d48bf004d77a6f8aa0e43c0f +U dan +Z 63aca3ea6fe182a1ada6faadbb0dd4d6 diff --git a/manifest.uuid b/manifest.uuid index b81d39b24f..233e543061 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b7e710e406ed22bcc316099b5e200b6bb2d9c872 \ No newline at end of file +bd5a342008575bf66f63881a0bebf43741f2a67b \ No newline at end of file diff --git a/src/wherecode.c b/src/wherecode.c index 03d1ac16df..13904c9c8e 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -663,25 +663,27 @@ static int codeAllEqualityTerms( testcase( pTerm->eOperator & WO_ISNULL ); testcase( pTerm->eOperator & WO_IN ); if( (pTerm->eOperator & WO_ISNULL)==0 ){ - Expr *pRight = 0; if( pTerm->eOperator & WO_IN ){ if( pTerm->pExpr->flags & EP_xIsSelect ){ - int iField = pTerm->iField ? pTerm->iField-1 : 0; - pRight = pTerm->pExpr->x.pSelect->pEList->a[iField].pExpr; + /* No affinity ever needs to be (or should be) applied to a value + ** from the RHS of an "? IN (SELECT ...)" expression. The + ** sqlite3FindInIndex() routine has already ensured that the + ** affinity of the comparison has been applied to the value. */ + zAff[j] = SQLITE_AFF_BLOB; } }else{ - pRight = pTerm->pExpr->pRight; + Expr *pRight = pTerm->pExpr->pRight; if( (pTerm->wtFlags & TERM_IS)==0 && sqlite3ExprCanBeNull(pRight) ){ sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk); VdbeCoverage(v); } - } - if( pRight && zAff ){ - if( sqlite3CompareAffinity(pRight, zAff[j])==SQLITE_AFF_BLOB ){ - zAff[j] = SQLITE_AFF_BLOB; - } - if( sqlite3ExprNeedsNoAffinityChange(pRight, zAff[j]) ){ - zAff[j] = SQLITE_AFF_BLOB; + if( zAff ){ + if( sqlite3CompareAffinity(pRight, zAff[j])==SQLITE_AFF_BLOB ){ + zAff[j] = SQLITE_AFF_BLOB; + } + if( sqlite3ExprNeedsNoAffinityChange(pRight, zAff[j]) ){ + zAff[j] = SQLITE_AFF_BLOB; + } } } } From aaf8a0643e46e6c4a7ca5b83f0b779c21499b542 Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 6 Sep 2016 16:53:53 +0000 Subject: [PATCH 91/97] Avoid a NULL pointer deref in codeAllEqualityConstraints() following an OOM. FossilOrigin-Name: c6e528c1c0f17c69c7745b018afa36694118258f --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/wherecode.c | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 97b5702545..15e35ee022 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Simplify\sthe\sfix\sin\scommit\s[7d9bd22c]. -D 2016-09-06T16:33:24.850 +C Avoid\sa\sNULL\spointer\sderef\sin\scodeAllEqualityConstraints()\sfollowing\san\sOOM. +D 2016-09-06T16:53:53.727 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -467,7 +467,7 @@ F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 F src/where.c 48d705e5196a0611a7be90698eade455ee238536 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 -F src/wherecode.c ea92c232f9de5bf8a719356f8a5b028e7a287d93 +F src/wherecode.c 4bd3e4c3c9f441a9d1fbd8fb13528f9c1c2c8528 F src/whereexpr.c c5ec87e234faf62ac2d4e7f7ce18fb1f4bd475ff F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd @@ -1522,7 +1522,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 b7e710e406ed22bcc316099b5e200b6bb2d9c872 -R 7abdcc30d48bf004d77a6f8aa0e43c0f -U dan -Z 63aca3ea6fe182a1ada6faadbb0dd4d6 +P bd5a342008575bf66f63881a0bebf43741f2a67b +R fcb091661f586511d74c9857242646c5 +U drh +Z 30f14ba82e1e5f327ee9c3b7b6a25371 diff --git a/manifest.uuid b/manifest.uuid index 233e543061..b0f1c15887 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bd5a342008575bf66f63881a0bebf43741f2a67b \ No newline at end of file +c6e528c1c0f17c69c7745b018afa36694118258f \ No newline at end of file diff --git a/src/wherecode.c b/src/wherecode.c index 13904c9c8e..0b1165f024 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -669,7 +669,7 @@ static int codeAllEqualityTerms( ** from the RHS of an "? IN (SELECT ...)" expression. The ** sqlite3FindInIndex() routine has already ensured that the ** affinity of the comparison has been applied to the value. */ - zAff[j] = SQLITE_AFF_BLOB; + if( zAff ) zAff[j] = SQLITE_AFF_BLOB; } }else{ Expr *pRight = pTerm->pExpr->pRight; From c7a77ae1c61f66a2ec94de49a4a92872faf4f0c9 Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 6 Sep 2016 17:13:40 +0000 Subject: [PATCH 92/97] Avoid unnecessary memory allocations for aiMap in codeEqualityConstraints. FossilOrigin-Name: 70319c3d76abd1e403fcf2a62668736b92a5f3d5 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/wherecode.c | 8 +++----- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index 15e35ee022..5f69a2d824 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Avoid\sa\sNULL\spointer\sderef\sin\scodeAllEqualityConstraints()\sfollowing\san\sOOM. -D 2016-09-06T16:53:53.727 +C Avoid\sunnecessary\smemory\sallocations\sfor\saiMap\sin\scodeEqualityConstraints. +D 2016-09-06T17:13:40.420 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -467,7 +467,7 @@ F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 F src/where.c 48d705e5196a0611a7be90698eade455ee238536 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 -F src/wherecode.c 4bd3e4c3c9f441a9d1fbd8fb13528f9c1c2c8528 +F src/wherecode.c b0d4febdd9de07ad68faadeacb30df9785f9b979 F src/whereexpr.c c5ec87e234faf62ac2d4e7f7ce18fb1f4bd475ff F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd @@ -1522,7 +1522,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 bd5a342008575bf66f63881a0bebf43741f2a67b -R fcb091661f586511d74c9857242646c5 +P c6e528c1c0f17c69c7745b018afa36694118258f +R 71337de042abb55d5e21dbccb2bcc3d7 U drh -Z 30f14ba82e1e5f327ee9c3b7b6a25371 +Z ad68562def745ab96bb3491072f1a7f4 diff --git a/manifest.uuid b/manifest.uuid index b0f1c15887..728933c62b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c6e528c1c0f17c69c7745b018afa36694118258f \ No newline at end of file +70319c3d76abd1e403fcf2a62668736b92a5f3d5 \ No newline at end of file diff --git a/src/wherecode.c b/src/wherecode.c index 0b1165f024..0524ae2771 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -442,11 +442,6 @@ static int codeEqualityTerm( if( ALWAYS(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{ @@ -475,9 +470,12 @@ static int codeEqualityTerm( pX->pLeft = pLhs->a[0].pExpr; }else{ pLeft->x.pList = pLhs; + aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int) * nEq); + testcase( aiMap==0 ); } pX->x.pSelect->pEList = pRhs; eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap); + testcase( aiMap!=0 && aiMap[0]!=0 ); pX->x.pSelect->pEList = pOrigRhs; pLeft->x.pList = pOrigLhs; pX->pLeft = pLeft; From 773d3afaa3780149cf7482a01f1609520ebcee34 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 6 Sep 2016 17:21:17 +0000 Subject: [PATCH 93/97] Remove an unnecessary branch from expr.c. FossilOrigin-Name: 7cc9746c5414e02012efb8180f9eca2646800e74 --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/expr.c | 2 +- test/rowvalue9.test | 6 ++++++ 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 5f69a2d824..378bf07279 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Avoid\sunnecessary\smemory\sallocations\sfor\saiMap\sin\scodeEqualityConstraints. -D 2016-09-06T17:13:40.420 +C Remove\san\sunnecessary\sbranch\sfrom\sexpr.c. +D 2016-09-06T17:21:17.043 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05 -F src/expr.c 4c2b9886927fb6628560ad40d14b04946d71abbd +F src/expr.c 028c34005cb804abe8f73453ac08baa44f4b63f9 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 @@ -1028,7 +1028,7 @@ F test/rowvalue5.test a440d490c8c0bf606034c09d5c6bbf7840b98f95 F test/rowvalue6.test d19b54feb604d5601f8614b15e214e0774c01087 F test/rowvalue7.test 5d06ff19d9e6969e574a2e662a531dd0c67801a8 F test/rowvalue8.test 5900eddad9e2c3c2e26f1a95f74aafc1232ee5e0 -F test/rowvalue9.test 480c60654941bebdc2d0caa0104df7ca358cbed2 +F test/rowvalue9.test 03a5edc94ab5983b98820f6541c62b43560cd84c F test/rowvaluefault.test 7b16485e3f2b371f3e3d05455b8ded6d0c090244 F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09 @@ -1522,7 +1522,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 c6e528c1c0f17c69c7745b018afa36694118258f -R 71337de042abb55d5e21dbccb2bcc3d7 -U drh -Z ad68562def745ab96bb3491072f1a7f4 +P 70319c3d76abd1e403fcf2a62668736b92a5f3d5 +R e9d987800340c93d5c8b755d8bc8e3a7 +U dan +Z f81283c76e744b298a5230c53cc06749 diff --git a/manifest.uuid b/manifest.uuid index 728933c62b..a94fb71aeb 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -70319c3d76abd1e403fcf2a62668736b92a5f3d5 \ No newline at end of file +7cc9746c5414e02012efb8180f9eca2646800e74 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index a83ede6cb5..80497be111 100644 --- a/src/expr.c +++ b/src/expr.c @@ -2482,7 +2482,7 @@ int sqlite3CodeSubselect( assert( pEList->nExpr>0 ); assert( sqlite3KeyInfoIsWriteable(pKeyInfo) ); for(i=0; i1) ? sqlite3VectorFieldSubexpr(pLeft, i) : pLeft; + Expr *p = sqlite3VectorFieldSubexpr(pLeft, i); pKeyInfo->aColl[i] = sqlite3BinaryCompareCollSeq( pParse, p, pEList->a[i].pExpr ); diff --git a/test/rowvalue9.test b/test/rowvalue9.test index a906c4e67c..570a5b98a8 100644 --- a/test/rowvalue9.test +++ b/test/rowvalue9.test @@ -235,6 +235,12 @@ do_execsql_test 5.0 { do_execsql_test 5.1 { SELECT rowid FROM e1 WHERE (a, c) IN (SELECT x, y FROM e2); } {1} +do_execsql_test 5.2 { + SELECT rowid FROM e2 WHERE rowid IN (SELECT +c FROM e1); +} {2} +do_execsql_test 5.3 { + SELECT rowid FROM e2 WHERE rowid IN (SELECT 0+c FROM e1); +} {2} finish_test From 1431807a0bd52a544b9a31d6f7324f8be7e7150c Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 6 Sep 2016 18:51:25 +0000 Subject: [PATCH 94/97] Fix corer cases of vector IN operators where the RHS is a compound SELECT that includes an ORDER BY clause. FossilOrigin-Name: 8329ac6f8d1edcc19c3e0559abe9a8011dbe1497 --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/select.c | 10 ++++------ src/whereexpr.c | 6 +++++- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/manifest b/manifest index 378bf07279..8def3253ea 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\san\sunnecessary\sbranch\sfrom\sexpr.c. -D 2016-09-06T17:21:17.043 +C Fix\scorer\scases\sof\svector\sIN\soperators\swhere\sthe\sRHS\sis\sa\scompound\sSELECT\nthat\sincludes\san\sORDER\sBY\sclause. +D 2016-09-06T18:51:25.666 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -385,7 +385,7 @@ F src/printf.c a5f0ca08ddede803c241266abb46356ec748ded1 F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 F src/resolve.c 24f40fd0c3475821d1ad762a3f2c3455cc839b42 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac -F src/select.c 4ccfc554a2d2313b0ac364120b1db9b74c41b248 +F src/select.c d6c1a6463a5e34352691e77f1475a63406c3984f F src/shell.c 79dda477be6c96eba6e952a934957ad36f87acc7 F src/sqlite.h.in 4a030e254e204570444b34bf7d40fb4a5416089e F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 @@ -468,7 +468,7 @@ F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 F src/where.c 48d705e5196a0611a7be90698eade455ee238536 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 F src/wherecode.c b0d4febdd9de07ad68faadeacb30df9785f9b979 -F src/whereexpr.c c5ec87e234faf62ac2d4e7f7ce18fb1f4bd475ff +F src/whereexpr.c e3db778ed205e982f31960896db71c50612ae009 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -1522,7 +1522,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 70319c3d76abd1e403fcf2a62668736b92a5f3d5 -R e9d987800340c93d5c8b755d8bc8e3a7 -U dan -Z f81283c76e744b298a5230c53cc06749 +P 7cc9746c5414e02012efb8180f9eca2646800e74 +R 36f92754f7a81ed3dc243351ed6e664e +U drh +Z 50c7e7d129c621501eed665f9216f5e0 diff --git a/manifest.uuid b/manifest.uuid index a94fb71aeb..6e1c09ed3d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7cc9746c5414e02012efb8180f9eca2646800e74 \ No newline at end of file +8329ac6f8d1edcc19c3e0559abe9a8011dbe1497 \ No newline at end of file diff --git a/src/select.c b/src/select.c index e5bb4a748a..aa714ceb48 100644 --- a/src/select.c +++ b/src/select.c @@ -2631,17 +2631,15 @@ static int generateOutputSubroutine( } #ifndef SQLITE_OMIT_SUBQUERY - /* If we are creating a set for an "expr IN (SELECT ...)" construct, - ** then there should be a single item on the stack. Write this - ** item into the set table with bogus data. + /* If we are creating a set for an "expr IN (SELECT ...)". */ case SRT_Set: { int r1; - assert( pIn->nSdst==1 || pParse->nErr>0 ); + testcase( pIn->nSdst>1 && pParse->nErr==0 ); r1 = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst, - r1, pDest->zAffSdst,1); - sqlite3ExprCacheAffinityChange(pParse, pIn->iSdst, 1); + r1, pDest->zAffSdst, pIn->nSdst); + sqlite3ExprCacheAffinityChange(pParse, pIn->iSdst, pIn->nSdst); sqlite3VdbeAddOp2(v, OP_IdxInsert, pDest->iSDParm, r1); sqlite3ReleaseTempReg(pParse, r1); break; diff --git a/src/whereexpr.c b/src/whereexpr.c index 92aed29bbc..e1059f73b4 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -1209,9 +1209,13 @@ static void exprAnalyze( ** a virtual term for each vector component. The expression object ** used by each such virtual term is pExpr (the full vector IN(...) ** expression). The WhereTerm.iField variable identifies the index within - ** the vector on the LHS that the virtual term represents. */ + ** the vector on the LHS that the virtual term represents. + ** + ** This only works if the RHS is a simple SELECT, not a compound + */ if( pWC->op==TK_AND && pExpr->op==TK_IN && pTerm->iField==0 && pExpr->pLeft->op==TK_VECTOR + && pExpr->x.pSelect->pPrior==0 ){ int i; for(i=0; ipLeft); i++){ From 63cecc41735c42f0fa42dd5a6e5fe7e0f69cc7be Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 6 Sep 2016 19:08:21 +0000 Subject: [PATCH 95/97] Remove obsolete vector-IN test cases. Fix a bad testcase() macro. FossilOrigin-Name: ab3f8f193a7ec36018bf26c9231a1a6a58b6a523 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/select.c | 2 +- test/rowvalue5.test | 14 ++++++-------- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/manifest b/manifest index 8def3253ea..b6583d963c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\scorer\scases\sof\svector\sIN\soperators\swhere\sthe\sRHS\sis\sa\scompound\sSELECT\nthat\sincludes\san\sORDER\sBY\sclause. -D 2016-09-06T18:51:25.666 +C Remove\sobsolete\svector-IN\stest\scases.\s\sFix\sa\sbad\stestcase()\smacro. +D 2016-09-06T19:08:21.705 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -385,7 +385,7 @@ F src/printf.c a5f0ca08ddede803c241266abb46356ec748ded1 F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 F src/resolve.c 24f40fd0c3475821d1ad762a3f2c3455cc839b42 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac -F src/select.c d6c1a6463a5e34352691e77f1475a63406c3984f +F src/select.c 244f9cc5e4662987cd2ef5c22d1b7027560f3425 F src/shell.c 79dda477be6c96eba6e952a934957ad36f87acc7 F src/sqlite.h.in 4a030e254e204570444b34bf7d40fb4a5416089e F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 @@ -1024,7 +1024,7 @@ F test/rowvalue.test 753eb744b7efeb5ac643d35d6e1e5066452ccf79 F test/rowvalue2.test 060d238b7e5639a7c5630cb5e63e311b44efef2b F test/rowvalue3.test 01399b7bf150b0d41abce76c18072da777c2500c F test/rowvalue4.test 4b556d7de161a0dd8cff095c336e913986398bea -F test/rowvalue5.test a440d490c8c0bf606034c09d5c6bbf7840b98f95 +F test/rowvalue5.test c1adfb2ea104e181f70d55bbd80d803b9917b22b F test/rowvalue6.test d19b54feb604d5601f8614b15e214e0774c01087 F test/rowvalue7.test 5d06ff19d9e6969e574a2e662a531dd0c67801a8 F test/rowvalue8.test 5900eddad9e2c3c2e26f1a95f74aafc1232ee5e0 @@ -1522,7 +1522,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 7cc9746c5414e02012efb8180f9eca2646800e74 -R 36f92754f7a81ed3dc243351ed6e664e +P 8329ac6f8d1edcc19c3e0559abe9a8011dbe1497 +R c63acfebc9bb94150dde03620f804079 U drh -Z 50c7e7d129c621501eed665f9216f5e0 +Z 4492b1886315f856574968e31e230cb0 diff --git a/manifest.uuid b/manifest.uuid index 6e1c09ed3d..130764cfa2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8329ac6f8d1edcc19c3e0559abe9a8011dbe1497 \ No newline at end of file +ab3f8f193a7ec36018bf26c9231a1a6a58b6a523 \ No newline at end of file diff --git a/src/select.c b/src/select.c index aa714ceb48..2d4e901ba4 100644 --- a/src/select.c +++ b/src/select.c @@ -2635,7 +2635,7 @@ static int generateOutputSubroutine( */ case SRT_Set: { int r1; - testcase( pIn->nSdst>1 && pParse->nErr==0 ); + testcase( pIn->nSdst>1 ); r1 = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst, r1, pDest->zAffSdst, pIn->nSdst); diff --git a/test/rowvalue5.test b/test/rowvalue5.test index d5976ccb89..45d501b55a 100644 --- a/test/rowvalue5.test +++ b/test/rowvalue5.test @@ -96,15 +96,13 @@ foreach {tn where res} { 6 "(a, b) = (SELECT 9, 10)" {{a = 9 AND b = 10}} 7 "(+a, b) = (SELECT 'a', 'b')" {{b = 'b'}} 8 "(a, +b) = (SELECT 'a', 'b')" {{a = 'a'}} - 10 "(a, b) IN (SELECT 9, 10 UNION SELECT 11, 12)" - {{a = 9 AND b = 10} {a = 11 AND b = 12}} - 11 "(+a, b) IN (SELECT 'a', 'b')" {{b = 'b'}} - 12 "(a, +b) IN (SELECT 'a', 'b')" {{a = 'a'}} + 11 "(+a, b) IN (SELECT 'a', 'b')" {{b = 'b'}} + 12 "(a, +b) IN (SELECT 'a', 'b')" {{a = 'a'}} - 13 "(a, b) < ('d', 'e')" {{a <= 'd'}} - 14 "(a, b) < ('a', 'c')" {{a <= 'a'}} - 15 "(a, b) <= ('a', 'b')" {{a <= 'a'}} - 16 "(a, b) < ('a', 'b')" {} + 13 "(a, b) < ('d', 'e')" {{a <= 'd'}} + 14 "(a, b) < ('a', 'c')" {{a <= 'a'}} + 15 "(a, b) <= ('a', 'b')" {{a <= 'a'}} + 16 "(a, b) < ('a', 'b')" {} } { do_execsql_test 1.$tn "SELECT expr FROM x1 WHERE $where" $res } From c097e122b90fceccc510049eb1cb438fc0bec5e7 Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 7 Sep 2016 13:30:40 +0000 Subject: [PATCH 96/97] Simplify the affinity handling logic in codeAllEqualityTerms(). Logically the same, just a little easier to read and understand. FossilOrigin-Name: bbab9621f512b04684163b98b6fc669c68038044 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/wherecode.c | 42 +++++++++++++++++++----------------------- 3 files changed, 26 insertions(+), 30 deletions(-) diff --git a/manifest b/manifest index d9dec4830b..2b16d0533f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sfixes\sfrom\strunk. -D 2016-09-07T13:12:13.158 +C Simplify\sthe\saffinity\shandling\slogic\sin\scodeAllEqualityTerms().\s\sLogically\nthe\ssame,\sjust\sa\slittle\seasier\sto\sread\sand\sunderstand. +D 2016-09-07T13:30:40.808 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -467,7 +467,7 @@ F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 F src/where.c edbd73a87ba2e186928e9bfc14348b1bbb2628c5 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 -F src/wherecode.c b0d4febdd9de07ad68faadeacb30df9785f9b979 +F src/wherecode.c 43522ac811e2e0374603f9fc9a3dda4e1a81f470 F src/whereexpr.c e3db778ed205e982f31960896db71c50612ae009 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd @@ -1522,7 +1522,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 ab3f8f193a7ec36018bf26c9231a1a6a58b6a523 d8451fe84d09db6ec7e1bd5f0708ea1b5e85f3d6 -R 9be49f79015d1d1f29328535fe4e7eb6 +P 193f036c87857bd77577ceb462af5034c7cc77da +R 5310dec13b5e77c999328204c9245316 U drh -Z 2891e6cf4d042c296a4853701a08d60f +Z 06433047ae27fe630ae84491904304f6 diff --git a/manifest.uuid b/manifest.uuid index c5b7f13bc8..e850f3a033 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -193f036c87857bd77577ceb462af5034c7cc77da \ No newline at end of file +bbab9621f512b04684163b98b6fc669c68038044 \ No newline at end of file diff --git a/src/wherecode.c b/src/wherecode.c index 0524ae2771..fdb986cbdf 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -658,30 +658,26 @@ static int codeAllEqualityTerms( sqlite3VdbeAddOp2(v, OP_SCopy, r1, regBase+j); } } - testcase( pTerm->eOperator & WO_ISNULL ); - testcase( pTerm->eOperator & WO_IN ); - if( (pTerm->eOperator & WO_ISNULL)==0 ){ - if( pTerm->eOperator & WO_IN ){ - if( pTerm->pExpr->flags & EP_xIsSelect ){ - /* No affinity ever needs to be (or should be) applied to a value - ** from the RHS of an "? IN (SELECT ...)" expression. The - ** sqlite3FindInIndex() routine has already ensured that the - ** affinity of the comparison has been applied to the value. */ - if( zAff ) zAff[j] = SQLITE_AFF_BLOB; + if( pTerm->eOperator & WO_IN ){ + if( pTerm->pExpr->flags & EP_xIsSelect ){ + /* No affinity ever needs to be (or should be) applied to a value + ** from the RHS of an "? IN (SELECT ...)" expression. The + ** sqlite3FindInIndex() routine has already ensured that the + ** affinity of the comparison has been applied to the value. */ + if( zAff ) zAff[j] = SQLITE_AFF_BLOB; + } + }else if( (pTerm->eOperator & WO_ISNULL)==0 ){ + Expr *pRight = pTerm->pExpr->pRight; + if( (pTerm->wtFlags & TERM_IS)==0 && sqlite3ExprCanBeNull(pRight) ){ + sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk); + VdbeCoverage(v); + } + if( zAff ){ + if( sqlite3CompareAffinity(pRight, zAff[j])==SQLITE_AFF_BLOB ){ + zAff[j] = SQLITE_AFF_BLOB; } - }else{ - Expr *pRight = pTerm->pExpr->pRight; - if( (pTerm->wtFlags & TERM_IS)==0 && sqlite3ExprCanBeNull(pRight) ){ - sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk); - VdbeCoverage(v); - } - if( zAff ){ - if( sqlite3CompareAffinity(pRight, zAff[j])==SQLITE_AFF_BLOB ){ - zAff[j] = SQLITE_AFF_BLOB; - } - if( sqlite3ExprNeedsNoAffinityChange(pRight, zAff[j]) ){ - zAff[j] = SQLITE_AFF_BLOB; - } + if( sqlite3ExprNeedsNoAffinityChange(pRight, zAff[j]) ){ + zAff[j] = SQLITE_AFF_BLOB; } } } From 26c8d0ca21d2254b9906ac3dcc02cb82ac0604b7 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 7 Sep 2016 19:37:20 +0000 Subject: [PATCH 97/97] Fix a problem handling expressions like "(a, b) IN (SELECT ... ORDER BY 1, 2)" when there is an index on "a" but not "b". FossilOrigin-Name: 7f2c5c9ee3628c968306a5ab2e5a9a761f1b8055 --- manifest | 16 ++++++------- manifest.uuid | 2 +- src/wherecode.c | 22 +++++++++++++++--- test/rowvalue9.test | 56 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index 2b16d0533f..274df7eb97 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Simplify\sthe\saffinity\shandling\slogic\sin\scodeAllEqualityTerms().\s\sLogically\nthe\ssame,\sjust\sa\slittle\seasier\sto\sread\sand\sunderstand. -D 2016-09-07T13:30:40.808 +C Fix\sa\sproblem\shandling\sexpressions\slike\s"(a,\sb)\sIN\s(SELECT\s...\sORDER\sBY\s1,\s2)"\swhen\sthere\sis\san\sindex\son\s"a"\sbut\snot\s"b". +D 2016-09-07T19:37:20.043 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c @@ -467,7 +467,7 @@ F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2 F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 F src/where.c edbd73a87ba2e186928e9bfc14348b1bbb2628c5 F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613 -F src/wherecode.c 43522ac811e2e0374603f9fc9a3dda4e1a81f470 +F src/wherecode.c d172dcf99932ba698dd304edc9a368cd52b4b2e5 F src/whereexpr.c e3db778ed205e982f31960896db71c50612ae009 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd @@ -1028,7 +1028,7 @@ F test/rowvalue5.test c1adfb2ea104e181f70d55bbd80d803b9917b22b F test/rowvalue6.test d19b54feb604d5601f8614b15e214e0774c01087 F test/rowvalue7.test 5d06ff19d9e6969e574a2e662a531dd0c67801a8 F test/rowvalue8.test 5900eddad9e2c3c2e26f1a95f74aafc1232ee5e0 -F test/rowvalue9.test 03a5edc94ab5983b98820f6541c62b43560cd84c +F test/rowvalue9.test e24f9eb02baffc6a67b6eed9e40d4c612c98079d F test/rowvaluefault.test 7b16485e3f2b371f3e3d05455b8ded6d0c090244 F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09 @@ -1522,7 +1522,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 193f036c87857bd77577ceb462af5034c7cc77da -R 5310dec13b5e77c999328204c9245316 -U drh -Z 06433047ae27fe630ae84491904304f6 +P bbab9621f512b04684163b98b6fc669c68038044 +R 09b192e18dc26d97d5a589656b60205f +U dan +Z 3d9ebf68fced17edbc91ddfab98198fe diff --git a/manifest.uuid b/manifest.uuid index e850f3a033..a5783a2c58 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bbab9621f512b04684163b98b6fc669c68038044 \ No newline at end of file +7f2c5c9ee3628c968306a5ab2e5a9a761f1b8055 \ No newline at end of file diff --git a/src/wherecode.c b/src/wherecode.c index fdb986cbdf..c440c56de3 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -445,8 +445,9 @@ static int codeEqualityTerm( if( (pX->flags & EP_xIsSelect)==0 || pX->x.pSelect->pEList->nExpr==1 ){ eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0); }else{ + Select *pSelect = pX->x.pSelect; sqlite3 *db = pParse->db; - ExprList *pOrigRhs = pX->x.pSelect->pEList; + ExprList *pOrigRhs = pSelect->pEList; ExprList *pOrigLhs = pX->pLeft->x.pList; ExprList *pRhs = 0; /* New Select.pEList for RHS */ ExprList *pLhs = 0; /* New pX->pLeft vector */ @@ -463,6 +464,21 @@ static int codeEqualityTerm( } if( !db->mallocFailed ){ Expr *pLeft = pX->pLeft; + + if( pSelect->pOrderBy ){ + /* If the SELECT statement has an ORDER BY clause, zero the + ** iOrderByCol variables. These are set to non-zero when an + ** ORDER BY term exactly matches one of the terms of the + ** result-set. Since the result-set of the SELECT statement may + ** have been modified or reordered, these variables are no longer + ** set correctly. Since setting them is just an optimization, + ** it's easiest just to zero them here. */ + ExprList *pOrderBy = pSelect->pOrderBy; + for(i=0; inExpr; i++){ + pOrderBy->a[i].u.x.iOrderByCol = 0; + } + } + /* Take care here not to generate a TK_VECTOR containing only a ** single value. Since the parser never creates such a vector, some ** of the subroutines do not handle this case. */ @@ -473,10 +489,10 @@ static int codeEqualityTerm( aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int) * nEq); testcase( aiMap==0 ); } - pX->x.pSelect->pEList = pRhs; + pSelect->pEList = pRhs; eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap); testcase( aiMap!=0 && aiMap[0]!=0 ); - pX->x.pSelect->pEList = pOrigRhs; + pSelect->pEList = pOrigRhs; pLeft->x.pList = pOrigLhs; pX->pLeft = pLeft; } diff --git a/test/rowvalue9.test b/test/rowvalue9.test index 570a5b98a8..ce324f0b42 100644 --- a/test/rowvalue9.test +++ b/test/rowvalue9.test @@ -242,5 +242,61 @@ do_execsql_test 5.3 { SELECT rowid FROM e2 WHERE rowid IN (SELECT 0+c FROM e1); } {2} +#------------------------------------------------------------------------- +# +do_execsql_test 6.0 { + CREATE TABLE f1(a, b); + CREATE TABLE f2(c, d); + CREATE TABLE f3(e, f); +} + +do_execsql_test 6.1 { + SELECT * FROM f3 WHERE (e, f) IN ( + SELECT a, b FROM f1 UNION ALL SELECT c, d FROM f2 + ); +} +do_execsql_test 6.2 { + CREATE INDEX f3e ON f3(e); + SELECT * FROM f3 WHERE (e, f) IN ( + SELECT a, b FROM f1 UNION ALL SELECT c, d FROM f2 + ); +} + + +#------------------------------------------------------------------------- +# +do_execsql_test 7.0 { + CREATE TABLE g1(a, b); + INSERT INTO g1 VALUES + (1, 1), (1, 2), (1, 3), (1, 'i'), (1, 'j'), + (1, 6), (1, 7), (1, 8), (1, 9), (1, 10), + (1, 4), (1, 5); + + CREATE TABLE g2(x, y); + CREATE INDEX g2x ON g2(x); + + INSERT INTO g2 VALUES(1, 4); + INSERT INTO g2 VALUES(1, 5); +} + +do_execsql_test 7.1 { + SELECT * FROM g2 WHERE (x, y) IN ( + SELECT a, b FROM g1 ORDER BY +a, +b LIMIT 10 + ); +} { 1 4 1 5 } + +do_execsql_test 7.2 { + SELECT * FROM g2 WHERE (x, y) IN ( + SELECT a, b FROM g1 ORDER BY a, b LIMIT 10 + ); +} { 1 4 1 5 } + +do_execsql_test 7.3 { + SELECT * FROM g2 WHERE (x, y) IN ( + SELECT a, b FROM g1 ORDER BY 1, 2 LIMIT 10 + ); +} { 1 4 1 5 } + + finish_test