aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser')
-rw-r--r--src/backend/parser/analyze.c92
-rw-r--r--src/backend/parser/gram.y109
-rw-r--r--src/backend/parser/parse_func.c207
-rw-r--r--src/backend/parser/parse_oper.c4
4 files changed, 294 insertions, 118 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 9cede29d6a8..438b077004d 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -25,6 +25,7 @@
#include "postgres.h"
#include "access/sysattr.h"
+#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
@@ -50,6 +51,7 @@
#include "utils/guc.h"
#include "utils/queryjumble.h"
#include "utils/rel.h"
+#include "utils/syscache.h"
/* Hook for plugins to get control at end of parse analysis */
@@ -2933,8 +2935,6 @@ transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt)
/*
* transform a CallStmt
- *
- * We need to do parse analysis on the procedure call and its arguments.
*/
static Query *
transformCallStmt(ParseState *pstate, CallStmt *stmt)
@@ -2942,8 +2942,17 @@ transformCallStmt(ParseState *pstate, CallStmt *stmt)
List *targs;
ListCell *lc;
Node *node;
+ FuncExpr *fexpr;
+ HeapTuple proctup;
+ Datum proargmodes;
+ bool isNull;
+ List *outargs = NIL;
Query *result;
+ /*
+ * First, do standard parse analysis on the procedure call and its
+ * arguments, allowing us to identify the called procedure.
+ */
targs = NIL;
foreach(lc, stmt->funccall->args)
{
@@ -2962,8 +2971,85 @@ transformCallStmt(ParseState *pstate, CallStmt *stmt)
assign_expr_collations(pstate, node);
- stmt->funcexpr = castNode(FuncExpr, node);
+ fexpr = castNode(FuncExpr, node);
+
+ proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fexpr->funcid));
+ if (!HeapTupleIsValid(proctup))
+ elog(ERROR, "cache lookup failed for function %u", fexpr->funcid);
+
+ /*
+ * Expand the argument list to deal with named-argument notation and
+ * default arguments. For ordinary FuncExprs this'd be done during
+ * planning, but a CallStmt doesn't go through planning, and there seems
+ * no good reason not to do it here.
+ */
+ fexpr->args = expand_function_arguments(fexpr->args,
+ true,
+ fexpr->funcresulttype,
+ proctup);
+
+ /* Fetch proargmodes; if it's null, there are no output args */
+ proargmodes = SysCacheGetAttr(PROCOID, proctup,
+ Anum_pg_proc_proargmodes,
+ &isNull);
+ if (!isNull)
+ {
+ /*
+ * Split the list into input arguments in fexpr->args and output
+ * arguments in stmt->outargs. INOUT arguments appear in both lists.
+ */
+ ArrayType *arr;
+ int numargs;
+ char *argmodes;
+ List *inargs;
+ int i;
+
+ arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */
+ numargs = list_length(fexpr->args);
+ if (ARR_NDIM(arr) != 1 ||
+ ARR_DIMS(arr)[0] != numargs ||
+ ARR_HASNULL(arr) ||
+ ARR_ELEMTYPE(arr) != CHAROID)
+ elog(ERROR, "proargmodes is not a 1-D char array of length %d or it contains nulls",
+ numargs);
+ argmodes = (char *) ARR_DATA_PTR(arr);
+
+ inargs = NIL;
+ i = 0;
+ foreach(lc, fexpr->args)
+ {
+ Node *n = lfirst(lc);
+
+ switch (argmodes[i])
+ {
+ case PROARGMODE_IN:
+ case PROARGMODE_VARIADIC:
+ inargs = lappend(inargs, n);
+ break;
+ case PROARGMODE_OUT:
+ outargs = lappend(outargs, n);
+ break;
+ case PROARGMODE_INOUT:
+ inargs = lappend(inargs, n);
+ outargs = lappend(outargs, copyObject(n));
+ break;
+ default:
+ /* note we don't support PROARGMODE_TABLE */
+ elog(ERROR, "invalid argmode %c for procedure",
+ argmodes[i]);
+ break;
+ }
+ i++;
+ }
+ fexpr->args = inargs;
+ }
+ stmt->funcexpr = fexpr;
+ stmt->outargs = outargs;
+
+ ReleaseSysCache(proctup);
+
+ /* represent the command as a utility Query */
result = makeNode(Query);
result->commandType = CMD_UTILITY;
result->utilityStmt = (Node *) stmt;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 52a254928f8..eb241954387 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -172,7 +172,7 @@ static RoleSpec *makeRoleSpec(RoleSpecType type, int location);
static void check_qualified_name(List *names, core_yyscan_t yyscanner);
static List *check_func_name(List *names, core_yyscan_t yyscanner);
static List *check_indirection(List *indirection, core_yyscan_t yyscanner);
-static List *extractArgTypes(ObjectType objtype, List *parameters);
+static List *extractArgTypes(List *parameters);
static List *extractAggrArgTypes(List *aggrargs);
static List *makeOrderedSetArgs(List *directargs, List *orderedargs,
core_yyscan_t yyscanner);
@@ -385,8 +385,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <accesspriv> privilege
%type <list> privileges privilege_list
%type <privtarget> privilege_target
-%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes procedure_with_argtypes function_with_argtypes_common
-%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list procedure_with_argtypes_list
+%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes
+%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list
%type <ival> defacl_privilege_target
%type <defelt> DefACLOption
%type <list> DefACLOptionList
@@ -4757,7 +4757,7 @@ AlterExtensionContentsStmt:
n->object = (Node *) lcons(makeString($9), $7);
$$ = (Node *)n;
}
- | ALTER EXTENSION name add_drop PROCEDURE procedure_with_argtypes
+ | ALTER EXTENSION name add_drop PROCEDURE function_with_argtypes
{
AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt);
n->extname = $3;
@@ -4766,7 +4766,7 @@ AlterExtensionContentsStmt:
n->object = (Node *) $6;
$$ = (Node *)n;
}
- | ALTER EXTENSION name add_drop ROUTINE procedure_with_argtypes
+ | ALTER EXTENSION name add_drop ROUTINE function_with_argtypes
{
AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt);
n->extname = $3;
@@ -6505,7 +6505,7 @@ CommentStmt:
n->comment = $8;
$$ = (Node *) n;
}
- | COMMENT ON PROCEDURE procedure_with_argtypes IS comment_text
+ | COMMENT ON PROCEDURE function_with_argtypes IS comment_text
{
CommentStmt *n = makeNode(CommentStmt);
n->objtype = OBJECT_PROCEDURE;
@@ -6513,7 +6513,7 @@ CommentStmt:
n->comment = $6;
$$ = (Node *) n;
}
- | COMMENT ON ROUTINE procedure_with_argtypes IS comment_text
+ | COMMENT ON ROUTINE function_with_argtypes IS comment_text
{
CommentStmt *n = makeNode(CommentStmt);
n->objtype = OBJECT_ROUTINE;
@@ -6659,7 +6659,7 @@ SecLabelStmt:
n->label = $9;
$$ = (Node *) n;
}
- | SECURITY LABEL opt_provider ON PROCEDURE procedure_with_argtypes
+ | SECURITY LABEL opt_provider ON PROCEDURE function_with_argtypes
IS security_label
{
SecLabelStmt *n = makeNode(SecLabelStmt);
@@ -7023,7 +7023,7 @@ privilege_target:
n->objs = $2;
$$ = n;
}
- | PROCEDURE procedure_with_argtypes_list
+ | PROCEDURE function_with_argtypes_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
n->targtype = ACL_TARGET_OBJECT;
@@ -7031,7 +7031,7 @@ privilege_target:
n->objs = $2;
$$ = n;
}
- | ROUTINE procedure_with_argtypes_list
+ | ROUTINE function_with_argtypes_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
n->targtype = ACL_TARGET_OBJECT;
@@ -7556,33 +7556,21 @@ function_with_argtypes_list:
{ $$ = lappend($1, $3); }
;
-procedure_with_argtypes_list:
- procedure_with_argtypes { $$ = list_make1($1); }
- | procedure_with_argtypes_list ',' procedure_with_argtypes
- { $$ = lappend($1, $3); }
- ;
-
function_with_argtypes:
func_name func_args
{
ObjectWithArgs *n = makeNode(ObjectWithArgs);
n->objname = $1;
- n->objargs = extractArgTypes(OBJECT_FUNCTION, $2);
+ n->objargs = extractArgTypes($2);
+ n->objfuncargs = $2;
$$ = n;
}
- | function_with_argtypes_common
- {
- $$ = $1;
- }
- ;
-
-function_with_argtypes_common:
/*
* Because of reduce/reduce conflicts, we can't use func_name
* below, but we can write it out the long way, which actually
* allows more cases.
*/
- type_func_name_keyword
+ | type_func_name_keyword
{
ObjectWithArgs *n = makeNode(ObjectWithArgs);
n->objname = list_make1(makeString(pstrdup($1)));
@@ -7607,24 +7595,6 @@ function_with_argtypes_common:
;
/*
- * This is different from function_with_argtypes in the call to
- * extractArgTypes().
- */
-procedure_with_argtypes:
- func_name func_args
- {
- ObjectWithArgs *n = makeNode(ObjectWithArgs);
- n->objname = $1;
- n->objargs = extractArgTypes(OBJECT_PROCEDURE, $2);
- $$ = n;
- }
- | function_with_argtypes_common
- {
- $$ = $1;
- }
- ;
-
-/*
* func_args_with_defaults is separate because we only want to accept
* defaults in CREATE FUNCTION, not in ALTER etc.
*/
@@ -7673,7 +7643,7 @@ func_arg:
FunctionParameter *n = makeNode(FunctionParameter);
n->name = $1;
n->argType = $2;
- n->mode = FUNC_PARAM_IN;
+ n->mode = FUNC_PARAM_DEFAULT;
n->defexpr = NULL;
$$ = n;
}
@@ -7691,7 +7661,7 @@ func_arg:
FunctionParameter *n = makeNode(FunctionParameter);
n->name = NULL;
n->argType = $1;
- n->mode = FUNC_PARAM_IN;
+ n->mode = FUNC_PARAM_DEFAULT;
n->defexpr = NULL;
$$ = n;
}
@@ -7763,7 +7733,8 @@ func_arg_with_default:
/* Aggregate args can be most things that function args can be */
aggr_arg: func_arg
{
- if (!($1->mode == FUNC_PARAM_IN ||
+ if (!($1->mode == FUNC_PARAM_DEFAULT ||
+ $1->mode == FUNC_PARAM_IN ||
$1->mode == FUNC_PARAM_VARIADIC))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -7832,6 +7803,7 @@ aggregate_with_argtypes:
ObjectWithArgs *n = makeNode(ObjectWithArgs);
n->objname = $1;
n->objargs = extractAggrArgTypes($2);
+ n->objfuncargs = (List *) linitial($2);
$$ = n;
}
;
@@ -8056,7 +8028,7 @@ AlterFunctionStmt:
n->actions = $4;
$$ = (Node *) n;
}
- | ALTER PROCEDURE procedure_with_argtypes alterfunc_opt_list opt_restrict
+ | ALTER PROCEDURE function_with_argtypes alterfunc_opt_list opt_restrict
{
AlterFunctionStmt *n = makeNode(AlterFunctionStmt);
n->objtype = OBJECT_PROCEDURE;
@@ -8064,7 +8036,7 @@ AlterFunctionStmt:
n->actions = $4;
$$ = (Node *) n;
}
- | ALTER ROUTINE procedure_with_argtypes alterfunc_opt_list opt_restrict
+ | ALTER ROUTINE function_with_argtypes alterfunc_opt_list opt_restrict
{
AlterFunctionStmt *n = makeNode(AlterFunctionStmt);
n->objtype = OBJECT_ROUTINE;
@@ -8120,7 +8092,7 @@ RemoveFuncStmt:
n->concurrent = false;
$$ = (Node *)n;
}
- | DROP PROCEDURE procedure_with_argtypes_list opt_drop_behavior
+ | DROP PROCEDURE function_with_argtypes_list opt_drop_behavior
{
DropStmt *n = makeNode(DropStmt);
n->removeType = OBJECT_PROCEDURE;
@@ -8130,7 +8102,7 @@ RemoveFuncStmt:
n->concurrent = false;
$$ = (Node *)n;
}
- | DROP PROCEDURE IF_P EXISTS procedure_with_argtypes_list opt_drop_behavior
+ | DROP PROCEDURE IF_P EXISTS function_with_argtypes_list opt_drop_behavior
{
DropStmt *n = makeNode(DropStmt);
n->removeType = OBJECT_PROCEDURE;
@@ -8140,7 +8112,7 @@ RemoveFuncStmt:
n->concurrent = false;
$$ = (Node *)n;
}
- | DROP ROUTINE procedure_with_argtypes_list opt_drop_behavior
+ | DROP ROUTINE function_with_argtypes_list opt_drop_behavior
{
DropStmt *n = makeNode(DropStmt);
n->removeType = OBJECT_ROUTINE;
@@ -8150,7 +8122,7 @@ RemoveFuncStmt:
n->concurrent = false;
$$ = (Node *)n;
}
- | DROP ROUTINE IF_P EXISTS procedure_with_argtypes_list opt_drop_behavior
+ | DROP ROUTINE IF_P EXISTS function_with_argtypes_list opt_drop_behavior
{
DropStmt *n = makeNode(DropStmt);
n->removeType = OBJECT_ROUTINE;
@@ -8622,7 +8594,7 @@ RenameStmt: ALTER AGGREGATE aggregate_with_argtypes RENAME TO name
n->missing_ok = true;
$$ = (Node *)n;
}
- | ALTER PROCEDURE procedure_with_argtypes RENAME TO name
+ | ALTER PROCEDURE function_with_argtypes RENAME TO name
{
RenameStmt *n = makeNode(RenameStmt);
n->renameType = OBJECT_PROCEDURE;
@@ -8640,7 +8612,7 @@ RenameStmt: ALTER AGGREGATE aggregate_with_argtypes RENAME TO name
n->missing_ok = false;
$$ = (Node *)n;
}
- | ALTER ROUTINE procedure_with_argtypes RENAME TO name
+ | ALTER ROUTINE function_with_argtypes RENAME TO name
{
RenameStmt *n = makeNode(RenameStmt);
n->renameType = OBJECT_ROUTINE;
@@ -9051,7 +9023,7 @@ AlterObjectDependsStmt:
n->remove = $4;
$$ = (Node *)n;
}
- | ALTER PROCEDURE procedure_with_argtypes opt_no DEPENDS ON EXTENSION name
+ | ALTER PROCEDURE function_with_argtypes opt_no DEPENDS ON EXTENSION name
{
AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
n->objectType = OBJECT_PROCEDURE;
@@ -9060,7 +9032,7 @@ AlterObjectDependsStmt:
n->remove = $4;
$$ = (Node *)n;
}
- | ALTER ROUTINE procedure_with_argtypes opt_no DEPENDS ON EXTENSION name
+ | ALTER ROUTINE function_with_argtypes opt_no DEPENDS ON EXTENSION name
{
AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
n->objectType = OBJECT_ROUTINE;
@@ -9191,7 +9163,7 @@ AlterObjectSchemaStmt:
n->missing_ok = false;
$$ = (Node *)n;
}
- | ALTER PROCEDURE procedure_with_argtypes SET SCHEMA name
+ | ALTER PROCEDURE function_with_argtypes SET SCHEMA name
{
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
n->objectType = OBJECT_PROCEDURE;
@@ -9200,7 +9172,7 @@ AlterObjectSchemaStmt:
n->missing_ok = false;
$$ = (Node *)n;
}
- | ALTER ROUTINE procedure_with_argtypes SET SCHEMA name
+ | ALTER ROUTINE function_with_argtypes SET SCHEMA name
{
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
n->objectType = OBJECT_ROUTINE;
@@ -9502,7 +9474,7 @@ AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec
n->newowner = $9;
$$ = (Node *)n;
}
- | ALTER PROCEDURE procedure_with_argtypes OWNER TO RoleSpec
+ | ALTER PROCEDURE function_with_argtypes OWNER TO RoleSpec
{
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
n->objectType = OBJECT_PROCEDURE;
@@ -9510,7 +9482,7 @@ AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec
n->newowner = $6;
$$ = (Node *)n;
}
- | ALTER ROUTINE procedure_with_argtypes OWNER TO RoleSpec
+ | ALTER ROUTINE function_with_argtypes OWNER TO RoleSpec
{
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
n->objectType = OBJECT_ROUTINE;
@@ -16698,14 +16670,13 @@ check_indirection(List *indirection, core_yyscan_t yyscanner)
}
/* extractArgTypes()
- *
* Given a list of FunctionParameter nodes, extract a list of just the
- * argument types (TypeNames) for signature parameters only (e.g., only input
- * parameters for functions). This is what is needed to look up an existing
- * function, which is what is wanted by the productions that use this call.
+ * argument types (TypeNames) for input parameters only. This is what
+ * is needed to look up an existing function, which is what is wanted by
+ * the productions that use this call.
*/
static List *
-extractArgTypes(ObjectType objtype, List *parameters)
+extractArgTypes(List *parameters)
{
List *result = NIL;
ListCell *i;
@@ -16714,7 +16685,7 @@ extractArgTypes(ObjectType objtype, List *parameters)
{
FunctionParameter *p = (FunctionParameter *) lfirst(i);
- if ((p->mode != FUNC_PARAM_OUT || objtype == OBJECT_PROCEDURE) && p->mode != FUNC_PARAM_TABLE)
+ if (p->mode != FUNC_PARAM_OUT && p->mode != FUNC_PARAM_TABLE)
result = lappend(result, p->argType);
}
return result;
@@ -16727,7 +16698,7 @@ static List *
extractAggrArgTypes(List *aggrargs)
{
Assert(list_length(aggrargs) == 2);
- return extractArgTypes(OBJECT_AGGREGATE, (List *) linitial(aggrargs));
+ return extractArgTypes((List *) linitial(aggrargs));
}
/* makeOrderedSetArgs()
@@ -17023,7 +16994,9 @@ mergeTableFuncParameters(List *func_args, List *columns)
{
FunctionParameter *p = (FunctionParameter *) lfirst(lc);
- if (p->mode != FUNC_PARAM_IN && p->mode != FUNC_PARAM_VARIADIC)
+ if (p->mode != FUNC_PARAM_DEFAULT &&
+ p->mode != FUNC_PARAM_IN &&
+ p->mode != FUNC_PARAM_VARIADIC)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("OUT and INOUT arguments aren't allowed in TABLE functions")));
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index fb0ba58ff77..3cec8de7da8 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -48,9 +48,10 @@ static void unify_hypothetical_args(ParseState *pstate,
static Oid FuncNameAsType(List *funcname);
static Node *ParseComplexProjection(ParseState *pstate, const char *funcname,
Node *first_arg, int location);
-static Oid LookupFuncNameInternal(List *funcname, int nargs,
- const Oid *argtypes,
- bool missing_ok, FuncLookupError *lookupError);
+static Oid LookupFuncNameInternal(ObjectType objtype, List *funcname,
+ int nargs, const Oid *argtypes,
+ bool include_out_arguments, bool missing_ok,
+ FuncLookupError *lookupError);
/*
@@ -82,7 +83,8 @@ static Oid LookupFuncNameInternal(List *funcname, int nargs,
* contain any SRF calls, last_srf can just be pstate->p_last_srf.
*
* proc_call is true if we are considering a CALL statement, so that the
- * name must resolve to a procedure name, not anything else.
+ * name must resolve to a procedure name, not anything else. This flag
+ * also specifies that the argument list includes any OUT-mode arguments.
*/
Node *
ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
@@ -263,7 +265,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
fdresult = func_get_detail(funcname, fargs, argnames, nargs,
actual_arg_types,
- !func_variadic, true,
+ !func_variadic, true, proc_call,
&funcid, &rettype, &retset,
&nvargs, &vatype,
&declared_arg_types, &argdefaults);
@@ -1395,6 +1397,7 @@ func_get_detail(List *funcname,
Oid *argtypes,
bool expand_variadic,
bool expand_defaults,
+ bool include_out_arguments,
Oid *funcid, /* return value */
Oid *rettype, /* return value */
bool *retset, /* return value */
@@ -1419,7 +1422,7 @@ func_get_detail(List *funcname,
/* Get list of possible candidates from namespace search */
raw_candidates = FuncnameGetCandidates(funcname, nargs, fargnames,
expand_variadic, expand_defaults,
- false);
+ include_out_arguments, false);
/*
* Quickly check if there is an exact match to the input datatypes (there
@@ -1667,7 +1670,7 @@ func_get_detail(List *funcname,
defargnumbers = bms_add_member(defargnumbers,
firstdefarg[i]);
newdefaults = NIL;
- i = pform->pronargs - pform->pronargdefaults;
+ i = best_candidate->nominalnargs - pform->pronargdefaults;
foreach(lc, defaults)
{
if (bms_is_member(i, defargnumbers))
@@ -2041,12 +2044,15 @@ func_signature_string(List *funcname, int nargs,
*
* Possible errors:
* FUNCLOOKUP_NOSUCHFUNC: we can't find a function of this name.
- * FUNCLOOKUP_AMBIGUOUS: nargs == -1 and more than one function matches.
+ * FUNCLOOKUP_AMBIGUOUS: more than one function matches.
*/
static Oid
-LookupFuncNameInternal(List *funcname, int nargs, const Oid *argtypes,
- bool missing_ok, FuncLookupError *lookupError)
+LookupFuncNameInternal(ObjectType objtype, List *funcname,
+ int nargs, const Oid *argtypes,
+ bool include_out_arguments, bool missing_ok,
+ FuncLookupError *lookupError)
{
+ Oid result = InvalidOid;
FuncCandidateList clist;
/* NULL argtypes allowed for nullary functions only */
@@ -2055,43 +2061,62 @@ LookupFuncNameInternal(List *funcname, int nargs, const Oid *argtypes,
/* Always set *lookupError, to forestall uninitialized-variable warnings */
*lookupError = FUNCLOOKUP_NOSUCHFUNC;
+ /* Get list of candidate objects */
clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false,
- missing_ok);
+ include_out_arguments, missing_ok);
- /*
- * If no arguments were specified, the name must yield a unique candidate.
- */
- if (nargs < 0)
+ /* Scan list for a match to the arg types (if specified) and the objtype */
+ for (; clist != NULL; clist = clist->next)
{
- if (clist)
+ /* Check arg type match, if specified */
+ if (nargs >= 0)
{
- /* If there is a second match then it's ambiguous */
- if (clist->next)
- {
- *lookupError = FUNCLOOKUP_AMBIGUOUS;
- return InvalidOid;
- }
- /* Otherwise return the match */
- return clist->oid;
+ /* if nargs==0, argtypes can be null; don't pass that to memcmp */
+ if (nargs > 0 &&
+ memcmp(argtypes, clist->args, nargs * sizeof(Oid)) != 0)
+ continue;
}
- else
+
+ /* Check for duplicates reported by FuncnameGetCandidates */
+ if (!OidIsValid(clist->oid))
+ {
+ *lookupError = FUNCLOOKUP_AMBIGUOUS;
return InvalidOid;
- }
+ }
- /*
- * Otherwise, look for a match to the arg types. FuncnameGetCandidates
- * has ensured that there's at most one match in the returned list.
- */
- while (clist)
- {
- /* if nargs==0, argtypes can be null; don't pass that to memcmp */
- if (nargs == 0 ||
- memcmp(argtypes, clist->args, nargs * sizeof(Oid)) == 0)
- return clist->oid;
- clist = clist->next;
+ /* Check objtype match, if specified */
+ switch (objtype)
+ {
+ case OBJECT_FUNCTION:
+ case OBJECT_AGGREGATE:
+ /* Ignore procedures */
+ if (get_func_prokind(clist->oid) == PROKIND_PROCEDURE)
+ continue;
+ break;
+ case OBJECT_PROCEDURE:
+ /* Ignore non-procedures */
+ if (get_func_prokind(clist->oid) != PROKIND_PROCEDURE)
+ continue;
+ break;
+ case OBJECT_ROUTINE:
+ /* no restriction */
+ break;
+ default:
+ Assert(false);
+ }
+
+ /* Check for multiple matches */
+ if (OidIsValid(result))
+ {
+ *lookupError = FUNCLOOKUP_AMBIGUOUS;
+ return InvalidOid;
+ }
+
+ /* OK, we have a candidate */
+ result = clist->oid;
}
- return InvalidOid;
+ return result;
}
/*
@@ -2111,6 +2136,10 @@ LookupFuncNameInternal(List *funcname, int nargs, const Oid *argtypes,
* If nargs == -1 and multiple functions are found matching this function name
* we will raise an ambiguous-function error, regardless of what missing_ok is
* set to.
+ *
+ * Only functions will be found; procedures will be ignored even if they
+ * match the name and argument types. (However, we don't trouble to reject
+ * aggregates or window functions here.)
*/
Oid
LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool missing_ok)
@@ -2118,7 +2147,9 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool missing_ok)
Oid funcoid;
FuncLookupError lookupError;
- funcoid = LookupFuncNameInternal(funcname, nargs, argtypes, missing_ok,
+ funcoid = LookupFuncNameInternal(OBJECT_FUNCTION,
+ funcname, nargs, argtypes,
+ false, missing_ok,
&lookupError);
if (OidIsValid(funcoid))
@@ -2207,10 +2238,14 @@ LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool missing_ok)
FUNC_MAX_ARGS)));
}
+ /*
+ * First, perform a lookup considering only input arguments (traditional
+ * Postgres rules).
+ */
i = 0;
foreach(args_item, func->objargs)
{
- TypeName *t = (TypeName *) lfirst(args_item);
+ TypeName *t = lfirst_node(TypeName, args_item);
argoids[i] = LookupTypeNameOid(NULL, t, missing_ok);
if (!OidIsValid(argoids[i]))
@@ -2224,9 +2259,83 @@ LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool missing_ok)
*/
nargs = func->args_unspecified ? -1 : argcount;
- oid = LookupFuncNameInternal(func->objname, nargs, argoids, missing_ok,
+ /*
+ * In args_unspecified mode, also tell LookupFuncNameInternal to consider
+ * the object type, since there seems no reason not to. However, if we
+ * have an argument list, disable the objtype check, because we'd rather
+ * complain about "object is of wrong type" than "object doesn't exist".
+ * (Note that with args, FuncnameGetCandidates will have ensured there's
+ * only one argtype match, so we're not risking an ambiguity failure via
+ * this choice.)
+ */
+ oid = LookupFuncNameInternal(func->args_unspecified ? objtype : OBJECT_ROUTINE,
+ func->objname, nargs, argoids,
+ false, missing_ok,
&lookupError);
+ /*
+ * If PROCEDURE or ROUTINE was specified, and we have an argument list
+ * that contains no parameter mode markers, and we didn't already discover
+ * that there's ambiguity, perform a lookup considering all arguments.
+ * (Note: for a zero-argument procedure, or in args_unspecified mode, the
+ * normal lookup is sufficient; so it's OK to require non-NIL objfuncargs
+ * to perform this lookup.)
+ */
+ if ((objtype == OBJECT_PROCEDURE || objtype == OBJECT_ROUTINE) &&
+ func->objfuncargs != NIL &&
+ lookupError != FUNCLOOKUP_AMBIGUOUS)
+ {
+ bool have_param_mode = false;
+
+ /*
+ * Check for non-default parameter mode markers. If there are any,
+ * then the command does not conform to SQL-spec syntax, so we may
+ * assume that the traditional Postgres lookup method of considering
+ * only input parameters is sufficient. (Note that because the spec
+ * doesn't have OUT arguments for functions, we also don't need this
+ * hack in FUNCTION or AGGREGATE mode.)
+ */
+ foreach(args_item, func->objfuncargs)
+ {
+ FunctionParameter *fp = lfirst_node(FunctionParameter, args_item);
+
+ if (fp->mode != FUNC_PARAM_DEFAULT)
+ {
+ have_param_mode = true;
+ break;
+ }
+ }
+
+ if (!have_param_mode)
+ {
+ Oid poid;
+
+ /* Without mode marks, objargs surely includes all params */
+ Assert(list_length(func->objfuncargs) == argcount);
+
+ /* For objtype == OBJECT_PROCEDURE, we can ignore non-procedures */
+ poid = LookupFuncNameInternal(objtype, func->objname,
+ argcount, argoids,
+ true, missing_ok,
+ &lookupError);
+
+ /* Combine results, handling ambiguity */
+ if (OidIsValid(poid))
+ {
+ if (OidIsValid(oid) && oid != poid)
+ {
+ /* oops, we got hits both ways, on different objects */
+ oid = InvalidOid;
+ lookupError = FUNCLOOKUP_AMBIGUOUS;
+ }
+ else
+ oid = poid;
+ }
+ else if (lookupError == FUNCLOOKUP_AMBIGUOUS)
+ oid = InvalidOid;
+ }
+ }
+
if (OidIsValid(oid))
{
/*
@@ -2235,6 +2344,10 @@ LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool missing_ok)
* we allow the objtype of FUNCTION to include aggregates and window
* functions; but we draw the line if the object is a procedure. That
* is a new enough feature that this historical rule does not apply.
+ *
+ * (This check is partially redundant with the objtype check in
+ * LookupFuncNameInternal; but not entirely, since we often don't tell
+ * LookupFuncNameInternal to apply that check at all.)
*/
switch (objtype)
{
@@ -2345,28 +2458,32 @@ LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool missing_ok)
(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
errmsg("function name \"%s\" is not unique",
NameListToString(func->objname)),
- errhint("Specify the argument list to select the function unambiguously.")));
+ func->args_unspecified ?
+ errhint("Specify the argument list to select the function unambiguously.") : 0));
break;
case OBJECT_PROCEDURE:
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
errmsg("procedure name \"%s\" is not unique",
NameListToString(func->objname)),
- errhint("Specify the argument list to select the procedure unambiguously.")));
+ func->args_unspecified ?
+ errhint("Specify the argument list to select the procedure unambiguously.") : 0));
break;
case OBJECT_AGGREGATE:
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
errmsg("aggregate name \"%s\" is not unique",
NameListToString(func->objname)),
- errhint("Specify the argument list to select the aggregate unambiguously.")));
+ func->args_unspecified ?
+ errhint("Specify the argument list to select the aggregate unambiguously.") : 0));
break;
case OBJECT_ROUTINE:
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
errmsg("routine name \"%s\" is not unique",
NameListToString(func->objname)),
- errhint("Specify the argument list to select the routine unambiguously.")));
+ func->args_unspecified ?
+ errhint("Specify the argument list to select the routine unambiguously.") : 0));
break;
default:
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index c379d72fcec..4e460799903 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -150,8 +150,8 @@ LookupOperWithArgs(ObjectWithArgs *oper, bool noError)
rightoid;
Assert(list_length(oper->objargs) == 2);
- oprleft = linitial(oper->objargs);
- oprright = lsecond(oper->objargs);
+ oprleft = linitial_node(TypeName, oper->objargs);
+ oprright = lsecond_node(TypeName, oper->objargs);
if (oprleft == NULL)
leftoid = InvalidOid;