diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2009-10-08 02:39:25 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2009-10-08 02:39:25 +0000 |
commit | 717fa274d14d9cd25396b85bb92f567e1c623f0c (patch) | |
tree | 4fe298a9faa1fc8f038a9a1f35ee033abc3e41ed /src/backend/optimizer/util/clauses.c | |
parent | 2eda8dfb52ed9962920282d8384da8bb4c22514d (diff) | |
download | postgresql-717fa274d14d9cd25396b85bb92f567e1c623f0c.tar.gz postgresql-717fa274d14d9cd25396b85bb92f567e1c623f0c.zip |
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
Diffstat (limited to 'src/backend/optimizer/util/clauses.c')
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 326 |
1 files changed, 255 insertions, 71 deletions
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index f2038c73af1..dcfc731a17a 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.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, @@ -3287,12 +3312,112 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod, } /* + * 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,41 +3425,96 @@ 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); - 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, - Anum_pg_proc_proargdefaults, - &isnull); - if (isnull) - elog(ERROR, "not enough default arguments"); - str = TextDatumGetCString(proargdefaults); - defaults = (List *) stringToNode(str); - Assert(IsA(defaults, List)); - pfree(str); + /* 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); - Assert(list_length(args) == funcform->pronargs); + + /* Recheck argument types and add casts if needed */ + recheck_cast_function_args(args, result_type, func_tuple); /* - * The next part should be a no-op if there are no polymorphic arguments, - * but we do it anyway to be sure. + * 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; + + /* The error cases here shouldn't happen, but check anyway */ + proargdefaults = SysCacheGetAttr(PROCOID, func_tuple, + Anum_pg_proc_proargdefaults, + &isnull); + if (isnull) + elog(ERROR, "not enough default arguments"); + str = TextDatumGetCString(proargdefaults); + defaults = (List *) stringToNode(str); + Assert(IsA(defaults, List)); + pfree(str); + 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; + 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; |