aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/catalog/namespace.c68
-rw-r--r--src/backend/catalog/pg_aggregate.c2
-rw-r--r--src/backend/catalog/pg_proc.c6
-rw-r--r--src/backend/commands/functioncmds.c137
-rw-r--r--src/backend/executor/functions.c36
-rw-r--r--src/backend/nodes/copyfuncs.c2
-rw-r--r--src/backend/nodes/equalfuncs.c2
-rw-r--r--src/backend/optimizer/util/clauses.c89
-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
-rw-r--r--src/backend/utils/adt/regproc.c11
-rw-r--r--src/backend/utils/adt/ruleutils.c12
-rw-r--r--src/backend/utils/fmgr/funcapi.c4
-rw-r--r--src/bin/pg_dump/t/002_pg_dump.pl2
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/catalog/namespace.h2
-rw-r--r--src/include/catalog/pg_proc.h2
-rw-r--r--src/include/funcapi.h3
-rw-r--r--src/include/nodes/parsenodes.h37
-rw-r--r--src/include/optimizer/optimizer.h3
-rw-r--r--src/include/parser/parse_func.h1
-rw-r--r--src/pl/plpgsql/src/expected/plpgsql_call.out76
-rw-r--r--src/pl/plpgsql/src/pl_comp.c1
-rw-r--r--src/pl/plpgsql/src/pl_exec.c36
-rw-r--r--src/pl/plpgsql/src/sql/plpgsql_call.sql64
-rw-r--r--src/pl/plpython/expected/plpython_call.out4
-rw-r--r--src/pl/plpython/plpy_procedure.c4
-rw-r--r--src/pl/plpython/sql/plpython_call.sql2
-rw-r--r--src/pl/tcl/expected/pltcl_call.out4
-rw-r--r--src/pl/tcl/sql/pltcl_call.sql2
-rw-r--r--src/test/regress/expected/create_procedure.out149
-rw-r--r--src/test/regress/sql/create_procedure.sql45
34 files changed, 887 insertions, 333 deletions
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 005e029c382..fd767fc5cfa 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -206,6 +206,7 @@ static void RemoveTempRelations(Oid tempNamespaceId);
static void RemoveTempRelationsCallback(int code, Datum arg);
static void NamespaceCallback(Datum arg, int cacheid, uint32 hashvalue);
static bool MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
+ bool include_out_arguments, int pronargs,
int **argnumbers);
@@ -901,6 +902,12 @@ TypeIsVisible(Oid typid)
* of additional args (which can be retrieved from the function's
* proargdefaults entry).
*
+ * If include_out_arguments is true, then OUT-mode arguments are considered to
+ * be included in the argument list. Their types are included in the returned
+ * arrays, and argnumbers are indexes in proallargtypes not proargtypes.
+ * We also set nominalnargs to be the length of proallargtypes not proargtypes.
+ * Otherwise OUT-mode arguments are ignored.
+ *
* It is not possible for nvargs and ndargs to both be nonzero in the same
* list entry, since default insertion allows matches to functions with more
* than nargs arguments while the variadic transformation requires the same
@@ -911,7 +918,8 @@ TypeIsVisible(Oid typid)
* first any positional arguments, then the named arguments, then defaulted
* arguments (if needed and allowed by expand_defaults). The argnumbers[]
* array can be used to map this back to the catalog information.
- * argnumbers[k] is set to the proargtypes index of the k'th call argument.
+ * argnumbers[k] is set to the proargtypes or proallargtypes index of the
+ * k'th call argument.
*
* We search a single namespace if the function name is qualified, else
* all namespaces in the search path. In the multiple-namespace case,
@@ -935,13 +943,13 @@ TypeIsVisible(Oid typid)
* such an entry it should react as though the call were ambiguous.
*
* If missing_ok is true, an empty list (NULL) is returned if the name was
- * schema- qualified with a schema that does not exist. Likewise if no
+ * schema-qualified with a schema that does not exist. Likewise if no
* candidate is found for other reasons.
*/
FuncCandidateList
FuncnameGetCandidates(List *names, int nargs, List *argnames,
bool expand_variadic, bool expand_defaults,
- bool missing_ok)
+ bool include_out_arguments, bool missing_ok)
{
FuncCandidateList resultList = NULL;
bool any_special = false;
@@ -978,6 +986,7 @@ FuncnameGetCandidates(List *names, int nargs, List *argnames,
{
HeapTuple proctup = &catlist->members[i]->tuple;
Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
+ Oid *proargtypes = procform->proargtypes.values;
int pronargs = procform->pronargs;
int effective_nargs;
int pathpos = 0;
@@ -1012,6 +1021,35 @@ FuncnameGetCandidates(List *names, int nargs, List *argnames,
continue; /* proc is not in search path */
}
+ /*
+ * If we are asked to match to OUT arguments, then use the
+ * proallargtypes array (which includes those); otherwise use
+ * proargtypes (which doesn't). Of course, if proallargtypes is null,
+ * we always use proargtypes.
+ */
+ if (include_out_arguments)
+ {
+ Datum proallargtypes;
+ bool isNull;
+
+ proallargtypes = SysCacheGetAttr(PROCNAMEARGSNSP, proctup,
+ Anum_pg_proc_proallargtypes,
+ &isNull);
+ if (!isNull)
+ {
+ ArrayType *arr = DatumGetArrayTypeP(proallargtypes);
+
+ pronargs = ARR_DIMS(arr)[0];
+ if (ARR_NDIM(arr) != 1 ||
+ pronargs < 0 ||
+ ARR_HASNULL(arr) ||
+ ARR_ELEMTYPE(arr) != OIDOID)
+ elog(ERROR, "proallargtypes is not a 1-D Oid array or it contains nulls");
+ Assert(pronargs >= procform->pronargs);
+ proargtypes = (Oid *) ARR_DATA_PTR(arr);
+ }
+ }
+
if (argnames != NIL)
{
/*
@@ -1047,6 +1085,7 @@ FuncnameGetCandidates(List *names, int nargs, List *argnames,
/* Check for argument name match, generate positional mapping */
if (!MatchNamedCall(proctup, nargs, argnames,
+ include_out_arguments, pronargs,
&argnumbers))
continue;
@@ -1105,12 +1144,12 @@ FuncnameGetCandidates(List *names, int nargs, List *argnames,
effective_nargs * sizeof(Oid));
newResult->pathpos = pathpos;
newResult->oid = procform->oid;
+ newResult->nominalnargs = pronargs;
newResult->nargs = effective_nargs;
newResult->argnumbers = argnumbers;
if (argnumbers)
{
/* Re-order the argument types into call's logical order */
- Oid *proargtypes = procform->proargtypes.values;
int i;
for (i = 0; i < pronargs; i++)
@@ -1119,8 +1158,7 @@ FuncnameGetCandidates(List *names, int nargs, List *argnames,
else
{
/* Simple positional case, just copy proargtypes as-is */
- memcpy(newResult->args, procform->proargtypes.values,
- pronargs * sizeof(Oid));
+ memcpy(newResult->args, proargtypes, pronargs * sizeof(Oid));
}
if (variadic)
{
@@ -1293,6 +1331,10 @@ FuncnameGetCandidates(List *names, int nargs, List *argnames,
* the function, in positions after the last positional argument, and there
* are defaults for all unsupplied arguments.
*
+ * If include_out_arguments is true, we are treating OUT arguments as
+ * included in the argument list. pronargs is the number of arguments
+ * we're considering (the length of either proargtypes or proallargtypes).
+ *
* The number of positional arguments is nargs - list_length(argnames).
* Note caller has already done basic checks on argument count.
*
@@ -1303,10 +1345,10 @@ FuncnameGetCandidates(List *names, int nargs, List *argnames,
*/
static bool
MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
+ bool include_out_arguments, int pronargs,
int **argnumbers)
{
Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
- int pronargs = procform->pronargs;
int numposargs = nargs - list_length(argnames);
int pronallargs;
Oid *p_argtypes;
@@ -1333,6 +1375,8 @@ MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
&p_argtypes, &p_argnames, &p_argmodes);
Assert(p_argnames != NULL);
+ Assert(include_out_arguments ? (pronargs == pronallargs) : (pronargs <= pronallargs));
+
/* initialize state for matching */
*argnumbers = (int *) palloc(pronargs * sizeof(int));
memset(arggiven, false, pronargs * sizeof(bool));
@@ -1355,8 +1399,9 @@ MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
found = false;
for (i = 0; i < pronallargs; i++)
{
- /* consider only input parameters */
- if (p_argmodes &&
+ /* consider only input params, except with include_out_arguments */
+ if (!include_out_arguments &&
+ p_argmodes &&
(p_argmodes[i] != FUNC_PARAM_IN &&
p_argmodes[i] != FUNC_PARAM_INOUT &&
p_argmodes[i] != FUNC_PARAM_VARIADIC))
@@ -1371,7 +1416,7 @@ MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
found = true;
break;
}
- /* increase pp only for input parameters */
+ /* increase pp only for considered parameters */
pp++;
}
/* if name isn't in proargnames, fail */
@@ -1448,7 +1493,7 @@ FunctionIsVisible(Oid funcid)
visible = false;
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
- nargs, NIL, false, false, false);
+ nargs, NIL, false, false, false, false);
for (; clist; clist = clist->next)
{
@@ -1721,6 +1766,7 @@ OpernameGetCandidates(List *names, char oprkind, bool missing_schema_ok)
newResult->pathpos = pathpos;
newResult->oid = operform->oid;
+ newResult->nominalnargs = 2;
newResult->nargs = 2;
newResult->nvargs = 0;
newResult->ndargs = 0;
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index 5197076c760..1f63d8081b2 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -846,7 +846,7 @@ lookup_agg_function(List *fnName,
* the function.
*/
fdresult = func_get_detail(fnName, NIL, NIL,
- nargs, input_types, false, false,
+ nargs, input_types, false, false, false,
&fnOid, rettype, &retset,
&nvargs, &vatype,
&true_oid_array, NULL);
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 54031108204..1454d2fb67b 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -471,12 +471,10 @@ ProcedureCreate(const char *procedureName,
if (isnull)
proargmodes = PointerGetDatum(NULL); /* just to be sure */
- n_old_arg_names = get_func_input_arg_names(prokind,
- proargnames,
+ n_old_arg_names = get_func_input_arg_names(proargnames,
proargmodes,
&old_arg_names);
- n_new_arg_names = get_func_input_arg_names(prokind,
- parameterNames,
+ n_new_arg_names = get_func_input_arg_names(parameterNames,
parameterModes,
&new_arg_names);
for (j = 0; j < n_old_arg_names; j++)
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 4c12aa33dfc..736d04780a7 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -169,16 +169,16 @@ compute_return_type(TypeName *returnType, Oid languageOid,
}
/*
- * Interpret the function parameter list of a CREATE FUNCTION or
- * CREATE AGGREGATE statement.
+ * Interpret the function parameter list of a CREATE FUNCTION,
+ * CREATE PROCEDURE, or CREATE AGGREGATE statement.
*
* Input parameters:
* parameters: list of FunctionParameter structs
* languageOid: OID of function language (InvalidOid if it's CREATE AGGREGATE)
- * objtype: needed only to determine error handling and required result type
+ * objtype: identifies type of object being created
*
* Results are stored into output parameters. parameterTypes must always
- * be created, but the other arrays are set to NULL if not needed.
+ * be created, but the other arrays/lists can be NULL pointers if not needed.
* variadicArgType is set to the variadic array type if there's a VARIADIC
* parameter (there can be only one); or to InvalidOid if not.
* requiredResultType is set to InvalidOid if there are no OUT parameters,
@@ -200,8 +200,8 @@ interpret_function_parameter_list(ParseState *pstate,
Oid *requiredResultType)
{
int parameterCount = list_length(parameters);
- Oid *sigArgTypes;
- int sigArgCount = 0;
+ Oid *inTypes;
+ int inCount = 0;
Datum *allTypes;
Datum *paramModes;
Datum *paramNames;
@@ -215,7 +215,7 @@ interpret_function_parameter_list(ParseState *pstate,
*variadicArgType = InvalidOid; /* default result */
*requiredResultType = InvalidOid; /* default result */
- sigArgTypes = (Oid *) palloc(parameterCount * sizeof(Oid));
+ inTypes = (Oid *) palloc(parameterCount * sizeof(Oid));
allTypes = (Datum *) palloc(parameterCount * sizeof(Datum));
paramModes = (Datum *) palloc(parameterCount * sizeof(Datum));
paramNames = (Datum *) palloc0(parameterCount * sizeof(Datum));
@@ -227,11 +227,16 @@ interpret_function_parameter_list(ParseState *pstate,
{
FunctionParameter *fp = (FunctionParameter *) lfirst(x);
TypeName *t = fp->argType;
+ FunctionParameterMode fpmode = fp->mode;
bool isinput = false;
Oid toid;
Type typtup;
AclResult aclresult;
+ /* For our purposes here, a defaulted mode spec is identical to IN */
+ if (fpmode == FUNC_PARAM_DEFAULT)
+ fpmode = FUNC_PARAM_IN;
+
typtup = LookupTypeName(NULL, t, NULL, false);
if (typtup)
{
@@ -288,37 +293,42 @@ interpret_function_parameter_list(ParseState *pstate,
}
/* handle input parameters */
- if (fp->mode != FUNC_PARAM_OUT && fp->mode != FUNC_PARAM_TABLE)
+ if (fpmode != FUNC_PARAM_OUT && fpmode != FUNC_PARAM_TABLE)
{
- isinput = true;
- if (parameterTypes_list)
- *parameterTypes_list = lappend_oid(*parameterTypes_list, toid);
- }
-
- /* handle signature parameters */
- if (fp->mode == FUNC_PARAM_IN || fp->mode == FUNC_PARAM_INOUT ||
- (objtype == OBJECT_PROCEDURE && fp->mode == FUNC_PARAM_OUT) ||
- fp->mode == FUNC_PARAM_VARIADIC)
- {
- /* other signature parameters can't follow a VARIADIC parameter */
+ /* other input parameters can't follow a VARIADIC parameter */
if (varCount > 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
- errmsg("VARIADIC parameter must be the last signature parameter")));
- sigArgTypes[sigArgCount++] = toid;
+ errmsg("VARIADIC parameter must be the last input parameter")));
+ inTypes[inCount++] = toid;
+ isinput = true;
+ if (parameterTypes_list)
+ *parameterTypes_list = lappend_oid(*parameterTypes_list, toid);
}
/* handle output parameters */
- if (fp->mode != FUNC_PARAM_IN && fp->mode != FUNC_PARAM_VARIADIC)
+ if (fpmode != FUNC_PARAM_IN && fpmode != FUNC_PARAM_VARIADIC)
{
if (objtype == OBJECT_PROCEDURE)
+ {
+ /*
+ * We disallow OUT-after-VARIADIC only for procedures. While
+ * such a case causes no confusion in ordinary function calls,
+ * it would cause confusion in a CALL statement.
+ */
+ if (varCount > 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("VARIADIC parameter must be the last parameter")));
+ /* Procedures with output parameters always return RECORD */
*requiredResultType = RECORDOID;
+ }
else if (outCount == 0) /* save first output param's type */
*requiredResultType = toid;
outCount++;
}
- if (fp->mode == FUNC_PARAM_VARIADIC)
+ if (fpmode == FUNC_PARAM_VARIADIC)
{
*variadicArgType = toid;
varCount++;
@@ -341,7 +351,7 @@ interpret_function_parameter_list(ParseState *pstate,
allTypes[i] = ObjectIdGetDatum(toid);
- paramModes[i] = CharGetDatum(fp->mode);
+ paramModes[i] = CharGetDatum(fpmode);
if (fp->name && fp->name[0])
{
@@ -356,19 +366,24 @@ interpret_function_parameter_list(ParseState *pstate,
foreach(px, parameters)
{
FunctionParameter *prevfp = (FunctionParameter *) lfirst(px);
+ FunctionParameterMode prevfpmode;
if (prevfp == fp)
break;
+ /* as above, default mode is IN */
+ prevfpmode = prevfp->mode;
+ if (prevfpmode == FUNC_PARAM_DEFAULT)
+ prevfpmode = FUNC_PARAM_IN;
/* pure in doesn't conflict with pure out */
- if ((fp->mode == FUNC_PARAM_IN ||
- fp->mode == FUNC_PARAM_VARIADIC) &&
- (prevfp->mode == FUNC_PARAM_OUT ||
- prevfp->mode == FUNC_PARAM_TABLE))
+ if ((fpmode == FUNC_PARAM_IN ||
+ fpmode == FUNC_PARAM_VARIADIC) &&
+ (prevfpmode == FUNC_PARAM_OUT ||
+ prevfpmode == FUNC_PARAM_TABLE))
continue;
- if ((prevfp->mode == FUNC_PARAM_IN ||
- prevfp->mode == FUNC_PARAM_VARIADIC) &&
- (fp->mode == FUNC_PARAM_OUT ||
- fp->mode == FUNC_PARAM_TABLE))
+ if ((prevfpmode == FUNC_PARAM_IN ||
+ prevfpmode == FUNC_PARAM_VARIADIC) &&
+ (fpmode == FUNC_PARAM_OUT ||
+ fpmode == FUNC_PARAM_TABLE))
continue;
if (prevfp->name && prevfp->name[0] &&
strcmp(prevfp->name, fp->name) == 0)
@@ -432,13 +447,23 @@ interpret_function_parameter_list(ParseState *pstate,
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("input parameters after one with a default value must also have defaults")));
+
+ /*
+ * For procedures, we also can't allow OUT parameters after one
+ * with a default, because the same sort of confusion arises in a
+ * CALL statement.
+ */
+ if (objtype == OBJECT_PROCEDURE && have_defaults)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("procedure OUT parameters cannot appear after one with a default value")));
}
i++;
}
/* Now construct the proper outputs as needed */
- *parameterTypes = buildoidvector(sigArgTypes, sigArgCount);
+ *parameterTypes = buildoidvector(inTypes, inCount);
if (outCount > 0 || varCount > 0)
{
@@ -2179,9 +2204,6 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
int nargs;
int i;
AclResult aclresult;
- Oid *argtypes;
- char **argnames;
- char *argmodes;
FmgrInfo flinfo;
CallContext *callcontext;
EState *estate;
@@ -2224,29 +2246,10 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
if (((Form_pg_proc) GETSTRUCT(tp))->prosecdef)
callcontext->atomic = true;
- /*
- * Expand named arguments, defaults, etc. We do not want to scribble on
- * the passed-in CallStmt parse tree, so first flat-copy fexpr, allowing
- * us to replace its args field. (Note that expand_function_arguments
- * will not modify any of the passed-in data structure.)
- */
- {
- FuncExpr *nexpr = makeNode(FuncExpr);
-
- memcpy(nexpr, fexpr, sizeof(FuncExpr));
- fexpr = nexpr;
- }
-
- fexpr->args = expand_function_arguments(fexpr->args,
- fexpr->funcresulttype,
- tp);
- nargs = list_length(fexpr->args);
-
- get_func_arg_info(tp, &argtypes, &argnames, &argmodes);
-
ReleaseSysCache(tp);
/* safety check; see ExecInitFunc() */
+ nargs = list_length(fexpr->args);
if (nargs > FUNC_MAX_ARGS)
ereport(ERROR,
(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
@@ -2273,24 +2276,16 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
i = 0;
foreach(lc, fexpr->args)
{
- if (argmodes && argmodes[i] == PROARGMODE_OUT)
- {
- fcinfo->args[i].value = 0;
- fcinfo->args[i].isnull = true;
- }
- else
- {
- ExprState *exprstate;
- Datum val;
- bool isnull;
+ ExprState *exprstate;
+ Datum val;
+ bool isnull;
- exprstate = ExecPrepareExpr(lfirst(lc), estate);
+ exprstate = ExecPrepareExpr(lfirst(lc), estate);
- val = ExecEvalExprSwitchContext(exprstate, econtext, &isnull);
+ val = ExecEvalExprSwitchContext(exprstate, econtext, &isnull);
- fcinfo->args[i].value = val;
- fcinfo->args[i].isnull = isnull;
- }
+ fcinfo->args[i].value = val;
+ fcinfo->args[i].isnull = isnull;
i++;
}
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 39580f7d577..32668f85a1d 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -245,8 +245,7 @@ prepare_sql_fn_parse_info(HeapTuple procedureTuple,
if (isNull)
proargmodes = PointerGetDatum(NULL); /* just to be sure */
- n_arg_names = get_func_input_arg_names(procedureStruct->prokind,
- proargnames, proargmodes,
+ n_arg_names = get_func_input_arg_names(proargnames, proargmodes,
&pinfo->argnames);
/* Paranoia: ignore the result if too few array entries */
@@ -1536,7 +1535,7 @@ check_sql_fn_statements(List *queryTreeLists)
Query *query = lfirst_node(Query, lc2);
/*
- * Disallow procedures with output arguments. The current
+ * Disallow calling procedures with output arguments. The current
* implementation would just throw the output values away, unless
* the statement is the last one. Per SQL standard, we should
* assign the output values by name. By disallowing this here, we
@@ -1545,31 +1544,12 @@ check_sql_fn_statements(List *queryTreeLists)
if (query->commandType == CMD_UTILITY &&
IsA(query->utilityStmt, CallStmt))
{
- CallStmt *stmt = castNode(CallStmt, query->utilityStmt);
- HeapTuple tuple;
- int numargs;
- Oid *argtypes;
- char **argnames;
- char *argmodes;
- int i;
-
- tuple = SearchSysCache1(PROCOID,
- ObjectIdGetDatum(stmt->funcexpr->funcid));
- if (!HeapTupleIsValid(tuple))
- elog(ERROR, "cache lookup failed for function %u",
- stmt->funcexpr->funcid);
- numargs = get_func_arg_info(tuple,
- &argtypes, &argnames, &argmodes);
- ReleaseSysCache(tuple);
-
- for (i = 0; i < numargs; i++)
- {
- if (argmodes && (argmodes[i] == PROARGMODE_INOUT ||
- argmodes[i] == PROARGMODE_OUT))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("calling procedures with output arguments is not supported in SQL functions")));
- }
+ CallStmt *stmt = (CallStmt *) query->utilityStmt;
+
+ if (stmt->outargs != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("calling procedures with output arguments is not supported in SQL functions")));
}
}
}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 621f7ce0687..bd87f237846 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3407,6 +3407,7 @@ _copyObjectWithArgs(const ObjectWithArgs *from)
COPY_NODE_FIELD(objname);
COPY_NODE_FIELD(objargs);
+ COPY_NODE_FIELD(objfuncargs);
COPY_SCALAR_FIELD(args_unspecified);
return newnode;
@@ -3478,6 +3479,7 @@ _copyCallStmt(const CallStmt *from)
COPY_NODE_FIELD(funccall);
COPY_NODE_FIELD(funcexpr);
+ COPY_NODE_FIELD(outargs);
return newnode;
}
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 3033c1934c3..dba3e6b31e6 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1182,6 +1182,7 @@ _equalObjectWithArgs(const ObjectWithArgs *a, const ObjectWithArgs *b)
{
COMPARE_NODE_FIELD(objname);
COMPARE_NODE_FIELD(objargs);
+ COMPARE_NODE_FIELD(objfuncargs);
COMPARE_SCALAR_FIELD(args_unspecified);
return true;
@@ -1241,6 +1242,7 @@ _equalCallStmt(const CallStmt *a, const CallStmt *b)
{
COMPARE_NODE_FIELD(funccall);
COMPARE_NODE_FIELD(funcexpr);
+ COMPARE_NODE_FIELD(outargs);
return true;
}
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 517712a8f4c..059fa702549 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -124,10 +124,13 @@ static Expr *simplify_function(Oid funcid,
Oid result_collid, Oid input_collid, List **args_p,
bool funcvariadic, bool process_args, bool allow_non_const,
eval_const_expressions_context *context);
-static List *reorder_function_arguments(List *args, HeapTuple func_tuple);
-static List *add_function_defaults(List *args, HeapTuple func_tuple);
+static List *reorder_function_arguments(List *args, int pronargs,
+ HeapTuple func_tuple);
+static List *add_function_defaults(List *args, int pronargs,
+ HeapTuple func_tuple);
static List *fetch_function_defaults(HeapTuple func_tuple);
static void recheck_cast_function_args(List *args, Oid result_type,
+ Oid *proargtypes, int pronargs,
HeapTuple func_tuple);
static Expr *evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
Oid result_collid, Oid input_collid, List *args,
@@ -2326,7 +2329,8 @@ eval_const_expressions_mutator(Node *node,
if (!HeapTupleIsValid(func_tuple))
elog(ERROR, "cache lookup failed for function %u", funcid);
- args = expand_function_arguments(expr->args, expr->wintype,
+ args = expand_function_arguments(expr->args,
+ false, expr->wintype,
func_tuple);
ReleaseSysCache(func_tuple);
@@ -3841,7 +3845,7 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
*/
if (process_args)
{
- args = expand_function_arguments(args, result_type, func_tuple);
+ args = expand_function_arguments(args, false, result_type, func_tuple);
args = (List *) expression_tree_mutator((Node *) args,
eval_const_expressions_mutator,
(void *) context);
@@ -3905,6 +3909,15 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
* expand_function_arguments: convert named-notation args to positional args
* and/or insert default args, as needed
*
+ * Returns a possibly-transformed version of the args list.
+ *
+ * If include_out_arguments is true, then the args list and the result
+ * include OUT arguments.
+ *
+ * The expected result type of the call must be given, for sanity-checking
+ * purposes. Also, we ask the caller to provide the function's actual
+ * pg_proc tuple, not just its OID.
+ *
* If we need to change anything, the input argument list is copied, not
* modified.
*
@@ -3913,12 +3926,46 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
* will fall through very quickly if there's nothing to do.
*/
List *
-expand_function_arguments(List *args, Oid result_type, HeapTuple func_tuple)
+expand_function_arguments(List *args, bool include_out_arguments,
+ Oid result_type, HeapTuple func_tuple)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
+ Oid *proargtypes = funcform->proargtypes.values;
+ int pronargs = funcform->pronargs;
bool has_named_args = false;
ListCell *lc;
+ /*
+ * If we are asked to match to OUT arguments, then use the proallargtypes
+ * array (which includes those); otherwise use proargtypes (which
+ * doesn't). Of course, if proallargtypes is null, we always use
+ * proargtypes. (Fetching proallargtypes is annoyingly expensive
+ * considering that we may have nothing to do here, but fortunately the
+ * common case is include_out_arguments == false.)
+ */
+ if (include_out_arguments)
+ {
+ Datum proallargtypes;
+ bool isNull;
+
+ proallargtypes = SysCacheGetAttr(PROCOID, func_tuple,
+ Anum_pg_proc_proallargtypes,
+ &isNull);
+ if (!isNull)
+ {
+ ArrayType *arr = DatumGetArrayTypeP(proallargtypes);
+
+ pronargs = ARR_DIMS(arr)[0];
+ if (ARR_NDIM(arr) != 1 ||
+ pronargs < 0 ||
+ ARR_HASNULL(arr) ||
+ ARR_ELEMTYPE(arr) != OIDOID)
+ elog(ERROR, "proallargtypes is not a 1-D Oid array or it contains nulls");
+ Assert(pronargs >= funcform->pronargs);
+ proargtypes = (Oid *) ARR_DATA_PTR(arr);
+ }
+ }
+
/* Do we have any named arguments? */
foreach(lc, args)
{
@@ -3934,16 +3981,20 @@ expand_function_arguments(List *args, Oid result_type, HeapTuple func_tuple)
/* If so, we must apply reorder_function_arguments */
if (has_named_args)
{
- args = reorder_function_arguments(args, func_tuple);
+ args = reorder_function_arguments(args, pronargs, func_tuple);
/* Recheck argument types and add casts if needed */
- recheck_cast_function_args(args, result_type, func_tuple);
+ recheck_cast_function_args(args, result_type,
+ proargtypes, pronargs,
+ func_tuple);
}
- else if (list_length(args) < funcform->pronargs)
+ else if (list_length(args) < pronargs)
{
/* No named args, but we seem to be short some defaults */
- args = add_function_defaults(args, func_tuple);
+ args = add_function_defaults(args, pronargs, func_tuple);
/* Recheck argument types and add casts if needed */
- recheck_cast_function_args(args, result_type, func_tuple);
+ recheck_cast_function_args(args, result_type,
+ proargtypes, pronargs,
+ func_tuple);
}
return args;
@@ -3956,10 +4007,9 @@ expand_function_arguments(List *args, Oid result_type, HeapTuple func_tuple)
* impossible to form a truly valid positional call without that.
*/
static List *
-reorder_function_arguments(List *args, HeapTuple func_tuple)
+reorder_function_arguments(List *args, int pronargs, HeapTuple func_tuple)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
- int pronargs = funcform->pronargs;
int nargsprovided = list_length(args);
Node *argarray[FUNC_MAX_ARGS];
ListCell *lc;
@@ -3986,6 +4036,7 @@ reorder_function_arguments(List *args, HeapTuple func_tuple)
{
NamedArgExpr *na = (NamedArgExpr *) arg;
+ Assert(na->argnumber >= 0 && na->argnumber < pronargs);
Assert(argarray[na->argnumber] == NULL);
argarray[na->argnumber] = (Node *) na->arg;
}
@@ -4026,9 +4077,8 @@ reorder_function_arguments(List *args, HeapTuple func_tuple)
* and so we know we just need to add defaults at the end.
*/
static List *
-add_function_defaults(List *args, HeapTuple func_tuple)
+add_function_defaults(List *args, int pronargs, HeapTuple func_tuple)
{
- Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
int nargsprovided = list_length(args);
List *defaults;
int ndelete;
@@ -4037,7 +4087,7 @@ add_function_defaults(List *args, HeapTuple func_tuple)
defaults = fetch_function_defaults(func_tuple);
/* Delete any unused defaults from the list */
- ndelete = nargsprovided + list_length(defaults) - funcform->pronargs;
+ ndelete = nargsprovided + list_length(defaults) - pronargs;
if (ndelete < 0)
elog(ERROR, "not enough default arguments");
if (ndelete > 0)
@@ -4086,7 +4136,9 @@ fetch_function_defaults(HeapTuple func_tuple)
* caller should have already copied the list structure.
*/
static void
-recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple)
+recheck_cast_function_args(List *args, Oid result_type,
+ Oid *proargtypes, int pronargs,
+ HeapTuple func_tuple)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
int nargs;
@@ -4102,9 +4154,8 @@ recheck_cast_function_args(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));
+ Assert(nargs == pronargs);
+ memcpy(declared_arg_types, proargtypes, pronargs * sizeof(Oid));
rettype = enforce_generic_type_consistency(actual_arg_types,
declared_arg_types,
nargs,
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;
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index f998fe20763..e4fb9d31d92 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -93,7 +93,7 @@ regprocin(PG_FUNCTION_ARGS)
* pg_proc entries in the current search path.
*/
names = stringToQualifiedNameList(pro_name_or_oid);
- clist = FuncnameGetCandidates(names, -1, NIL, false, false, false);
+ clist = FuncnameGetCandidates(names, -1, NIL, false, false, false, false);
if (clist == NULL)
ereport(ERROR,
@@ -127,7 +127,7 @@ to_regproc(PG_FUNCTION_ARGS)
* entries in the current search path.
*/
names = stringToQualifiedNameList(pro_name);
- clist = FuncnameGetCandidates(names, -1, NIL, false, false, true);
+ clist = FuncnameGetCandidates(names, -1, NIL, false, false, false, true);
if (clist == NULL || clist->next != NULL)
PG_RETURN_NULL();
@@ -175,7 +175,7 @@ regprocout(PG_FUNCTION_ARGS)
* qualify it.
*/
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
- -1, NIL, false, false, false);
+ -1, NIL, false, false, false, false);
if (clist != NULL && clist->next == NULL &&
clist->oid == proid)
nspname = NULL;
@@ -262,7 +262,8 @@ regprocedurein(PG_FUNCTION_ARGS)
*/
parseNameAndArgTypes(pro_name_or_oid, false, &names, &nargs, argtypes);
- clist = FuncnameGetCandidates(names, nargs, NIL, false, false, false);
+ clist = FuncnameGetCandidates(names, nargs, NIL, false, false,
+ false, false);
for (; clist; clist = clist->next)
{
@@ -301,7 +302,7 @@ to_regprocedure(PG_FUNCTION_ARGS)
*/
parseNameAndArgTypes(pro_name, false, &names, &nargs, argtypes);
- clist = FuncnameGetCandidates(names, nargs, NIL, false, false, true);
+ clist = FuncnameGetCandidates(names, nargs, NIL, false, false, false, true);
for (; clist; clist = clist->next)
{
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 84ad62caea3..3719755a0de 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3217,7 +3217,15 @@ print_function_arguments(StringInfo buf, HeapTuple proctup,
switch (argmode)
{
case PROARGMODE_IN:
- modename = "";
+
+ /*
+ * For procedures, explicitly mark all argument modes, so as
+ * to avoid ambiguity with the SQL syntax for DROP PROCEDURE.
+ */
+ if (proc->prokind == PROKIND_PROCEDURE)
+ modename = "IN ";
+ else
+ modename = "";
isinput = true;
break;
case PROARGMODE_INOUT:
@@ -11615,7 +11623,7 @@ generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
if (!force_qualify)
p_result = func_get_detail(list_make1(makeString(proname)),
NIL, argnames, nargs, argtypes,
- !use_variadic, true,
+ !use_variadic, true, false,
&p_funcid, &p_rettype,
&p_retset, &p_nvargs, &p_vatype,
&p_true_typeids, NULL);
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 717b62907c7..e94b8037ec0 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -1409,8 +1409,7 @@ get_func_trftypes(HeapTuple procTup,
* are set to NULL. You don't get anything if proargnames is NULL.
*/
int
-get_func_input_arg_names(char prokind,
- Datum proargnames, Datum proargmodes,
+get_func_input_arg_names(Datum proargnames, Datum proargmodes,
char ***arg_names)
{
ArrayType *arr;
@@ -1469,7 +1468,6 @@ get_func_input_arg_names(char prokind,
if (argmodes == NULL ||
argmodes[i] == PROARGMODE_IN ||
argmodes[i] == PROARGMODE_INOUT ||
- (argmodes[i] == PROARGMODE_OUT && prokind == PROKIND_PROCEDURE) ||
argmodes[i] == PROARGMODE_VARIADIC)
{
char *pname = TextDatumGetCString(argnames[i]);
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index 4bc845e57c3..244507c97c1 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -1898,7 +1898,7 @@ my %tests = (
create_sql => 'CREATE PROCEDURE dump_test.ptest1(a int)
LANGUAGE SQL AS $$ INSERT INTO dump_test.test_table (col1) VALUES (a) $$;',
regexp => qr/^
- \QCREATE PROCEDURE dump_test.ptest1(a integer)\E
+ \QCREATE PROCEDURE dump_test.ptest1(IN a integer)\E
\n\s+\QLANGUAGE sql\E
\n\s+AS\ \$\$\Q INSERT INTO dump_test.test_table (col1) VALUES (a) \E\$\$;
/xm,
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 38fb958a8db..d5dada949c3 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 202106062
+#define CATALOG_VERSION_NO 202106101
#endif
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index aa2774e2d4e..b98f2843560 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -30,6 +30,7 @@ typedef struct _FuncCandidateList
struct _FuncCandidateList *next;
int pathpos; /* for internal use of namespace lookup */
Oid oid; /* the function or operator's OID */
+ int nominalnargs; /* either pronargs or length(proallargtypes) */
int nargs; /* number of arg types returned */
int nvargs; /* number of args to become variadic array */
int ndargs; /* number of defaulted args */
@@ -99,6 +100,7 @@ extern FuncCandidateList FuncnameGetCandidates(List *names,
int nargs, List *argnames,
bool expand_variadic,
bool expand_defaults,
+ bool include_out_arguments,
bool missing_ok);
extern bool FunctionIsVisible(Oid funcid);
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 448d9898cb3..a65afe7bc4b 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -91,7 +91,7 @@ CATALOG(pg_proc,1255,ProcedureRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(81,Proce
* proargtypes
*/
- /* parameter types (excludes OUT params of functions) */
+ /* parameter types (excludes OUT params) */
oidvector proargtypes BKI_LOOKUP(pg_type) BKI_FORCE_NOT_NULL;
#ifdef CATALOG_VARLEN
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 83e6bc2a1fb..f1304d47e34 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -172,8 +172,7 @@ extern int get_func_arg_info(HeapTuple procTup,
Oid **p_argtypes, char ***p_argnames,
char **p_argmodes);
-extern int get_func_input_arg_names(char prokind,
- Datum proargnames, Datum proargmodes,
+extern int get_func_input_arg_names(Datum proargnames, Datum proargmodes,
char ***arg_names);
extern int get_func_trftypes(HeapTuple procTup, Oid **p_trftypes);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ef73342019f..58328c43774 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2024,18 +2024,29 @@ typedef struct GrantStmt
} GrantStmt;
/*
- * Note: ObjectWithArgs carries only the types of the input parameters of the
- * function. So it is sufficient to identify an existing function, but it
- * is not enough info to define a function nor to call it.
+ * ObjectWithArgs represents a function/procedure/operator name plus parameter
+ * identification.
+ *
+ * objargs includes only the types of the input parameters of the object.
+ * In some contexts, that will be all we have, and it's enough to look up
+ * objects according to the traditional Postgres rules (i.e., when only input
+ * arguments matter).
+ *
+ * objfuncargs, if not NIL, carries the full specification of the parameter
+ * list, including parameter mode annotations.
+ *
+ * Some grammar productions can set args_unspecified = true instead of
+ * providing parameter info. In this case, lookup will succeed only if
+ * the object name is unique. Note that otherwise, NIL parameter lists
+ * mean zero arguments.
*/
typedef struct ObjectWithArgs
{
NodeTag type;
List *objname; /* qualified name of function/operator */
- List *objargs; /* list of Typename nodes */
- bool args_unspecified; /* argument list was omitted, so name must
- * be unique (note that objargs == NIL
- * means zero args) */
+ List *objargs; /* list of Typename nodes (input args only) */
+ List *objfuncargs; /* list of FunctionParameter nodes */
+ bool args_unspecified; /* argument list was omitted? */
} ObjectWithArgs;
/*
@@ -2955,7 +2966,9 @@ typedef enum FunctionParameterMode
FUNC_PARAM_OUT = 'o', /* output only */
FUNC_PARAM_INOUT = 'b', /* both */
FUNC_PARAM_VARIADIC = 'v', /* variadic (always input) */
- FUNC_PARAM_TABLE = 't' /* table function output column */
+ FUNC_PARAM_TABLE = 't', /* table function output column */
+ /* this is not used in pg_proc: */
+ FUNC_PARAM_DEFAULT = 'd' /* default; effectively same as IN */
} FunctionParameterMode;
typedef struct FunctionParameter
@@ -2998,13 +3011,19 @@ typedef struct InlineCodeBlock
/* ----------------------
* CALL statement
+ *
+ * OUT-mode arguments are removed from the transformed funcexpr. The outargs
+ * list contains copies of the expressions for all output arguments, in the
+ * order of the procedure's declared arguments. (outargs is never evaluated,
+ * but is useful to the caller as a reference for what to assign to.)
* ----------------------
*/
typedef struct CallStmt
{
NodeTag type;
FuncCall *funccall; /* from the parser */
- FuncExpr *funcexpr; /* transformed */
+ FuncExpr *funcexpr; /* transformed call, with only input args */
+ List *outargs; /* transformed output-argument expressions */
} CallStmt;
typedef struct CallContext
diff --git a/src/include/optimizer/optimizer.h b/src/include/optimizer/optimizer.h
index 68ebb84bf5d..41b49b26625 100644
--- a/src/include/optimizer/optimizer.h
+++ b/src/include/optimizer/optimizer.h
@@ -153,7 +153,8 @@ extern Node *estimate_expression_value(PlannerInfo *root, Node *node);
extern Expr *evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod,
Oid result_collation);
-extern List *expand_function_arguments(List *args, Oid result_type,
+extern List *expand_function_arguments(List *args, bool include_out_arguments,
+ Oid result_type,
struct HeapTupleData *func_tuple);
/* in util/predtest.c: */
diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h
index aaf07f8f73d..57795a86e4a 100644
--- a/src/include/parser/parse_func.h
+++ b/src/include/parser/parse_func.h
@@ -39,6 +39,7 @@ extern FuncDetailCode func_get_detail(List *funcname,
List *fargs, List *fargnames,
int nargs, Oid *argtypes,
bool expand_variadic, bool expand_defaults,
+ bool include_out_arguments,
Oid *funcid, Oid *rettype,
bool *retset, int *nvargs, Oid *vatype,
Oid **true_typeids, List **argdefaults);
diff --git a/src/pl/plpgsql/src/expected/plpgsql_call.out b/src/pl/plpgsql/src/expected/plpgsql_call.out
index c804a3ffbfe..7b156f34893 100644
--- a/src/pl/plpgsql/src/expected/plpgsql_call.out
+++ b/src/pl/plpgsql/src/expected/plpgsql_call.out
@@ -100,9 +100,12 @@ DECLARE
BEGIN
CALL test_proc6(2, x, y);
RAISE INFO 'x = %, y = %', x, y;
+ CALL test_proc6(2, c => y, b => x);
+ RAISE INFO 'x = %, y = %', x, y;
END;
$$;
INFO: x = 6, y = 8
+INFO: x = 12, y = 16
DO
LANGUAGE plpgsql
$$
@@ -304,6 +307,79 @@ END
$$;
NOTICE: a: 10, b: <NULL>
NOTICE: _a: 10, _b: 20
+CREATE PROCEDURE test_proc10(IN a int, OUT b int, IN c int DEFAULT 11)
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ RAISE NOTICE 'a: %, b: %, c: %', a, b, c;
+ b := a - c;
+END;
+$$;
+DO $$
+DECLARE _a int; _b int; _c int;
+BEGIN
+ _a := 10; _b := 30; _c := 7;
+ CALL test_proc10(_a, _b, _c);
+ RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
+
+ _a := 10; _b := 30; _c := 7;
+ CALL test_proc10(_a, _b, c => _c);
+ RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
+
+ _a := 10; _b := 30; _c := 7;
+ CALL test_proc10(a => _a, b => _b, c => _c);
+ RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
+
+ _a := 10; _b := 30; _c := 7;
+ CALL test_proc10(_a, c => _c, b => _b);
+ RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
+
+ _a := 10; _b := 30; _c := 7;
+ CALL test_proc10(_a, _b);
+ RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
+
+ _a := 10; _b := 30; _c := 7;
+ CALL test_proc10(_a, b => _b);
+ RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
+
+ _a := 10; _b := 30; _c := 7;
+ CALL test_proc10(b => _b, a => _a);
+ RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
+END
+$$;
+NOTICE: a: 10, b: <NULL>, c: 7
+NOTICE: _a: 10, _b: 3, _c: 7
+NOTICE: a: 10, b: <NULL>, c: 7
+NOTICE: _a: 10, _b: 3, _c: 7
+NOTICE: a: 10, b: <NULL>, c: 7
+NOTICE: _a: 10, _b: 3, _c: 7
+NOTICE: a: 10, b: <NULL>, c: 7
+NOTICE: _a: 10, _b: 3, _c: 7
+NOTICE: a: 10, b: <NULL>, c: 11
+NOTICE: _a: 10, _b: -1, _c: 7
+NOTICE: a: 10, b: <NULL>, c: 11
+NOTICE: _a: 10, _b: -1, _c: 7
+NOTICE: a: 10, b: <NULL>, c: 11
+NOTICE: _a: 10, _b: -1, _c: 7
+-- OUT + VARIADIC
+CREATE PROCEDURE test_proc11(a OUT int, VARIADIC b int[])
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ RAISE NOTICE 'a: %, b: %', a, b;
+ a := b[1] + b[2];
+END;
+$$;
+DO $$
+DECLARE _a int; _b int; _c int;
+BEGIN
+ _a := 10; _b := 30; _c := 7;
+ CALL test_proc11(_a, _b, _c);
+ RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
+END
+$$;
+NOTICE: a: <NULL>, b: {30,7}
+NOTICE: _a: 37, _b: 30, _c: 7
-- transition variable assignment
TRUNCATE test1;
CREATE FUNCTION triggerfunc1() RETURNS trigger
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index ce8d97447d1..a68a2077c36 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -460,7 +460,6 @@ do_compile(FunctionCallInfo fcinfo,
/* Remember arguments in appropriate arrays */
if (argmode == PROARGMODE_IN ||
argmode == PROARGMODE_INOUT ||
- (argmode == PROARGMODE_OUT && function->fn_prokind == PROKIND_PROCEDURE) ||
argmode == PROARGMODE_VARIADIC)
in_arg_varnos[num_in_args++] = argvariable->dno;
if (argmode == PROARGMODE_OUT ||
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 207c4424fac..0ce382e1232 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -2246,18 +2246,17 @@ make_callstmt_target(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
{
List *plansources;
CachedPlanSource *plansource;
- Node *node;
+ CallStmt *stmt;
FuncExpr *funcexpr;
HeapTuple func_tuple;
- List *funcargs;
Oid *argtypes;
char **argnames;
char *argmodes;
+ int numargs;
MemoryContext oldcontext;
PLpgSQL_row *row;
int nfields;
int i;
- ListCell *lc;
/* Use eval_mcontext for any cruft accumulated here */
oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
@@ -2271,11 +2270,12 @@ make_callstmt_target(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
plansource = (CachedPlanSource *) linitial(plansources);
if (list_length(plansource->query_list) != 1)
elog(ERROR, "query for CALL statement is not a CallStmt");
- node = linitial_node(Query, plansource->query_list)->utilityStmt;
- if (node == NULL || !IsA(node, CallStmt))
+ stmt = (CallStmt *) linitial_node(Query,
+ plansource->query_list)->utilityStmt;
+ if (stmt == NULL || !IsA(stmt, CallStmt))
elog(ERROR, "query for CALL statement is not a CallStmt");
- funcexpr = ((CallStmt *) node)->funcexpr;
+ funcexpr = stmt->funcexpr;
func_tuple = SearchSysCache1(PROCOID,
ObjectIdGetDatum(funcexpr->funcid));
@@ -2284,16 +2284,10 @@ make_callstmt_target(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
funcexpr->funcid);
/*
- * Extract function arguments, and expand any named-arg notation
+ * Get the argument names and modes, so that we can deliver on-point error
+ * messages when something is wrong.
*/
- funcargs = expand_function_arguments(funcexpr->args,
- funcexpr->funcresulttype,
- func_tuple);
-
- /*
- * Get the argument names and modes, too
- */
- get_func_arg_info(func_tuple, &argtypes, &argnames, &argmodes);
+ numargs = get_func_arg_info(func_tuple, &argtypes, &argnames, &argmodes);
ReleaseSysCache(func_tuple);
@@ -2307,7 +2301,7 @@ make_callstmt_target(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
row->dtype = PLPGSQL_DTYPE_ROW;
row->refname = "(unnamed row)";
row->lineno = -1;
- row->varnos = (int *) palloc(sizeof(int) * list_length(funcargs));
+ row->varnos = (int *) palloc(numargs * sizeof(int));
MemoryContextSwitchTo(get_eval_mcontext(estate));
@@ -2317,15 +2311,14 @@ make_callstmt_target(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
* Datum.
*/
nfields = 0;
- i = 0;
- foreach(lc, funcargs)
+ for (i = 0; i < numargs; i++)
{
- Node *n = lfirst(lc);
-
if (argmodes &&
(argmodes[i] == PROARGMODE_INOUT ||
argmodes[i] == PROARGMODE_OUT))
{
+ Node *n = list_nth(stmt->outargs, nfields);
+
if (IsA(n, Param))
{
Param *param = (Param *) n;
@@ -2348,9 +2341,10 @@ make_callstmt_target(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
i + 1)));
}
}
- i++;
}
+ Assert(nfields == list_length(stmt->outargs));
+
row->nfields = nfields;
MemoryContextSwitchTo(oldcontext);
diff --git a/src/pl/plpgsql/src/sql/plpgsql_call.sql b/src/pl/plpgsql/src/sql/plpgsql_call.sql
index c61a75be9bd..8108d050609 100644
--- a/src/pl/plpgsql/src/sql/plpgsql_call.sql
+++ b/src/pl/plpgsql/src/sql/plpgsql_call.sql
@@ -93,6 +93,8 @@ DECLARE
BEGIN
CALL test_proc6(2, x, y);
RAISE INFO 'x = %, y = %', x, y;
+ CALL test_proc6(2, c => y, b => x);
+ RAISE INFO 'x = %, y = %', x, y;
END;
$$;
@@ -281,6 +283,68 @@ BEGIN
END
$$;
+CREATE PROCEDURE test_proc10(IN a int, OUT b int, IN c int DEFAULT 11)
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ RAISE NOTICE 'a: %, b: %, c: %', a, b, c;
+ b := a - c;
+END;
+$$;
+
+DO $$
+DECLARE _a int; _b int; _c int;
+BEGIN
+ _a := 10; _b := 30; _c := 7;
+ CALL test_proc10(_a, _b, _c);
+ RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
+
+ _a := 10; _b := 30; _c := 7;
+ CALL test_proc10(_a, _b, c => _c);
+ RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
+
+ _a := 10; _b := 30; _c := 7;
+ CALL test_proc10(a => _a, b => _b, c => _c);
+ RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
+
+ _a := 10; _b := 30; _c := 7;
+ CALL test_proc10(_a, c => _c, b => _b);
+ RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
+
+ _a := 10; _b := 30; _c := 7;
+ CALL test_proc10(_a, _b);
+ RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
+
+ _a := 10; _b := 30; _c := 7;
+ CALL test_proc10(_a, b => _b);
+ RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
+
+ _a := 10; _b := 30; _c := 7;
+ CALL test_proc10(b => _b, a => _a);
+ RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
+END
+$$;
+
+-- OUT + VARIADIC
+
+CREATE PROCEDURE test_proc11(a OUT int, VARIADIC b int[])
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ RAISE NOTICE 'a: %, b: %', a, b;
+ a := b[1] + b[2];
+END;
+$$;
+
+DO $$
+DECLARE _a int; _b int; _c int;
+BEGIN
+ _a := 10; _b := 30; _c := 7;
+ CALL test_proc11(_a, _b, _c);
+ RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
+END
+$$;
+
-- transition variable assignment
diff --git a/src/pl/plpython/expected/plpython_call.out b/src/pl/plpython/expected/plpython_call.out
index c3f3c8e95e5..55e1027246a 100644
--- a/src/pl/plpython/expected/plpython_call.out
+++ b/src/pl/plpython/expected/plpython_call.out
@@ -56,7 +56,7 @@ CALL test_proc6(2, 3, 4);
CREATE PROCEDURE test_proc9(IN a int, OUT b int)
LANGUAGE plpythonu
AS $$
-plpy.notice("a: %s, b: %s" % (a, b))
+plpy.notice("a: %s" % (a))
return (a * 2,)
$$;
DO $$
@@ -67,7 +67,7 @@ BEGIN
RAISE NOTICE '_a: %, _b: %', _a, _b;
END
$$;
-NOTICE: a: 10, b: None
+NOTICE: a: 10
NOTICE: _a: 10, _b: 20
DROP PROCEDURE test_proc1;
DROP PROCEDURE test_proc2;
diff --git a/src/pl/plpython/plpy_procedure.c b/src/pl/plpython/plpy_procedure.c
index b7c0b5cebed..494f109b323 100644
--- a/src/pl/plpython/plpy_procedure.c
+++ b/src/pl/plpython/plpy_procedure.c
@@ -272,7 +272,7 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
/* proc->nargs was initialized to 0 above */
for (i = 0; i < total; i++)
{
- if ((modes[i] != PROARGMODE_OUT || proc->is_procedure) &&
+ if (modes[i] != PROARGMODE_OUT &&
modes[i] != PROARGMODE_TABLE)
(proc->nargs)++;
}
@@ -288,7 +288,7 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
Form_pg_type argTypeStruct;
if (modes &&
- ((modes[i] == PROARGMODE_OUT && !proc->is_procedure) ||
+ (modes[i] == PROARGMODE_OUT ||
modes[i] == PROARGMODE_TABLE))
continue; /* skip OUT arguments */
diff --git a/src/pl/plpython/sql/plpython_call.sql b/src/pl/plpython/sql/plpython_call.sql
index 46e89b1a9e1..b0b3705ae3c 100644
--- a/src/pl/plpython/sql/plpython_call.sql
+++ b/src/pl/plpython/sql/plpython_call.sql
@@ -59,7 +59,7 @@ CALL test_proc6(2, 3, 4);
CREATE PROCEDURE test_proc9(IN a int, OUT b int)
LANGUAGE plpythonu
AS $$
-plpy.notice("a: %s, b: %s" % (a, b))
+plpy.notice("a: %s" % (a))
return (a * 2,)
$$;
diff --git a/src/pl/tcl/expected/pltcl_call.out b/src/pl/tcl/expected/pltcl_call.out
index f0eb356cf23..e4498375ec1 100644
--- a/src/pl/tcl/expected/pltcl_call.out
+++ b/src/pl/tcl/expected/pltcl_call.out
@@ -53,7 +53,7 @@ CALL test_proc6(2, 3, 4);
CREATE PROCEDURE test_proc9(IN a int, OUT b int)
LANGUAGE pltcl
AS $$
-elog NOTICE "a: $1, b: $2"
+elog NOTICE "a: $1"
return [list b [expr {$1 * 2}]]
$$;
DO $$
@@ -64,7 +64,7 @@ BEGIN
RAISE NOTICE '_a: %, _b: %', _a, _b;
END
$$;
-NOTICE: a: 10, b:
+NOTICE: a: 10
NOTICE: _a: 10, _b: 20
DROP PROCEDURE test_proc1;
DROP PROCEDURE test_proc2;
diff --git a/src/pl/tcl/sql/pltcl_call.sql b/src/pl/tcl/sql/pltcl_call.sql
index 963277e1fb8..37efbdefc23 100644
--- a/src/pl/tcl/sql/pltcl_call.sql
+++ b/src/pl/tcl/sql/pltcl_call.sql
@@ -57,7 +57,7 @@ CALL test_proc6(2, 3, 4);
CREATE PROCEDURE test_proc9(IN a int, OUT b int)
LANGUAGE pltcl
AS $$
-elog NOTICE "a: $1, b: $2"
+elog NOTICE "a: $1"
return [list b [expr {$1 * 2}]]
$$;
diff --git a/src/test/regress/expected/create_procedure.out b/src/test/regress/expected/create_procedure.out
index d45575561e4..46c827f9791 100644
--- a/src/test/regress/expected/create_procedure.out
+++ b/src/test/regress/expected/create_procedure.out
@@ -19,17 +19,17 @@ $$;
List of functions
Schema | Name | Result data type | Argument data types | Type
--------+--------+------------------+---------------------+------
- public | ptest1 | | x text | proc
+ public | ptest1 | | IN x text | proc
(1 row)
SELECT pg_get_functiondef('ptest1'::regproc);
- pg_get_functiondef
----------------------------------------------------
- CREATE OR REPLACE PROCEDURE public.ptest1(x text)+
- LANGUAGE sql +
- AS $procedure$ +
- INSERT INTO cp_test VALUES (1, x); +
- $procedure$ +
+ pg_get_functiondef
+------------------------------------------------------
+ CREATE OR REPLACE PROCEDURE public.ptest1(IN x text)+
+ LANGUAGE sql +
+ AS $procedure$ +
+ INSERT INTO cp_test VALUES (1, x); +
+ $procedure$ +
(1 row)
@@ -46,7 +46,7 @@ SELECT pg_get_functiondef('ptest1'::regproc);
List of functions
Schema | Name | Result data type | Argument data types | Type
--------+--------+------------------+---------------------+------
- public | ptest1 | | x text | proc
+ public | ptest1 | | IN x text | proc
(1 row)
SELECT ptest1('x'); -- error
@@ -75,18 +75,18 @@ END;
List of functions
Schema | Name | Result data type | Argument data types | Type
--------+---------+------------------+---------------------+------
- public | ptest1s | | x text | proc
+ public | ptest1s | | IN x text | proc
(1 row)
SELECT pg_get_functiondef('ptest1s'::regproc);
- pg_get_functiondef
-----------------------------------------------------
- CREATE OR REPLACE PROCEDURE public.ptest1s(x text)+
- LANGUAGE sql +
- BEGIN ATOMIC +
- INSERT INTO cp_test (a, b) +
- VALUES (1, ptest1s.x); +
- END +
+ pg_get_functiondef
+-------------------------------------------------------
+ CREATE OR REPLACE PROCEDURE public.ptest1s(IN x text)+
+ LANGUAGE sql +
+ BEGIN ATOMIC +
+ INSERT INTO cp_test (a, b) +
+ VALUES (1, ptest1s.x); +
+ END +
(1 row)
@@ -196,16 +196,16 @@ END;
List of functions
Schema | Name | Result data type | Argument data types | Type
--------+--------+------------------+---------------------+------
- public | ptest8 | | x text | proc
+ public | ptest8 | | IN x text | proc
(1 row)
SELECT pg_get_functiondef('ptest8'::regproc);
- pg_get_functiondef
----------------------------------------------------
- CREATE OR REPLACE PROCEDURE public.ptest8(x text)+
- LANGUAGE sql +
- BEGIN ATOMIC +
- END +
+ pg_get_functiondef
+------------------------------------------------------
+ CREATE OR REPLACE PROCEDURE public.ptest8(IN x text)+
+ LANGUAGE sql +
+ BEGIN ATOMIC +
+ END +
(1 row)
@@ -217,12 +217,105 @@ AS $$
INSERT INTO cp_test VALUES (1, 'a');
SELECT 1;
$$;
+-- standard way to do a call:
CALL ptest9(NULL);
a
---
1
(1 row)
+-- you can write an expression, but it's not evaluated
+CALL ptest9(1/0); -- no error
+ a
+---
+ 1
+(1 row)
+
+-- ... and it had better match the type of the parameter
+CALL ptest9(1./0.); -- error
+ERROR: procedure ptest9(numeric) does not exist
+LINE 1: CALL ptest9(1./0.);
+ ^
+HINT: No procedure matches the given name and argument types. You might need to add explicit type casts.
+-- check named-parameter matching
+CREATE PROCEDURE ptest10(OUT a int, IN b int, IN c int)
+LANGUAGE SQL AS $$ SELECT b - c $$;
+CALL ptest10(null, 7, 4);
+ a
+---
+ 3
+(1 row)
+
+CALL ptest10(a => null, b => 8, c => 2);
+ a
+---
+ 6
+(1 row)
+
+CALL ptest10(null, 7, c => 2);
+ a
+---
+ 5
+(1 row)
+
+CALL ptest10(null, c => 4, b => 11);
+ a
+---
+ 7
+(1 row)
+
+CALL ptest10(b => 8, c => 2, a => 0);
+ a
+---
+ 6
+(1 row)
+
+CREATE PROCEDURE ptest11(a OUT int, VARIADIC b int[]) LANGUAGE SQL
+ AS $$ SELECT b[1] + b[2] $$;
+CALL ptest11(null, 11, 12, 13);
+ a
+----
+ 23
+(1 row)
+
+-- check resolution of ambiguous DROP commands
+CREATE PROCEDURE ptest10(IN a int, IN b int, IN c int)
+LANGUAGE SQL AS $$ SELECT a + b - c $$;
+\df ptest10
+ List of functions
+ Schema | Name | Result data type | Argument data types | Type
+--------+---------+------------------+-------------------------------------------+------
+ public | ptest10 | | IN a integer, IN b integer, IN c integer | proc
+ public | ptest10 | | OUT a integer, IN b integer, IN c integer | proc
+(2 rows)
+
+drop procedure ptest10; -- fail
+ERROR: procedure name "ptest10" is not unique
+HINT: Specify the argument list to select the procedure unambiguously.
+drop procedure ptest10(int, int, int); -- fail
+ERROR: procedure name "ptest10" is not unique
+begin;
+drop procedure ptest10(out int, int, int);
+\df ptest10
+ List of functions
+ Schema | Name | Result data type | Argument data types | Type
+--------+---------+------------------+------------------------------------------+------
+ public | ptest10 | | IN a integer, IN b integer, IN c integer | proc
+(1 row)
+
+drop procedure ptest10(int, int, int); -- now this would work
+rollback;
+begin;
+drop procedure ptest10(in int, int, int);
+\df ptest10
+ List of functions
+ Schema | Name | Result data type | Argument data types | Type
+--------+---------+------------------+-------------------------------------------+------
+ public | ptest10 | | OUT a integer, IN b integer, IN c integer | proc
+(1 row)
+
+drop procedure ptest10(int, int, int); -- now this would work
+rollback;
-- various error cases
CALL version(); -- error: not a procedure
ERROR: version() is not a procedure
@@ -242,6 +335,12 @@ CREATE PROCEDURE ptestx() LANGUAGE SQL STRICT AS $$ INSERT INTO cp_test VALUES (
ERROR: invalid attribute in procedure definition
LINE 1: CREATE PROCEDURE ptestx() LANGUAGE SQL STRICT AS $$ INSERT I...
^
+CREATE PROCEDURE ptestx(a VARIADIC int[], b OUT int) LANGUAGE SQL
+ AS $$ SELECT a[1] $$;
+ERROR: VARIADIC parameter must be the last parameter
+CREATE PROCEDURE ptestx(a int DEFAULT 42, b OUT int) LANGUAGE SQL
+ AS $$ SELECT a $$;
+ERROR: procedure OUT parameters cannot appear after one with a default value
ALTER PROCEDURE ptest1(text) STRICT;
ERROR: invalid attribute in procedure definition
LINE 1: ALTER PROCEDURE ptest1(text) STRICT;
diff --git a/src/test/regress/sql/create_procedure.sql b/src/test/regress/sql/create_procedure.sql
index 76f781c0b90..75cc0fcf2a6 100644
--- a/src/test/regress/sql/create_procedure.sql
+++ b/src/test/regress/sql/create_procedure.sql
@@ -153,8 +153,47 @@ INSERT INTO cp_test VALUES (1, 'a');
SELECT 1;
$$;
+-- standard way to do a call:
CALL ptest9(NULL);
-
+-- you can write an expression, but it's not evaluated
+CALL ptest9(1/0); -- no error
+-- ... and it had better match the type of the parameter
+CALL ptest9(1./0.); -- error
+
+-- check named-parameter matching
+CREATE PROCEDURE ptest10(OUT a int, IN b int, IN c int)
+LANGUAGE SQL AS $$ SELECT b - c $$;
+
+CALL ptest10(null, 7, 4);
+CALL ptest10(a => null, b => 8, c => 2);
+CALL ptest10(null, 7, c => 2);
+CALL ptest10(null, c => 4, b => 11);
+CALL ptest10(b => 8, c => 2, a => 0);
+
+CREATE PROCEDURE ptest11(a OUT int, VARIADIC b int[]) LANGUAGE SQL
+ AS $$ SELECT b[1] + b[2] $$;
+
+CALL ptest11(null, 11, 12, 13);
+
+-- check resolution of ambiguous DROP commands
+
+CREATE PROCEDURE ptest10(IN a int, IN b int, IN c int)
+LANGUAGE SQL AS $$ SELECT a + b - c $$;
+
+\df ptest10
+
+drop procedure ptest10; -- fail
+drop procedure ptest10(int, int, int); -- fail
+begin;
+drop procedure ptest10(out int, int, int);
+\df ptest10
+drop procedure ptest10(int, int, int); -- now this would work
+rollback;
+begin;
+drop procedure ptest10(in int, int, int);
+\df ptest10
+drop procedure ptest10(int, int, int); -- now this would work
+rollback;
-- various error cases
@@ -163,6 +202,10 @@ CALL sum(1); -- error: not a procedure
CREATE PROCEDURE ptestx() LANGUAGE SQL WINDOW AS $$ INSERT INTO cp_test VALUES (1, 'a') $$;
CREATE PROCEDURE ptestx() LANGUAGE SQL STRICT AS $$ INSERT INTO cp_test VALUES (1, 'a') $$;
+CREATE PROCEDURE ptestx(a VARIADIC int[], b OUT int) LANGUAGE SQL
+ AS $$ SELECT a[1] $$;
+CREATE PROCEDURE ptestx(a int DEFAULT 42, b OUT int) LANGUAGE SQL
+ AS $$ SELECT a $$;
ALTER PROCEDURE ptest1(text) STRICT;
ALTER FUNCTION ptest1(text) VOLATILE; -- error: not a function