From: Tom Lane Date: Tue, 18 Mar 2008 22:04:14 +0000 (+0000) Subject: Arrange to "inline" SQL functions that appear in a query's FROM clause, X-Git-Tag: REL8_4_BETA1~1749 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0d49838df6f54c5b49d40a8cf396ea799bd3c4d9;p=postgresql Arrange to "inline" SQL functions that appear in a query's FROM clause, are declared to return set, and consist of just a single SELECT. We can replace the FROM-item with a sub-SELECT and then optimize much as if we were dealing with a view. Patch from Richard Rowell, cleaned up by me. --- diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index a9feb2a56b..f86b742cad 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 0dec881d37..da6976b62f 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 0103ea826c..2f469bd924 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,13 +253,20 @@ 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 8e726ff7da..755bed363b 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 $ * *------------------------------------------------------------------------- */ @@ -124,6 +125,52 @@ pull_up_IN_clauses(PlannerInfo *root, Node *node) return 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 @@ -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; @@ -307,6 +355,11 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, subquery->jointree->quals = pull_up_IN_clauses(subroot, 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 diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index bee9c7a9dd..818894886a 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 @@ -3122,21 +3142,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. @@ -3307,6 +3312,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 * diff --git a/src/include/executor/functions.h b/src/include/executor/functions.h index 3a5c4eb012..b5451ad43b 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 0a2b3a2bb8..b5d618595f 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 b98040a498..80fa3b5152 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);