aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/catalog/pg_proc.c5
-rw-r--r--src/backend/executor/functions.c61
-rw-r--r--src/backend/optimizer/plan/planner.c13
-rw-r--r--src/backend/optimizer/prep/prepjointree.c55
-rw-r--r--src/backend/optimizer/util/clauses.c328
-rw-r--r--src/include/executor/functions.h3
-rw-r--r--src/include/optimizer/clauses.h4
-rw-r--r--src/include/optimizer/prep.h3
8 files changed, 424 insertions, 48 deletions
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index a9feb2a56bd..f86b742cada 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.148 2008/01/01 19:45:48 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.149 2008/03/18 22:04:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -601,7 +601,8 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
proc->proargtypes.values,
proc->pronargs);
(void) check_sql_fn_retval(funcoid, proc->prorettype,
- querytree_list, NULL);
+ querytree_list,
+ false, NULL);
}
else
querytree_list = pg_parse_query(prosrc);
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 0dec881d37e..da6976b62f7 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.120 2008/01/01 19:45:49 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.121 2008/03/18 22:04:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -20,6 +20,7 @@
#include "commands/trigger.h"
#include "executor/functions.h"
#include "funcapi.h"
+#include "nodes/makefuncs.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "tcop/tcopprot.h"
@@ -269,6 +270,7 @@ init_sql_fcache(FmgrInfo *finfo)
fcache->returnsTuple = check_sql_fn_retval(foid,
rettype,
queryTree_list,
+ false,
&fcache->junkFilter);
/* Finally, plan the queries */
@@ -856,7 +858,9 @@ ShutdownSQLFunction(Datum arg)
*
* The return value of a sql function is the value returned by
* the final query in the function. We do some ad-hoc type checking here
- * to be sure that the user is returning the type he claims.
+ * to be sure that the user is returning the type he claims. There are
+ * also a couple of strange-looking features to assist callers in dealing
+ * with allowed special cases, such as binary-compatible result types.
*
* For a polymorphic function the passed rettype must be the actual resolved
* output type of the function; we should never see a polymorphic pseudotype
@@ -868,6 +872,10 @@ ShutdownSQLFunction(Datum arg)
* allow "SELECT rowtype_expression", this may be false even when the declared
* function return type is a rowtype.
*
+ * If insertRelabels is true, then binary-compatible cases are dealt with
+ * by actually inserting RelabelType nodes into the final SELECT; obviously
+ * the caller must pass a parsetree that it's okay to modify in this case.
+ *
* If junkFilter isn't NULL, then *junkFilter is set to a JunkFilter defined
* to convert the function's tuple result to the correct output tuple type.
* Whenever the result value is false (ie, the function isn't returning a
@@ -875,6 +883,7 @@ ShutdownSQLFunction(Datum arg)
*/
bool
check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
+ bool insertRelabels,
JunkFilter **junkFilter)
{
Query *parse;
@@ -945,10 +954,12 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
rettype == VOIDOID)
{
/*
- * For scalar-type returns, the target list should have exactly one
- * entry, and its type should agree with what the user declared. (As
- * of Postgres 7.2, we accept binary-compatible types too.)
+ * For scalar-type returns, the target list must have exactly one
+ * non-junk entry, and its type must agree with what the user
+ * declared; except we allow binary-compatible types too.
*/
+ TargetEntry *tle;
+
if (tlistlen != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
@@ -956,7 +967,11 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
format_type_be(rettype)),
errdetail("Final SELECT must return exactly one column.")));
- restype = exprType((Node *) ((TargetEntry *) linitial(tlist))->expr);
+ /* We assume here that non-junk TLEs must come first in tlists */
+ tle = (TargetEntry *) linitial(tlist);
+ Assert(!tle->resjunk);
+
+ restype = exprType((Node *) tle->expr);
if (!IsBinaryCoercible(restype, rettype))
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
@@ -964,6 +979,11 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
format_type_be(rettype)),
errdetail("Actual return type is %s.",
format_type_be(restype))));
+ if (insertRelabels && restype != rettype)
+ tle->expr = (Expr *) makeRelabelType(tle->expr,
+ rettype,
+ -1,
+ COERCE_DONTCARE);
}
else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID)
{
@@ -977,14 +997,24 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
* If the target list is of length 1, and the type of the varnode in
* the target list matches the declared return type, this is okay.
* This can happen, for example, where the body of the function is
- * 'SELECT func2()', where func2 has the same return type as the
- * function that's calling it.
+ * 'SELECT func2()', where func2 has the same composite return type
+ * as the function that's calling it.
*/
if (tlistlen == 1)
{
- restype = exprType((Node *) ((TargetEntry *) linitial(tlist))->expr);
+ TargetEntry *tle = (TargetEntry *) linitial(tlist);
+
+ Assert(!tle->resjunk);
+ restype = exprType((Node *) tle->expr);
if (IsBinaryCoercible(restype, rettype))
+ {
+ if (insertRelabels && restype != rettype)
+ tle->expr = (Expr *) makeRelabelType(tle->expr,
+ rettype,
+ -1,
+ COERCE_DONTCARE);
return false; /* NOT returning whole tuple */
+ }
}
/* Is the rowtype fixed, or determined only at runtime? */
@@ -1043,6 +1073,11 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
format_type_be(tletype),
format_type_be(atttype),
tuplogcols)));
+ if (insertRelabels && tletype != atttype)
+ tle->expr = (Expr *) makeRelabelType(tle->expr,
+ atttype,
+ -1,
+ COERCE_DONTCARE);
}
for (;;)
@@ -1070,14 +1105,6 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
/* Report that we are returning entire tuple result */
return true;
}
- else if (IsPolymorphicType(rettype))
- {
- /* This should already have been caught ... */
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
- errmsg("cannot determine result data type"),
- errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
- }
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 0103ea826ca..2f469bd924e 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.226 2008/01/01 19:45:50 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.227 2008/03/18 22:04:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -253,14 +253,21 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
/*
* Look for IN clauses at the top level of WHERE, and transform them into
* joins. Note that this step only handles IN clauses originally at top
- * level of WHERE; if we pull up any subqueries in the next step, their
- * INs are processed just before pulling them up.
+ * level of WHERE; if we pull up any subqueries below, their INs are
+ * processed just before pulling them up.
*/
if (parse->hasSubLinks)
parse->jointree->quals = pull_up_IN_clauses(root,
parse->jointree->quals);
/*
+ * Scan the rangetable for set-returning functions, and inline them
+ * if possible (producing subqueries that might get pulled up next).
+ * Recursion issues here are handled in the same way as for IN clauses.
+ */
+ inline_set_returning_functions(root);
+
+ /*
* Check to see if any subqueries in the rangetable can be merged into
* this query.
*/
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 8e726ff7dad..755bed363b1 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -5,6 +5,7 @@
*
* NOTE: the intended sequence for invoking these operations is
* pull_up_IN_clauses
+ * inline_set_returning_functions
* pull_up_subqueries
* do expression preprocessing (including flattening JOIN alias vars)
* reduce_outer_joins
@@ -15,7 +16,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.49 2008/01/01 19:45:50 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.50 2008/03/18 22:04:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -125,6 +126,52 @@ pull_up_IN_clauses(PlannerInfo *root, Node *node)
}
/*
+ * inline_set_returning_functions
+ * Attempt to "inline" set-returning functions in the FROM clause.
+ *
+ * If an RTE_FUNCTION rtable entry invokes a set-returning function that
+ * contains just a simple SELECT, we can convert the rtable entry to an
+ * RTE_SUBQUERY entry exposing the SELECT directly. This is especially
+ * useful if the subquery can then be "pulled up" for further optimization,
+ * but we do it even if not, to reduce executor overhead.
+ *
+ * This has to be done before we have started to do any optimization of
+ * subqueries, else any such steps wouldn't get applied to subqueries
+ * obtained via inlining. However, we do it after pull_up_IN_clauses
+ * so that we can inline any functions used in IN subselects.
+ *
+ * Like most of the planner, this feels free to scribble on its input data
+ * structure.
+ */
+void
+inline_set_returning_functions(PlannerInfo *root)
+{
+ ListCell *rt;
+
+ foreach(rt, root->parse->rtable)
+ {
+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
+
+ if (rte->rtekind == RTE_FUNCTION)
+ {
+ Query *funcquery;
+
+ /* Check safety of expansion, and expand if possible */
+ funcquery = inline_set_returning_function(root, rte->funcexpr);
+ if (funcquery)
+ {
+ /* Successful expansion, replace the rtable entry */
+ rte->rtekind = RTE_SUBQUERY;
+ rte->subquery = funcquery;
+ rte->funcexpr = NULL;
+ rte->funccoltypes = NIL;
+ rte->funccoltypmods = NIL;
+ }
+ }
+ }
+}
+
+/*
* pull_up_subqueries
* Look for subqueries in the rangetable that can be pulled up into
* the parent query. If the subquery has no special features like
@@ -296,6 +343,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
subroot->query_level = root->query_level;
subroot->planner_cxt = CurrentMemoryContext;
subroot->init_plans = NIL;
+ subroot->eq_classes = NIL;
subroot->in_info_list = NIL;
subroot->append_rel_list = NIL;
@@ -308,6 +356,11 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
subquery->jointree->quals);
/*
+ * Similarly, inline any set-returning functions in its rangetable.
+ */
+ inline_set_returning_functions(subroot);
+
+ /*
* Recursively pull up the subquery's subqueries, so that
* pull_up_subqueries' processing is complete for its jointree and
* rangetable.
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index bee9c7a9dda..818894886a5 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.254 2008/01/11 18:39:40 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.255 2008/03/18 22:04:14 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -38,6 +38,7 @@
#include "parser/parse_clause.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
+#include "rewrite/rewriteManip.h"
#include "tcop/tcopprot.h"
#include "utils/acl.h"
#include "utils/builtins.h"
@@ -63,6 +64,13 @@ typedef struct
int *usecounts;
} substitute_actual_parameters_context;
+typedef struct
+{
+ int nargs;
+ List *args;
+ int sublevels_up;
+} substitute_actual_srf_parameters_context;
+
static bool contain_agg_clause_walker(Node *node, void *context);
static bool count_agg_clauses_walker(Node *node, AggClauseCounts *counts);
static bool expression_returns_set_walker(Node *node, void *context);
@@ -100,6 +108,10 @@ static Node *substitute_actual_parameters_mutator(Node *node,
substitute_actual_parameters_context *context);
static void sql_inline_error_callback(void *arg);
static Expr *evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod);
+static Query *substitute_actual_srf_parameters(Query *expr,
+ int nargs, List *args);
+static Node *substitute_actual_srf_parameters_mutator(Node *node,
+ substitute_actual_srf_parameters_context *context);
/*****************************************************************************
@@ -3027,18 +3039,26 @@ inline_function(Oid funcid, Oid result_type, List *args,
list_length(querytree->targetList) != 1)
goto fail;
- newexpr = (Node *) ((TargetEntry *) linitial(querytree->targetList))->expr;
-
/*
* Make sure the function (still) returns what it's declared to. This
* will raise an error if wrong, but that's okay since the function would
- * fail at runtime anyway. Note we do not try this until we have verified
- * that no rewriting was needed; that's probably not important, but let's
- * be careful.
+ * fail at runtime anyway. Note that check_sql_fn_retval will also insert
+ * a RelabelType if needed to make the tlist expression match the declared
+ * type of the function.
+ *
+ * Note: we do not try this until we have verified that no rewriting was
+ * needed; that's probably not important, but let's be careful.
*/
- if (check_sql_fn_retval(funcid, result_type, list_make1(querytree), NULL))
+ if (check_sql_fn_retval(funcid, result_type, list_make1(querytree),
+ true, NULL))
goto fail; /* reject whole-tuple-result cases */
+ /* Now we can grab the tlist expression */
+ newexpr = (Node *) ((TargetEntry *) linitial(querytree->targetList))->expr;
+
+ /* Assert that check_sql_fn_retval did the right thing */
+ Assert(exprType(newexpr) == result_type);
+
/*
* Additional validity checks on the expression. It mustn't return a set,
* and it mustn't be more volatile than the surrounding function (this is
@@ -3123,21 +3143,6 @@ inline_function(Oid funcid, Oid result_type, List *args,
MemoryContextDelete(mycxt);
/*
- * Since check_sql_fn_retval allows binary-compatibility cases, the
- * expression we now have might return some type that's only binary
- * compatible with the original expression result type. To avoid
- * confusing matters, insert a RelabelType in such cases.
- */
- if (exprType(newexpr) != result_type)
- {
- Assert(IsBinaryCoercible(exprType(newexpr), result_type));
- newexpr = (Node *) makeRelabelType((Expr *) newexpr,
- result_type,
- -1,
- COERCE_IMPLICIT_CAST);
- }
-
- /*
* Recursively try to simplify the modified expression. Here we must add
* the current function to the context list of active functions.
*/
@@ -3308,6 +3313,285 @@ evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod)
/*
+ * inline_set_returning_function
+ * Attempt to "inline" a set-returning function in the FROM clause.
+ *
+ * "node" is the expression from an RTE_FUNCTION rangetable entry. If it
+ * represents a call of a set-returning SQL function that can safely be
+ * inlined, expand the function and return the substitute Query structure.
+ * Otherwise, return NULL.
+ *
+ * This has a good deal of similarity to inline_function(), but that's
+ * for the non-set-returning case, and there are enough differences to
+ * justify separate functions.
+ */
+Query *
+inline_set_returning_function(PlannerInfo *root, Node *node)
+{
+ FuncExpr *fexpr;
+ HeapTuple func_tuple;
+ Form_pg_proc funcform;
+ Oid *argtypes;
+ char *src;
+ Datum tmp;
+ bool isNull;
+ MemoryContext oldcxt;
+ MemoryContext mycxt;
+ ErrorContextCallback sqlerrcontext;
+ List *raw_parsetree_list;
+ List *querytree_list;
+ Query *querytree;
+ int i;
+
+ /*
+ * It doesn't make a lot of sense for a SQL SRF to refer to itself
+ * in its own FROM clause, since that must cause infinite recursion
+ * at runtime. It will cause this code to recurse too, so check
+ * for stack overflow. (There's no need to do more.)
+ */
+ check_stack_depth();
+
+ /* Fail if FROM item isn't a simple FuncExpr */
+ if (node == NULL || !IsA(node, FuncExpr))
+ return NULL;
+ fexpr = (FuncExpr *) node;
+
+ /*
+ * The function must be declared to return a set, else inlining would
+ * change the results if the contained SELECT didn't return exactly
+ * one row.
+ */
+ if (!fexpr->funcretset)
+ return NULL;
+
+ /* Fail if function returns RECORD ... we don't have enough context */
+ if (fexpr->funcresulttype == RECORDOID)
+ return NULL;
+
+ /*
+ * Refuse to inline if the arguments contain any volatile functions or
+ * sub-selects. Volatile functions are rejected because inlining may
+ * result in the arguments being evaluated multiple times, risking a
+ * change in behavior. Sub-selects are rejected partly for implementation
+ * reasons (pushing them down another level might change their behavior)
+ * and partly because they're likely to be expensive and so multiple
+ * evaluation would be bad.
+ */
+ if (contain_volatile_functions((Node *) fexpr->args) ||
+ contain_subplans((Node *) fexpr->args))
+ return NULL;
+
+ /* Check permission to call function (fail later, if not) */
+ if (pg_proc_aclcheck(fexpr->funcid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK)
+ return NULL;
+
+ /*
+ * OK, let's take a look at the function's pg_proc entry.
+ */
+ func_tuple = SearchSysCache(PROCOID,
+ ObjectIdGetDatum(fexpr->funcid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(func_tuple))
+ elog(ERROR, "cache lookup failed for function %u", fexpr->funcid);
+ funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
+
+ /*
+ * Forget it if the function is not SQL-language or has other showstopper
+ * properties. In particular it mustn't be declared STRICT, since we
+ * couldn't enforce that. It also mustn't be VOLATILE, because that is
+ * supposed to cause it to be executed with its own snapshot, rather than
+ * sharing the snapshot of the calling query. (The nargs check is just
+ * paranoia, ditto rechecking proretset.)
+ */
+ if (funcform->prolang != SQLlanguageId ||
+ funcform->proisstrict ||
+ funcform->provolatile == PROVOLATILE_VOLATILE ||
+ funcform->prosecdef ||
+ !funcform->proretset ||
+ !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) ||
+ funcform->pronargs != list_length(fexpr->args))
+ {
+ ReleaseSysCache(func_tuple);
+ return NULL;
+ }
+
+ /*
+ * Setup error traceback support for ereport(). This is so that we can
+ * finger the function that bad information came from.
+ */
+ sqlerrcontext.callback = sql_inline_error_callback;
+ sqlerrcontext.arg = func_tuple;
+ sqlerrcontext.previous = error_context_stack;
+ error_context_stack = &sqlerrcontext;
+
+ /*
+ * Make a temporary memory context, so that we don't leak all the stuff
+ * that parsing might create.
+ */
+ mycxt = AllocSetContextCreate(CurrentMemoryContext,
+ "inline_set_returning_function",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+ oldcxt = MemoryContextSwitchTo(mycxt);
+
+ /* Check for polymorphic arguments, and substitute actual arg types */
+ argtypes = (Oid *) palloc(funcform->pronargs * sizeof(Oid));
+ memcpy(argtypes, funcform->proargtypes.values,
+ funcform->pronargs * sizeof(Oid));
+ for (i = 0; i < funcform->pronargs; i++)
+ {
+ if (IsPolymorphicType(argtypes[i]))
+ {
+ argtypes[i] = exprType((Node *) list_nth(fexpr->args, i));
+ }
+ }
+
+ /* Fetch and parse the function body */
+ tmp = SysCacheGetAttr(PROCOID,
+ func_tuple,
+ Anum_pg_proc_prosrc,
+ &isNull);
+ if (isNull)
+ elog(ERROR, "null prosrc for function %u", fexpr->funcid);
+ src = DatumGetCString(DirectFunctionCall1(textout, tmp));
+
+ /*
+ * 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(linitial(raw_parsetree_list), src,
+ argtypes, funcform->pronargs);
+ if (list_length(querytree_list) != 1)
+ goto fail;
+ querytree = linitial(querytree_list);
+
+ /*
+ * The single command must be a regular results-returning SELECT.
+ */
+ if (!IsA(querytree, Query) ||
+ querytree->commandType != CMD_SELECT ||
+ querytree->utilityStmt ||
+ querytree->intoClause)
+ goto fail;
+
+ /*
+ * Make sure the function (still) returns what it's declared to. This
+ * will raise an error if wrong, but that's okay since the function would
+ * fail at runtime anyway. Note that check_sql_fn_retval will also insert
+ * RelabelType(s) if needed to make the tlist expression(s) match the
+ * declared type of the function.
+ *
+ * If the function returns a composite type, don't inline unless the
+ * check shows it's returning a whole tuple result; otherwise what
+ * it's returning is a single composite column which is not what we need.
+ */
+ if (!check_sql_fn_retval(fexpr->funcid, fexpr->funcresulttype,
+ querytree_list,
+ true, NULL) &&
+ get_typtype(fexpr->funcresulttype) == TYPTYPE_COMPOSITE)
+ goto fail; /* reject not-whole-tuple-result cases */
+
+ /*
+ * Looks good --- substitute parameters into the query.
+ */
+ querytree = substitute_actual_srf_parameters(querytree,
+ funcform->pronargs,
+ fexpr->args);
+
+ /*
+ * Copy the modified query out of the temporary memory context,
+ * and clean up.
+ */
+ MemoryContextSwitchTo(oldcxt);
+
+ querytree = copyObject(querytree);
+
+ MemoryContextDelete(mycxt);
+ error_context_stack = sqlerrcontext.previous;
+ ReleaseSysCache(func_tuple);
+
+ return querytree;
+
+ /* Here if func is not inlinable: release temp memory and return NULL */
+fail:
+ MemoryContextSwitchTo(oldcxt);
+ MemoryContextDelete(mycxt);
+ error_context_stack = sqlerrcontext.previous;
+ ReleaseSysCache(func_tuple);
+
+ return NULL;
+}
+
+/*
+ * Replace Param nodes by appropriate actual parameters
+ *
+ * This is just enough different from substitute_actual_parameters()
+ * that it needs its own code.
+ */
+static Query *
+substitute_actual_srf_parameters(Query *expr, int nargs, List *args)
+{
+ substitute_actual_srf_parameters_context context;
+
+ context.nargs = nargs;
+ context.args = args;
+ context.sublevels_up = 1;
+
+ return query_tree_mutator(expr,
+ substitute_actual_srf_parameters_mutator,
+ &context,
+ 0);
+}
+
+static Node *
+substitute_actual_srf_parameters_mutator(Node *node,
+ substitute_actual_srf_parameters_context *context)
+{
+ Node *result;
+
+ if (node == NULL)
+ return NULL;
+ if (IsA(node, Query))
+ {
+ context->sublevels_up++;
+ result = (Node *) query_tree_mutator((Query *) node,
+ substitute_actual_srf_parameters_mutator,
+ (void *) context,
+ 0);
+ context->sublevels_up--;
+ return result;
+ }
+ if (IsA(node, Param))
+ {
+ Param *param = (Param *) node;
+
+ if (param->paramkind == PARAM_EXTERN)
+ {
+ if (param->paramid <= 0 || param->paramid > context->nargs)
+ elog(ERROR, "invalid paramid: %d", param->paramid);
+
+ /*
+ * Since the parameter is being inserted into a subquery,
+ * we must adjust levels.
+ */
+ result = copyObject(list_nth(context->args, param->paramid - 1));
+ IncrementVarSublevelsUp(result, context->sublevels_up, 0);
+ return result;
+ }
+ }
+ return expression_tree_mutator(node,
+ substitute_actual_srf_parameters_mutator,
+ (void *) context);
+}
+
+
+/*
* Standard expression-tree walking support
*
* We used to have near-duplicate code in many different routines that
diff --git a/src/include/executor/functions.h b/src/include/executor/functions.h
index 3a5c4eb012e..b5451ad43b8 100644
--- a/src/include/executor/functions.h
+++ b/src/include/executor/functions.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/executor/functions.h,v 1.30 2008/01/01 19:45:57 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/executor/functions.h,v 1.31 2008/03/18 22:04:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -21,6 +21,7 @@ extern Datum fmgr_sql(PG_FUNCTION_ARGS);
extern bool check_sql_fn_retval(Oid func_id, Oid rettype,
List *queryTreeList,
+ bool insertRelabels,
JunkFilter **junkFilter);
#endif /* FUNCTIONS_H */
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
index 0a2b3a2bb80..b5d618595f7 100644
--- a/src/include/optimizer/clauses.h
+++ b/src/include/optimizer/clauses.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.88 2008/01/01 19:45:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.89 2008/03/18 22:04:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -79,6 +79,8 @@ extern Node *eval_const_expressions(Node *node);
extern Node *estimate_expression_value(PlannerInfo *root, Node *node);
+extern Query *inline_set_returning_function(PlannerInfo *root, Node *node);
+
extern bool expression_tree_walker(Node *node, bool (*walker) (),
void *context);
extern Node *expression_tree_mutator(Node *node, Node *(*mutator) (),
diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h
index b98040a498a..80fa3b51526 100644
--- a/src/include/optimizer/prep.h
+++ b/src/include/optimizer/prep.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.59 2008/01/01 19:45:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.60 2008/03/18 22:04:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -22,6 +22,7 @@
* prototypes for prepjointree.c
*/
extern Node *pull_up_IN_clauses(PlannerInfo *root, Node *node);
+extern void inline_set_returning_functions(PlannerInfo *root);
extern Node *pull_up_subqueries(PlannerInfo *root, Node *jtnode,
bool below_outer_join, bool append_rel_member);
extern void reduce_outer_joins(PlannerInfo *root);