Fix a problem with IN(...) constraints where the LHS is a sub-select that is an aggregate query.
FossilOrigin-Name: 1f4dba87da4a44ad26223ad965731164c0d9bad9
This commit is contained in:
+40
-46
@@ -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.
|
||||
**
|
||||
** 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;
|
||||
|
||||
+4
-37
@@ -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)
|
||||
|
||||
+18
-6
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user