From a5b370943e1a7936e0118641f16acf6dbcc829cc Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 18 May 2002 18:49:41 +0000
Subject: [PATCH] Teach query_tree_walker, query_tree_mutator, and
 SS_finalize_plan to process function RTE expressions, which they were
 previously missing. This allows outer-Var references and subselects to work
 correctly in the arguments of a function RTE.  Install check to prevent
 function RTEs from cross-referencing Vars of sibling FROM-items, which
 doesn't make any sense (if you want to join, write a JOIN or WHERE clause).

---
 src/backend/optimizer/plan/planner.c   |  4 +-
 src/backend/optimizer/plan/subselect.c | 58 ++++++++++++++++----------
 src/backend/optimizer/util/clauses.c   | 13 ++++--
 src/backend/parser/parse_clause.c      | 47 ++++++++++++++++-----
 src/include/optimizer/subselect.h      |  2 +-
 5 files changed, 85 insertions(+), 39 deletions(-)

diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index a3eeced7bb1..a28c088e8fa 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.118 2002/05/18 02:25:49 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.119 2002/05/18 18:49:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -246,7 +246,7 @@ subquery_planner(Query *parse, double tuple_fraction)
 	 */
 	if (PlannerPlanId != saved_planid || PlannerQueryLevel > 1)
 	{
-		(void) SS_finalize_plan(plan);
+		(void) SS_finalize_plan(plan, parse->rtable);
 
 		/*
 		 * At the moment, SS_finalize_plan doesn't handle initPlans and so
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 57930e9a502..556fe07b5cd 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.52 2002/05/12 20:10:03 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.53 2002/05/18 18:49:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,6 +21,7 @@
 #include "optimizer/planmain.h"
 #include "optimizer/planner.h"
 #include "optimizer/subselect.h"
+#include "parser/parsetree.h"
 #include "parser/parse_expr.h"
 #include "parser/parse_oper.h"
 #include "utils/syscache.h"
@@ -586,7 +587,7 @@ process_sublinks_mutator(Node *node, void *context)
 }
 
 List *
-SS_finalize_plan(Plan *plan)
+SS_finalize_plan(Plan *plan, List *rtable)
 {
 	List	   *extParam = NIL;
 	List	   *locParam = NIL;
@@ -619,10 +620,20 @@ SS_finalize_plan(Plan *plan)
 							  &results);
 			break;
 
-		case T_Append:
-			foreach(lst, ((Append *) plan)->appendplans)
-				results.paramids = set_unioni(results.paramids,
-								 SS_finalize_plan((Plan *) lfirst(lst)));
+		case T_IndexScan:
+			finalize_primnode((Node *) ((IndexScan *) plan)->indxqual,
+							  &results);
+
+			/*
+			 * we need not look at indxqualorig, since it will have the
+			 * same param references as indxqual, and we aren't really
+			 * concerned yet about having a complete subplan list.
+			 */
+			break;
+
+		case T_TidScan:
+			finalize_primnode((Node *) ((TidScan *) plan)->tideval,
+							  &results);
 			break;
 
 		case T_SubqueryScan:
@@ -638,15 +649,22 @@ SS_finalize_plan(Plan *plan)
 							 ((SubqueryScan *) plan)->subplan->extParam);
 			break;
 
-		case T_IndexScan:
-			finalize_primnode((Node *) ((IndexScan *) plan)->indxqual,
-							  &results);
+		case T_FunctionScan:
+			{
+				RangeTblEntry *rte;
 
-			/*
-			 * we need not look at indxqualorig, since it will have the
-			 * same param references as indxqual, and we aren't really
-			 * concerned yet about having a complete subplan list.
-			 */
+				rte = rt_fetch(((FunctionScan *) plan)->scan.scanrelid,
+							   rtable);
+				Assert(rte->rtekind == RTE_FUNCTION);
+				finalize_primnode(rte->funcexpr, &results);
+			}
+			break;
+
+		case T_Append:
+			foreach(lst, ((Append *) plan)->appendplans)
+				results.paramids = set_unioni(results.paramids,
+								 SS_finalize_plan((Plan *) lfirst(lst),
+												  rtable));
 			break;
 
 		case T_NestLoop:
@@ -673,11 +691,6 @@ SS_finalize_plan(Plan *plan)
 							  &results);
 			break;
 
-		case T_TidScan:
-			finalize_primnode((Node *) ((TidScan *) plan)->tideval,
-							  &results);
-			break;
-
 		case T_Agg:
 		case T_SeqScan:
 		case T_Material:
@@ -686,7 +699,6 @@ SS_finalize_plan(Plan *plan)
 		case T_SetOp:
 		case T_Limit:
 		case T_Group:
-		case T_FunctionScan:
 			break;
 
 		default:
