mirror of
https://github.com/postgres/postgres.git
synced 2025-06-16 06:01:02 +03:00
Fix problems seen when result of a subselect was used in an
expression context (ie, not at the top level of a WHERE clause). Examples like this one work now: SELECT name, value FROM t1 as touter WHERE (value/(SELECT AVG(value) FROM t1 WHERE name = touter.name)) > 0.75;
This commit is contained in:
@ -29,10 +29,11 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext)
|
|||||||
{
|
{
|
||||||
Plan *plan = node->plan;
|
Plan *plan = node->plan;
|
||||||
SubLink *sublink = node->sublink;
|
SubLink *sublink = node->sublink;
|
||||||
|
SubLinkType subLinkType = sublink->subLinkType;
|
||||||
TupleTableSlot *slot;
|
TupleTableSlot *slot;
|
||||||
List *lst;
|
List *lst;
|
||||||
bool result = false;
|
Datum result = (Datum) false;
|
||||||
bool found = false;
|
bool found = false; /* TRUE if got at least one subplan tuple */
|
||||||
|
|
||||||
if (node->setParam != NULL)
|
if (node->setParam != NULL)
|
||||||
elog(ERROR, "ExecSubPlan: can't set parent params from subquery");
|
elog(ERROR, "ExecSubPlan: can't set parent params from subquery");
|
||||||
@ -56,6 +57,18 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext)
|
|||||||
|
|
||||||
ExecReScan(plan, (ExprContext *) NULL, plan);
|
ExecReScan(plan, (ExprContext *) NULL, plan);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For all sublink types except EXPR_SUBLINK, the result type is boolean,
|
||||||
|
* and we have a fairly clear idea of how to combine multiple subitems
|
||||||
|
* and deal with NULL values or an empty subplan result.
|
||||||
|
*
|
||||||
|
* For EXPR_SUBLINK, the result type is whatever the combining operator
|
||||||
|
* returns. We have no way to deal with more than one column in the
|
||||||
|
* subplan result --- hopefully the parser forbids that. More seriously,
|
||||||
|
* it's unclear what to do with NULL values or an empty subplan result.
|
||||||
|
* For now, we error out, but should something else happen?
|
||||||
|
*/
|
||||||
|
|
||||||
for (slot = ExecProcNode(plan, plan);
|
for (slot = ExecProcNode(plan, plan);
|
||||||
!TupIsNull(slot);
|
!TupIsNull(slot);
|
||||||
slot = ExecProcNode(plan, plan))
|
slot = ExecProcNode(plan, plan))
|
||||||
@ -64,13 +77,13 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext)
|
|||||||
TupleDesc tdesc = slot->ttc_tupleDescriptor;
|
TupleDesc tdesc = slot->ttc_tupleDescriptor;
|
||||||
int i = 1;
|
int i = 1;
|
||||||
|
|
||||||
if (sublink->subLinkType == EXPR_SUBLINK && found)
|
if (subLinkType == EXPR_SUBLINK && found)
|
||||||
{
|
{
|
||||||
elog(ERROR, "ExecSubPlan: more than one tuple returned by expression subselect");
|
elog(ERROR, "ExecSubPlan: more than one tuple returned by expression subselect");
|
||||||
return (Datum) false;
|
return (Datum) false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sublink->subLinkType == EXISTS_SUBLINK)
|
if (subLinkType == EXISTS_SUBLINK)
|
||||||
return (Datum) true;
|
return (Datum) true;
|
||||||
|
|
||||||
found = true;
|
found = true;
|
||||||
@ -82,23 +95,39 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext)
|
|||||||
bool isnull;
|
bool isnull;
|
||||||
|
|
||||||
con->constvalue = heap_getattr(tup, i, tdesc, &(con->constisnull));
|
con->constvalue = heap_getattr(tup, i, tdesc, &(con->constisnull));
|
||||||
result = (bool) ExecEvalExpr((Node *) expr, econtext, &isnull, (bool *) NULL);
|
result = ExecEvalExpr((Node *) expr, econtext, &isnull, (bool *) NULL);
|
||||||
if (isnull)
|
if (isnull)
|
||||||
result = false;
|
{
|
||||||
if ((!result && !(sublink->useor)) || (result && sublink->useor))
|
if (subLinkType == EXPR_SUBLINK)
|
||||||
|
elog(ERROR, "ExecSubPlan: null value returned by expression subselect");
|
||||||
|
else
|
||||||
|
result = (Datum) false;
|
||||||
|
}
|
||||||
|
if (subLinkType != EXPR_SUBLINK)
|
||||||
|
{
|
||||||
|
if ((! (bool) result && !(sublink->useor)) ||
|
||||||
|
((bool) result && sublink->useor))
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((!result && sublink->subLinkType == ALL_SUBLINK) ||
|
if (subLinkType == ALL_SUBLINK && ! (bool) result)
|
||||||
(result && sublink->subLinkType == ANY_SUBLINK))
|
break;
|
||||||
|
if (subLinkType == ANY_SUBLINK && (bool) result)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found && sublink->subLinkType == ALL_SUBLINK)
|
if (!found)
|
||||||
return (Datum) true;
|
{
|
||||||
|
/* deal with empty subplan result. Note default result is 'false' */
|
||||||
|
if (subLinkType == ALL_SUBLINK)
|
||||||
|
result = (Datum) true;
|
||||||
|
else if (subLinkType == EXPR_SUBLINK)
|
||||||
|
elog(ERROR, "ExecSubPlan: no tuples returned by expression subselect");
|
||||||
|
}
|
||||||
|
|
||||||
return (Datum) result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.41 1999/04/18 17:35:51 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.42 1999/04/19 04:17:11 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -312,14 +312,6 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
|||||||
|
|
||||||
op_expr = make_op(op, lexpr, tent->expr);
|
op_expr = make_op(op, lexpr, tent->expr);
|
||||||
|
|
||||||
/*
|
|
||||||
* HACK! Second IF is more valid but currently we
|
|
||||||
* don't support EXPR subqueries inside
|
|
||||||
* expressions generally, only in WHERE clauses.
|
|
||||||
* After fixing this, first IF must be removed.
|
|
||||||
*/
|
|
||||||
if (op_expr->typeOid != BOOLOID)
|
|
||||||
elog(ERROR, "parser: '%s' must return 'bool' to be used with subquery", op);
|
|
||||||
if (op_expr->typeOid != BOOLOID &&
|
if (op_expr->typeOid != BOOLOID &&
|
||||||
sublink->subLinkType != EXPR_SUBLINK)
|
sublink->subLinkType != EXPR_SUBLINK)
|
||||||
elog(ERROR, "parser: '%s' must return 'bool' to be used with quantified predicate subquery", op);
|
elog(ERROR, "parser: '%s' must return 'bool' to be used with quantified predicate subquery", op);
|
||||||
@ -598,7 +590,20 @@ exprType(Node *expr)
|
|||||||
type = ((Param *) expr)->paramtype;
|
type = ((Param *) expr)->paramtype;
|
||||||
break;
|
break;
|
||||||
case T_SubLink:
|
case T_SubLink:
|
||||||
|
{
|
||||||
|
SubLink *sublink = (SubLink *) expr;
|
||||||
|
if (sublink->subLinkType == EXPR_SUBLINK)
|
||||||
|
{
|
||||||
|
/* return the result type of the combining operator */
|
||||||
|
Expr *op_expr = (Expr *) lfirst(sublink->oper);
|
||||||
|
type = op_expr->typeOid;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* for all other sublink types, result is boolean */
|
||||||
type = BOOLOID;
|
type = BOOLOID;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case T_CaseExpr:
|
case T_CaseExpr:
|
||||||
type = ((CaseExpr *) expr)->casetype;
|
type = ((CaseExpr *) expr)->casetype;
|
||||||
|
Reference in New Issue
Block a user