]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix the vector BETWEEN operator so that it only evaluates the left-most
authordrh <drh@noemail.net>
Mon, 22 Aug 2016 14:30:05 +0000 (14:30 +0000)
committerdrh <drh@noemail.net>
Mon, 22 Aug 2016 14:30:05 +0000 (14:30 +0000)
vector expression once.  Add support for vector comparisons in the CASE
operator.

FossilOrigin-Name: 07e69f43a294d35b5145a2b0242ee42d50adab14

manifest
manifest.uuid
src/expr.c
test/e_expr.test
test/rowvalue8.test [new file with mode: 0644]

index ac59fb1a27bcb180eea70a5243efdf507a017704..21dcf79efec6a5d904f0cb6c1b91ac0ca0be2710 100644 (file)
--- 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
index 20c785fdcaf039416d999330d53e644ba12859ed..e35c3101301b52342937d38ded534b88010d7483 100644 (file)
@@ -1 +1 @@
-2f39987f21bd6dae8d2be610a1fd5f06f8878e9e
\ No newline at end of file
+07e69f43a294d35b5145a2b0242ee42d50adab14
\ No newline at end of file
index 88ff3e40f02185c54aef96f2c3204307c63107a9..b304e2783574596b4f1c73051b0700a5ac7fb089 100644 (file)
 */
 #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( i<sqlite3ExprVectorSize(pVector) );
   if( sqlite3ExprIsVector(pVector) ){
-    if( pVector->op==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; i<nVector; i++){
-      sqlite3VdbeAddOp3(v, OP_Copy, regSelect+i, r1+aiMap[i], 0);
-    }
-  }else{
-    for(i=0; i<nVector; i++){
-      Expr *pLhs = sqlite3VectorFieldSubexpr(pLeft, i);
-      sqlite3ExprCode(pParse, pLhs, r1+aiMap[i]);
-    }
+  r2 = exprCodeVector(pParse, pLeft, &iDummy);
+  for(i=0; i<nVector; i++){
+    sqlite3VdbeAddOp3(v, OP_Copy, r2+i, r1+aiMap[i], 0);
   }
 
   /* If sqlite3FindInIndex() did not find or create an index that is
@@ -3228,7 +3234,9 @@ static int usedAsColumnCache(Parse *pParse, int iFrom, int iTo){
 
 
 /*
-** Convert an expression node to a TK_REGISTER
+** Convert a scalar expression node to a TK_REGISTER referencing
+** register iReg.  The caller must ensure that iReg already contains
+** the correct value for the expression.
 */
 static void exprToRegister(Expr *p, int iReg){
   p->op2 = 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; i<nResult; i++){
+        sqlite3ExprCode(pParse, p->x.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, &regFree1));
+        exprToRegister(&tempX, exprCodeVector(pParse, &tempX, &regFree1));
         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, &regFree1));
-  }
+  exprToRegister(&exprX, exprCodeVector(pParse, &exprX, &regFree1));
   if( xJump ){
     xJump(pParse, &exprAnd, dest, jumpIfNull);
   }else{
index 6ef55ce8a65dcdd2c30858baa207321a420dd513..6165aa35885616a5e602789059abbc149a7b63f9 100644 (file)
@@ -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 (file)
index 0000000..432dad1
--- /dev/null
@@ -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