mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Support for OUT parameters in procedures
Unlike for functions, OUT parameters for procedures are part of the signature. Therefore, they have to be listed in pg_proc.proargtypes as well as mentioned in ALTER PROCEDURE and DROP PROCEDURE. Reviewed-by: Andrew Dunstan <andrew.dunstan@2ndquadrant.com> Reviewed-by: Pavel Stehule <pavel.stehule@gmail.com> Discussion: https://www.postgresql.org/message-id/flat/2b8490fe-51af-e671-c504-47359dc453c5@2ndquadrant.com
This commit is contained in:
@ -5875,8 +5875,9 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
|
|||||||
<para>
|
<para>
|
||||||
An array with the data types of the function arguments. This includes
|
An array with the data types of the function arguments. This includes
|
||||||
only input arguments (including <literal>INOUT</literal> and
|
only input arguments (including <literal>INOUT</literal> and
|
||||||
<literal>VARIADIC</literal> arguments), and thus represents
|
<literal>VARIADIC</literal> arguments), as well as
|
||||||
the call signature of the function.
|
<literal>OUT</literal> parameters of procedures, and thus represents
|
||||||
|
the call signature of the function or procedure.
|
||||||
</para></entry>
|
</para></entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
|
@ -478,6 +478,14 @@ $$ LANGUAGE plpgsql;
|
|||||||
included it, but it would be redundant.
|
included it, but it would be redundant.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
To call a function with <literal>OUT</literal> parameters, omit the
|
||||||
|
output parameter in the function call:
|
||||||
|
<programlisting>
|
||||||
|
SELECT sales_tax(100.00);
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Output parameters are most useful when returning multiple values.
|
Output parameters are most useful when returning multiple values.
|
||||||
A trivial example is:
|
A trivial example is:
|
||||||
@ -489,6 +497,11 @@ BEGIN
|
|||||||
prod := x * y;
|
prod := x * y;
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
SELECT * FROM sum_n_product(2, 4);
|
||||||
|
sum | prod
|
||||||
|
-----+------
|
||||||
|
6 | 8
|
||||||
</programlisting>
|
</programlisting>
|
||||||
|
|
||||||
As discussed in <xref linkend="xfunc-output-parameters"/>, this
|
As discussed in <xref linkend="xfunc-output-parameters"/>, this
|
||||||
@ -497,6 +510,31 @@ $$ LANGUAGE plpgsql;
|
|||||||
<literal>RETURNS record</literal>.
|
<literal>RETURNS record</literal>.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
This also works with procedures, for example:
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
CREATE PROCEDURE sum_n_product(x int, y int, OUT sum int, OUT prod int) AS $$
|
||||||
|
BEGIN
|
||||||
|
sum := x + y;
|
||||||
|
prod := x * y;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
</programlisting>
|
||||||
|
|
||||||
|
In a call to a procedure, all the parameters must be specified. For
|
||||||
|
output parameters, <literal>NULL</literal> may be specified.
|
||||||
|
<programlisting>
|
||||||
|
CALL sum_n_product(2, 4, NULL, NULL);
|
||||||
|
sum | prod
|
||||||
|
-----+------
|
||||||
|
6 | 8
|
||||||
|
</programlisting>
|
||||||
|
Output parameters in procedures become more interesting in nested calls,
|
||||||
|
where they can be assigned to variables. See <xref
|
||||||
|
linkend="plpgsql-statements-calling-procedure"/> for details.
|
||||||
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Another way to declare a <application>PL/pgSQL</application> function
|
Another way to declare a <application>PL/pgSQL</application> function
|
||||||
is with <literal>RETURNS TABLE</literal>, for example:
|
is with <literal>RETURNS TABLE</literal>, for example:
|
||||||
|
@ -212,11 +212,12 @@ ALTER EXTENSION <replaceable class="parameter">name</replaceable> DROP <replacea
|
|||||||
argument: <literal>IN</literal>, <literal>OUT</literal>,
|
argument: <literal>IN</literal>, <literal>OUT</literal>,
|
||||||
<literal>INOUT</literal>, or <literal>VARIADIC</literal>.
|
<literal>INOUT</literal>, or <literal>VARIADIC</literal>.
|
||||||
If omitted, the default is <literal>IN</literal>.
|
If omitted, the default is <literal>IN</literal>.
|
||||||
Note that <command>ALTER EXTENSION</command> does not actually pay
|
Note that <command>ALTER EXTENSION</command> does not actually pay any
|
||||||
any attention to <literal>OUT</literal> arguments, since only the input
|
attention to <literal>OUT</literal> arguments for functions and
|
||||||
arguments are needed to determine the function's identity.
|
aggregates (but not procedures), since only the input arguments are
|
||||||
So it is sufficient to list the <literal>IN</literal>, <literal>INOUT</literal>,
|
needed to determine the function's identity. So it is sufficient to
|
||||||
and <literal>VARIADIC</literal> arguments.
|
list the <literal>IN</literal>, <literal>INOUT</literal>, and
|
||||||
|
<literal>VARIADIC</literal> arguments for functions and aggregates.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
@ -81,8 +81,9 @@ ALTER PROCEDURE <replaceable>name</replaceable> [ ( [ [ <replaceable class="para
|
|||||||
|
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
The mode of an argument: <literal>IN</literal> or <literal>VARIADIC</literal>.
|
The mode of an argument: <literal>IN</literal>, <literal>OUT</literal>,
|
||||||
If omitted, the default is <literal>IN</literal>.
|
<literal>INOUT</literal>, or <literal>VARIADIC</literal>. If omitted,
|
||||||
|
the default is <literal>IN</literal>.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
@ -178,11 +178,12 @@ COMMENT ON
|
|||||||
argument: <literal>IN</literal>, <literal>OUT</literal>,
|
argument: <literal>IN</literal>, <literal>OUT</literal>,
|
||||||
<literal>INOUT</literal>, or <literal>VARIADIC</literal>.
|
<literal>INOUT</literal>, or <literal>VARIADIC</literal>.
|
||||||
If omitted, the default is <literal>IN</literal>.
|
If omitted, the default is <literal>IN</literal>.
|
||||||
Note that <command>COMMENT</command> does not actually pay
|
Note that <command>COMMENT</command> does not actually pay any attention
|
||||||
any attention to <literal>OUT</literal> arguments, since only the input
|
to <literal>OUT</literal> arguments for functions and aggregates (but
|
||||||
arguments are needed to determine the function's identity.
|
not procedures), since only the input arguments are needed to determine
|
||||||
So it is sufficient to list the <literal>IN</literal>, <literal>INOUT</literal>,
|
the function's identity. So it is sufficient to list the
|
||||||
and <literal>VARIADIC</literal> arguments.
|
<literal>IN</literal>, <literal>INOUT</literal>, and
|
||||||
|
<literal>VARIADIC</literal> arguments for functions and aggregates.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
@ -97,11 +97,9 @@ CREATE [ OR REPLACE ] PROCEDURE
|
|||||||
|
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
The mode of an argument: <literal>IN</literal>,
|
The mode of an argument: <literal>IN</literal>, <literal>OUT</literal>,
|
||||||
<literal>INOUT</literal>, or <literal>VARIADIC</literal>. If omitted,
|
<literal>INOUT</literal>, or <literal>VARIADIC</literal>. If omitted,
|
||||||
the default is <literal>IN</literal>. (<literal>OUT</literal>
|
the default is <literal>IN</literal>.
|
||||||
arguments are currently not supported for procedures. Use
|
|
||||||
<literal>INOUT</literal> instead.)
|
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
@ -67,8 +67,9 @@ DROP PROCEDURE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [
|
|||||||
|
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
The mode of an argument: <literal>IN</literal> or <literal>VARIADIC</literal>.
|
The mode of an argument: <literal>IN</literal>, <literal>OUT</literal>,
|
||||||
If omitted, the default is <literal>IN</literal>.
|
<literal>INOUT</literal>, or <literal>VARIADIC</literal>. If omitted,
|
||||||
|
the default is <literal>IN</literal>.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
@ -127,11 +127,12 @@ SECURITY LABEL [ FOR <replaceable class="parameter">provider</replaceable> ] ON
|
|||||||
argument: <literal>IN</literal>, <literal>OUT</literal>,
|
argument: <literal>IN</literal>, <literal>OUT</literal>,
|
||||||
<literal>INOUT</literal>, or <literal>VARIADIC</literal>.
|
<literal>INOUT</literal>, or <literal>VARIADIC</literal>.
|
||||||
If omitted, the default is <literal>IN</literal>.
|
If omitted, the default is <literal>IN</literal>.
|
||||||
Note that <command>SECURITY LABEL</command> does not actually
|
Note that <command>SECURITY LABEL</command> does not actually pay any
|
||||||
pay any attention to <literal>OUT</literal> arguments, since only the input
|
attention to <literal>OUT</literal> arguments for functions and
|
||||||
arguments are needed to determine the function's identity.
|
aggregates (but not procedures), since only the input arguments are
|
||||||
So it is sufficient to list the <literal>IN</literal>, <literal>INOUT</literal>,
|
needed to determine the function's identity. So it is sufficient to
|
||||||
and <literal>VARIADIC</literal> arguments.
|
list the <literal>IN</literal>, <literal>INOUT</literal>, and
|
||||||
|
<literal>VARIADIC</literal> arguments for functions and aggregates.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
@ -179,6 +179,24 @@ SELECT clean_emp();
|
|||||||
</screen>
|
</screen>
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
You can also write this as a procedure, thus avoiding the issue of the
|
||||||
|
return type. For example:
|
||||||
|
<screen>
|
||||||
|
CREATE PROCEDURE clean_emp() AS '
|
||||||
|
DELETE FROM emp
|
||||||
|
WHERE salary < 0;
|
||||||
|
' LANGUAGE SQL;
|
||||||
|
|
||||||
|
CALL clean_emp();
|
||||||
|
</screen>
|
||||||
|
In simple cases like this, the difference between a function returning
|
||||||
|
<type>void</type> and a procedure is mostly stylistic. However,
|
||||||
|
procedures offer additional functionality such as transaction control
|
||||||
|
that is not available in functions. Also, procedures are SQL standard
|
||||||
|
whereas returning <type>void</type> is a PostgreSQL extension.
|
||||||
|
</para>
|
||||||
|
|
||||||
<note>
|
<note>
|
||||||
<para>
|
<para>
|
||||||
The entire body of a SQL function is parsed before any of it is
|
The entire body of a SQL function is parsed before any of it is
|
||||||
@ -716,6 +734,47 @@ DROP FUNCTION sum_n_product (int, int);
|
|||||||
</para>
|
</para>
|
||||||
</sect2>
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="xfunc-output-parameters-proc">
|
||||||
|
<title><acronym>SQL</acronym> Procedures with Output Parameters</title>
|
||||||
|
|
||||||
|
<indexterm>
|
||||||
|
<primary>procedures</primary>
|
||||||
|
<secondary>output parameter</secondary>
|
||||||
|
</indexterm>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Output parameters are also supported in procedures, but they work a bit
|
||||||
|
differently from functions. Notably, output parameters
|
||||||
|
<emphasis>are</emphasis> included in the signature of a procedure and
|
||||||
|
must be specified in the procedure call.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
For example, the bank account debiting routine from earlier could be
|
||||||
|
written like this:
|
||||||
|
<programlisting>
|
||||||
|
CREATE PROCEDURE tp1 (accountno integer, debit numeric, OUT new_balance numeric) AS $$
|
||||||
|
UPDATE bank
|
||||||
|
SET balance = balance - debit
|
||||||
|
WHERE accountno = tp1.accountno
|
||||||
|
RETURNING balance;
|
||||||
|
$$ LANGUAGE SQL;
|
||||||
|
</programlisting>
|
||||||
|
To call this procedure, it is irrelevant what is passed as the argument
|
||||||
|
of the <literal>OUT</literal> parameter, so you could pass
|
||||||
|
<literal>NULL</literal>:
|
||||||
|
<programlisting>
|
||||||
|
CALL tp1(17, 100.0, NULL);
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Procedures with output parameters are more useful in PL/pgSQL, where the
|
||||||
|
output parameters can be assigned to variables. See <xref
|
||||||
|
linkend="plpgsql-statements-calling-procedure"/> for details.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
<sect2 id="xfunc-sql-variadic-functions">
|
<sect2 id="xfunc-sql-variadic-functions">
|
||||||
<title><acronym>SQL</acronym> Functions with Variable Numbers of Arguments</title>
|
<title><acronym>SQL</acronym> Functions with Variable Numbers of Arguments</title>
|
||||||
|
|
||||||
|
@ -249,6 +249,9 @@ ProcedureCreate(const char *procedureName,
|
|||||||
elog(ERROR, "variadic parameter must be last");
|
elog(ERROR, "variadic parameter must be last");
|
||||||
break;
|
break;
|
||||||
case PROARGMODE_OUT:
|
case PROARGMODE_OUT:
|
||||||
|
if (OidIsValid(variadicType) && prokind == PROKIND_PROCEDURE)
|
||||||
|
elog(ERROR, "variadic parameter must be last");
|
||||||
|
break;
|
||||||
case PROARGMODE_TABLE:
|
case PROARGMODE_TABLE:
|
||||||
/* okay */
|
/* okay */
|
||||||
break;
|
break;
|
||||||
@ -462,10 +465,12 @@ ProcedureCreate(const char *procedureName,
|
|||||||
if (isnull)
|
if (isnull)
|
||||||
proargmodes = PointerGetDatum(NULL); /* just to be sure */
|
proargmodes = PointerGetDatum(NULL); /* just to be sure */
|
||||||
|
|
||||||
n_old_arg_names = get_func_input_arg_names(proargnames,
|
n_old_arg_names = get_func_input_arg_names(prokind,
|
||||||
|
proargnames,
|
||||||
proargmodes,
|
proargmodes,
|
||||||
&old_arg_names);
|
&old_arg_names);
|
||||||
n_new_arg_names = get_func_input_arg_names(parameterNames,
|
n_new_arg_names = get_func_input_arg_names(prokind,
|
||||||
|
parameterNames,
|
||||||
parameterModes,
|
parameterModes,
|
||||||
&new_arg_names);
|
&new_arg_names);
|
||||||
for (j = 0; j < n_old_arg_names; j++)
|
for (j = 0; j < n_old_arg_names; j++)
|
||||||
|
@ -194,8 +194,8 @@ interpret_function_parameter_list(ParseState *pstate,
|
|||||||
Oid *requiredResultType)
|
Oid *requiredResultType)
|
||||||
{
|
{
|
||||||
int parameterCount = list_length(parameters);
|
int parameterCount = list_length(parameters);
|
||||||
Oid *inTypes;
|
Oid *sigArgTypes;
|
||||||
int inCount = 0;
|
int sigArgCount = 0;
|
||||||
Datum *allTypes;
|
Datum *allTypes;
|
||||||
Datum *paramModes;
|
Datum *paramModes;
|
||||||
Datum *paramNames;
|
Datum *paramNames;
|
||||||
@ -209,7 +209,7 @@ interpret_function_parameter_list(ParseState *pstate,
|
|||||||
*variadicArgType = InvalidOid; /* default result */
|
*variadicArgType = InvalidOid; /* default result */
|
||||||
*requiredResultType = InvalidOid; /* default result */
|
*requiredResultType = InvalidOid; /* default result */
|
||||||
|
|
||||||
inTypes = (Oid *) palloc(parameterCount * sizeof(Oid));
|
sigArgTypes = (Oid *) palloc(parameterCount * sizeof(Oid));
|
||||||
allTypes = (Datum *) palloc(parameterCount * sizeof(Datum));
|
allTypes = (Datum *) palloc(parameterCount * sizeof(Datum));
|
||||||
paramModes = (Datum *) palloc(parameterCount * sizeof(Datum));
|
paramModes = (Datum *) palloc(parameterCount * sizeof(Datum));
|
||||||
paramNames = (Datum *) palloc0(parameterCount * sizeof(Datum));
|
paramNames = (Datum *) palloc0(parameterCount * sizeof(Datum));
|
||||||
@ -281,25 +281,21 @@ interpret_function_parameter_list(ParseState *pstate,
|
|||||||
errmsg("functions cannot accept set arguments")));
|
errmsg("functions cannot accept set arguments")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (objtype == OBJECT_PROCEDURE)
|
|
||||||
{
|
|
||||||
if (fp->mode == FUNC_PARAM_OUT)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
||||||
errmsg("procedures cannot have OUT arguments"),
|
|
||||||
errhint("INOUT arguments are permitted.")));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* handle input parameters */
|
/* handle input parameters */
|
||||||
if (fp->mode != FUNC_PARAM_OUT && fp->mode != FUNC_PARAM_TABLE)
|
if (fp->mode != FUNC_PARAM_OUT && fp->mode != FUNC_PARAM_TABLE)
|
||||||
|
isinput = true;
|
||||||
|
|
||||||
|
/* 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 input parameters can't follow a VARIADIC parameter */
|
/* other signature parameters can't follow a VARIADIC parameter */
|
||||||
if (varCount > 0)
|
if (varCount > 0)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||||
errmsg("VARIADIC parameter must be the last input parameter")));
|
errmsg("VARIADIC parameter must be the last signature parameter")));
|
||||||
inTypes[inCount++] = toid;
|
sigArgTypes[sigArgCount++] = toid;
|
||||||
isinput = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* handle output parameters */
|
/* handle output parameters */
|
||||||
@ -429,7 +425,7 @@ interpret_function_parameter_list(ParseState *pstate,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Now construct the proper outputs as needed */
|
/* Now construct the proper outputs as needed */
|
||||||
*parameterTypes = buildoidvector(inTypes, inCount);
|
*parameterTypes = buildoidvector(sigArgTypes, sigArgCount);
|
||||||
|
|
||||||
if (outCount > 0 || varCount > 0)
|
if (outCount > 0 || varCount > 0)
|
||||||
{
|
{
|
||||||
@ -2067,6 +2063,9 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
|
|||||||
int nargs;
|
int nargs;
|
||||||
int i;
|
int i;
|
||||||
AclResult aclresult;
|
AclResult aclresult;
|
||||||
|
Oid *argtypes;
|
||||||
|
char **argnames;
|
||||||
|
char *argmodes;
|
||||||
FmgrInfo flinfo;
|
FmgrInfo flinfo;
|
||||||
CallContext *callcontext;
|
CallContext *callcontext;
|
||||||
EState *estate;
|
EState *estate;
|
||||||
@ -2127,6 +2126,8 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
|
|||||||
tp);
|
tp);
|
||||||
nargs = list_length(fexpr->args);
|
nargs = list_length(fexpr->args);
|
||||||
|
|
||||||
|
get_func_arg_info(tp, &argtypes, &argnames, &argmodes);
|
||||||
|
|
||||||
ReleaseSysCache(tp);
|
ReleaseSysCache(tp);
|
||||||
|
|
||||||
/* safety check; see ExecInitFunc() */
|
/* safety check; see ExecInitFunc() */
|
||||||
@ -2155,6 +2156,13 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
|
|||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
foreach(lc, fexpr->args)
|
foreach(lc, fexpr->args)
|
||||||
|
{
|
||||||
|
if (argmodes && argmodes[i] == PROARGMODE_OUT)
|
||||||
|
{
|
||||||
|
fcinfo->args[i].value = 0;
|
||||||
|
fcinfo->args[i].isnull = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
ExprState *exprstate;
|
ExprState *exprstate;
|
||||||
Datum val;
|
Datum val;
|
||||||
@ -2166,6 +2174,7 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
|
|||||||
|
|
||||||
fcinfo->args[i].value = val;
|
fcinfo->args[i].value = val;
|
||||||
fcinfo->args[i].isnull = isnull;
|
fcinfo->args[i].isnull = isnull;
|
||||||
|
}
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
@ -259,7 +259,8 @@ prepare_sql_fn_parse_info(HeapTuple procedureTuple,
|
|||||||
if (isNull)
|
if (isNull)
|
||||||
proargmodes = PointerGetDatum(NULL); /* just to be sure */
|
proargmodes = PointerGetDatum(NULL); /* just to be sure */
|
||||||
|
|
||||||
n_arg_names = get_func_input_arg_names(proargnames, proargmodes,
|
n_arg_names = get_func_input_arg_names(procedureStruct->prokind,
|
||||||
|
proargnames, proargmodes,
|
||||||
&pinfo->argnames);
|
&pinfo->argnames);
|
||||||
|
|
||||||
/* Paranoia: ignore the result if too few array entries */
|
/* Paranoia: ignore the result if too few array entries */
|
||||||
|
@ -166,7 +166,7 @@ static RoleSpec *makeRoleSpec(RoleSpecType type, int location);
|
|||||||
static void check_qualified_name(List *names, core_yyscan_t yyscanner);
|
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_func_name(List *names, core_yyscan_t yyscanner);
|
||||||
static List *check_indirection(List *indirection, core_yyscan_t yyscanner);
|
static List *check_indirection(List *indirection, core_yyscan_t yyscanner);
|
||||||
static List *extractArgTypes(List *parameters);
|
static List *extractArgTypes(ObjectType objtype, List *parameters);
|
||||||
static List *extractAggrArgTypes(List *aggrargs);
|
static List *extractAggrArgTypes(List *aggrargs);
|
||||||
static List *makeOrderedSetArgs(List *directargs, List *orderedargs,
|
static List *makeOrderedSetArgs(List *directargs, List *orderedargs,
|
||||||
core_yyscan_t yyscanner);
|
core_yyscan_t yyscanner);
|
||||||
@ -375,8 +375,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
|||||||
%type <accesspriv> privilege
|
%type <accesspriv> privilege
|
||||||
%type <list> privileges privilege_list
|
%type <list> privileges privilege_list
|
||||||
%type <privtarget> privilege_target
|
%type <privtarget> privilege_target
|
||||||
%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes
|
%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
|
%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list procedure_with_argtypes_list
|
||||||
%type <ival> defacl_privilege_target
|
%type <ival> defacl_privilege_target
|
||||||
%type <defelt> DefACLOption
|
%type <defelt> DefACLOption
|
||||||
%type <list> DefACLOptionList
|
%type <list> DefACLOptionList
|
||||||
@ -4623,7 +4623,7 @@ AlterExtensionContentsStmt:
|
|||||||
n->object = (Node *) lcons(makeString($9), $7);
|
n->object = (Node *) lcons(makeString($9), $7);
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| ALTER EXTENSION name add_drop PROCEDURE function_with_argtypes
|
| ALTER EXTENSION name add_drop PROCEDURE procedure_with_argtypes
|
||||||
{
|
{
|
||||||
AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt);
|
AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt);
|
||||||
n->extname = $3;
|
n->extname = $3;
|
||||||
@ -4632,7 +4632,7 @@ AlterExtensionContentsStmt:
|
|||||||
n->object = (Node *) $6;
|
n->object = (Node *) $6;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| ALTER EXTENSION name add_drop ROUTINE function_with_argtypes
|
| ALTER EXTENSION name add_drop ROUTINE procedure_with_argtypes
|
||||||
{
|
{
|
||||||
AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt);
|
AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt);
|
||||||
n->extname = $3;
|
n->extname = $3;
|
||||||
@ -6365,7 +6365,7 @@ CommentStmt:
|
|||||||
n->comment = $8;
|
n->comment = $8;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
| COMMENT ON PROCEDURE function_with_argtypes IS comment_text
|
| COMMENT ON PROCEDURE procedure_with_argtypes IS comment_text
|
||||||
{
|
{
|
||||||
CommentStmt *n = makeNode(CommentStmt);
|
CommentStmt *n = makeNode(CommentStmt);
|
||||||
n->objtype = OBJECT_PROCEDURE;
|
n->objtype = OBJECT_PROCEDURE;
|
||||||
@ -6373,7 +6373,7 @@ CommentStmt:
|
|||||||
n->comment = $6;
|
n->comment = $6;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
| COMMENT ON ROUTINE function_with_argtypes IS comment_text
|
| COMMENT ON ROUTINE procedure_with_argtypes IS comment_text
|
||||||
{
|
{
|
||||||
CommentStmt *n = makeNode(CommentStmt);
|
CommentStmt *n = makeNode(CommentStmt);
|
||||||
n->objtype = OBJECT_ROUTINE;
|
n->objtype = OBJECT_ROUTINE;
|
||||||
@ -6519,7 +6519,7 @@ SecLabelStmt:
|
|||||||
n->label = $9;
|
n->label = $9;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
| SECURITY LABEL opt_provider ON PROCEDURE function_with_argtypes
|
| SECURITY LABEL opt_provider ON PROCEDURE procedure_with_argtypes
|
||||||
IS security_label
|
IS security_label
|
||||||
{
|
{
|
||||||
SecLabelStmt *n = makeNode(SecLabelStmt);
|
SecLabelStmt *n = makeNode(SecLabelStmt);
|
||||||
@ -6880,7 +6880,7 @@ privilege_target:
|
|||||||
n->objs = $2;
|
n->objs = $2;
|
||||||
$$ = n;
|
$$ = n;
|
||||||
}
|
}
|
||||||
| PROCEDURE function_with_argtypes_list
|
| PROCEDURE procedure_with_argtypes_list
|
||||||
{
|
{
|
||||||
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
|
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
|
||||||
n->targtype = ACL_TARGET_OBJECT;
|
n->targtype = ACL_TARGET_OBJECT;
|
||||||
@ -6888,7 +6888,7 @@ privilege_target:
|
|||||||
n->objs = $2;
|
n->objs = $2;
|
||||||
$$ = n;
|
$$ = n;
|
||||||
}
|
}
|
||||||
| ROUTINE function_with_argtypes_list
|
| ROUTINE procedure_with_argtypes_list
|
||||||
{
|
{
|
||||||
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
|
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
|
||||||
n->targtype = ACL_TARGET_OBJECT;
|
n->targtype = ACL_TARGET_OBJECT;
|
||||||
@ -7409,20 +7409,33 @@ function_with_argtypes_list:
|
|||||||
{ $$ = lappend($1, $3); }
|
{ $$ = 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:
|
function_with_argtypes:
|
||||||
func_name func_args
|
func_name func_args
|
||||||
{
|
{
|
||||||
ObjectWithArgs *n = makeNode(ObjectWithArgs);
|
ObjectWithArgs *n = makeNode(ObjectWithArgs);
|
||||||
n->objname = $1;
|
n->objname = $1;
|
||||||
n->objargs = extractArgTypes($2);
|
n->objargs = extractArgTypes(OBJECT_FUNCTION, $2);
|
||||||
$$ = n;
|
$$ = n;
|
||||||
}
|
}
|
||||||
|
| function_with_argtypes_common
|
||||||
|
{
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
function_with_argtypes_common:
|
||||||
/*
|
/*
|
||||||
* Because of reduce/reduce conflicts, we can't use func_name
|
* Because of reduce/reduce conflicts, we can't use func_name
|
||||||
* below, but we can write it out the long way, which actually
|
* below, but we can write it out the long way, which actually
|
||||||
* allows more cases.
|
* allows more cases.
|
||||||
*/
|
*/
|
||||||
| type_func_name_keyword
|
type_func_name_keyword
|
||||||
{
|
{
|
||||||
ObjectWithArgs *n = makeNode(ObjectWithArgs);
|
ObjectWithArgs *n = makeNode(ObjectWithArgs);
|
||||||
n->objname = list_make1(makeString(pstrdup($1)));
|
n->objname = list_make1(makeString(pstrdup($1)));
|
||||||
@ -7446,6 +7459,24 @@ function_with_argtypes:
|
|||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
* func_args_with_defaults is separate because we only want to accept
|
||||||
* defaults in CREATE FUNCTION, not in ALTER etc.
|
* defaults in CREATE FUNCTION, not in ALTER etc.
|
||||||
@ -7824,7 +7855,7 @@ AlterFunctionStmt:
|
|||||||
n->actions = $4;
|
n->actions = $4;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
| ALTER PROCEDURE function_with_argtypes alterfunc_opt_list opt_restrict
|
| ALTER PROCEDURE procedure_with_argtypes alterfunc_opt_list opt_restrict
|
||||||
{
|
{
|
||||||
AlterFunctionStmt *n = makeNode(AlterFunctionStmt);
|
AlterFunctionStmt *n = makeNode(AlterFunctionStmt);
|
||||||
n->objtype = OBJECT_PROCEDURE;
|
n->objtype = OBJECT_PROCEDURE;
|
||||||
@ -7832,7 +7863,7 @@ AlterFunctionStmt:
|
|||||||
n->actions = $4;
|
n->actions = $4;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
| ALTER ROUTINE function_with_argtypes alterfunc_opt_list opt_restrict
|
| ALTER ROUTINE procedure_with_argtypes alterfunc_opt_list opt_restrict
|
||||||
{
|
{
|
||||||
AlterFunctionStmt *n = makeNode(AlterFunctionStmt);
|
AlterFunctionStmt *n = makeNode(AlterFunctionStmt);
|
||||||
n->objtype = OBJECT_ROUTINE;
|
n->objtype = OBJECT_ROUTINE;
|
||||||
@ -7888,7 +7919,7 @@ RemoveFuncStmt:
|
|||||||
n->concurrent = false;
|
n->concurrent = false;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| DROP PROCEDURE function_with_argtypes_list opt_drop_behavior
|
| DROP PROCEDURE procedure_with_argtypes_list opt_drop_behavior
|
||||||
{
|
{
|
||||||
DropStmt *n = makeNode(DropStmt);
|
DropStmt *n = makeNode(DropStmt);
|
||||||
n->removeType = OBJECT_PROCEDURE;
|
n->removeType = OBJECT_PROCEDURE;
|
||||||
@ -7898,7 +7929,7 @@ RemoveFuncStmt:
|
|||||||
n->concurrent = false;
|
n->concurrent = false;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| DROP PROCEDURE IF_P EXISTS function_with_argtypes_list opt_drop_behavior
|
| DROP PROCEDURE IF_P EXISTS procedure_with_argtypes_list opt_drop_behavior
|
||||||
{
|
{
|
||||||
DropStmt *n = makeNode(DropStmt);
|
DropStmt *n = makeNode(DropStmt);
|
||||||
n->removeType = OBJECT_PROCEDURE;
|
n->removeType = OBJECT_PROCEDURE;
|
||||||
@ -7908,7 +7939,7 @@ RemoveFuncStmt:
|
|||||||
n->concurrent = false;
|
n->concurrent = false;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| DROP ROUTINE function_with_argtypes_list opt_drop_behavior
|
| DROP ROUTINE procedure_with_argtypes_list opt_drop_behavior
|
||||||
{
|
{
|
||||||
DropStmt *n = makeNode(DropStmt);
|
DropStmt *n = makeNode(DropStmt);
|
||||||
n->removeType = OBJECT_ROUTINE;
|
n->removeType = OBJECT_ROUTINE;
|
||||||
@ -7918,7 +7949,7 @@ RemoveFuncStmt:
|
|||||||
n->concurrent = false;
|
n->concurrent = false;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| DROP ROUTINE IF_P EXISTS function_with_argtypes_list opt_drop_behavior
|
| DROP ROUTINE IF_P EXISTS procedure_with_argtypes_list opt_drop_behavior
|
||||||
{
|
{
|
||||||
DropStmt *n = makeNode(DropStmt);
|
DropStmt *n = makeNode(DropStmt);
|
||||||
n->removeType = OBJECT_ROUTINE;
|
n->removeType = OBJECT_ROUTINE;
|
||||||
@ -8393,7 +8424,7 @@ RenameStmt: ALTER AGGREGATE aggregate_with_argtypes RENAME TO name
|
|||||||
n->missing_ok = true;
|
n->missing_ok = true;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| ALTER PROCEDURE function_with_argtypes RENAME TO name
|
| ALTER PROCEDURE procedure_with_argtypes RENAME TO name
|
||||||
{
|
{
|
||||||
RenameStmt *n = makeNode(RenameStmt);
|
RenameStmt *n = makeNode(RenameStmt);
|
||||||
n->renameType = OBJECT_PROCEDURE;
|
n->renameType = OBJECT_PROCEDURE;
|
||||||
@ -8411,7 +8442,7 @@ RenameStmt: ALTER AGGREGATE aggregate_with_argtypes RENAME TO name
|
|||||||
n->missing_ok = false;
|
n->missing_ok = false;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| ALTER ROUTINE function_with_argtypes RENAME TO name
|
| ALTER ROUTINE procedure_with_argtypes RENAME TO name
|
||||||
{
|
{
|
||||||
RenameStmt *n = makeNode(RenameStmt);
|
RenameStmt *n = makeNode(RenameStmt);
|
||||||
n->renameType = OBJECT_ROUTINE;
|
n->renameType = OBJECT_ROUTINE;
|
||||||
@ -8822,7 +8853,7 @@ AlterObjectDependsStmt:
|
|||||||
n->remove = $4;
|
n->remove = $4;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| ALTER PROCEDURE function_with_argtypes opt_no DEPENDS ON EXTENSION name
|
| ALTER PROCEDURE procedure_with_argtypes opt_no DEPENDS ON EXTENSION name
|
||||||
{
|
{
|
||||||
AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
|
AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
|
||||||
n->objectType = OBJECT_PROCEDURE;
|
n->objectType = OBJECT_PROCEDURE;
|
||||||
@ -8831,7 +8862,7 @@ AlterObjectDependsStmt:
|
|||||||
n->remove = $4;
|
n->remove = $4;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| ALTER ROUTINE function_with_argtypes opt_no DEPENDS ON EXTENSION name
|
| ALTER ROUTINE procedure_with_argtypes opt_no DEPENDS ON EXTENSION name
|
||||||
{
|
{
|
||||||
AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
|
AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
|
||||||
n->objectType = OBJECT_ROUTINE;
|
n->objectType = OBJECT_ROUTINE;
|
||||||
@ -8962,7 +8993,7 @@ AlterObjectSchemaStmt:
|
|||||||
n->missing_ok = false;
|
n->missing_ok = false;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| ALTER PROCEDURE function_with_argtypes SET SCHEMA name
|
| ALTER PROCEDURE procedure_with_argtypes SET SCHEMA name
|
||||||
{
|
{
|
||||||
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
|
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
|
||||||
n->objectType = OBJECT_PROCEDURE;
|
n->objectType = OBJECT_PROCEDURE;
|
||||||
@ -8971,7 +9002,7 @@ AlterObjectSchemaStmt:
|
|||||||
n->missing_ok = false;
|
n->missing_ok = false;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| ALTER ROUTINE function_with_argtypes SET SCHEMA name
|
| ALTER ROUTINE procedure_with_argtypes SET SCHEMA name
|
||||||
{
|
{
|
||||||
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
|
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
|
||||||
n->objectType = OBJECT_ROUTINE;
|
n->objectType = OBJECT_ROUTINE;
|
||||||
@ -9273,7 +9304,7 @@ AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec
|
|||||||
n->newowner = $9;
|
n->newowner = $9;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| ALTER PROCEDURE function_with_argtypes OWNER TO RoleSpec
|
| ALTER PROCEDURE procedure_with_argtypes OWNER TO RoleSpec
|
||||||
{
|
{
|
||||||
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
|
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
|
||||||
n->objectType = OBJECT_PROCEDURE;
|
n->objectType = OBJECT_PROCEDURE;
|
||||||
@ -9281,7 +9312,7 @@ AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec
|
|||||||
n->newowner = $6;
|
n->newowner = $6;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| ALTER ROUTINE function_with_argtypes OWNER TO RoleSpec
|
| ALTER ROUTINE procedure_with_argtypes OWNER TO RoleSpec
|
||||||
{
|
{
|
||||||
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
|
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
|
||||||
n->objectType = OBJECT_ROUTINE;
|
n->objectType = OBJECT_ROUTINE;
|
||||||
@ -16218,13 +16249,14 @@ check_indirection(List *indirection, core_yyscan_t yyscanner)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* extractArgTypes()
|
/* extractArgTypes()
|
||||||
|
*
|
||||||
* Given a list of FunctionParameter nodes, extract a list of just the
|
* Given a list of FunctionParameter nodes, extract a list of just the
|
||||||
* argument types (TypeNames) for input parameters only. This is what
|
* argument types (TypeNames) for signature parameters only (e.g., only input
|
||||||
* is needed to look up an existing function, which is what is wanted by
|
* parameters for functions). This is what is needed to look up an existing
|
||||||
* the productions that use this call.
|
* function, which is what is wanted by the productions that use this call.
|
||||||
*/
|
*/
|
||||||
static List *
|
static List *
|
||||||
extractArgTypes(List *parameters)
|
extractArgTypes(ObjectType objtype, List *parameters)
|
||||||
{
|
{
|
||||||
List *result = NIL;
|
List *result = NIL;
|
||||||
ListCell *i;
|
ListCell *i;
|
||||||
@ -16233,7 +16265,7 @@ extractArgTypes(List *parameters)
|
|||||||
{
|
{
|
||||||
FunctionParameter *p = (FunctionParameter *) lfirst(i);
|
FunctionParameter *p = (FunctionParameter *) lfirst(i);
|
||||||
|
|
||||||
if (p->mode != FUNC_PARAM_OUT && p->mode != FUNC_PARAM_TABLE)
|
if ((p->mode != FUNC_PARAM_OUT || objtype == OBJECT_PROCEDURE) && p->mode != FUNC_PARAM_TABLE)
|
||||||
result = lappend(result, p->argType);
|
result = lappend(result, p->argType);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@ -16246,7 +16278,7 @@ static List *
|
|||||||
extractAggrArgTypes(List *aggrargs)
|
extractAggrArgTypes(List *aggrargs)
|
||||||
{
|
{
|
||||||
Assert(list_length(aggrargs) == 2);
|
Assert(list_length(aggrargs) == 2);
|
||||||
return extractArgTypes((List *) linitial(aggrargs));
|
return extractArgTypes(OBJECT_AGGREGATE, (List *) linitial(aggrargs));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* makeOrderedSetArgs()
|
/* makeOrderedSetArgs()
|
||||||
|
@ -1233,7 +1233,8 @@ get_func_trftypes(HeapTuple procTup,
|
|||||||
* are set to NULL. You don't get anything if proargnames is NULL.
|
* are set to NULL. You don't get anything if proargnames is NULL.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
get_func_input_arg_names(Datum proargnames, Datum proargmodes,
|
get_func_input_arg_names(char prokind,
|
||||||
|
Datum proargnames, Datum proargmodes,
|
||||||
char ***arg_names)
|
char ***arg_names)
|
||||||
{
|
{
|
||||||
ArrayType *arr;
|
ArrayType *arr;
|
||||||
@ -1291,6 +1292,7 @@ get_func_input_arg_names(Datum proargnames, Datum proargmodes,
|
|||||||
if (argmodes == NULL ||
|
if (argmodes == NULL ||
|
||||||
argmodes[i] == PROARGMODE_IN ||
|
argmodes[i] == PROARGMODE_IN ||
|
||||||
argmodes[i] == PROARGMODE_INOUT ||
|
argmodes[i] == PROARGMODE_INOUT ||
|
||||||
|
(argmodes[i] == PROARGMODE_OUT && prokind == PROKIND_PROCEDURE) ||
|
||||||
argmodes[i] == PROARGMODE_VARIADIC)
|
argmodes[i] == PROARGMODE_VARIADIC)
|
||||||
{
|
{
|
||||||
char *pname = TextDatumGetCString(argnames[i]);
|
char *pname = TextDatumGetCString(argnames[i]);
|
||||||
|
@ -91,7 +91,7 @@ CATALOG(pg_proc,1255,ProcedureRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(81,Proce
|
|||||||
* proargtypes
|
* proargtypes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* parameter types (excludes OUT params) */
|
/* parameter types (excludes OUT params of functions) */
|
||||||
oidvector proargtypes BKI_LOOKUP(pg_type) BKI_FORCE_NOT_NULL;
|
oidvector proargtypes BKI_LOOKUP(pg_type) BKI_FORCE_NOT_NULL;
|
||||||
|
|
||||||
#ifdef CATALOG_VARLEN
|
#ifdef CATALOG_VARLEN
|
||||||
|
@ -172,7 +172,8 @@ extern int get_func_arg_info(HeapTuple procTup,
|
|||||||
Oid **p_argtypes, char ***p_argnames,
|
Oid **p_argtypes, char ***p_argnames,
|
||||||
char **p_argmodes);
|
char **p_argmodes);
|
||||||
|
|
||||||
extern int get_func_input_arg_names(Datum proargnames, Datum proargmodes,
|
extern int get_func_input_arg_names(char prokind,
|
||||||
|
Datum proargnames, Datum proargmodes,
|
||||||
char ***arg_names);
|
char ***arg_names);
|
||||||
|
|
||||||
extern int get_func_trftypes(HeapTuple procTup, Oid **p_trftypes);
|
extern int get_func_trftypes(HeapTuple procTup, Oid **p_trftypes);
|
||||||
|
@ -48,6 +48,24 @@ CALL test_proc6(2, 3, 4);
|
|||||||
6 | 8
|
6 | 8
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
-- OUT parameters
|
||||||
|
CREATE PROCEDURE test_proc9(IN a int, OUT b int)
|
||||||
|
LANGUAGE plperl
|
||||||
|
AS $$
|
||||||
|
my ($a, $b) = @_;
|
||||||
|
elog(NOTICE, "a: $a, b: $b");
|
||||||
|
return { b => $a * 2 };
|
||||||
|
$$;
|
||||||
|
DO $$
|
||||||
|
DECLARE _a int; _b int;
|
||||||
|
BEGIN
|
||||||
|
_a := 10; _b := 30;
|
||||||
|
CALL test_proc9(_a, _b);
|
||||||
|
RAISE NOTICE '_a: %, _b: %', _a, _b;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
NOTICE: a: 10, b:
|
||||||
|
NOTICE: _a: 10, _b: 20
|
||||||
DROP PROCEDURE test_proc1;
|
DROP PROCEDURE test_proc1;
|
||||||
DROP PROCEDURE test_proc2;
|
DROP PROCEDURE test_proc2;
|
||||||
DROP PROCEDURE test_proc3;
|
DROP PROCEDURE test_proc3;
|
||||||
|
@ -51,6 +51,26 @@ $$;
|
|||||||
CALL test_proc6(2, 3, 4);
|
CALL test_proc6(2, 3, 4);
|
||||||
|
|
||||||
|
|
||||||
|
-- OUT parameters
|
||||||
|
|
||||||
|
CREATE PROCEDURE test_proc9(IN a int, OUT b int)
|
||||||
|
LANGUAGE plperl
|
||||||
|
AS $$
|
||||||
|
my ($a, $b) = @_;
|
||||||
|
elog(NOTICE, "a: $a, b: $b");
|
||||||
|
return { b => $a * 2 };
|
||||||
|
$$;
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
DECLARE _a int; _b int;
|
||||||
|
BEGIN
|
||||||
|
_a := 10; _b := 30;
|
||||||
|
CALL test_proc9(_a, _b);
|
||||||
|
RAISE NOTICE '_a: %, _b: %', _a, _b;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
|
||||||
|
|
||||||
DROP PROCEDURE test_proc1;
|
DROP PROCEDURE test_proc1;
|
||||||
DROP PROCEDURE test_proc2;
|
DROP PROCEDURE test_proc2;
|
||||||
DROP PROCEDURE test_proc3;
|
DROP PROCEDURE test_proc3;
|
||||||
|
@ -264,6 +264,25 @@ END
|
|||||||
$$;
|
$$;
|
||||||
ERROR: procedure parameter "c" is an output parameter but corresponding argument is not writable
|
ERROR: procedure parameter "c" is an output parameter but corresponding argument is not writable
|
||||||
CONTEXT: PL/pgSQL function inline_code_block line 5 at CALL
|
CONTEXT: PL/pgSQL function inline_code_block line 5 at CALL
|
||||||
|
-- OUT parameters
|
||||||
|
CREATE PROCEDURE test_proc9(IN a int, OUT b int)
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $$
|
||||||
|
BEGIN
|
||||||
|
RAISE NOTICE 'a: %, b: %', a, b;
|
||||||
|
b := a * 2;
|
||||||
|
END;
|
||||||
|
$$;
|
||||||
|
DO $$
|
||||||
|
DECLARE _a int; _b int;
|
||||||
|
BEGIN
|
||||||
|
_a := 10; _b := 30;
|
||||||
|
CALL test_proc9(_a, _b);
|
||||||
|
RAISE NOTICE '_a: %, _b: %', _a, _b;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
NOTICE: a: 10, b: <NULL>
|
||||||
|
NOTICE: _a: 10, _b: 20
|
||||||
-- transition variable assignment
|
-- transition variable assignment
|
||||||
TRUNCATE test1;
|
TRUNCATE test1;
|
||||||
CREATE FUNCTION triggerfunc1() RETURNS trigger
|
CREATE FUNCTION triggerfunc1() RETURNS trigger
|
||||||
|
@ -458,6 +458,7 @@ do_compile(FunctionCallInfo fcinfo,
|
|||||||
/* Remember arguments in appropriate arrays */
|
/* Remember arguments in appropriate arrays */
|
||||||
if (argmode == PROARGMODE_IN ||
|
if (argmode == PROARGMODE_IN ||
|
||||||
argmode == PROARGMODE_INOUT ||
|
argmode == PROARGMODE_INOUT ||
|
||||||
|
(argmode == PROARGMODE_OUT && function->fn_prokind == PROKIND_PROCEDURE) ||
|
||||||
argmode == PROARGMODE_VARIADIC)
|
argmode == PROARGMODE_VARIADIC)
|
||||||
in_arg_varnos[num_in_args++] = argvariable->dno;
|
in_arg_varnos[num_in_args++] = argvariable->dno;
|
||||||
if (argmode == PROARGMODE_OUT ||
|
if (argmode == PROARGMODE_OUT ||
|
||||||
|
@ -237,6 +237,27 @@ END
|
|||||||
$$;
|
$$;
|
||||||
|
|
||||||
|
|
||||||
|
-- OUT parameters
|
||||||
|
|
||||||
|
CREATE PROCEDURE test_proc9(IN a int, OUT b int)
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $$
|
||||||
|
BEGIN
|
||||||
|
RAISE NOTICE 'a: %, b: %', a, b;
|
||||||
|
b := a * 2;
|
||||||
|
END;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
DECLARE _a int; _b int;
|
||||||
|
BEGIN
|
||||||
|
_a := 10; _b := 30;
|
||||||
|
CALL test_proc9(_a, _b);
|
||||||
|
RAISE NOTICE '_a: %, _b: %', _a, _b;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
|
||||||
|
|
||||||
-- transition variable assignment
|
-- transition variable assignment
|
||||||
|
|
||||||
TRUNCATE test1;
|
TRUNCATE test1;
|
||||||
|
@ -52,6 +52,23 @@ CALL test_proc6(2, 3, 4);
|
|||||||
6 | 8
|
6 | 8
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
-- OUT parameters
|
||||||
|
CREATE PROCEDURE test_proc9(IN a int, OUT b int)
|
||||||
|
LANGUAGE plpythonu
|
||||||
|
AS $$
|
||||||
|
plpy.notice("a: %s, b: %s" % (a, b))
|
||||||
|
return (a * 2,)
|
||||||
|
$$;
|
||||||
|
DO $$
|
||||||
|
DECLARE _a int; _b int;
|
||||||
|
BEGIN
|
||||||
|
_a := 10; _b := 30;
|
||||||
|
CALL test_proc9(_a, _b);
|
||||||
|
RAISE NOTICE '_a: %, _b: %', _a, _b;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
NOTICE: a: 10, b: None
|
||||||
|
NOTICE: _a: 10, _b: 20
|
||||||
DROP PROCEDURE test_proc1;
|
DROP PROCEDURE test_proc1;
|
||||||
DROP PROCEDURE test_proc2;
|
DROP PROCEDURE test_proc2;
|
||||||
DROP PROCEDURE test_proc3;
|
DROP PROCEDURE test_proc3;
|
||||||
|
@ -273,7 +273,7 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
|
|||||||
/* proc->nargs was initialized to 0 above */
|
/* proc->nargs was initialized to 0 above */
|
||||||
for (i = 0; i < total; i++)
|
for (i = 0; i < total; i++)
|
||||||
{
|
{
|
||||||
if (modes[i] != PROARGMODE_OUT &&
|
if ((modes[i] != PROARGMODE_OUT || proc->is_procedure) &&
|
||||||
modes[i] != PROARGMODE_TABLE)
|
modes[i] != PROARGMODE_TABLE)
|
||||||
(proc->nargs)++;
|
(proc->nargs)++;
|
||||||
}
|
}
|
||||||
@ -289,7 +289,7 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
|
|||||||
Form_pg_type argTypeStruct;
|
Form_pg_type argTypeStruct;
|
||||||
|
|
||||||
if (modes &&
|
if (modes &&
|
||||||
(modes[i] == PROARGMODE_OUT ||
|
((modes[i] == PROARGMODE_OUT && !proc->is_procedure) ||
|
||||||
modes[i] == PROARGMODE_TABLE))
|
modes[i] == PROARGMODE_TABLE))
|
||||||
continue; /* skip OUT arguments */
|
continue; /* skip OUT arguments */
|
||||||
|
|
||||||
|
@ -54,6 +54,25 @@ $$;
|
|||||||
CALL test_proc6(2, 3, 4);
|
CALL test_proc6(2, 3, 4);
|
||||||
|
|
||||||
|
|
||||||
|
-- OUT parameters
|
||||||
|
|
||||||
|
CREATE PROCEDURE test_proc9(IN a int, OUT b int)
|
||||||
|
LANGUAGE plpythonu
|
||||||
|
AS $$
|
||||||
|
plpy.notice("a: %s, b: %s" % (a, b))
|
||||||
|
return (a * 2,)
|
||||||
|
$$;
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
DECLARE _a int; _b int;
|
||||||
|
BEGIN
|
||||||
|
_a := 10; _b := 30;
|
||||||
|
CALL test_proc9(_a, _b);
|
||||||
|
RAISE NOTICE '_a: %, _b: %', _a, _b;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
|
||||||
|
|
||||||
DROP PROCEDURE test_proc1;
|
DROP PROCEDURE test_proc1;
|
||||||
DROP PROCEDURE test_proc2;
|
DROP PROCEDURE test_proc2;
|
||||||
DROP PROCEDURE test_proc3;
|
DROP PROCEDURE test_proc3;
|
||||||
|
@ -49,6 +49,23 @@ CALL test_proc6(2, 3, 4);
|
|||||||
6 | 8
|
6 | 8
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
-- OUT parameters
|
||||||
|
CREATE PROCEDURE test_proc9(IN a int, OUT b int)
|
||||||
|
LANGUAGE pltcl
|
||||||
|
AS $$
|
||||||
|
elog NOTICE "a: $1, b: $2"
|
||||||
|
return [list b [expr {$1 * 2}]]
|
||||||
|
$$;
|
||||||
|
DO $$
|
||||||
|
DECLARE _a int; _b int;
|
||||||
|
BEGIN
|
||||||
|
_a := 10; _b := 30;
|
||||||
|
CALL test_proc9(_a, _b);
|
||||||
|
RAISE NOTICE '_a: %, _b: %', _a, _b;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
NOTICE: a: 10, b:
|
||||||
|
NOTICE: _a: 10, _b: 20
|
||||||
DROP PROCEDURE test_proc1;
|
DROP PROCEDURE test_proc1;
|
||||||
DROP PROCEDURE test_proc2;
|
DROP PROCEDURE test_proc2;
|
||||||
DROP PROCEDURE test_proc3;
|
DROP PROCEDURE test_proc3;
|
||||||
|
@ -52,6 +52,25 @@ $$;
|
|||||||
CALL test_proc6(2, 3, 4);
|
CALL test_proc6(2, 3, 4);
|
||||||
|
|
||||||
|
|
||||||
|
-- OUT parameters
|
||||||
|
|
||||||
|
CREATE PROCEDURE test_proc9(IN a int, OUT b int)
|
||||||
|
LANGUAGE pltcl
|
||||||
|
AS $$
|
||||||
|
elog NOTICE "a: $1, b: $2"
|
||||||
|
return [list b [expr {$1 * 2}]]
|
||||||
|
$$;
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
DECLARE _a int; _b int;
|
||||||
|
BEGIN
|
||||||
|
_a := 10; _b := 30;
|
||||||
|
CALL test_proc9(_a, _b);
|
||||||
|
RAISE NOTICE '_a: %, _b: %', _a, _b;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
|
||||||
|
|
||||||
DROP PROCEDURE test_proc1;
|
DROP PROCEDURE test_proc1;
|
||||||
DROP PROCEDURE test_proc2;
|
DROP PROCEDURE test_proc2;
|
||||||
DROP PROCEDURE test_proc3;
|
DROP PROCEDURE test_proc3;
|
||||||
|
@ -146,6 +146,19 @@ AS $$
|
|||||||
SELECT a = b;
|
SELECT a = b;
|
||||||
$$;
|
$$;
|
||||||
CALL ptest7(least('a', 'b'), 'a');
|
CALL ptest7(least('a', 'b'), 'a');
|
||||||
|
-- OUT parameters
|
||||||
|
CREATE PROCEDURE ptest9(OUT a int)
|
||||||
|
LANGUAGE SQL
|
||||||
|
AS $$
|
||||||
|
INSERT INTO cp_test VALUES (1, 'a');
|
||||||
|
SELECT 1;
|
||||||
|
$$;
|
||||||
|
CALL ptest9(NULL);
|
||||||
|
a
|
||||||
|
---
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
-- various error cases
|
-- various error cases
|
||||||
CALL version(); -- error: not a procedure
|
CALL version(); -- error: not a procedure
|
||||||
ERROR: version() is not a procedure
|
ERROR: version() is not a procedure
|
||||||
@ -165,9 +178,6 @@ CREATE PROCEDURE ptestx() LANGUAGE SQL STRICT AS $$ INSERT INTO cp_test VALUES (
|
|||||||
ERROR: invalid attribute in procedure definition
|
ERROR: invalid attribute in procedure definition
|
||||||
LINE 1: CREATE PROCEDURE ptestx() LANGUAGE SQL STRICT AS $$ INSERT I...
|
LINE 1: CREATE PROCEDURE ptestx() LANGUAGE SQL STRICT AS $$ INSERT I...
|
||||||
^
|
^
|
||||||
CREATE PROCEDURE ptestx(OUT a int) LANGUAGE SQL AS $$ INSERT INTO cp_test VALUES (1, 'a') $$;
|
|
||||||
ERROR: procedures cannot have OUT arguments
|
|
||||||
HINT: INOUT arguments are permitted.
|
|
||||||
ALTER PROCEDURE ptest1(text) STRICT;
|
ALTER PROCEDURE ptest1(text) STRICT;
|
||||||
ERROR: invalid attribute in procedure definition
|
ERROR: invalid attribute in procedure definition
|
||||||
LINE 1: ALTER PROCEDURE ptest1(text) STRICT;
|
LINE 1: ALTER PROCEDURE ptest1(text) STRICT;
|
||||||
|
@ -112,6 +112,18 @@ $$;
|
|||||||
CALL ptest7(least('a', 'b'), 'a');
|
CALL ptest7(least('a', 'b'), 'a');
|
||||||
|
|
||||||
|
|
||||||
|
-- OUT parameters
|
||||||
|
|
||||||
|
CREATE PROCEDURE ptest9(OUT a int)
|
||||||
|
LANGUAGE SQL
|
||||||
|
AS $$
|
||||||
|
INSERT INTO cp_test VALUES (1, 'a');
|
||||||
|
SELECT 1;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
CALL ptest9(NULL);
|
||||||
|
|
||||||
|
|
||||||
-- various error cases
|
-- various error cases
|
||||||
|
|
||||||
CALL version(); -- error: not a procedure
|
CALL version(); -- error: not a procedure
|
||||||
@ -119,7 +131,6 @@ 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 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() LANGUAGE SQL STRICT AS $$ INSERT INTO cp_test VALUES (1, 'a') $$;
|
||||||
CREATE PROCEDURE ptestx(OUT a int) LANGUAGE SQL AS $$ INSERT INTO cp_test VALUES (1, 'a') $$;
|
|
||||||
|
|
||||||
ALTER PROCEDURE ptest1(text) STRICT;
|
ALTER PROCEDURE ptest1(text) STRICT;
|
||||||
ALTER FUNCTION ptest1(text) VOLATILE; -- error: not a function
|
ALTER FUNCTION ptest1(text) VOLATILE; -- error: not a function
|
||||||
|
Reference in New Issue
Block a user