From 591de3a06ec97cf1bff10d603946f0bac176b9d9 Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <pj@illuminatedcomputing.com>
Date: Thu, 29 Aug 2024 18:17:55 -0700
Subject: [PATCH v2 1/3] Add indented section

---
 src/backend/optimizer/util/clauses.c | 74 +++++++++++++++++++++++++++-
 1 file changed, 73 insertions(+), 1 deletion(-)

diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index b4e085e9d4b..ef8282ded49 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -5069,7 +5069,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
 	TupleDesc	rettupdesc;
 	List	   *raw_parsetree_list;
 	List	   *querytree_list;
-	Query	   *querytree;
+	Query	   *querytree = NULL;
 
 	Assert(rte->rtekind == RTE_FUNCTION);
 
@@ -5235,6 +5235,78 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
 			goto fail;
 		querytree = linitial(querytree_list);
 	}
+	if (!querytree)
+	{
+		/* Fetch the function body */
+		tmp = SysCacheGetAttrNotNull(PROCOID, func_tuple, Anum_pg_proc_prosrc);
+		src = TextDatumGetCString(tmp);
+
+		/*
+		 * Setup error traceback support for ereport().  This is so that we can
+		 * finger the function that bad information came from.
+		 */
+		callback_arg.proname = NameStr(funcform->proname);
+		callback_arg.prosrc = src;
+
+		sqlerrcontext.callback = sql_inline_error_callback;
+		sqlerrcontext.arg = (void *) &callback_arg;
+		sqlerrcontext.previous = error_context_stack;
+		error_context_stack = &sqlerrcontext;
+
+		/* If we have prosqlbody, pay attention to that not prosrc */
+		tmp = SysCacheGetAttr(PROCOID,
+							  func_tuple,
+							  Anum_pg_proc_prosqlbody,
+							  &isNull);
+		if (!isNull)
+		{
+			Node	   *n;
+
+			n = stringToNode(TextDatumGetCString(tmp));
+			if (IsA(n, List))
+				querytree_list = linitial_node(List, castNode(List, n));
+			else
+				querytree_list = list_make1(n);
+			if (list_length(querytree_list) != 1)
+				goto fail;
+			querytree = linitial(querytree_list);
+
+			/* Acquire necessary locks, then apply rewriter. */
+			AcquireRewriteLocks(querytree, true, false);
+			querytree_list = pg_rewrite_query(querytree);
+			if (list_length(querytree_list) != 1)
+				goto fail;
+			querytree = linitial(querytree_list);
+		}
+		else
+		{
+			/*
+			 * Set up to handle parameters while parsing the function body.  We
+			 * can use the FuncExpr just created as the input for
+			 * prepare_sql_fn_parse_info.
+			 */
+			pinfo = prepare_sql_fn_parse_info(func_tuple,
+											  (Node *) fexpr,
+											  fexpr->inputcollid);
+
+			/*
+			 * Parse, analyze, and rewrite (unlike inline_function(), we can't
+			 * skip rewriting here).  We can fail as soon as we find more than one
+			 * query, though.
+			 */
+			raw_parsetree_list = pg_parse_query(src);
+			if (list_length(raw_parsetree_list) != 1)
+				goto fail;
+
+			querytree_list = pg_analyze_and_rewrite_withcb(linitial(raw_parsetree_list),
+														   src,
+														   (ParserSetupHook) sql_fn_parser_setup,
+														   pinfo, NULL);
+			if (list_length(querytree_list) != 1)
+				goto fail;
+			querytree = linitial(querytree_list);
+		}
+	}
 
 	/*
 	 * Also resolve the actual function result tupdesc, if composite.  If we
-- 
2.42.0

