Commit a58843b4 authored by Tom Lane's avatar Tom Lane

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;
parent 4438b70b
...@@ -29,10 +29,11 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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)
break; 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;
}
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 @@ ...@@ -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) ...@@ -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) ...@@ -598,7 +590,20 @@ exprType(Node *expr)
type = ((Param *) expr)->paramtype; type = ((Param *) expr)->paramtype;
break; break;
case T_SubLink: case T_SubLink:
type = BOOLOID; {
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;
}
}
break; break;
case T_CaseExpr: case T_CaseExpr:
type = ((CaseExpr *) expr)->casetype; type = ((CaseExpr *) expr)->casetype;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment