1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-30 11:03:19 +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:
Peter Eisentraut
2020-10-05 09:09:09 +02:00
parent e899742081
commit 2453ea1422
28 changed files with 416 additions and 93 deletions

View File

@ -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++)

View File

@ -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++;
}

View File

@ -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 */

View File

@ -166,7 +166,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(List *parameters);
static List *extractArgTypes(ObjectType objtype, List *parameters);
static List *extractAggrArgTypes(List *aggrargs);
static List *makeOrderedSetArgs(List *directargs, List *orderedargs,
core_yyscan_t yyscanner);
@ -375,8 +375,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
%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list
%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 <ival> defacl_privilege_target
%type <defelt> DefACLOption
%type <list> DefACLOptionList
@ -4623,7 +4623,7 @@ AlterExtensionContentsStmt:
n->object = (Node *) lcons(makeString($9), $7);
$$ = (Node *)n;
}
| ALTER EXTENSION name add_drop PROCEDURE function_with_argtypes
| ALTER EXTENSION name add_drop PROCEDURE procedure_with_argtypes
{
AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt);
n->extname = $3;
@ -4632,7 +4632,7 @@ AlterExtensionContentsStmt:
n->object = (Node *) $6;
$$ = (Node *)n;
}
| ALTER EXTENSION name add_drop ROUTINE function_with_argtypes
| ALTER EXTENSION name add_drop ROUTINE procedure_with_argtypes
{
AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt);
n->extname = $3;
@ -6365,7 +6365,7 @@ CommentStmt:
n->comment = $8;
$$ = (Node *) n;
}
| COMMENT ON PROCEDURE function_with_argtypes IS comment_text
| COMMENT ON PROCEDURE procedure_with_argtypes IS comment_text
{
CommentStmt *n = makeNode(CommentStmt);
n->objtype = OBJECT_PROCEDURE;
@ -6373,7 +6373,7 @@ CommentStmt:
n->comment = $6;
$$ = (Node *) n;
}
| COMMENT ON ROUTINE function_with_argtypes IS comment_text
| COMMENT ON ROUTINE procedure_with_argtypes IS comment_text
{
CommentStmt *n = makeNode(CommentStmt);
n->objtype = OBJECT_ROUTINE;
@ -6519,7 +6519,7 @@ SecLabelStmt:
n->label = $9;
$$ = (Node *) n;
}
| SECURITY LABEL opt_provider ON PROCEDURE function_with_argtypes
| SECURITY LABEL opt_provider ON PROCEDURE procedure_with_argtypes
IS security_label
{
SecLabelStmt *n = makeNode(SecLabelStmt);
@ -6880,7 +6880,7 @@ privilege_target:
n->objs = $2;
$$ = n;
}
| PROCEDURE function_with_argtypes_list
| PROCEDURE procedure_with_argtypes_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
n->targtype = ACL_TARGET_OBJECT;
@ -6888,7 +6888,7 @@ privilege_target:
n->objs = $2;
$$ = n;
}
| ROUTINE function_with_argtypes_list
| ROUTINE procedure_with_argtypes_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
n->targtype = ACL_TARGET_OBJECT;
@ -7409,20 +7409,33 @@ 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($2);
n->objargs = extractArgTypes(OBJECT_FUNCTION, $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)));
@ -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
* defaults in CREATE FUNCTION, not in ALTER etc.
@ -7824,7 +7855,7 @@ AlterFunctionStmt:
n->actions = $4;
$$ = (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);
n->objtype = OBJECT_PROCEDURE;
@ -7832,7 +7863,7 @@ AlterFunctionStmt:
n->actions = $4;
$$ = (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);
n->objtype = OBJECT_ROUTINE;
@ -7888,7 +7919,7 @@ RemoveFuncStmt:
n->concurrent = false;
$$ = (Node *)n;
}
| DROP PROCEDURE function_with_argtypes_list opt_drop_behavior
| DROP PROCEDURE procedure_with_argtypes_list opt_drop_behavior
{
DropStmt *n = makeNode(DropStmt);
n->removeType = OBJECT_PROCEDURE;
@ -7898,7 +7929,7 @@ RemoveFuncStmt:
n->concurrent = false;
$$ = (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);
n->removeType = OBJECT_PROCEDURE;
@ -7908,7 +7939,7 @@ RemoveFuncStmt:
n->concurrent = false;
$$ = (Node *)n;
}
| DROP ROUTINE function_with_argtypes_list opt_drop_behavior
| DROP ROUTINE procedure_with_argtypes_list opt_drop_behavior
{
DropStmt *n = makeNode(DropStmt);
n->removeType = OBJECT_ROUTINE;
@ -7918,7 +7949,7 @@ RemoveFuncStmt:
n->concurrent = false;
$$ = (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);
n->removeType = OBJECT_ROUTINE;
@ -8393,7 +8424,7 @@ RenameStmt: ALTER AGGREGATE aggregate_with_argtypes RENAME TO name
n->missing_ok = true;
$$ = (Node *)n;
}
| ALTER PROCEDURE function_with_argtypes RENAME TO name
| ALTER PROCEDURE procedure_with_argtypes RENAME TO name
{
RenameStmt *n = makeNode(RenameStmt);
n->renameType = OBJECT_PROCEDURE;
@ -8411,7 +8442,7 @@ RenameStmt: ALTER AGGREGATE aggregate_with_argtypes RENAME TO name
n->missing_ok = false;
$$ = (Node *)n;
}
| ALTER ROUTINE function_with_argtypes RENAME TO name
| ALTER ROUTINE procedure_with_argtypes RENAME TO name
{
RenameStmt *n = makeNode(RenameStmt);
n->renameType = OBJECT_ROUTINE;
@ -8822,7 +8853,7 @@ AlterObjectDependsStmt:
n->remove = $4;
$$ = (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);
n->objectType = OBJECT_PROCEDURE;
@ -8831,7 +8862,7 @@ AlterObjectDependsStmt:
n->remove = $4;
$$ = (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);
n->objectType = OBJECT_ROUTINE;
@ -8962,7 +8993,7 @@ AlterObjectSchemaStmt:
n->missing_ok = false;
$$ = (Node *)n;
}
| ALTER PROCEDURE function_with_argtypes SET SCHEMA name
| ALTER PROCEDURE procedure_with_argtypes SET SCHEMA name
{
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
n->objectType = OBJECT_PROCEDURE;
@ -8971,7 +9002,7 @@ AlterObjectSchemaStmt:
n->missing_ok = false;
$$ = (Node *)n;
}
| ALTER ROUTINE function_with_argtypes SET SCHEMA name
| ALTER ROUTINE procedure_with_argtypes SET SCHEMA name
{
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
n->objectType = OBJECT_ROUTINE;
@ -9273,7 +9304,7 @@ AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec
n->newowner = $9;
$$ = (Node *)n;
}
| ALTER PROCEDURE function_with_argtypes OWNER TO RoleSpec
| ALTER PROCEDURE procedure_with_argtypes OWNER TO RoleSpec
{
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
n->objectType = OBJECT_PROCEDURE;
@ -9281,7 +9312,7 @@ AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec
n->newowner = $6;
$$ = (Node *)n;
}
| ALTER ROUTINE function_with_argtypes OWNER TO RoleSpec
| ALTER ROUTINE procedure_with_argtypes OWNER TO RoleSpec
{
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
n->objectType = OBJECT_ROUTINE;
@ -16218,13 +16249,14 @@ 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 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.
* 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.
*/
static List *
extractArgTypes(List *parameters)
extractArgTypes(ObjectType objtype, List *parameters)
{
List *result = NIL;
ListCell *i;
@ -16233,7 +16265,7 @@ extractArgTypes(List *parameters)
{
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);
}
return result;
@ -16246,7 +16278,7 @@ static List *
extractAggrArgTypes(List *aggrargs)
{
Assert(list_length(aggrargs) == 2);
return extractArgTypes((List *) linitial(aggrargs));
return extractArgTypes(OBJECT_AGGREGATE, (List *) linitial(aggrargs));
}
/* makeOrderedSetArgs()

View File

@ -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]);

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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 ||

View File

@ -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;

View File

@ -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;

View File

@ -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 */

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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