@@ -696,9 +708,11 @@ SS_finalize_plan(Plan *plan)
 
 	/* Process left and right subplans, if any */
 	results.paramids = set_unioni(results.paramids,
-								  SS_finalize_plan(plan->lefttree));
+								  SS_finalize_plan(plan->lefttree,
+												   rtable));
 	results.paramids = set_unioni(results.paramids,
-								  SS_finalize_plan(plan->righttree));
+								  SS_finalize_plan(plan->righttree,
+												   rtable));
 
 	/* Now we have all the paramids and subplans */
 
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 972c7f94633..9f059521f4d 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.99 2002/05/12 23:43:03 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.100 2002/05/18 18:49:41 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -1915,7 +1915,6 @@ query_tree_walker(Query *query,
 		{
 			case RTE_RELATION:
 			case RTE_SPECIAL:
-			case RTE_FUNCTION:
 				/* nothing to do */
 				break;
 			case RTE_SUBQUERY:
@@ -1927,6 +1926,10 @@ query_tree_walker(Query *query,
 				if (walker(rte->joinaliasvars, context))
 					return true;
 				break;
+			case RTE_FUNCTION:
+				if (walker(rte->funcexpr, context))
+					return true;
+				break;
 		}
 	}
 	return false;
@@ -2293,7 +2296,6 @@ query_tree_mutator(Query *query,
 		{
 			case RTE_RELATION:
 			case RTE_SPECIAL:
-			case RTE_FUNCTION:
 				/* nothing to do, don't bother to make a copy */
 				break;
 			case RTE_SUBQUERY:
@@ -2310,6 +2312,11 @@ query_tree_mutator(Query *query,
 				MUTATE(newrte->joinaliasvars, rte->joinaliasvars, List *);
 				rte = newrte;
 				break;
+			case RTE_FUNCTION:
+				FLATCOPY(newrte, rte, RangeTblEntry);
+				MUTATE(newrte->funcexpr, rte->funcexpr, Node *);
+				rte = newrte;
+				break;
 		}
 		newrt = lappend(newrt, rte);
 	}
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index a41182e9398..461e7c15cf1 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.92 2002/05/12 23:43:03 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.93 2002/05/18 18:49:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -410,7 +410,9 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
 	 */
 	save_namespace = pstate->p_namespace;
 	pstate->p_namespace = NIL;
+
 	parsetrees = parse_analyze(r->subquery, pstate);
+
 	pstate->p_namespace = save_namespace;
 
 	/*
@@ -455,26 +457,49 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
 {
 	Node	   *funcexpr;
 	char	   *funcname;
+	List	   *save_namespace;
 	RangeTblEntry *rte;
 	RangeTblRef *rtr;
 
+	/* Get function name for possible use as alias */
+	Assert(IsA(r->funccallnode, FuncCall));
+	funcname = strVal(llast(((FuncCall *) r->funccallnode)->funcname));
+
 	/*
-	 * Transform the raw FuncCall node
+	 * Transform the raw FuncCall node.  This is a bit tricky because we don't
+	 * want the function expression to be able to see any FROM items already
+	 * created in the current query (compare to transformRangeSubselect).
+	 * But it does need to be able to see any further-up parent states.
+	 * So, temporarily make the current query level have an empty namespace.
+	 * NOTE: this code is OK only because the expression can't legally alter
+	 * the namespace by causing implicit relation refs to be added.
 	 */
+	save_namespace = pstate->p_namespace;
+	pstate->p_namespace = NIL;
+
 	funcexpr = transformExpr(pstate, r->funccallnode);
 
-	Assert(IsA(r->funccallnode, FuncCall));
-	funcname = strVal(llast(((FuncCall *) r->funccallnode)->funcname));
+	pstate->p_namespace = save_namespace;
+
+	/*
+	 * We still need to check that the function parameters don't refer
+	 * to any other rels.  That could happen despite our hack on the namespace
+	 * if fully-qualified names are used.  So, check there are no local
+	 * Var references in the transformed expression.  (Outer references
+	 * are OK, and are ignored here.)
+	 */
+	if (pull_varnos(funcexpr) != NIL)
+		elog(ERROR, "FROM function expression may not refer to other relations of same query level");
 
 	/*
-	 * Disallow aggregate functions and subselects in the expression.
-	 * (Aggregates clearly make no sense; perhaps later we could support
-	 * subselects, though.)
+	 * Disallow aggregate functions in the expression.  (No reason to postpone
+	 * this check until parseCheckAggregates.)
 	 */
-	if (contain_agg_clause(funcexpr))
-		elog(ERROR, "cannot use aggregate function in FROM function expression");
-	if (contain_subplans(funcexpr))
-		elog(ERROR, "cannot use subselect in FROM function expression");
+	if (pstate->p_hasAggs)
+	{
+		if (contain_agg_clause(funcexpr))
+			elog(ERROR, "cannot use aggregate function in FROM function expression");
+	}
 
 	/*
 	 * Insist we have a bare function call (explain.c is the only place
diff --git a/src/include/optimizer/subselect.h b/src/include/optimizer/subselect.h
index 0b54f2e3fdb..b504cd7bee6 100644
--- a/src/include/optimizer/subselect.h
+++ b/src/include/optimizer/subselect.h
@@ -14,7 +14,7 @@ extern List *PlannerInitPlan;	/* init subplans for current query */
 extern List *PlannerParamVar;	/* to get Var from Param->paramid */
 extern int	PlannerPlanId;		/* to assign unique ID to subquery plans */
 
-extern List *SS_finalize_plan(Plan *plan);
+extern List *SS_finalize_plan(Plan *plan, List *rtable);
 extern Node *SS_replace_correlation_vars(Node *expr);
 extern Node *SS_process_sublinks(Node *expr);
 
-- 
GitLab