]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix a problem with IN(...) constraints where the LHS is a sub-select that is an aggre...
authordan <dan@noemail.net>
Mon, 1 Aug 2016 16:37:43 +0000 (16:37 +0000)
committerdan <dan@noemail.net>
Mon, 1 Aug 2016 16:37:43 +0000 (16:37 +0000)
FossilOrigin-Name: 1f4dba87da4a44ad26223ad965731164c0d9bad9

manifest
manifest.uuid
src/expr.c
src/select.c
src/whereexpr.c
test/rowvalue.test

index 6cdc700a0e5430fdd39a2eab77bd4f9c6b76bc80..ea0371ab19ea6c9fed0636664290bdc8aa1bfa34 100644 (file)
--- 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
index f6c7658784a98a9bce75742616b8ea47e59947ac..7f308a68d68c357f4f7ab56714cf8936a57ca74e 100644 (file)
@@ -1 +1 @@
-059d0d05354e6efab7892c97b339ffa0b5303587
\ No newline at end of file
+1f4dba87da4a44ad26223ad965731164c0d9bad9
\ No newline at end of file
index 5acce2f0c783c7a5fec21ff3b685be224465f3b2..3bbd015fd103fe7c4a3df12419efff840a422f3b 100644 (file)
@@ -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( i<sqlite3ExprVectorSize(pVector) );
+  if( sqlite3ExprIsVector(pVector) ){
+    if( pVector->op==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.
 **
-** 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 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 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 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.
 **
-** 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.
+** Before returning, output parameter (*ppExpr) is set to point to the
+** Expr object corresponding to element iElem of the vector.
 */
 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;
index f49a5c1fed3f4d396dc61a758cbe4107d65d68df..fb7217c1b4ad26690e9ec6372f33b709abae3dd4 100644 (file)
@@ -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)
index 5997da1e3a3b0cc27d7a57379d7701c9dbe5f546..33ce7edb38cc66dbd891a6e86cf9b8bc35e1ddd5 100644 (file)
@@ -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; i<sqlite3ExprVectorSize(pExpr->pLeft); 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);
index efa870009fb2c9051edf2a7489219fa884a58592..d8231563dc50a29527d827f037d97e451dadaf66 100644 (file)
@@ -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