From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 25 Oct 2010 17:04:37 +0000 (-0400)
Subject: Fix inline_set_returning_function() to preserve the invalItems list properly.
X-Git-Tag: REL9_1_ALPHA2~32
X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ef55e294e66725c412d55b9689328235141d816d;p=postgresql

Fix inline_set_returning_function() to preserve the invalItems list properly.

This avoids a possible crash when inlining a SRF whose argument list
contains a reference to an inline-able user function.  The crash is quite
reproducible with CLOBBER_FREED_MEMORY enabled, but would be less certain
in a production build.  Problem introduced in 9.0 by the named-arguments
patch, which requires invoking eval_const_expressions() before we can try
to inline a SRF.  Per report from Brendan Jurd.
---

diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 13e89ec667..e948bccc1a 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -2018,11 +2018,16 @@ rowtype_field_matches(Oid rowtypeid, int fieldnum,
  * will not be pre-evaluated here, although we will reduce their
  * arguments as far as possible.
  *
+ * Whenever a function is eliminated from the expression by means of
+ * constant-expression evaluation or inlining, we add the function to
+ * root->glob->invalItems.  This ensures the plan is known to depend on
+ * such functions, even though they aren't referenced anymore.
+ *
  * We assume that the tree has already been type-checked and contains
  * only operators and functions that are reasonable to try to execute.
  *
  * NOTE: "root" can be passed as NULL if the caller never wants to do any
- * Param substitutions.
+ * Param substitutions nor receive info about inlined functions.
  *
  * NOTE: the planner assumes that this will always flatten nested AND and
  * OR clauses into N-argument form.  See comments in prepqual.c.
@@ -4095,6 +4100,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
 	bool		modifyTargetList;
 	MemoryContext oldcxt;
 	MemoryContext mycxt;
+	List	   *saveInvalItems;
 	inline_error_callback_arg callback_arg;
 	ErrorContextCallback sqlerrcontext;
 	List	   *raw_parsetree_list;
@@ -4181,6 +4187,16 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
 								  ALLOCSET_DEFAULT_MAXSIZE);
 	oldcxt = MemoryContextSwitchTo(mycxt);
 
+	/*
+	 * When we call eval_const_expressions below, it might try to add items
+	 * to root->glob->invalItems.  Since it is running in the temp context,
+	 * those items will be in that context, and will need to be copied out
+	 * if we're successful.  Temporarily reset the list so that we can keep
+	 * those items separate from the pre-existing list contents.
+	 */
+	saveInvalItems = root->glob->invalItems;
+	root->glob->invalItems = NIL;
+
 	/* Fetch the function body */
 	tmp = SysCacheGetAttr(PROCOID,
 						  func_tuple,
@@ -4307,6 +4323,10 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
 
 	querytree = copyObject(querytree);
 
+	/* copy up any new invalItems, too */
+	root->glob->invalItems = list_concat(saveInvalItems,
+										 copyObject(root->glob->invalItems));
+
 	MemoryContextDelete(mycxt);
 	error_context_stack = sqlerrcontext.previous;
 	ReleaseSysCache(func_tuple);
@@ -4322,6 +4342,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
 	/* Here if func is not inlinable: release temp memory and return NULL */
 fail:
 	MemoryContextSwitchTo(oldcxt);
+	root->glob->invalItems = saveInvalItems;
 	MemoryContextDelete(mycxt);
 	error_context_stack = sqlerrcontext.previous;
 	ReleaseSysCache(func_tuple);