From: drh Date: Mon, 22 Aug 2016 14:30:05 +0000 (+0000) Subject: Fix the vector BETWEEN operator so that it only evaluates the left-most X-Git-Tag: version-3.15.0~110^2~53 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=12abf408ff0f89efce2405c172f130e7b2ea011d;p=thirdparty%2Fsqlite.git 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 --- 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