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.
This commit is contained in:
Tom Lane 2010-10-25 13:04:37 -04:00
parent 9350824e70
commit ef55e294e6
1 changed files with 22 additions and 1 deletions

View File

@ -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);