From a58843b49afdc15c3e319072916a4d9390cf70b1 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 19 Apr 1999 04:17:11 +0000
Subject: [PATCH] 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;

---
 src/backend/executor/nodeSubplan.c | 55 +++++++++++++++++++++++-------
 src/backend/parser/parse_expr.c    | 25 ++++++++------
 2 files changed, 57 insertions(+), 23 deletions(-)

diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index 9f872c60682..2eebbf1f1e2 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -29,10 +29,11 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext)
 {
 	Plan	   *plan = node->plan;
 	SubLink    *sublink = node->sublink;
+	SubLinkType subLinkType = sublink->subLinkType;
 	TupleTableSlot *slot;
 	List	   *lst;
-	bool		result = false;
-	bool		found = false;
+	Datum		result = (Datum) false;
+	bool		found = false;	/* TRUE if got at least one subplan tuple */
 
 	if (node->setParam != NULL)
 		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);
 
+	/*
+	 * 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);
 		 !TupIsNull(slot);
 		 slot = ExecProcNode(plan, plan))
@@ -64,13 +77,13 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext)
 		TupleDesc	tdesc = slot->ttc_tupleDescriptor;
 		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");
 			return (Datum) false;
 		}
 
-		if (sublink->subLinkType == EXISTS_SUBLINK)
+		if (subLinkType == EXISTS_SUBLINK)
 			return (Datum) true;
 
 		found = true;
@@ -82,23 +95,39 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext)
 			bool		isnull;
 
 			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)
-				result = false;
-			if ((!result && !(sublink->useor)) || (result && sublink->useor))
-				break;
+			{
+				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;
+			}
 			i++;
 		}
 
-		if ((!result && sublink->subLinkType == ALL_SUBLINK) ||
-			(result && sublink->subLinkType == ANY_SUBLINK))
+		if (subLinkType == ALL_SUBLINK && ! (bool) result)
+			break;
+		if (subLinkType == ANY_SUBLINK && (bool) result)
 			break;
 	}
 
-	if (!found && sublink->subLinkType == ALL_SUBLINK)
-		return (Datum) true;
+	if (!found)
+	{
+		/* 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;
 }
 
 /* ----------------------------------------------------------------
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index d23d7214a34..f18fbd46eed 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -7,7 +7,7 @@
  *
  *
  * 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);
 
-						/*
-						 * 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 &&
 							sublink->subLinkType != EXPR_SUBLINK)
 							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;
 			break;
 		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;
 		case T_CaseExpr:
 			type = ((CaseExpr *) expr)->casetype;
-- 
GitLab