aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_func.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/parse_func.c')
-rw-r--r--src/backend/parser/parse_func.c302
1 files changed, 246 insertions, 56 deletions
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index fd0706e9608..e752dd8d1ec 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.216 2009/06/11 14:49:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.217 2009/10/08 02:39:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -70,6 +70,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
int nargsplusdefs;
Oid actual_arg_types[FUNC_MAX_ARGS];
Oid *declared_arg_types;
+ List *argnames;
List *argdefaults;
Node *retval;
bool retset;
@@ -117,6 +118,46 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
actual_arg_types[nargs++] = argtype;
}
+ /*
+ * Check for named arguments; if there are any, build a list of names.
+ *
+ * We allow mixed notation (some named and some not), but only with all
+ * the named parameters after all the unnamed ones. So the name list
+ * corresponds to the last N actual parameters and we don't need any
+ * extra bookkeeping to match things up.
+ */
+ argnames = NIL;
+ foreach(l, fargs)
+ {
+ Node *arg = lfirst(l);
+
+ if (IsA(arg, NamedArgExpr))
+ {
+ NamedArgExpr *na = (NamedArgExpr *) arg;
+ ListCell *lc;
+
+ /* Reject duplicate arg names */
+ foreach(lc, argnames)
+ {
+ if (strcmp(na->name, (char *) lfirst(lc)) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("argument name \"%s\" used more than once",
+ na->name),
+ parser_errposition(pstate, na->location)));
+ }
+ argnames = lappend(argnames, na->name);
+ }
+ else
+ {
+ if (argnames != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("positional argument cannot follow named argument"),
+ parser_errposition(pstate, exprLocation(arg))));
+ }
+ }
+
if (fargs)
{
first_arg = linitial(fargs);
@@ -127,10 +168,10 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
* Check for column projection: if function has one argument, and that
* argument is of complex type, and function name is not qualified, then
* the "function call" could be a projection. We also check that there
- * wasn't any aggregate or variadic decoration.
+ * wasn't any aggregate or variadic decoration, nor an argument name.
*/
if (nargs == 1 && !agg_star && !agg_distinct && over == NULL &&
- !func_variadic && list_length(funcname) == 1)
+ !func_variadic && argnames == NIL && list_length(funcname) == 1)
{
Oid argtype = actual_arg_types[0];
@@ -156,12 +197,17 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
* disambiguation for polymorphic functions, handles inheritance, and
* returns the funcid and type and set or singleton status of the
* function's return value. It also returns the true argument types to
- * the function. In the case of a variadic function call, the reported
- * "true" types aren't really what is in pg_proc: the variadic argument is
- * replaced by a suitable number of copies of its element type. We'll fix
- * it up below. We may also have to deal with default arguments.
+ * the function.
+ *
+ * Note: for a named-notation or variadic function call, the reported
+ * "true" types aren't really what is in pg_proc: the types are reordered
+ * to match the given argument order of named arguments, and a variadic
+ * argument is replaced by a suitable number of copies of its element
+ * type. We'll fix up the variadic case below. We may also have to deal
+ * with default arguments.
*/
- fdresult = func_get_detail(funcname, fargs, nargs, actual_arg_types,
+ fdresult = func_get_detail(funcname, fargs, argnames, nargs,
+ actual_arg_types,
!func_variadic, true,
&funcid, &rettype, &retset, &nvargs,
&declared_arg_types, &argdefaults);
@@ -225,7 +271,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
errmsg("function %s is not unique",
- func_signature_string(funcname, nargs,
+ func_signature_string(funcname, nargs, argnames,
actual_arg_types)),
errhint("Could not choose a best candidate function. "
"You might need to add explicit type casts."),
@@ -234,7 +280,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
- func_signature_string(funcname, nargs,
+ func_signature_string(funcname, nargs, argnames,
actual_arg_types)),
errhint("No function matches the given name and argument types. "
"You might need to add explicit type casts."),
@@ -353,6 +399,18 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
errmsg("aggregates cannot return sets"),
parser_errposition(pstate, location)));
+ /*
+ * Currently it's not possible to define an aggregate with named
+ * arguments, so this case should be impossible. Check anyway
+ * because the planner and executor wouldn't cope with NamedArgExprs
+ * in an Aggref node.
+ */
+ if (argnames != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("aggregates cannot use named arguments"),
+ parser_errposition(pstate, location)));
+
/* parse_agg.c does additional aggregate-specific processing */
transformAggregateCall(pstate, aggref);
@@ -406,6 +464,17 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
errmsg("window functions cannot return sets"),
parser_errposition(pstate, location)));
+ /*
+ * We might want to support this later, but for now reject it
+ * because the planner and executor wouldn't cope with NamedArgExprs
+ * in a WindowFunc node.
+ */
+ if (argnames != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("window functions cannot use named arguments"),
+ parser_errposition(pstate, location)));
+
/* parse_agg.c does additional window-func-specific processing */
transformWindowFuncCall(pstate, wfunc, over);
@@ -801,14 +870,29 @@ func_select_candidate(int nargs,
* 1) check for possible interpretation as a type coercion request
* 2) apply the ambiguous-function resolution rules
*
- * Note: we rely primarily on nargs/argtypes as the argument description.
+ * Return values *funcid through *true_typeids receive info about the function.
+ * If argdefaults isn't NULL, *argdefaults receives a list of any default
+ * argument expressions that need to be added to the given arguments.
+ *
+ * When processing a named- or mixed-notation call (ie, fargnames isn't NIL),
+ * the returned true_typeids and argdefaults are ordered according to the
+ * call's argument ordering: first any positional arguments, then the named
+ * arguments, then defaulted arguments (if needed and allowed by
+ * expand_defaults). Some care is needed if this information is to be compared
+ * to the function's pg_proc entry, but in practice the caller can usually
+ * just work with the call's argument ordering.
+ *
+ * We rely primarily on fargnames/nargs/argtypes as the argument description.
* The actual expression node list is passed in fargs so that we can check
- * for type coercion of a constant. Some callers pass fargs == NIL
- * indicating they don't want that check made.
+ * for type coercion of a constant. Some callers pass fargs == NIL indicating
+ * they don't need that check made. Note also that when fargnames isn't NIL,
+ * the fargs list must be passed if the caller wants actual argument position
+ * information to be returned into the NamedArgExpr nodes.
*/
FuncDetailCode
func_get_detail(List *funcname,
List *fargs,
+ List *fargnames,
int nargs,
Oid *argtypes,
bool expand_variadic,
@@ -833,7 +917,7 @@ func_get_detail(List *funcname,
*argdefaults = NIL;
/* Get list of possible candidates from namespace search */
- raw_candidates = FuncnameGetCandidates(funcname, nargs,
+ raw_candidates = FuncnameGetCandidates(funcname, nargs, fargnames,
expand_variadic, expand_defaults);
/*
@@ -884,7 +968,7 @@ func_get_detail(List *funcname,
* coerce_type can't handle, we'll cause infinite recursion between
* this module and coerce_type!
*/
- if (nargs == 1 && fargs != NIL)
+ if (nargs == 1 && fargs != NIL && fargnames == NIL)
{
Oid targetType = FuncNameAsType(funcname);
@@ -967,17 +1051,47 @@ func_get_detail(List *funcname,
FuncDetailCode result;
/*
- * If expanding variadics or defaults, the "best candidate" might
- * represent multiple equivalently good functions; treat this case as
- * ambiguous.
+ * If processing named args or expanding variadics or defaults, the
+ * "best candidate" might represent multiple equivalently good
+ * functions; treat this case as ambiguous.
*/
if (!OidIsValid(best_candidate->oid))
return FUNCDETAIL_MULTIPLE;
+ /*
+ * We disallow VARIADIC with named arguments unless the last
+ * argument (the one with VARIADIC attached) actually matched the
+ * variadic parameter. This is mere pedantry, really, but some
+ * folks insisted.
+ */
+ if (fargnames != NIL && !expand_variadic && nargs > 0 &&
+ best_candidate->argnumbers[nargs - 1] != nargs - 1)
+ return FUNCDETAIL_NOTFOUND;
+
*funcid = best_candidate->oid;
*nvargs = best_candidate->nvargs;
*true_typeids = best_candidate->args;
+ /*
+ * If processing named args, return actual argument positions into
+ * NamedArgExpr nodes in the fargs list. This is a bit ugly but not
+ * worth the extra notation needed to do it differently.
+ */
+ if (best_candidate->argnumbers != NULL)
+ {
+ int i = 0;
+ ListCell *lc;
+
+ foreach(lc, fargs)
+ {
+ NamedArgExpr *na = (NamedArgExpr *) lfirst(lc);
+
+ if (IsA(na, NamedArgExpr))
+ na->argnumber = best_candidate->argnumbers[i];
+ i++;
+ }
+ }
+
ftup = SearchSysCache(PROCOID,
ObjectIdGetDatum(best_candidate->oid),
0, 0, 0);
@@ -988,36 +1102,73 @@ func_get_detail(List *funcname,
*rettype = pform->prorettype;
*retset = pform->proretset;
/* fetch default args if caller wants 'em */
- if (argdefaults)
+ if (argdefaults && best_candidate->ndargs > 0)
{
- if (best_candidate->ndargs > 0)
+ Datum proargdefaults;
+ bool isnull;
+ char *str;
+ List *defaults;
+
+ /* shouldn't happen, FuncnameGetCandidates messed up */
+ if (best_candidate->ndargs > pform->pronargdefaults)
+ elog(ERROR, "not enough default arguments");
+
+ proargdefaults = SysCacheGetAttr(PROCOID, ftup,
+ Anum_pg_proc_proargdefaults,
+ &isnull);
+ Assert(!isnull);
+ str = TextDatumGetCString(proargdefaults);
+ defaults = (List *) stringToNode(str);
+ Assert(IsA(defaults, List));
+ pfree(str);
+
+ /* Delete any unused defaults from the returned list */
+ if (best_candidate->argnumbers != NULL)
+ {
+ /*
+ * This is a bit tricky in named notation, since the supplied
+ * arguments could replace any subset of the defaults. We
+ * work by making a bitmapset of the argnumbers of defaulted
+ * arguments, then scanning the defaults list and selecting
+ * the needed items. (This assumes that defaulted arguments
+ * should be supplied in their positional order.)
+ */
+ Bitmapset *defargnumbers;
+ int *firstdefarg;
+ List *newdefaults;
+ ListCell *lc;
+ int i;
+
+ defargnumbers = NULL;
+ firstdefarg = &best_candidate->argnumbers[best_candidate->nargs - best_candidate->ndargs];
+ for (i = 0; i < best_candidate->ndargs; i++)
+ defargnumbers = bms_add_member(defargnumbers,
+ firstdefarg[i]);
+ newdefaults = NIL;
+ i = pform->pronargs - pform->pronargdefaults;
+ foreach(lc, defaults)
+ {
+ if (bms_is_member(i, defargnumbers))
+ newdefaults = lappend(newdefaults, lfirst(lc));
+ i++;
+ }
+ Assert(list_length(newdefaults) == best_candidate->ndargs);
+ bms_free(defargnumbers);
+ *argdefaults = newdefaults;
+ }
+ else
{
- Datum proargdefaults;
- bool isnull;
- char *str;
- List *defaults;
+ /*
+ * Defaults for positional notation are lots easier;
+ * just remove any unwanted ones from the front.
+ */
int ndelete;
- /* shouldn't happen, FuncnameGetCandidates messed up */
- if (best_candidate->ndargs > pform->pronargdefaults)
- elog(ERROR, "not enough default arguments");
-
- proargdefaults = SysCacheGetAttr(PROCOID, ftup,
- Anum_pg_proc_proargdefaults,
- &isnull);
- Assert(!isnull);
- str = TextDatumGetCString(proargdefaults);
- defaults = (List *) stringToNode(str);
- Assert(IsA(defaults, List));
- pfree(str);
- /* Delete any unused defaults from the returned list */
ndelete = list_length(defaults) - best_candidate->ndargs;
while (ndelete-- > 0)
defaults = list_delete_first(defaults);
*argdefaults = defaults;
}
- else
- *argdefaults = NIL;
}
if (pform->proisagg)
result = FUNCDETAIL_AGGREGATE;
@@ -1060,13 +1211,36 @@ make_fn_arguments(ParseState *pstate,
/* types don't match? then force coercion using a function call... */
if (actual_arg_types[i] != declared_arg_types[i])
{
- lfirst(current_fargs) = coerce_type(pstate,
- lfirst(current_fargs),
- actual_arg_types[i],
- declared_arg_types[i], -1,
- COERCION_IMPLICIT,
- COERCE_IMPLICIT_CAST,
- -1);
+ Node *node = (Node *) lfirst(current_fargs);
+
+ /*
+ * If arg is a NamedArgExpr, coerce its input expr instead ---
+ * we want the NamedArgExpr to stay at the top level of the list.
+ */
+ if (IsA(node, NamedArgExpr))
+ {
+ NamedArgExpr *na = (NamedArgExpr *) node;
+
+ node = coerce_type(pstate,
+ (Node *) na->arg,
+ actual_arg_types[i],
+ declared_arg_types[i], -1,
+ COERCION_IMPLICIT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ na->arg = (Expr *) node;
+ }
+ else
+ {
+ node = coerce_type(pstate,
+ node,
+ actual_arg_types[i],
+ declared_arg_types[i], -1,
+ COERCION_IMPLICIT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ lfirst(current_fargs) = node;
+ }
}
i++;
}
@@ -1223,25 +1397,39 @@ unknown_attribute(ParseState *pstate, Node *relref, char *attname,
* Build a string representing a function name, including arg types.
* The result is something like "foo(integer)".
*
+ * If argnames isn't NIL, it is a list of C strings representing the actual
+ * arg names for the last N arguments. This must be considered part of the
+ * function signature too, when dealing with named-notation function calls.
+ *
* This is typically used in the construction of function-not-found error
* messages.
*/
const char *
-funcname_signature_string(const char *funcname,
- int nargs, const Oid *argtypes)
+funcname_signature_string(const char *funcname, int nargs,
+ List *argnames, const Oid *argtypes)
{
StringInfoData argbuf;
+ int numposargs;
+ ListCell *lc;
int i;
initStringInfo(&argbuf);
appendStringInfo(&argbuf, "%s(", funcname);
+ numposargs = nargs - list_length(argnames);
+ lc = list_head(argnames);
+
for (i = 0; i < nargs; i++)
{
if (i)
appendStringInfoString(&argbuf, ", ");
appendStringInfoString(&argbuf, format_type_be(argtypes[i]));
+ if (i >= numposargs)
+ {
+ appendStringInfo(&argbuf, " AS %s", (char *) lfirst(lc));
+ lc = lnext(lc);
+ }
}
appendStringInfoChar(&argbuf, ')');
@@ -1254,10 +1442,11 @@ funcname_signature_string(const char *funcname,
* As above, but function name is passed as a qualified name list.
*/
const char *
-func_signature_string(List *funcname, int nargs, const Oid *argtypes)
+func_signature_string(List *funcname, int nargs,
+ List *argnames, const Oid *argtypes)
{
return funcname_signature_string(NameListToString(funcname),
- nargs, argtypes);
+ nargs, argnames, argtypes);
}
/*
@@ -1276,7 +1465,7 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
{
FuncCandidateList clist;
- clist = FuncnameGetCandidates(funcname, nargs, false, false);
+ clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false);
while (clist)
{
@@ -1289,7 +1478,8 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
- func_signature_string(funcname, nargs, argtypes))));
+ func_signature_string(funcname, nargs,
+ NIL, argtypes))));
return InvalidOid;
}
@@ -1401,8 +1591,8 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("aggregate %s does not exist",
- func_signature_string(aggname,
- argcount, argoids))));
+ func_signature_string(aggname, argcount,
+ NIL, argoids))));
}
/* Make sure it's an aggregate */
@@ -1422,8 +1612,8 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("function %s is not an aggregate",
- func_signature_string(aggname,
- argcount, argoids))));
+ func_signature_string(aggname, argcount,
+ NIL, argoids))));
}
ReleaseSysCache(ftup);