aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordan <dan@noemail.net>2016-08-01 16:37:43 +0000
committerdan <dan@noemail.net>2016-08-01 16:37:43 +0000
commit870a0705feb00a9c60c3442431beeeff37ca0c6b (patch)
tree769f2050c38d2f74429f6e816bb21fd6525b9fab /src
parent5c288b929a80fb532c00e74853f097b267eff860 (diff)
downloadsqlite-870a0705feb00a9c60c3442431beeeff37ca0c6b.tar.gz
sqlite-870a0705feb00a9c60c3442431beeeff37ca0c6b.zip
Fix a problem with IN(...) constraints where the LHS is a sub-select that is an aggregate query.
FossilOrigin-Name: 1f4dba87da4a44ad26223ad965731164c0d9bad9
Diffstat (limited to 'src')
-rw-r--r--src/expr.c86
-rw-r--r--src/select.c41
-rw-r--r--src/whereexpr.c24
3 files changed, 62 insertions, 89 deletions
diff --git a/src/expr.c b/src/expr.c
index 5acce2f0c..3bbd015fd 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -310,6 +310,15 @@ 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)
+ );
+}
+
+/*
** 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
@@ -324,29 +333,22 @@ int sqlite3ExprVectorSize(Expr *pExpr){
}
/*
-** 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;
diff --git a/src/select.c b/src/select.c
index f49a5c1fe..fb7217c1b 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 5997da1e3..33ce7edb3 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; 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);