Commit 2453ea14 authored by Peter Eisentraut's avatar Peter Eisentraut

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: default avatarAndrew Dunstan <andrew.dunstan@2ndquadrant.com>
Reviewed-by: default avatarPavel Stehule <pavel.stehule@gmail.com>
Discussion: https://www.postgresql.org/message-id/flat/2b8490fe-51af-e671-c504-47359dc453c5@2ndquadrant.com
parent e8997420
......@@ -5875,8 +5875,9 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
<para>
An array with the data types of the function arguments. This includes
only input arguments (including <literal>INOUT</literal> and
<literal>VARIADIC</literal> arguments), and thus represents
the call signature of the function.
<literal>VARIADIC</literal> arguments), as well as
<literal>OUT</literal> parameters of procedures, and thus represents
the call signature of the function or procedure.
</para></entry>
</row>
......
......@@ -478,6 +478,14 @@ $$ LANGUAGE plpgsql;
included it, but it would be redundant.
</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>
Output parameters are most useful when returning multiple values.
A trivial example is:
......@@ -489,6 +497,11 @@ BEGIN
prod := x * y;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM sum_n_product(2, 4);
sum | prod
-----+------
6 | 8
</programlisting>
As discussed in <xref linkend="xfunc-output-parameters"/>, this
......@@ -497,6 +510,31 @@ $$ LANGUAGE plpgsql;
<literal>RETURNS record</literal>.
</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>
Another way to declare a <application>PL/pgSQL</application> function
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>,
<literal>INOUT</literal>, or <literal>VARIADIC</literal>.
If omitted, the default is <literal>IN</literal>.
Note that <command>ALTER EXTENSION</command> does not actually pay
any attention to <literal>OUT</literal> arguments, since only the input
arguments are needed to determine the function's identity.
So it is sufficient to list the <literal>IN</literal>, <literal>INOUT</literal>,
and <literal>VARIADIC</literal> arguments.
Note that <command>ALTER EXTENSION</command> does not actually pay any
attention to <literal>OUT</literal> arguments for functions and
aggregates (but not procedures), since only the input arguments are
needed to determine the function's identity. So it is sufficient to
list the <literal>IN</literal>, <literal>INOUT</literal>, and
<literal>VARIADIC</literal> arguments for functions and aggregates.
</para>
</listitem>
</varlistentry>
......
......@@ -81,8 +81,9 @@ ALTER PROCEDURE <replaceable>name</replaceable> [ ( [ [ <replaceable class="para
<listitem>
<para>
The mode of an argument: <literal>IN</literal> or <literal>VARIADIC</literal>.
If omitted, the default is <literal>IN</literal>.
The mode of an argument: <literal>IN</literal>, <literal>OUT</literal>,
<literal>INOUT</literal>, or <literal>VARIADIC</literal>. If omitted,
the default is <literal>IN</literal>.
</para>
</listitem>
</varlistentry>
......
......@@ -178,11 +178,12 @@ COMMENT ON
argument: <literal>IN</literal>, <literal>OUT</literal>,
<literal>INOUT</literal>, or <literal>VARIADIC</literal>.
If omitted, the default is <literal>IN</literal>.
Note that <command>COMMENT</command> does not actually pay
any attention to <literal>OUT</literal> arguments, since only the input
arguments are needed to determine the function's identity.
So it is sufficient to list the <literal>IN</literal>, <literal>INOUT</literal>,
and <literal>VARIADIC</literal> arguments.
Note that <command>COMMENT</command> does not actually pay any attention
to <literal>OUT</literal> arguments for functions and aggregates (but
not procedures), since only the input arguments are needed to determine
the function's identity. So it is sufficient to list the
<literal>IN</literal>, <literal>INOUT</literal>, and
<literal>VARIADIC</literal> arguments for functions and aggregates.
</para>
</listitem>
</varlistentry>
......
......@@ -97,11 +97,9 @@ CREATE [ OR REPLACE ] PROCEDURE
<listitem>
<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,
the default is <literal>IN</literal>. (<literal>OUT</literal>
arguments are currently not supported for procedures. Use
<literal>INOUT</literal> instead.)
the default is <literal>IN</literal>.
</para>
</listitem>
</varlistentry>
......
......@@ -67,8 +67,9 @@ DROP PROCEDURE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [
<listitem>
<para>
The mode of an argument: <literal>IN</literal> or <literal>VARIADIC</literal>.
If omitted, the default is <literal>IN</literal>.
The mode of an argument: <literal>IN</literal>, <literal>OUT</literal>,
<literal>INOUT</literal>, or <literal>VARIADIC</literal>. If omitted,
the default is <literal>IN</literal>.
</para>
</listitem>
</varlistentry>
......
......@@ -127,11 +127,12 @@ SECURITY LABEL [ FOR <replaceable class="parameter">provider</replaceable> ] ON
argument: <literal>IN</literal>, <literal>OUT</literal>,
<literal>INOUT</literal>, or <literal>VARIADIC</literal>.
If omitted, the default is <literal>IN</literal>.
Note that <command>SECURITY LABEL</command> does not actually
pay any attention to <literal>OUT</literal> arguments, since only the input
arguments are needed to determine the function's identity.
So it is sufficient to list the <literal>IN</literal>, <literal>INOUT</literal>,
and <literal>VARIADIC</literal> arguments.
Note that <command>SECURITY LABEL</command> does not actually pay any
attention to <literal>OUT</literal> arguments for functions and
aggregates (but not procedures), since only the input arguments are
needed to determine the function's identity. So it is sufficient to
list the <literal>IN</literal>, <literal>INOUT</literal>, and
<literal>VARIADIC</literal> arguments for functions and aggregates.
</para>
</listitem>
</varlistentry>
......
......@@ -179,6 +179,24 @@ SELECT clean_emp();
</screen>
</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 &lt; 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>
<para>
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>
</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">
<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");
break;
case PROARGMODE_OUT:
if (OidIsValid(variadicType) && prokind == PROKIND_PROCEDURE)
elog(ERROR, "variadic parameter must be last");
break;
case PROARGMODE_TABLE:
/* okay */
break;
......@@ -462,10 +465,12 @@ ProcedureCreate(const char *procedureName,
if (isnull)
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,
&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,
&new_arg_names);
for (j = 0; j < n_old_arg_names; j++)
......
......@@ -194,8 +194,8 @@ interpret_function_parameter_list(ParseState *pstate,
Oid *requiredResultType)
{
int parameterCount = list_length(parameters);
Oid *inTypes;
int inCount = 0;
Oid *sigArgTypes;
int sigArgCount = 0;
Datum *allTypes;
Datum *paramModes;
Datum *paramNames;
......@@ -209,7 +209,7 @@ interpret_function_parameter_list(ParseState *pstate,
*variadicArgType = 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));
paramModes = (Datum *) palloc(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")));
}
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 */
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)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("VARIADIC parameter must be the last input parameter")));
inTypes[inCount++] = toid;
isinput = true;
errmsg("VARIADIC parameter must be the last signature parameter")));
sigArgTypes[sigArgCount++] = toid;
}
/* handle output parameters */
......@@ -429,7 +425,7 @@ interpret_function_parameter_list(ParseState *pstate,
}
/* Now construct the proper outputs as needed */
*parameterTypes = buildoidvector(inTypes, inCount);
*parameterTypes = buildoidvector(sigArgTypes, sigArgCount);
if (outCount > 0 || varCount > 0)
{
......@@ -2067,6 +2063,9 @@ 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;
......@@ -2127,6 +2126,8 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
tp);
nargs = list_length(fexpr->args);
get_func_arg_info(tp, &argtypes, &argnames, &argmodes);
ReleaseSysCache(tp);
/* safety check; see ExecInitFunc() */
......@@ -2156,16 +2157,24 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
i = 0;
foreach(lc, fexpr->args)
{
ExprState *exprstate;
Datum val;
bool isnull;
if (argmodes && argmodes[i] == PROARGMODE_OUT)
{
fcinfo->args[i].value = 0;
fcinfo->args[i].isnull = true;
}
else
{
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++;
}
......
......@@ -259,7 +259,8 @@ prepare_sql_fn_parse_info(HeapTuple procedureTuple,
if (isNull)
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);
/* Paranoia: ignore the result if too few array entries */
......
This diff is collapsed.
......@@ -1233,7 +1233,8 @@ get_func_trftypes(HeapTuple procTup,
* are set to NULL. You don't get anything if proargnames is NULL.
*/
int
get_func_input_arg_names(Datum proargnames, Datum proargmodes,
get_func_input_arg_names(char prokind,
Datum proargnames, Datum proargmodes,
char ***arg_names)
{
ArrayType *arr;
......@@ -1291,6 +1292,7 @@ get_func_input_arg_names(Datum proargnames, Datum proargmodes,
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]);
......
......@@ -91,7 +91,7 @@ CATALOG(pg_proc,1255,ProcedureRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(81,Proce
* proargtypes
*/
/* parameter types (excludes OUT params) */
/* parameter types (excludes OUT params of functions) */
oidvector proargtypes BKI_LOOKUP(pg_type) BKI_FORCE_NOT_NULL;
#ifdef CATALOG_VARLEN
......
......@@ -172,7 +172,8 @@ extern int get_func_arg_info(HeapTuple procTup,
Oid **p_argtypes, char ***p_argnames,
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);
extern int get_func_trftypes(HeapTuple procTup, Oid **p_trftypes);
......
......@@ -48,6 +48,24 @@ CALL test_proc6(2, 3, 4);
6 | 8
(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_proc2;
DROP PROCEDURE test_proc3;
......
......@@ -51,6 +51,26 @@ $$;
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_proc2;
DROP PROCEDURE test_proc3;
......
......@@ -264,6 +264,25 @@ END
$$;
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
-- 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
TRUNCATE test1;
CREATE FUNCTION triggerfunc1() RETURNS trigger
......
......@@ -458,6 +458,7 @@ 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 ||
......
......@@ -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
TRUNCATE test1;
......
......@@ -52,6 +52,23 @@ CALL test_proc6(2, 3, 4);
6 | 8
(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_proc2;
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 */
for (i = 0; i < total; i++)
{
if (modes[i] != PROARGMODE_OUT &&
if ((modes[i] != PROARGMODE_OUT || proc->is_procedure) &&
modes[i] != PROARGMODE_TABLE)
(proc->nargs)++;
}
......@@ -289,7 +289,7 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
Form_pg_type argTypeStruct;
if (modes &&
(modes[i] == PROARGMODE_OUT ||
((modes[i] == PROARGMODE_OUT && !proc->is_procedure) ||
modes[i] == PROARGMODE_TABLE))
continue; /* skip OUT arguments */
......
......@@ -54,6 +54,25 @@ $$;
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_proc2;
DROP PROCEDURE test_proc3;
......
......@@ -49,6 +49,23 @@ CALL test_proc6(2, 3, 4);
6 | 8
(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_proc2;
DROP PROCEDURE test_proc3;
......
......@@ -52,6 +52,25 @@ $$;
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_proc2;
DROP PROCEDURE test_proc3;
......
......@@ -146,6 +146,19 @@ AS $$
SELECT a = b;
$$;
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
CALL version(); -- error: 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
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;
ERROR: invalid attribute in procedure definition
LINE 1: ALTER PROCEDURE ptest1(text) STRICT;
......
......@@ -112,6 +112,18 @@ $$;
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
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 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 FUNCTION ptest1(text) VOLATILE; -- error: not a function
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment