From 8aea617c03483dd5fee06feeec5eabea85125588 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 15 Jul 1999 01:52:09 +0000
Subject: [PATCH] Several routines failed to cope with CASE expressions, and
 indeed some of 'em were missing support for more node types than that...

---
 src/backend/optimizer/plan/subselect.c | 337 +++++++++++++++----------
 1 file changed, 200 insertions(+), 137 deletions(-)

diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 7170d727ef0..2eab2d3d6d4 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -6,7 +6,7 @@
  * Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.18 1999/06/21 01:20:57 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.19 1999/07/15 01:52:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -292,108 +292,129 @@ set_unioni(List *l1, List *l2)
 	return nconc(l1, set_differencei(l2, l1));
 }
 
-static List *
-_finalize_primnode(void *expr, List **subplan)
-{
-	List	   *result = NULL;
+typedef struct finalize_primnode_results {
+	List	*subplans;			/* List of subplans found in expr */
+	List	*paramids;			/* List of PARAM_EXEC paramids found */
+} finalize_primnode_results;
 
-	if (expr == NULL)
-		return NULL;
+static bool finalize_primnode_walker(Node *node,
+									 finalize_primnode_results *results);
 
-	if (IsA(expr, Param))
-	{
-		if (((Param *) expr)->paramkind == PARAM_EXEC)
-			return lconsi(((Param *) expr)->paramid, (List *) NULL);
-	}
-	else if (single_node(expr))
-		return NULL;
-	else if (IsA(expr, List))
-	{
-		List	   *le;
+static void
+finalize_primnode(Node *expr, finalize_primnode_results *results)
+{
+	results->subplans = NIL;	/* initialize */
+	results->paramids = NIL;
+	(void) finalize_primnode_walker(expr, results);
+}
 
-		foreach(le, (List *) expr)
-			result = set_unioni(result,
-								_finalize_primnode(lfirst(le), subplan));
-	}
-	else if (IsA(expr, Iter))
-		return _finalize_primnode(((Iter *) expr)->iterexpr, subplan);
-	else if (or_clause(expr) || and_clause(expr) || is_opclause(expr) ||
-			 not_clause(expr) || is_funcclause(expr))
-		return _finalize_primnode(((Expr *) expr)->args, subplan);
-	else if (IsA(expr, Aggref))
-		return _finalize_primnode(((Aggref *) expr)->target, subplan);
-	else if (IsA(expr, ArrayRef))
+static bool
+finalize_primnode_walker(Node *node,
+						 finalize_primnode_results *results)
+{
+	if (node == NULL)
+		return false;
+	if (IsA(node, Param))
 	{
-		result = _finalize_primnode(((ArrayRef *) expr)->refupperindexpr, subplan);
-		result = set_unioni(result,
-							_finalize_primnode(((ArrayRef *) expr)->reflowerindexpr, subplan));
-		result = set_unioni(result,
-			  _finalize_primnode(((ArrayRef *) expr)->refexpr, subplan));
-		result = set_unioni(result,
-		 _finalize_primnode(((ArrayRef *) expr)->refassgnexpr, subplan));
+		if (((Param *) node)->paramkind == PARAM_EXEC)
+		{
+			int		paramid = (int) ((Param *) node)->paramid;
+
+			if (! intMember(paramid, results->paramids))
+				results->paramids = lconsi(paramid, results->paramids);
+		}
+		return false;			/* no more to do here */
 	}
-	else if (IsA(expr, TargetEntry))
-		return _finalize_primnode(((TargetEntry *) expr)->expr, subplan);
-	else if (is_subplan(expr))
+	if (is_subplan(node))
 	{
+		SubPlan	   *subplan = (SubPlan *) ((Expr *) node)->oper;
 		List	   *lst;
 
-		*subplan = lappend(*subplan, ((Expr *) expr)->oper);
-		foreach(lst, ((SubPlan *) ((Expr *) expr)->oper)->plan->extParam)
+		/* Add subplan to subplans list */
+		results->subplans = lappend(results->subplans, subplan);
+		/* Check extParam list for params to add to paramids */
+		foreach(lst, subplan->plan->extParam)
 		{
-			Var		   *var = nth(lfirsti(lst), PlannerParamVar);
+			int			paramid = lfirsti(lst);
+			Var		   *var = nth(paramid, PlannerParamVar);
 
 			/* note varlevelsup is absolute level number */
 			if (var->varlevelsup < PlannerQueryLevel &&
-				!intMember(lfirsti(lst), result))
-				result = lappendi(result, lfirsti(lst));
+				! intMember(paramid, results->paramids))
+				results->paramids = lconsi(paramid, results->paramids);
 		}
+		/* XXX We do NOT allow expression_tree_walker to examine the args
+		 * passed to the subplan.  Is that correct???  It's what the
+		 * old code did, but it seems mighty bogus...  tgl 7/14/99
+		 */
+		return false;			/* don't recurse into subplan args */
 	}
-	else
-		elog(ERROR, "_finalize_primnode: can't handle node %d", nodeTag(expr));
-
-	return result;
+	return expression_tree_walker(node, finalize_primnode_walker,
+								  (void *) results);
 }
 
+/* Replace correlation vars (uplevel vars) with Params. */
+
+/* XXX should replace this with use of a generalized tree rebuilder,
+ * designed along the same lines as expression_tree_walker.
+ * Not done yet.
+ */
 Node *
 SS_replace_correlation_vars(Node *expr)
 {
-
 	if (expr == NULL)
 		return NULL;
-	if (IsA(expr, List))
+	if (IsA(expr, Var))
+	{
+		if (((Var *) expr)->varlevelsup > 0)
+			expr = (Node *) _replace_var((Var *) expr);
+	}
+	else if (single_node(expr))
+		return expr;
+	else if (IsA(expr, List))
 	{
 		List	   *le;
 
 		foreach(le, (List *) expr)
 			lfirst(le) = SS_replace_correlation_vars((Node *) lfirst(le));
 	}
-	else if (IsA(expr, Var))
+	else if (IsA(expr, Expr))
 	{
-		if (((Var *) expr)->varlevelsup > 0)
-			expr = (Node *) _replace_var((Var *) expr);
-	}
-	else if (IsA(expr, Iter))
-		((Iter *) expr)->iterexpr = SS_replace_correlation_vars(((Iter *) expr)->iterexpr);
-	else if (single_node(expr))
-		return expr;
-	else if (or_clause(expr) || and_clause(expr) || is_opclause(expr) ||
-			 not_clause(expr) || is_funcclause(expr))
+		/* XXX do we need to do anything special with subplans? */
 		((Expr *) expr)->args = (List *)
 			SS_replace_correlation_vars((Node *) ((Expr *) expr)->args);
+	}
 	else if (IsA(expr, Aggref))
-		((Aggref *) expr)->target = SS_replace_correlation_vars((Node *) ((Aggref *) expr)->target);
+		((Aggref *) expr)->target = SS_replace_correlation_vars(((Aggref *) expr)->target);
+	else if (IsA(expr, Iter))
+		((Iter *) expr)->iterexpr = SS_replace_correlation_vars(((Iter *) expr)->iterexpr);
 	else if (IsA(expr, ArrayRef))
 	{
 		((ArrayRef *) expr)->refupperindexpr = (List *)
 			SS_replace_correlation_vars((Node *) ((ArrayRef *) expr)->refupperindexpr);
 		((ArrayRef *) expr)->reflowerindexpr = (List *)
 			SS_replace_correlation_vars((Node *) ((ArrayRef *) expr)->reflowerindexpr);
-		((ArrayRef *) expr)->refexpr = SS_replace_correlation_vars((Node *) ((ArrayRef *) expr)->refexpr);
+		((ArrayRef *) expr)->refexpr = SS_replace_correlation_vars(((ArrayRef *) expr)->refexpr);
 		((ArrayRef *) expr)->refassgnexpr = SS_replace_correlation_vars(((ArrayRef *) expr)->refassgnexpr);
 	}
+	else if (IsA(expr, CaseExpr))
+	{
+		CaseExpr   *caseexpr = (CaseExpr *) expr;
+		List	   *le;
+
+		foreach(le, caseexpr->args)
+		{
+			CaseWhen   *when = (CaseWhen *) lfirst(le);
+			Assert(IsA(when, CaseWhen));
+			when->expr = SS_replace_correlation_vars(when->expr);
+			when->result = SS_replace_correlation_vars(when->result);
+		}
+		/* caseexpr->arg should be null, but we'll check it anyway */
+		caseexpr->arg = SS_replace_correlation_vars(caseexpr->arg);
+		caseexpr->defresult = SS_replace_correlation_vars(caseexpr->defresult);
+	}
 	else if (IsA(expr, TargetEntry))
-		((TargetEntry *) expr)->expr = SS_replace_correlation_vars((Node *) ((TargetEntry *) expr)->expr);
+		((TargetEntry *) expr)->expr = SS_replace_correlation_vars(((TargetEntry *) expr)->expr);
 	else if (IsA(expr, SubLink))
 	{
 		List	   *le;
@@ -409,30 +430,76 @@ SS_replace_correlation_vars(Node *expr)
 			SS_replace_correlation_vars((Node *) ((SubLink *) expr)->lefthand);
 	}
 	else
-		elog(NOTICE, "SS_replace_correlation_vars: can't handle node %d",
+		elog(ERROR, "SS_replace_correlation_vars: can't handle node %d",
 			 nodeTag(expr));
 
 	return expr;
 }
 
+/* Replace sublinks by subplans in the given expression */
+
+/* XXX should replace this with use of a generalized tree rebuilder,
+ * designed along the same lines as expression_tree_walker.
+ * Not done yet.
+ */
 Node *
 SS_process_sublinks(Node *expr)
 {
 	if (expr == NULL)
 		return NULL;
-	if (IsA(expr, List))
+	if (IsA(expr, SubLink))
+	{
+		expr = _make_subplan((SubLink *) expr);
+	}
+	else if (single_node(expr))
+		return expr;
+	else if (IsA(expr, List))
 	{
 		List	   *le;
 
 		foreach(le, (List *) expr)
 			lfirst(le) = SS_process_sublinks((Node *) lfirst(le));
 	}
-	else if (or_clause(expr) || and_clause(expr) || is_opclause(expr) ||
-			 not_clause(expr) || is_funcclause(expr))
+	else if (IsA(expr, Expr))
+	{
+		/* We should never see a subplan node here, since this is the
+		 * routine that makes 'em in the first place.  No need to check.
+		 */
 		((Expr *) expr)->args = (List *)
 			SS_process_sublinks((Node *) ((Expr *) expr)->args);
-	else if (IsA(expr, SubLink))/* got it! */
-		expr = _make_subplan((SubLink *) expr);
+	}
+	else if (IsA(expr, Aggref))
+		((Aggref *) expr)->target = SS_process_sublinks(((Aggref *) expr)->target);
+	else if (IsA(expr, Iter))
+		((Iter *) expr)->iterexpr = SS_process_sublinks(((Iter *) expr)->iterexpr);
+	else if (IsA(expr, ArrayRef))
+	{
+		((ArrayRef *) expr)->refupperindexpr = (List *)
+			SS_process_sublinks((Node *) ((ArrayRef *) expr)->refupperindexpr);
+		((ArrayRef *) expr)->reflowerindexpr = (List *)
+			SS_process_sublinks((Node *) ((ArrayRef *) expr)->reflowerindexpr);
+		((ArrayRef *) expr)->refexpr = SS_process_sublinks(((ArrayRef *) expr)->refexpr);
+		((ArrayRef *) expr)->refassgnexpr = SS_process_sublinks(((ArrayRef *) expr)->refassgnexpr);
+	}
+	else if (IsA(expr, CaseExpr))
+	{
+		CaseExpr   *caseexpr = (CaseExpr *) expr;
+		List	   *le;
+
+		foreach(le, caseexpr->args)
+		{
+			CaseWhen   *when = (CaseWhen *) lfirst(le);
+			Assert(IsA(when, CaseWhen));
+			when->expr = SS_process_sublinks(when->expr);
+			when->result = SS_process_sublinks(when->result);
+		}
+		/* caseexpr->arg should be null, but we'll check it anyway */
+		caseexpr->arg = SS_process_sublinks(caseexpr->arg);
+		caseexpr->defresult = SS_process_sublinks(caseexpr->defresult);
+	}
+	else
+		elog(ERROR, "SS_process_sublinks: can't handle node %d",
+			 nodeTag(expr));
 
 	return expr;
 }
@@ -440,60 +507,65 @@ SS_process_sublinks(Node *expr)
 List *
 SS_finalize_plan(Plan *plan)
 {
-	List	   *extParam = NULL;
-	List	   *locParam = NULL;
-	List	   *subPlan = NULL;
-	List	   *param_list;
+	List	   *extParam = NIL;
+	List	   *locParam = NIL;
+	finalize_primnode_results results;
 	List	   *lst;
 
 	if (plan == NULL)
 		return NULL;
 
-	param_list = _finalize_primnode(plan->targetlist, &subPlan);
-	Assert(subPlan == NULL);
+	/* Find params in targetlist, make sure there are no subplans there */
+	finalize_primnode((Node *) plan->targetlist, &results);
+	Assert(results.subplans == NIL);
 
+	/* From here on, we invoke finalize_primnode_walker not finalize_primnode,
+	 * so that results.paramids lists are automatically merged together and
+	 * we don't have to do it the hard way.  But when recursing to self,
+	 * we do have to merge the lists.  Oh well.
+	 */
 	switch (nodeTag(plan))
 	{
 		case T_Result:
-			param_list = set_unioni(param_list,
-									_finalize_primnode(((Result *) plan)->resconstantqual, &subPlan));
-			/* subPlan is NOT necessarily NULL here ... */
+			finalize_primnode_walker(((Result *) plan)->resconstantqual,
+									 &results);
+			/* results.subplans is NOT necessarily empty here ... */
 			break;
 
 		case T_Append:
 			foreach(lst, ((Append *) plan)->appendplans)
-				param_list = set_unioni(param_list,
-								 SS_finalize_plan((Plan *) lfirst(lst)));
+				results.paramids = set_unioni(results.paramids,
+								SS_finalize_plan((Plan *) lfirst(lst)));
 			break;
 
 		case T_IndexScan:
-			param_list = set_unioni(param_list,
-			_finalize_primnode(((IndexScan *) plan)->indxqual, &subPlan));
-			Assert(subPlan == NULL);
+			finalize_primnode_walker((Node *) ((IndexScan *) plan)->indxqual,
+									 &results);
+			Assert(results.subplans == NIL);
 			break;
 
 		case T_MergeJoin:
-			param_list = set_unioni(param_list,
-									_finalize_primnode(((MergeJoin *) plan)->mergeclauses, &subPlan));
-			Assert(subPlan == NULL);
+			finalize_primnode_walker((Node *) ((MergeJoin *) plan)->mergeclauses,
+									 &results);
+			Assert(results.subplans == NIL);
 			break;
 
 		case T_HashJoin:
-			param_list = set_unioni(param_list,
-									_finalize_primnode(((HashJoin *) plan)->hashclauses, &subPlan));
-			Assert(subPlan == NULL);
+			finalize_primnode_walker((Node *) ((HashJoin *) plan)->hashclauses,
+									 &results);
+			Assert(results.subplans == NIL);
 			break;
 
 		case T_Hash:
-			param_list = set_unioni(param_list,
-				 _finalize_primnode(((Hash *) plan)->hashkey, &subPlan));
-			Assert(subPlan == NULL);
+			finalize_primnode_walker((Node *) ((Hash *) plan)->hashkey,
+									 &results);
+			Assert(results.subplans == NIL);
 			break;
 
 		case T_Agg:
-			param_list = set_unioni(param_list,
-					 _finalize_primnode(((Agg *) plan)->aggs, &subPlan));
-			Assert(subPlan == NULL);
+			finalize_primnode_walker((Node *) ((Agg *) plan)->aggs,
+									 &results);
+			Assert(results.subplans == NIL);
 			break;
 
 		case T_SeqScan:
@@ -503,16 +575,24 @@ SS_finalize_plan(Plan *plan)
 		case T_Unique:
 		case T_Group:
 			break;
+
 		default:
-			elog(ERROR, "SS_finalize_plan: node %d unsupported", nodeTag(plan));
+			elog(ERROR, "SS_finalize_plan: node %d unsupported",
+				 nodeTag(plan));
 			return NULL;
 	}
 
-	param_list = set_unioni(param_list, _finalize_primnode(plan->qual, &subPlan));
-	param_list = set_unioni(param_list, SS_finalize_plan(plan->lefttree));
-	param_list = set_unioni(param_list, SS_finalize_plan(plan->righttree));
+	finalize_primnode_walker((Node *) plan->qual, &results);
+	/* subplans are OK in the qual... */
+
+	results.paramids = set_unioni(results.paramids,
+								  SS_finalize_plan(plan->lefttree));
+	results.paramids = set_unioni(results.paramids,
+								  SS_finalize_plan(plan->righttree));
+
+	/* Now we have all the paramids and subplans */
 
-	foreach(lst, param_list)
+	foreach(lst, results.paramids)
 	{
 		Var		   *var = nth(lfirsti(lst), PlannerParamVar);
 
@@ -520,7 +600,7 @@ SS_finalize_plan(Plan *plan)
 		if (var->varlevelsup < PlannerQueryLevel)
 			extParam = lappendi(extParam, lfirsti(lst));
 		else if (var->varlevelsup > PlannerQueryLevel)
-			elog(ERROR, "SS_finalize_plan: plan shouldn't reference subplan' variable");
+			elog(ERROR, "SS_finalize_plan: plan shouldn't reference subplan's variable");
 		else
 		{
 			Assert(var->varno == 0 && var->varattno == 0);
@@ -530,52 +610,35 @@ SS_finalize_plan(Plan *plan)
 
 	plan->extParam = extParam;
 	plan->locParam = locParam;
-	plan->subPlan = subPlan;
-
-	return param_list;
+	plan->subPlan = results.subplans;
 
+	return results.paramids;
 }
 
 /* Construct a list of all subplans found within the given node tree */
 
+static bool SS_pull_subplan_walker(Node *node, List **listptr);
+
 List *
 SS_pull_subplan(Node *expr)
 {
-	List	   *result = NULL;
-
-	if (expr == NULL || single_node(expr))
-		return NULL;
+	List	   *result = NIL;
 
-	if (IsA(expr, List))
-	{
-		List	   *le;
+	SS_pull_subplan_walker(expr, &result);
+	return result;
+}
 
-		foreach(le, (List *) expr)
-			result = nconc(result, SS_pull_subplan(lfirst(le)));
-	}
-	else if (IsA(expr, Iter))
-		return SS_pull_subplan(((Iter *) expr)->iterexpr);
-	else if (or_clause(expr) || and_clause(expr) || is_opclause(expr) ||
-			 not_clause(expr) || is_funcclause(expr))
-		return SS_pull_subplan((Node *) ((Expr *) expr)->args);
-	else if (IsA(expr, Aggref))
-		return SS_pull_subplan(((Aggref *) expr)->target);
-	else if (IsA(expr, ArrayRef))
+static bool
+SS_pull_subplan_walker(Node *node, List **listptr)
+{
+	if (node == NULL)
+		return false;
+	if (is_subplan(node))
 	{
-		result = SS_pull_subplan((Node *) ((ArrayRef *) expr)->refupperindexpr);
-		result = nconc(result,
-		 SS_pull_subplan((Node *) ((ArrayRef *) expr)->reflowerindexpr));
-		result = nconc(result,
-					   SS_pull_subplan(((ArrayRef *) expr)->refexpr));
-		result = nconc(result,
-					 SS_pull_subplan(((ArrayRef *) expr)->refassgnexpr));
+		*listptr = lappend(*listptr, ((Expr *) node)->oper);
+		/* XXX original code did not examine args to subplan, is this right? */
+		return false;
 	}
-	else if (IsA(expr, TargetEntry))
-		return SS_pull_subplan(((TargetEntry *) expr)->expr);
-	else if (is_subplan(expr))
-		return lcons(((Expr *) expr)->oper, NULL);
-	else
-		elog(ERROR, "SS_pull_subplan: can't handle node %d", nodeTag(expr));
-
-	return result;
+	return expression_tree_walker(node, SS_pull_subplan_walker,
+								  (void *) listptr);
 }
-- 
GitLab