1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-20 05:03:10 +03:00

Support use of function argument names to identify which actual arguments

match which function parameters.  The syntax uses AS, for example
	funcname(value AS arg1, anothervalue AS arg2)

Pavel Stehule
This commit is contained in:
Tom Lane
2009-10-08 02:39:25 +00:00
parent 2eda8dfb52
commit 717fa274d1
34 changed files with 1925 additions and 274 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.256 2009/06/11 14:48:59 momjian Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.257 2009/10/08 02:39:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -526,7 +526,8 @@ preprocess_expression(PlannerInfo *root, Node *expr, int kind)
/*
* Simplify constant expressions.
*
* Note: one essential effect here is to insert the current actual values
* Note: an essential effect of this is to convert named-argument function
* calls to positional notation and insert the current actual values
* of any default arguments for functions. To ensure that happens, we
* *must* process all expressions here. Previous PG versions sometimes
* skipped const-simplification if it didn't seem worth the trouble, but
@ -2658,9 +2659,10 @@ get_column_info_for_window(PlannerInfo *root, WindowClause *wc, List *tlist,
* Currently, we disallow sublinks in standalone expressions, so there's no
* real "planning" involved here. (That might not always be true though.)
* What we must do is run eval_const_expressions to ensure that any function
* default arguments get inserted. The fact that constant subexpressions
* get simplified is a side-effect that is useful when the expression will
* get evaluated more than once. Also, we must fix operator function IDs.
* calls are converted to positional notation and function default arguments
* get inserted. The fact that constant subexpressions get simplified is a
* side-effect that is useful when the expression will get evaluated more than
* once. Also, we must fix operator function IDs.
*
* Note: this must not make any damaging changes to the passed-in expression
* tree. (It would actually be okay to apply fix_opfuncids to it, but since
@ -2672,7 +2674,10 @@ expression_planner(Expr *expr)
{
Node *result;
/* Insert default arguments and simplify constant subexprs */
/*
* Convert named-argument function calls, insert default arguments and
* simplify constant subexprs
*/
result = eval_const_expressions(NULL, (Node *) expr);
/* Fill in opfuncid values if missing */

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.278 2009/07/20 00:24:30 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.279 2009/10/08 02:39:21 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -95,11 +95,18 @@ static List *simplify_and_arguments(List *args,
static Expr *simplify_boolean_equality(Oid opno, List *args);
static Expr *simplify_function(Oid funcid,
Oid result_type, int32 result_typmod, List **args,
bool has_named_args,
bool allow_inline,
eval_const_expressions_context *context);
static List *reorder_function_arguments(List *args, Oid result_type,
HeapTuple func_tuple,
eval_const_expressions_context *context);
static List *add_function_defaults(List *args, Oid result_type,
HeapTuple func_tuple,
eval_const_expressions_context *context);
static List *fetch_function_defaults(HeapTuple func_tuple);
static void recheck_cast_function_args(List *args, Oid result_type,
HeapTuple func_tuple);
static Expr *evaluate_function(Oid funcid,
Oid result_type, int32 result_typmod, List *args,
HeapTuple func_tuple,
@ -2003,7 +2010,8 @@ rowtype_field_matches(Oid rowtypeid, int fieldnum,
* OR clauses into N-argument form. See comments in prepqual.c.
*
* NOTE: another critical effect is that any function calls that require
* default arguments will be expanded.
* default arguments will be expanded, and named-argument calls will be
* converted to positional notation. The executor won't handle either.
*--------------------
*/
Node *
@ -2113,17 +2121,26 @@ eval_const_expressions_mutator(Node *node,
{
FuncExpr *expr = (FuncExpr *) node;
List *args;
bool has_named_args;
Expr *simple;
FuncExpr *newexpr;
ListCell *lc;
/*
* Reduce constants in the FuncExpr's arguments. We know args is
* either NIL or a List node, so we can call expression_tree_mutator
* directly rather than recursing to self.
* Reduce constants in the FuncExpr's arguments, and check to see
* if there are any named args.
*/
args = (List *) expression_tree_mutator((Node *) expr->args,
eval_const_expressions_mutator,
(void *) context);
args = NIL;
has_named_args = false;
foreach(lc, expr->args)
{
Node *arg = (Node *) lfirst(lc);
arg = eval_const_expressions_mutator(arg, context);
if (IsA(arg, NamedArgExpr))
has_named_args = true;
args = lappend(args, arg);
}
/*
* Code for op/func reduction is pretty bulky, so split it out as a
@ -2134,14 +2151,15 @@ eval_const_expressions_mutator(Node *node,
simple = simplify_function(expr->funcid,
expr->funcresulttype, exprTypmod(node),
&args,
true, context);
has_named_args, true, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
/*
* The expression cannot be simplified any further, so build and
* return a replacement FuncExpr node using the possibly-simplified
* arguments.
* arguments. Note that we have also converted the argument list
* to positional notation.
*/
newexpr = makeNode(FuncExpr);
newexpr->funcid = expr->funcid;
@ -2181,7 +2199,7 @@ eval_const_expressions_mutator(Node *node,
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
&args,
true, context);
false, true, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
@ -2274,7 +2292,7 @@ eval_const_expressions_mutator(Node *node,
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
&args,
false, context);
false, false, context);
if (simple) /* successfully simplified it */
{
/*
@ -2466,7 +2484,7 @@ eval_const_expressions_mutator(Node *node,
simple = simplify_function(outfunc,
CSTRINGOID, -1,
&args,
true, context);
false, true, context);
if (simple) /* successfully simplified output fn */
{
/*
@ -2484,7 +2502,7 @@ eval_const_expressions_mutator(Node *node,
simple = simplify_function(infunc,
expr->resulttype, -1,
&args,
true, context);
false, true, context);
if (simple) /* successfully simplified input fn */
return (Node *) simple;
}
@ -3241,15 +3259,16 @@ simplify_boolean_equality(Oid opno, List *args)
* Returns a simplified expression if successful, or NULL if cannot
* simplify the function call.
*
* This function is also responsible for adding any default argument
* expressions onto the function argument list; which is a bit grotty,
* but it avoids an extra fetch of the function's pg_proc tuple. For this
* reason, the args list is pass-by-reference, and it may get modified
* even if simplification fails.
* This function is also responsible for converting named-notation argument
* lists into positional notation and/or adding any needed default argument
* expressions; which is a bit grotty, but it avoids an extra fetch of the
* function's pg_proc tuple. For this reason, the args list is
* pass-by-reference, and it may get modified even if simplification fails.
*/
static Expr *
simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
List **args,
bool has_named_args,
bool allow_inline,
eval_const_expressions_context *context)
{
@ -3270,8 +3289,14 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
if (!HeapTupleIsValid(func_tuple))
elog(ERROR, "cache lookup failed for function %u", funcid);
/* While we have the tuple, check if we need to add defaults */
if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(*args))
/*
* While we have the tuple, reorder named arguments and add default
* arguments if needed.
*/
if (has_named_args)
*args = reorder_function_arguments(*args, result_type, func_tuple,
context);
else if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(*args))
*args = add_function_defaults(*args, result_type, func_tuple, context);
newexpr = evaluate_function(funcid, result_type, result_typmod, *args,
@ -3286,13 +3311,113 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
return newexpr;
}
/*
* reorder_function_arguments: convert named-notation args to positional args
*
* This function also inserts default argument values as needed, since it's
* impossible to form a truly valid positional call without that.
*/
static List *
reorder_function_arguments(List *args, Oid result_type, HeapTuple func_tuple,
eval_const_expressions_context *context)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
int pronargs = funcform->pronargs;
int nargsprovided = list_length(args);
Node *argarray[FUNC_MAX_ARGS];
Bitmapset *defargnumbers;
ListCell *lc;
int i;
Assert(nargsprovided <= pronargs);
if (pronargs > FUNC_MAX_ARGS)
elog(ERROR, "too many function arguments");
MemSet(argarray, 0, pronargs * sizeof(Node *));
/* Deconstruct the argument list into an array indexed by argnumber */
i = 0;
foreach(lc, args)
{
Node *arg = (Node *) lfirst(lc);
if (!IsA(arg, NamedArgExpr))
{
/* positional argument, assumed to precede all named args */
Assert(argarray[i] == NULL);
argarray[i++] = arg;
}
else
{
NamedArgExpr *na = (NamedArgExpr *) arg;
Assert(argarray[na->argnumber] == NULL);
argarray[na->argnumber] = (Node *) na->arg;
}
}
/*
* Fetch default expressions, if needed, and insert into array at
* proper locations (they aren't necessarily consecutive or all used)
*/
defargnumbers = NULL;
if (nargsprovided < pronargs)
{
List *defaults = fetch_function_defaults(func_tuple);
i = pronargs - funcform->pronargdefaults;
foreach(lc, defaults)
{
if (argarray[i] == NULL)
{
argarray[i] = (Node *) lfirst(lc);
defargnumbers = bms_add_member(defargnumbers, i);
}
i++;
}
}
/* Now reconstruct the args list in proper order */
args = NIL;
for (i = 0; i < pronargs; i++)
{
Assert(argarray[i] != NULL);
args = lappend(args, argarray[i]);
}
/* Recheck argument types and add casts if needed */
recheck_cast_function_args(args, result_type, func_tuple);
/*
* Lastly, we have to recursively simplify the defaults we just added
* (but don't recurse on the args passed in, as we already did those).
* This isn't merely an optimization, it's *necessary* since there could
* be functions with named or defaulted arguments down in there.
*
* Note that we do this last in hopes of simplifying any typecasts that
* were added by recheck_cast_function_args --- there shouldn't be any new
* casts added to the explicit arguments, but casts on the defaults are
* possible.
*/
if (defargnumbers != NULL)
{
i = 0;
foreach(lc, args)
{
if (bms_is_member(i, defargnumbers))
lfirst(lc) = eval_const_expressions_mutator((Node *) lfirst(lc),
context);
i++;
}
}
return args;
}
/*
* add_function_defaults: add missing function arguments from its defaults
*
* It is possible for some of the defaulted arguments to be polymorphic;
* therefore we can't assume that the default expressions have the correct
* data types already. We have to re-resolve polymorphics and do coercion
* just like the parser did.
* This is used only when the argument list was positional to begin with,
* and so we know we just need to add defaults at the end.
*/
static List *
add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
@ -3300,16 +3425,58 @@ add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
int nargsprovided = list_length(args);
List *defaults;
int ndelete;
ListCell *lc;
/* Get all the default expressions from the pg_proc tuple */
defaults = fetch_function_defaults(func_tuple);
/* Delete any unused defaults from the list */
ndelete = nargsprovided + list_length(defaults) - funcform->pronargs;
if (ndelete < 0)
elog(ERROR, "not enough default arguments");
while (ndelete-- > 0)
defaults = list_delete_first(defaults);
/* And form the combined argument list */
args = list_concat(args, defaults);
/* Recheck argument types and add casts if needed */
recheck_cast_function_args(args, result_type, func_tuple);
/*
* Lastly, we have to recursively simplify the defaults we just added
* (but don't recurse on the args passed in, as we already did those).
* This isn't merely an optimization, it's *necessary* since there could
* be functions with named or defaulted arguments down in there.
*
* Note that we do this last in hopes of simplifying any typecasts that
* were added by recheck_cast_function_args --- there shouldn't be any new
* casts added to the explicit arguments, but casts on the defaults are
* possible.
*/
foreach(lc, args)
{
if (nargsprovided-- > 0)
continue; /* skip original arg positions */
lfirst(lc) = eval_const_expressions_mutator((Node *) lfirst(lc),
context);
}
return args;
}
/*
* fetch_function_defaults: get function's default arguments as expression list
*/
static List *
fetch_function_defaults(HeapTuple func_tuple)
{
List *defaults;
Datum proargdefaults;
bool isnull;
char *str;
List *defaults;
int ndelete;
int nargs;
Oid actual_arg_types[FUNC_MAX_ARGS];
Oid declared_arg_types[FUNC_MAX_ARGS];
Oid rettype;
ListCell *lc;
/* The error cases here shouldn't happen, but check anyway */
proargdefaults = SysCacheGetAttr(PROCOID, func_tuple,
@ -3321,20 +3488,33 @@ add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
defaults = (List *) stringToNode(str);
Assert(IsA(defaults, List));
pfree(str);
/* Delete any unused defaults from the list */
ndelete = nargsprovided + list_length(defaults) - funcform->pronargs;
if (ndelete < 0)
elog(ERROR, "not enough default arguments");
while (ndelete-- > 0)
defaults = list_delete_first(defaults);
/* And form the combined argument list */
args = list_concat(args, defaults);
Assert(list_length(args) == funcform->pronargs);
return defaults;
}
/*
* recheck_cast_function_args: recheck function args and typecast as needed
* after adding defaults.
*
* It is possible for some of the defaulted arguments to be polymorphic;
* therefore we can't assume that the default expressions have the correct
* data types already. We have to re-resolve polymorphics and do coercion
* just like the parser did.
*
* This should be a no-op if there are no polymorphic arguments,
* but we do it anyway to be sure.
*
* Note: if any casts are needed, the args list is modified in-place.
*/
static void
recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
int nargs;
Oid actual_arg_types[FUNC_MAX_ARGS];
Oid declared_arg_types[FUNC_MAX_ARGS];
Oid rettype;
ListCell *lc;
/*
* The next part should be a no-op if there are no polymorphic arguments,
* but we do it anyway to be sure.
*/
if (list_length(args) > FUNC_MAX_ARGS)
elog(ERROR, "too many function arguments");
nargs = 0;
@ -3342,6 +3522,7 @@ add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
{
actual_arg_types[nargs++] = exprType((Node *) lfirst(lc));
}
Assert(nargs == funcform->pronargs);
memcpy(declared_arg_types, funcform->proargtypes.values,
funcform->pronargs * sizeof(Oid));
rettype = enforce_generic_type_consistency(actual_arg_types,
@ -3355,22 +3536,6 @@ add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
/* perform any necessary typecasting of arguments */
make_fn_arguments(NULL, args, actual_arg_types, declared_arg_types);
/*
* Lastly, we have to recursively simplify the arguments we just added
* (but don't recurse on the ones passed in, as we already did those).
* This isn't merely an optimization, it's *necessary* since there could
* be functions with defaulted arguments down in there.
*/
foreach(lc, args)
{
if (nargsprovided-- > 0)
continue; /* skip original arg positions */
lfirst(lc) = eval_const_expressions_mutator((Node *) lfirst(lc),
context);
}
return args;
}
/*
@ -3916,6 +4081,7 @@ Query *
inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
{
FuncExpr *fexpr;
Oid func_oid;
HeapTuple func_tuple;
Form_pg_proc funcform;
Oid *argtypes;
@ -3944,6 +4110,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
fexpr = (FuncExpr *) rte->funcexpr;
if (fexpr == NULL || !IsA(fexpr, FuncExpr))
return NULL;
func_oid = fexpr->funcid;
/*
* The function must be declared to return a set, else inlining would
@ -3967,17 +4134,17 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
return NULL;
/* Check permission to call function (fail later, if not) */
if (pg_proc_aclcheck(fexpr->funcid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK)
if (pg_proc_aclcheck(func_oid, 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),
ObjectIdGetDatum(func_oid),
0, 0, 0);
if (!HeapTupleIsValid(func_tuple))
elog(ERROR, "cache lookup failed for function %u", fexpr->funcid);
elog(ERROR, "cache lookup failed for function %u", func_oid);
funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
/*
@ -3985,16 +4152,15 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
* 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.)
* sharing the snapshot of the calling query. (Rechecking proretset is
* just paranoia.)
*/
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))
!heap_attisnull(func_tuple, Anum_pg_proc_proconfig))
{
ReleaseSysCache(func_tuple);
return NULL;
@ -4020,6 +4186,24 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
ALLOCSET_DEFAULT_MAXSIZE);
oldcxt = MemoryContextSwitchTo(mycxt);
/*
* Run eval_const_expressions on the function call. This is necessary
* to ensure that named-argument notation is converted to positional
* notation and any default arguments are inserted. It's a bit of
* overkill for the arguments, since they'll get processed again later,
* but no harm will be done.
*/
fexpr = (FuncExpr *) eval_const_expressions(root, (Node *) fexpr);
/* It should still be a call of the same function, but let's check */
if (!IsA(fexpr, FuncExpr) ||
fexpr->funcid != func_oid)
goto fail;
/* Arg list length should now match the function */
if (list_length(fexpr->args) != funcform->pronargs)
goto fail;
/* Check for polymorphic arguments, and substitute actual arg types */
argtypes = (Oid *) palloc(funcform->pronargs * sizeof(Oid));
memcpy(argtypes, funcform->proargtypes.values,
@ -4038,7 +4222,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
Anum_pg_proc_prosrc,
&isNull);
if (isNull)
elog(ERROR, "null prosrc for function %u", fexpr->funcid);
elog(ERROR, "null prosrc for function %u", func_oid);
src = TextDatumGetCString(tmp);
/*
@ -4076,7 +4260,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
* 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,
if (!check_sql_fn_retval(func_oid, fexpr->funcresulttype,
querytree_list,
true, NULL) &&
(get_typtype(fexpr->funcresulttype) == TYPTYPE_COMPOSITE ||
@ -4116,7 +4300,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
* Since there is now no trace of the function in the plan tree, we must
* explicitly record the plan's dependency on the function.
*/
record_plan_function_dependency(root->glob, fexpr->funcid);
record_plan_function_dependency(root->glob, func_oid);
return querytree;