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.c125
1 files changed, 78 insertions, 47 deletions
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 21ddd5b7e01..abe1dbc521c 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -49,15 +49,17 @@ static Node *ParseComplexProjection(ParseState *pstate, const char *funcname,
* For historical reasons, Postgres tries to treat the notations tab.col
* and col(tab) as equivalent: if a single-argument function call has an
* argument of complex type and the (unqualified) function name matches
- * any attribute of the type, we take it as a column projection. Conversely
- * a function of a single complex-type argument can be written like a
- * column reference, allowing functions to act like computed columns.
+ * any attribute of the type, we can interpret it as a column projection.
+ * Conversely a function of a single complex-type argument can be written
+ * like a column reference, allowing functions to act like computed columns.
+ *
+ * If both interpretations are possible, we prefer the one matching the
+ * syntactic form, but otherwise the form does not matter.
*
* Hence, both cases come through here. If fn is null, we're dealing with
- * column syntax not function syntax, but in principle that should not
- * affect the lookup behavior, only which error messages we deliver.
- * The FuncCall struct is needed however to carry various decoration that
- * applies to aggregate and window functions.
+ * column syntax not function syntax. In the function-syntax case,
+ * the FuncCall struct is needed to carry various decoration that applies
+ * to aggregate and window functions.
*
* Also, when fn is null, we return NULL on failure rather than
* reporting a no-such-function error.
@@ -84,6 +86,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
bool agg_distinct = (fn ? fn->agg_distinct : false);
bool func_variadic = (fn ? fn->func_variadic : false);
WindowDef *over = (fn ? fn->over : NULL);
+ bool could_be_projection;
Oid rettype;
Oid funcid;
ListCell *l;
@@ -202,36 +205,39 @@ 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, nor an argument name.
+ * Decide whether it's legitimate to consider the construct to be a column
+ * projection. For that, there has to be a single argument of complex
+ * type, the function name must not be qualified, and there cannot be any
+ * syntactic decoration that'd require it to be a function (such as
+ * aggregate or variadic decoration, or named arguments).
*/
- if (nargs == 1 && !proc_call &&
- agg_order == NIL && agg_filter == NULL && !agg_star &&
- !agg_distinct && over == NULL && !func_variadic && argnames == NIL &&
- list_length(funcname) == 1)
- {
- Oid argtype = actual_arg_types[0];
+ could_be_projection = (nargs == 1 && !proc_call &&
+ agg_order == NIL && agg_filter == NULL &&
+ !agg_star && !agg_distinct && over == NULL &&
+ !func_variadic && argnames == NIL &&
+ list_length(funcname) == 1 &&
+ (actual_arg_types[0] == RECORDOID ||
+ ISCOMPLEX(actual_arg_types[0])));
- if (argtype == RECORDOID || ISCOMPLEX(argtype))
- {
- retval = ParseComplexProjection(pstate,
- strVal(linitial(funcname)),
- first_arg,
- location);
- if (retval)
- return retval;
+ /*
+ * If it's column syntax, check for column projection case first.
+ */
+ if (could_be_projection && is_column)
+ {
+ retval = ParseComplexProjection(pstate,
+ strVal(linitial(funcname)),
+ first_arg,
+ location);
+ if (retval)
+ return retval;
- /*
- * If ParseComplexProjection doesn't recognize it as a projection,
- * just press on.
- */
- }
+ /*
+ * If ParseComplexProjection doesn't recognize it as a projection,
+ * just press on.
+ */
}
/*
- * Okay, it's not a column projection, so it must really be a function.
* func_get_detail looks up the function in the catalogs, does
* disambiguation for polymorphic functions, handles inheritance, and
* returns the funcid and type and set or singleton status of the
@@ -334,7 +340,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
}
/*
- * So far so good, so do some routine-type-specific processing.
+ * So far so good, so do some fdresult-type-specific processing.
*/
if (fdresult == FUNCDETAIL_NORMAL || fdresult == FUNCDETAIL_PROCEDURE)
{
@@ -524,30 +530,55 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
actual_arg_types[0], rettype, -1,
COERCION_EXPLICIT, COERCE_EXPLICIT_CALL, location);
}
+ else if (fdresult == FUNCDETAIL_MULTIPLE)
+ {
+ /*
+ * We found multiple possible functional matches. If we are dealing
+ * with attribute notation, return failure, letting the caller report
+ * "no such column" (we already determined there wasn't one). If
+ * dealing with function notation, report "ambiguous function",
+ * regardless of whether there's also a column by this name.
+ */
+ if (is_column)
+ return NULL;
+
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+ errmsg("function %s is not unique",
+ 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."),
+ parser_errposition(pstate, location)));
+ }
else
{
/*
- * Oops. Time to die.
- *
- * If we are dealing with the attribute notation rel.function, let the
- * caller handle failure.
+ * Not found as a function. If we are dealing with attribute
+ * notation, return failure, letting the caller report "no such
+ * column" (we already determined there wasn't one).
*/
if (is_column)
return NULL;
/*
- * Else generate a detailed complaint for a function
+ * Check for column projection interpretation, since we didn't before.
*/
- if (fdresult == FUNCDETAIL_MULTIPLE)
- ereport(ERROR,
- (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
- errmsg("function %s is not unique",
- 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."),
- parser_errposition(pstate, location)));
- else if (list_length(agg_order) > 1 && !agg_within_group)
+ if (could_be_projection)
+ {
+ retval = ParseComplexProjection(pstate,
+ strVal(linitial(funcname)),
+ first_arg,
+ location);
+ if (retval)
+ return retval;
+ }
+
+ /*
+ * No function, and no column either. Since we're dealing with
+ * function notation, report "function does not exist".
+ */
+ if (list_length(agg_order) > 1 && !agg_within_group)
{
/* It's agg(x, ORDER BY y,z) ... perhaps misplaced ORDER BY */
ereport(ERROR,