summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/utils/fmgr/funcapi.c104
-rw-r--r--src/include/funcapi.h6
-rw-r--r--src/pl/plpgsql/src/gram.y60
-rw-r--r--src/pl/plpgsql/src/pl_comp.c458
-rw-r--r--src/pl/plpgsql/src/pl_exec.c314
-rw-r--r--src/pl/plpgsql/src/pl_funcs.c22
-rw-r--r--src/pl/plpgsql/src/plpgsql.h9
-rw-r--r--src/test/regress/expected/plpgsql.out119
-rw-r--r--src/test/regress/sql/plpgsql.sql85
9 files changed, 839 insertions, 338 deletions
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 160847de1e0..b16cbf9beac 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -7,7 +7,7 @@
* Copyright (c) 2002-2005, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.19 2005/03/31 22:46:16 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.20 2005/04/05 06:22:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -484,6 +484,108 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
}
/*
+ * Given the declared argument types and modes for a function,
+ * replace any polymorphic types (ANYELEMENT/ANYARRAY) with correct data
+ * types deduced from the input arguments. Returns TRUE if able to deduce
+ * all types, FALSE if not. This is the same logic as
+ * resolve_polymorphic_tupdesc, but with a different argument representation.
+ *
+ * argmodes may be NULL, in which case all arguments are assumed to be IN mode.
+ */
+bool
+resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
+ Node *call_expr)
+{
+ bool have_anyelement_result = false;
+ bool have_anyarray_result = false;
+ Oid anyelement_type = InvalidOid;
+ Oid anyarray_type = InvalidOid;
+ int inargno;
+ int i;
+
+ /* First pass: resolve polymorphic inputs, check for outputs */
+ inargno = 0;
+ for (i = 0; i < numargs; i++)
+ {
+ char argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
+
+ switch (argtypes[i])
+ {
+ case ANYELEMENTOID:
+ if (argmode == PROARGMODE_OUT)
+ have_anyelement_result = true;
+ else
+ {
+ if (!OidIsValid(anyelement_type))
+ {
+ anyelement_type = get_call_expr_argtype(call_expr,
+ inargno);
+ if (!OidIsValid(anyelement_type))
+ return false;
+ }
+ argtypes[i] = anyelement_type;
+ }
+ break;
+ case ANYARRAYOID:
+ if (argmode == PROARGMODE_OUT)
+ have_anyarray_result = true;
+ else
+ {
+ if (!OidIsValid(anyarray_type))
+ {
+ anyarray_type = get_call_expr_argtype(call_expr,
+ inargno);
+ if (!OidIsValid(anyarray_type))
+ return false;
+ }
+ argtypes[i] = anyarray_type;
+ }
+ break;
+ default:
+ break;
+ }
+ if (argmode != PROARGMODE_OUT)
+ inargno++;
+ }
+
+ /* Done? */
+ if (!have_anyelement_result && !have_anyarray_result)
+ return true;
+
+ /* If no input polymorphics, parser messed up */
+ if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type))
+ return false;
+
+ /* If needed, deduce one polymorphic type from the other */
+ if (have_anyelement_result && !OidIsValid(anyelement_type))
+ anyelement_type = resolve_generic_type(ANYELEMENTOID,
+ anyarray_type,
+ ANYARRAYOID);
+ if (have_anyarray_result && !OidIsValid(anyarray_type))
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+
+ /* And finally replace the output column types as needed */
+ for (i = 0; i < numargs; i++)
+ {
+ switch (argtypes[i])
+ {
+ case ANYELEMENTOID:
+ argtypes[i] = anyelement_type;
+ break;
+ case ANYARRAYOID:
+ argtypes[i] = anyarray_type;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return true;
+}
+
+/*
* get_type_func_class
* Given the type OID, obtain its TYPEFUNC classification.
*
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index c4d1809948d..2b4bfa28c4d 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -9,7 +9,7 @@
*
* Copyright (c) 2002-2005, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/include/funcapi.h,v 1.16 2005/03/31 22:46:24 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/funcapi.h,v 1.17 2005/04/05 06:22:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -167,6 +167,10 @@ extern TypeFuncClass get_func_result_type(Oid functionId,
Oid *resultTypeId,
TupleDesc *resultTupleDesc);
+extern bool resolve_polymorphic_argtypes(int numargs, Oid *argtypes,
+ char *argmodes,
+ Node *call_expr);
+
extern TupleDesc build_function_result_tupdesc_d(Datum proallargtypes,
Datum proargmodes,
Datum proargnames);
diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y
index 210d1442774..e2b5c7aab14 100644
--- a/src/pl/plpgsql/src/gram.y
+++ b/src/pl/plpgsql/src/gram.y
@@ -4,7 +4,7 @@
* procedural language
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.66 2005/02/22 07:18:24 neilc Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.67 2005/04/05 06:22:16 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -1052,28 +1052,41 @@ stmt_return : K_RETURN lno
PLpgSQL_stmt_return *new;
new = palloc0(sizeof(PLpgSQL_stmt_return));
+ new->cmd_type = PLPGSQL_STMT_RETURN;
+ new->lineno = $2;
new->expr = NULL;
- new->retrecno = -1;
- new->retrowno = -1;
+ new->retvarno = -1;
if (plpgsql_curr_compile->fn_retset)
{
if (yylex() != ';')
yyerror("RETURN cannot have a parameter in function returning set; use RETURN NEXT");
}
+ else if (plpgsql_curr_compile->out_param_varno >= 0)
+ {
+ if (yylex() != ';')
+ yyerror("RETURN cannot have a parameter in function with OUT parameters");
+ new->retvarno = plpgsql_curr_compile->out_param_varno;
+ }
+ else if (plpgsql_curr_compile->fn_rettype == VOIDOID)
+ {
+ if (yylex() != ';')
+ yyerror("function returning void cannot specify RETURN expression");
+ }
else if (plpgsql_curr_compile->fn_retistuple)
{
switch (yylex())
{
case K_NULL:
+ /* we allow this to support RETURN NULL in triggers */
break;
case T_ROW:
- new->retrowno = yylval.row->rowno;
+ new->retvarno = yylval.row->rowno;
break;
case T_RECORD:
- new->retrecno = yylval.rec->recno;
+ new->retvarno = yylval.rec->recno;
break;
default:
@@ -1083,11 +1096,6 @@ stmt_return : K_RETURN lno
if (yylex() != ';')
yyerror("RETURN must specify a record or row variable in function returning tuple");
}
- else if (plpgsql_curr_compile->fn_rettype == VOIDOID)
- {
- if (yylex() != ';')
- yyerror("function returning void cannot specify RETURN expression");
- }
else
{
/*
@@ -1098,9 +1106,6 @@ stmt_return : K_RETURN lno
new->expr = plpgsql_read_expression(';', ";");
}
- new->cmd_type = PLPGSQL_STMT_RETURN;
- new->lineno = $2;
-
$$ = (PLpgSQL_stmt *)new;
}
;
@@ -1115,18 +1120,31 @@ stmt_return_next: K_RETURN_NEXT lno
new = palloc0(sizeof(PLpgSQL_stmt_return_next));
new->cmd_type = PLPGSQL_STMT_RETURN_NEXT;
new->lineno = $2;
+ new->expr = NULL;
+ new->retvarno = -1;
- if (plpgsql_curr_compile->fn_retistuple)
+ if (plpgsql_curr_compile->out_param_varno >= 0)
{
- int tok = yylex();
+ if (yylex() != ';')
+ yyerror("RETURN NEXT cannot have a parameter in function with OUT parameters");
+ new->retvarno = plpgsql_curr_compile->out_param_varno;
+ }
+ else if (plpgsql_curr_compile->fn_retistuple)
+ {
+ switch (yylex())
+ {
+ case T_ROW:
+ new->retvarno = yylval.row->rowno;
+ break;
- if (tok == T_RECORD)
- new->rec = yylval.rec;
- else if (tok == T_ROW)
- new->row = yylval.row;
- else
- yyerror("RETURN NEXT must specify a record or row variable in function returning tuple");
+ case T_RECORD:
+ new->retvarno = yylval.rec->recno;
+ break;
+ default:
+ yyerror("RETURN NEXT must specify a record or row variable in function returning tuple");
+ break;
+ }
if (yylex() != ';')
yyerror("RETURN NEXT must specify a record or row variable in function returning tuple");
}
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 4d23062f6e3..23109c949f7 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.85 2005/03/29 00:17:23 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.86 2005/04/05 06:22:16 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -49,6 +49,7 @@
#include "catalog/pg_class.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
+#include "funcapi.h"
#include "nodes/makefuncs.h"
#include "parser/gramparse.h"
#include "parser/parse_type.h"
@@ -122,13 +123,20 @@ static PLpgSQL_function *do_compile(FunctionCallInfo fcinfo,
HeapTuple procTup,
PLpgSQL_func_hashkey *hashkey,
bool forValidator);
-static char **fetchArgNames(HeapTuple procTup, int nargs);
-static PLpgSQL_row *build_row_var(Oid classOid);
+static int fetchArgInfo(HeapTuple procTup,
+ Oid **p_argtypes, char ***p_argnames,
+ char **p_argmodes);
+static PLpgSQL_row *build_row_from_class(Oid classOid);
+static PLpgSQL_row *build_row_from_vars(PLpgSQL_variable **vars, int numvars);
static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod);
static void compute_function_hashkey(FunctionCallInfo fcinfo,
Form_pg_proc procStruct,
PLpgSQL_func_hashkey *hashkey,
bool forValidator);
+static void plpgsql_resolve_polymorphic_argtypes(int numargs,
+ Oid *argtypes, char *argmodes,
+ Node *call_expr, bool forValidator,
+ const char *proname);
static PLpgSQL_function *plpgsql_HashTableLookup(PLpgSQL_func_hashkey *func_key);
static void plpgsql_HashTableInsert(PLpgSQL_function *function,
PLpgSQL_func_hashkey *func_key);
@@ -259,11 +267,17 @@ do_compile(FunctionCallInfo fcinfo,
PLpgSQL_variable *var;
PLpgSQL_rec *rec;
int i;
- int arg_varnos[FUNC_MAX_ARGS];
ErrorContextCallback plerrcontext;
int parse_rc;
Oid rettypeid;
+ int numargs;
+ int num_in_args;
+ int num_out_args;
+ Oid *argtypes;
char **argnames;
+ char *argmodes;
+ int *in_arg_varnos = NULL;
+ PLpgSQL_variable **out_arg_variables;
MemoryContext func_cxt;
/*
@@ -330,11 +344,111 @@ do_compile(FunctionCallInfo fcinfo,
function->fn_cmin = HeapTupleHeaderGetCmin(procTup->t_data);
function->fn_functype = functype;
function->fn_cxt = func_cxt;
+ function->out_param_varno = -1; /* set up for no OUT param */
switch (functype)
{
case T_FUNCTION:
/*
+ * Fetch info about the procedure's parameters. Allocations
+ * aren't needed permanently, so make them in tmp cxt.
+ *
+ * We also need to resolve any polymorphic input or output
+ * argument types. In validation mode we won't be able to,
+ * so we arbitrarily assume we are dealing with integers.
+ */
+ MemoryContextSwitchTo(compile_tmp_cxt);
+
+ numargs = fetchArgInfo(procTup, &argtypes, &argnames, &argmodes);
+
+ plpgsql_resolve_polymorphic_argtypes(numargs, argtypes, argmodes,
+ fcinfo->flinfo->fn_expr,
+ forValidator,
+ plpgsql_error_funcname);
+
+ in_arg_varnos = (int *) palloc(numargs * sizeof(int));
+ out_arg_variables = (PLpgSQL_variable **) palloc(numargs * sizeof(PLpgSQL_variable *));
+
+ MemoryContextSwitchTo(func_cxt);
+
+ /*
+ * Create the variables for the procedure's parameters.
+ */
+ num_in_args = num_out_args = 0;
+ for (i = 0; i < numargs; i++)
+ {
+ char buf[32];
+ Oid argtypeid = argtypes[i];
+ char argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
+ PLpgSQL_type *argdtype;
+ PLpgSQL_variable *argvariable;
+ int argitemtype;
+
+ /* Create $n name for variable */
+ snprintf(buf, sizeof(buf), "$%d", i + 1);
+
+ /* Create datatype info */
+ argdtype = plpgsql_build_datatype(argtypeid, -1);
+
+ /* Disallow pseudotype argument */
+ /* (note we already replaced ANYARRAY/ANYELEMENT) */
+ /* (build_variable would do this, but wrong message) */
+ if (argdtype->ttype != PLPGSQL_TTYPE_SCALAR &&
+ argdtype->ttype != PLPGSQL_TTYPE_ROW)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("plpgsql functions cannot take type %s",
+ format_type_be(argtypeid))));
+
+ /* Build variable and add to datum list */
+ argvariable = plpgsql_build_variable(buf, 0,
+ argdtype, false);
+
+ if (argvariable->dtype == PLPGSQL_DTYPE_VAR)
+ {
+ argitemtype = PLPGSQL_NSTYPE_VAR;
+ /* input argument vars are forced to be CONSTANT */
+ if (argmode == PROARGMODE_IN)
+ ((PLpgSQL_var *) argvariable)->isconst = true;
+ }
+ else
+ {
+ Assert(argvariable->dtype == PLPGSQL_DTYPE_ROW);
+ argitemtype = PLPGSQL_NSTYPE_ROW;
+ }
+
+ /* Remember arguments in appropriate arrays */
+ if (argmode == PROARGMODE_IN || argmode == PROARGMODE_INOUT)
+ in_arg_varnos[num_in_args++] = argvariable->dno;
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_INOUT)
+ out_arg_variables[num_out_args++] = argvariable;
+
+ /* Add to namespace under the $n name */
+ plpgsql_ns_additem(argitemtype, argvariable->dno, buf);
+
+ /* If there's a name for the argument, make an alias */
+ if (argnames && argnames[i][0] != '\0')
+ plpgsql_ns_additem(argitemtype, argvariable->dno,
+ argnames[i]);
+ }
+
+ /*
+ * If there's just one OUT parameter, out_param_varno points
+ * directly to it. If there's more than one, build a row
+ * that holds all of them.
+ */
+ if (num_out_args == 1)
+ function->out_param_varno = out_arg_variables[0]->dno;
+ else if (num_out_args > 1)
+ {
+ PLpgSQL_row *row = build_row_from_vars(out_arg_variables,
+ num_out_args);
+
+ plpgsql_adddatum((PLpgSQL_datum *) row);
+ function->out_param_varno = row->rowno;
+ }
+
+ /*
* Check for a polymorphic returntype. If found, use the
* actual returntype type from the caller's FuncExpr node, if
* we have one. (In validation mode we arbitrarily assume we
@@ -355,13 +469,15 @@ do_compile(FunctionCallInfo fcinfo,
rettypeid = INT4OID;
}
else
+ {
rettypeid = get_fn_expr_rettype(fcinfo->flinfo);
- if (!OidIsValid(rettypeid))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("could not determine actual return type "
- "for polymorphic function \"%s\"",
- plpgsql_error_funcname)));
+ if (!OidIsValid(rettypeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("could not determine actual return type "
+ "for polymorphic function \"%s\"",
+ plpgsql_error_funcname)));
+ }
}
/*
@@ -410,10 +526,12 @@ do_compile(FunctionCallInfo fcinfo,
/*
* install $0 reference, but only for polymorphic return
- * types
+ * types, and not when the return is specified through an
+ * output parameter.
*/
- if (procStruct->prorettype == ANYARRAYOID ||
- procStruct->prorettype == ANYELEMENTOID)
+ if ((procStruct->prorettype == ANYARRAYOID ||
+ procStruct->prorettype == ANYELEMENTOID) &&
+ num_out_args == 0)
{
(void) plpgsql_build_variable("$0", 0,
build_datatype(typeTup, -1),
@@ -421,72 +539,6 @@ do_compile(FunctionCallInfo fcinfo,
}
}
ReleaseSysCache(typeTup);
-
- /*
- * Create the variables for the procedure's
- * parameters. Allocations aren't needed permanently, so
- * make them in tmp cxt.
- */
- MemoryContextSwitchTo(compile_tmp_cxt);
- argnames = fetchArgNames(procTup, procStruct->pronargs);
- MemoryContextSwitchTo(func_cxt);
-
- for (i = 0; i < procStruct->pronargs; i++)
- {
- char buf[32];
- Oid argtypeid;
- PLpgSQL_type *argdtype;
- PLpgSQL_variable *argvariable;
- int argitemtype;
-
- /* Create $n name for variable */
- snprintf(buf, sizeof(buf), "$%d", i + 1);
-
- /*
- * Since we already did the replacement of polymorphic
- * argument types by actual argument types while computing
- * the hashkey, we can just use those results.
- */
- argtypeid = hashkey->argtypes[i];
- argdtype = plpgsql_build_datatype(argtypeid, -1);
-
- /* Disallow pseudotype argument */
- /* (note we already replaced ANYARRAY/ANYELEMENT) */
- /* (build_variable would do this, but wrong message) */
- if (argdtype->ttype != PLPGSQL_TTYPE_SCALAR &&
- argdtype->ttype != PLPGSQL_TTYPE_ROW)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("plpgsql functions cannot take type %s",
- format_type_be(argtypeid))));
-
- /* Build variable and add to datum list */
- argvariable = plpgsql_build_variable(buf, 0,
- argdtype, false);
-
- if (argvariable->dtype == PLPGSQL_DTYPE_VAR)
- {
- /* argument vars are forced to be CONSTANT (why?) */
- ((PLpgSQL_var *) argvariable)->isconst = true;
- argitemtype = PLPGSQL_NSTYPE_VAR;
- }
- else
- {
- Assert(argvariable->dtype == PLPGSQL_DTYPE_ROW);
- argitemtype = PLPGSQL_NSTYPE_ROW;
- }
-
- /* Remember datum number */
- arg_varnos[i] = argvariable->dno;
-
- /* Add to namespace under the $n name */
- plpgsql_ns_additem(argitemtype, argvariable->dno, buf);
-
- /* If there's a name for the argument, make an alias */
- if (argnames)
- plpgsql_ns_additem(argitemtype, argvariable->dno,
- argnames[i]);
- }
break;
case T_TRIGGER:
@@ -598,7 +650,7 @@ do_compile(FunctionCallInfo fcinfo,
*/
function->fn_nargs = procStruct->pronargs;
for (i = 0; i < function->fn_nargs; i++)
- function->fn_argvarnos[i] = arg_varnos[i];
+ function->fn_argvarnos[i] = in_arg_varnos[i];
function->ndatums = plpgsql_nDatums;
function->datums = palloc(sizeof(PLpgSQL_datum *) * plpgsql_nDatums);
for (i = 0; i < plpgsql_nDatums; i++)
@@ -660,40 +712,96 @@ plpgsql_compile_error_callback(void *arg)
/*
- * Fetch the argument names, if any, from the proargnames field of the
- * pg_proc tuple. Results are palloc'd.
+ * Fetch info about the argument types, names, and IN/OUT modes from the
+ * pg_proc tuple. Return value is the number of arguments.
+ * Other results are palloc'd.
*/
-static char **
-fetchArgNames(HeapTuple procTup, int nargs)
+static int
+fetchArgInfo(HeapTuple procTup, Oid **p_argtypes, char ***p_argnames,
+ char **p_argmodes)
{
- Datum argnamesDatum;
+ Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup);
+ Datum proallargtypes;
+ Datum proargmodes;
+ Datum proargnames;
bool isNull;
+ ArrayType *arr;
+ int numargs;
Datum *elems;
int nelems;
- char **result;
int i;
- if (nargs == 0)
- return NULL;
+ /* First discover the total number of parameters and get their types */
+ proallargtypes = SysCacheGetAttr(PROCOID, procTup,
+ Anum_pg_proc_proallargtypes,
+ &isNull);
+ if (!isNull)
+ {
+ /*
+ * We expect the arrays to be 1-D arrays of the right types; verify
+ * that. For the OID and char arrays, we don't need to use
+ * deconstruct_array() since the array data is just going to look like
+ * a C array of values.
+ */
+ arr = DatumGetArrayTypeP(proallargtypes); /* ensure not toasted */
+ numargs = ARR_DIMS(arr)[0];
+ if (ARR_NDIM(arr) != 1 ||
+ numargs < 0 ||
+ ARR_ELEMTYPE(arr) != OIDOID)
+ elog(ERROR, "proallargtypes is not a 1-D Oid array");
+ Assert(numargs >= procStruct->pronargs);
+ *p_argtypes = (Oid *) palloc(numargs * sizeof(Oid));
+ memcpy(*p_argtypes, ARR_DATA_PTR(arr),
+ numargs * sizeof(Oid));
+ }
+ else
+ {
+ /* If no proallargtypes, use proargtypes */
+ numargs = procStruct->proargtypes.dim1;
+ Assert(numargs == procStruct->pronargs);
+ *p_argtypes = (Oid *) palloc(numargs * sizeof(Oid));
+ memcpy(*p_argtypes, procStruct->proargtypes.values,
+ numargs * sizeof(Oid));
+ }
- argnamesDatum = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_proargnames,
- &isNull);
+ /* Get argument names, if available */
+ proargnames = SysCacheGetAttr(PROCOID, procTup,
+ Anum_pg_proc_proargnames,
+ &isNull);
if (isNull)
- return NULL;
-
- deconstruct_array(DatumGetArrayTypeP(argnamesDatum),
- TEXTOID, -1, false, 'i',
- &elems, &nelems);
-
- if (nelems != nargs) /* should not happen */
- elog(ERROR, "proargnames must have the same number of elements as the function has arguments");
-
- result = (char **) palloc(sizeof(char *) * nargs);
+ *p_argnames = NULL;
+ else
+ {
+ deconstruct_array(DatumGetArrayTypeP(proargnames),
+ TEXTOID, -1, false, 'i',
+ &elems, &nelems);
+ if (nelems != numargs) /* should not happen */
+ elog(ERROR, "proargnames must have the same number of elements as the function has arguments");
+ *p_argnames = (char **) palloc(sizeof(char *) * numargs);
+ for (i = 0; i < numargs; i++)
+ (*p_argnames)[i] = DatumGetCString(DirectFunctionCall1(textout,
+ elems[i]));
+ }
- for (i = 0; i < nargs; i++)
- result[i] = DatumGetCString(DirectFunctionCall1(textout, elems[i]));
+ /* Get argument modes, if available */
+ proargmodes = SysCacheGetAttr(PROCOID, procTup,
+ Anum_pg_proc_proargmodes,
+ &isNull);
+ if (isNull)
+ *p_argmodes = NULL;
+ else
+ {
+ arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */
+ if (ARR_NDIM(arr) != 1 ||
+ ARR_DIMS(arr)[0] != numargs ||
+ ARR_ELEMTYPE(arr) != CHAROID)
+ elog(ERROR, "proargmodes is not a 1-D char array");
+ *p_argmodes = (char *) palloc(numargs * sizeof(char));
+ memcpy(*p_argmodes, ARR_DATA_PTR(arr),
+ numargs * sizeof(char));
+ }
- return result;
+ return numargs;
}
@@ -1449,7 +1557,7 @@ plpgsql_build_variable(const char *refname, int lineno, PLpgSQL_type *dtype,
/* Composite type -- build a row variable */
PLpgSQL_row *row;
- row = build_row_var(dtype->typrelid);
+ row = build_row_from_class(dtype->typrelid);
row->dtype = PLPGSQL_DTYPE_ROW;
row->refname = pstrdup(refname);
@@ -1504,7 +1612,7 @@ plpgsql_build_variable(const char *refname, int lineno, PLpgSQL_type *dtype,
* Build a row-variable data structure given the pg_class OID.
*/
static PLpgSQL_row *
-build_row_var(Oid classOid)
+build_row_from_class(Oid classOid)
{
PLpgSQL_row *row;
Relation rel;
@@ -1589,6 +1697,62 @@ build_row_var(Oid classOid)
return row;
}
+/*
+ * Build a row-variable data structure given the component variables.
+ */
+static PLpgSQL_row *
+build_row_from_vars(PLpgSQL_variable **vars, int numvars)
+{
+ PLpgSQL_row *row;
+ int i;
+
+ row = palloc0(sizeof(PLpgSQL_row));
+ row->dtype = PLPGSQL_DTYPE_ROW;
+ row->rowtupdesc = CreateTemplateTupleDesc(numvars, false);
+ row->nfields = numvars;
+ row->fieldnames = palloc(numvars * sizeof(char *));
+ row->varnos = palloc(numvars * sizeof(int));
+
+ for (i = 0; i < numvars; i++)
+ {
+ PLpgSQL_variable *var = vars[i];
+ Oid typoid = RECORDOID;
+ int32 typmod = -1;
+
+ switch (var->dtype)
+ {
+ case PLPGSQL_DTYPE_VAR:
+ typoid = ((PLpgSQL_var *) var)->datatype->typoid;
+ typmod = ((PLpgSQL_var *) var)->datatype->atttypmod;
+ break;
+
+ case PLPGSQL_DTYPE_REC:
+ break;
+
+ case PLPGSQL_DTYPE_ROW:
+ if (((PLpgSQL_row *) var)->rowtupdesc)
+ {
+ typoid = ((PLpgSQL_row *) var)->rowtupdesc->tdtypeid;
+ typmod = ((PLpgSQL_row *) var)->rowtupdesc->tdtypmod;
+ }
+ break;
+
+ default:
+ elog(ERROR, "unrecognized dtype: %d", var->dtype);
+ }
+
+ row->fieldnames[i] = var->refname;
+ row->varnos[i] = var->dno;
+
+ TupleDescInitEntry(row->rowtupdesc, i+1,
+ var->refname,
+ typoid, typmod,
+ 0);
+ }
+
+ return row;
+}
+
/* ----------
* plpgsql_parse_datatype Scanner found something that should
@@ -1820,8 +1984,6 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
PLpgSQL_func_hashkey *hashkey,
bool forValidator)
{
- int i;
-
/* Make sure any unused bytes of the struct are zero */
MemSet(hashkey, 0, sizeof(PLpgSQL_func_hashkey));
@@ -1840,42 +2002,64 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
hashkey->trigrelOid = RelationGetRelid(trigdata->tg_relation);
}
- /* get the argument types */
- for (i = 0; i < procStruct->pronargs; i++)
+ if (procStruct->pronargs > 0)
{
- Oid argtypeid = procStruct->proargtypes.values[i];
+ /* get the argument types */
+ memcpy(hashkey->argtypes, procStruct->proargtypes.values,
+ procStruct->pronargs * sizeof(Oid));
+
+ /* resolve any polymorphic argument types */
+ plpgsql_resolve_polymorphic_argtypes(procStruct->pronargs,
+ hashkey->argtypes,
+ NULL,
+ fcinfo->flinfo->fn_expr,
+ forValidator,
+ NameStr(procStruct->proname));
+ }
+}
- /*
- * Check for polymorphic arguments. If found, use the actual
- * parameter type from the caller's FuncExpr node, if we have one.
- * (In validation mode we arbitrarily assume we are dealing with
- * integers. This lets us build a valid, if possibly useless,
- * function hashtable entry.)
- *
- * We can support arguments of type ANY the same way as normal
- * polymorphic arguments.
- */
- if (argtypeid == ANYARRAYOID || argtypeid == ANYELEMENTOID ||
- argtypeid == ANYOID)
+/*
+ * This is the same as the standard resolve_polymorphic_argtypes() function,
+ * but with a special case for validation: assume that polymorphic arguments
+ * are integer or integer-array. Also, we go ahead and report the error
+ * if we can't resolve the types.
+ */
+static void
+plpgsql_resolve_polymorphic_argtypes(int numargs,
+ Oid *argtypes, char *argmodes,
+ Node *call_expr, bool forValidator,
+ const char *proname)
+{
+ int i;
+
+ if (!forValidator)
+ {
+ /* normal case, pass to standard routine */
+ if (!resolve_polymorphic_argtypes(numargs, argtypes, argmodes,
+ call_expr))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("could not determine actual argument "
+ "type for polymorphic function \"%s\"",
+ proname)));
+ }
+ else
+ {
+ /* special validation case */
+ for (i = 0; i < numargs; i++)
{
- if (forValidator)
+ switch (argtypes[i])
{
- if (argtypeid == ANYARRAYOID)
- argtypeid = INT4ARRAYOID;
- else
- argtypeid = INT4OID;
+ case ANYELEMENTOID:
+ argtypes[i] = INT4OID;
+ break;
+ case ANYARRAYOID:
+ argtypes[i] = INT4ARRAYOID;
+ break;
+ default:
+ break;
}
- else
- argtypeid = get_fn_expr_argtype(fcinfo->flinfo, i);
- if (!OidIsValid(argtypeid))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("could not determine actual argument "
- "type for polymorphic function \"%s\"",
- NameStr(procStruct->proname))));
}
-
- hashkey->argtypes[i] = argtypeid;
}
}
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 9fbe2897210..4454f2834a1 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.133 2005/03/25 01:45:42 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.134 2005/04/05 06:22:16 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -75,8 +75,6 @@ static PLpgSQL_expr *active_simple_exprs = NULL;
************************************************************/
static void plpgsql_exec_error_callback(void *arg);
static PLpgSQL_datum *copy_plpgsql_datum(PLpgSQL_datum *datum);
-static PLpgSQL_var *copy_var(PLpgSQL_var *var);
-static PLpgSQL_rec *copy_rec(PLpgSQL_rec *rec);
static int exec_stmt_block(PLpgSQL_execstate *estate,
PLpgSQL_stmt_block *block);
@@ -212,11 +210,11 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo)
* Make local execution copies of all the datums
*/
estate.err_text = gettext_noop("during initialization of execution state");
- for (i = 0; i < func->ndatums; i++)
+ for (i = 0; i < estate.ndatums; i++)
estate.datums[i] = copy_plpgsql_datum(func->datums[i]);
/*
- * Store the actual call argument values into the variables
+ * Store the actual call argument values into the appropriate variables
*/
estate.err_text = gettext_noop("while storing call arguments into local variables");
for (i = 0; i < func->fn_nargs; i++)
@@ -273,36 +271,6 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo)
}
/*
- * Initialize the other variables to NULL values for now. The default
- * values are set when the blocks are entered.
- */
- estate.err_text = gettext_noop("while initializing local variables to NULL");
- for (i = estate.found_varno; i < estate.ndatums; i++)
- {
- switch (estate.datums[i]->dtype)
- {
- case PLPGSQL_DTYPE_VAR:
- {
- PLpgSQL_var *var = (PLpgSQL_var *) estate.datums[i];
-
- var->value = 0;
- var->isnull = true;
- var->freeval = false;
- }
- break;
-
- case PLPGSQL_DTYPE_ROW:
- case PLPGSQL_DTYPE_REC:
- case PLPGSQL_DTYPE_RECFIELD:
- case PLPGSQL_DTYPE_ARRAYELEM:
- break;
-
- default:
- elog(ERROR, "unrecognized dtype: %d", func->datums[i]->dtype);
- }
- }
-
- /*
* Set the magic variable FOUND to false
*/
exec_set_found(&estate, false);
@@ -445,7 +413,7 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
* Make local execution copies of all the datums
*/
estate.err_text = gettext_noop("during initialization of execution state");
- for (i = 0; i < func->ndatums; i++)
+ for (i = 0; i < estate.ndatums; i++)
estate.datums[i] = copy_plpgsql_datum(func->datums[i]);
/*
@@ -551,7 +519,7 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
var->freeval = false;
/*
- * Store the actual call argument values into the special execution
+ * Store the trigger argument values into the special execution
* state variables
*/
estate.err_text = gettext_noop("while storing call arguments into local variables");
@@ -567,37 +535,6 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
}
/*
- * Initialize the other variables to NULL values for now. The default
- * values are set when the blocks are entered.
- */
- estate.err_text = gettext_noop("while initializing local variables to NULL");
- for (i = estate.found_varno; i < estate.ndatums; i++)
- {
- switch (estate.datums[i]->dtype)
- {
- case PLPGSQL_DTYPE_VAR:
- {
- PLpgSQL_var *var = (PLpgSQL_var *) estate.datums[i];
-
- var->value = 0;
- var->isnull = true;
- var->freeval = false;
- }
- break;
-
- case PLPGSQL_DTYPE_ROW:
- case PLPGSQL_DTYPE_REC:
- case PLPGSQL_DTYPE_RECFIELD:
- case PLPGSQL_DTYPE_ARRAYELEM:
- case PLPGSQL_DTYPE_TRIGARG:
- break;
-
- default:
- elog(ERROR, "unrecognized dtype: %d", func->datums[i]->dtype);
- }
- }
-
- /*
* Set the magic variable FOUND to false
*/
exec_set_found(&estate, false);
@@ -710,67 +647,65 @@ plpgsql_exec_error_callback(void *arg)
/* ----------
- * Support functions for copying local execution variables
- *
- * NB: this is not a generic copy operation because it assumes that any
- * pass-by-ref original values will live as long as the copy is needed.
+ * Support function for initializing local execution variables
* ----------
*/
static PLpgSQL_datum *
copy_plpgsql_datum(PLpgSQL_datum *datum)
{
- PLpgSQL_datum *result = NULL;
+ PLpgSQL_datum *result;
switch (datum->dtype)
{
case PLPGSQL_DTYPE_VAR:
- result = (PLpgSQL_datum *) copy_var((PLpgSQL_var *) datum);
- break;
+ {
+ PLpgSQL_var *new = palloc(sizeof(PLpgSQL_var));
+
+ memcpy(new, datum, sizeof(PLpgSQL_var));
+ /* Ensure the value is null (possibly not needed?) */
+ new->value = 0;
+ new->isnull = true;
+ new->freeval = false;
+
+ result = (PLpgSQL_datum *) new;
+ }
+ break;
case PLPGSQL_DTYPE_REC:
- result = (PLpgSQL_datum *) copy_rec((PLpgSQL_rec *) datum);
- break;
+ {
+ PLpgSQL_rec *new = palloc(sizeof(PLpgSQL_rec));
+
+ memcpy(new, datum, sizeof(PLpgSQL_rec));
+ /* Ensure the value is null (possibly not needed?) */
+ new->tup = NULL;
+ new->tupdesc = NULL;
+ new->freetup = false;
+ new->freetupdesc = false;
+
+ result = (PLpgSQL_datum *) new;
+ }
+ break;
case PLPGSQL_DTYPE_ROW:
case PLPGSQL_DTYPE_RECFIELD:
case PLPGSQL_DTYPE_ARRAYELEM:
case PLPGSQL_DTYPE_TRIGARG:
+ /*
+ * These datum records are read-only at runtime, so no need
+ * to copy them
+ */
result = datum;
break;
default:
elog(ERROR, "unrecognized dtype: %d", datum->dtype);
+ result = NULL; /* keep compiler quiet */
+ break;
}
return result;
}
-static PLpgSQL_var *
-copy_var(PLpgSQL_var *var)
-{
- PLpgSQL_var *new = palloc(sizeof(PLpgSQL_var));
-
- memcpy(new, var, sizeof(PLpgSQL_var));
- new->freeval = false;
-
- return new;
-}
-
-
-static PLpgSQL_rec *
-copy_rec(PLpgSQL_rec *rec)
-{
- PLpgSQL_rec *new = palloc(sizeof(PLpgSQL_rec));
-
- memcpy(new, rec, sizeof(PLpgSQL_rec));
- new->tup = NULL;
- new->tupdesc = NULL;
- new->freetup = false;
- new->freetupdesc = false;
-
- return new;
-}
-
static bool
exception_matches_conditions(ErrorData *edata, PLpgSQL_condition *cond)
@@ -1682,43 +1617,64 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
if (estate->retisset)
return PLPGSQL_RC_RETURN;
- if (estate->retistuple)
+ /* initialize for null result (possibly a tuple) */
+ estate->retval = (Datum) 0;
+ estate->rettupdesc = NULL;
+ estate->retisnull = true;
+
+ if (stmt->retvarno >= 0)
{
- /* initialize for null result tuple */
- estate->retval = (Datum) 0;
- estate->rettupdesc = NULL;
- estate->retisnull = true;
+ PLpgSQL_datum *retvar = estate->datums[stmt->retvarno];
- if (stmt->retrecno >= 0)
+ switch (retvar->dtype)
{
- PLpgSQL_rec *rec = (PLpgSQL_rec *) (estate->datums[stmt->retrecno]);
-
- if (HeapTupleIsValid(rec->tup))
+ case PLPGSQL_DTYPE_VAR:
{
- estate->retval = (Datum) rec->tup;
- estate->rettupdesc = rec->tupdesc;
- estate->retisnull = false;
+ PLpgSQL_var *var = (PLpgSQL_var *) retvar;
+
+ estate->retval = var->value;
+ estate->retisnull = var->isnull;
+ estate->rettype = var->datatype->typoid;
}
- return PLPGSQL_RC_RETURN;
- }
+ break;
- if (stmt->retrowno >= 0)
- {
- PLpgSQL_row *row = (PLpgSQL_row *) (estate->datums[stmt->retrowno]);
+ case PLPGSQL_DTYPE_REC:
+ {
+ PLpgSQL_rec *rec = (PLpgSQL_rec *) retvar;
- if (row->rowtupdesc) /* should always be true here */
+ if (HeapTupleIsValid(rec->tup))
+ {
+ estate->retval = (Datum) rec->tup;
+ estate->rettupdesc = rec->tupdesc;
+ estate->retisnull = false;
+ }
+ }
+ break;
+
+ case PLPGSQL_DTYPE_ROW:
{
+ PLpgSQL_row *row = (PLpgSQL_row *) retvar;
+
+ Assert(row->rowtupdesc);
estate->retval = (Datum) make_tuple_from_row(estate, row,
- row->rowtupdesc);
- if (estate->retval == (Datum) NULL) /* should not happen */
+ row->rowtupdesc);
+ if (estate->retval == (Datum) NULL) /* should not happen */
elog(ERROR, "row not compatible with its own tupdesc");
estate->rettupdesc = row->rowtupdesc;
estate->retisnull = false;
}
- return PLPGSQL_RC_RETURN;
+ break;
+
+ default:
+ elog(ERROR, "unrecognized dtype: %d", retvar->dtype);
}
- if (stmt->expr != NULL)
+ return PLPGSQL_RC_RETURN;
+ }
+
+ if (stmt->expr != NULL)
+ {
+ if (estate->retistuple)
{
exec_run_select(estate, stmt->expr, 1, NULL);
if (estate->eval_processed > 0)
@@ -1728,24 +1684,23 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
estate->retisnull = false;
}
}
- return PLPGSQL_RC_RETURN;
+ else
+ {
+ /* Normal case for scalar results */
+ estate->retval = exec_eval_expr(estate, stmt->expr,
+ &(estate->retisnull),
+ &(estate->rettype));
+ }
}
if (estate->fn_rettype == VOIDOID)
{
/* Special hack for function returning VOID */
- Assert(stmt->expr == NULL);
+ Assert(stmt->retvarno < 0 && stmt->expr == NULL);
estate->retval = (Datum) 0;
estate->retisnull = false;
estate->rettype = VOIDOID;
}
- else
- {
- /* Normal case for scalar results */
- estate->retval = exec_eval_expr(estate, stmt->expr,
- &(estate->retisnull),
- &(estate->rettype));
- }
return PLPGSQL_RC_RETURN;
}
@@ -1777,37 +1732,78 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
tupdesc = estate->rettupdesc;
natts = tupdesc->natts;
- if (stmt->rec)
+ if (stmt->retvarno >= 0)
{
- PLpgSQL_rec *rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]);
+ PLpgSQL_datum *retvar = estate->datums[stmt->retvarno];
- if (!HeapTupleIsValid(rec->tup))
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("record \"%s\" is not assigned yet",
- rec->refname),
- errdetail("The tuple structure of a not-yet-assigned record is indeterminate.")));
- if (!compatible_tupdesc(tupdesc, rec->tupdesc))
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("wrong record type supplied in RETURN NEXT")));
- tuple = rec->tup;
- }
- else if (stmt->row)
- {
- tuple = make_tuple_from_row(estate, stmt->row, tupdesc);
- if (tuple == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("wrong record type supplied in RETURN NEXT")));
- free_tuple = true;
+ switch (retvar->dtype)
+ {
+ case PLPGSQL_DTYPE_VAR:
+ {
+ PLpgSQL_var *var = (PLpgSQL_var *) retvar;
+ Datum retval = var->value;
+ bool isNull = var->isnull;
+
+ if (natts != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("wrong result type supplied in RETURN NEXT")));
+
+ /* coerce type if needed */
+ retval = exec_simple_cast_value(retval,
+ var->datatype->typoid,
+ tupdesc->attrs[0]->atttypid,
+ tupdesc->attrs[0]->atttypmod,
+ &isNull);
+
+ tuple = heap_form_tuple(tupdesc, &retval, &isNull);
+
+ free_tuple = true;
+ }
+ break;
+
+ case PLPGSQL_DTYPE_REC:
+ {
+ PLpgSQL_rec *rec = (PLpgSQL_rec *) retvar;
+
+ if (!HeapTupleIsValid(rec->tup))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("record \"%s\" is not assigned yet",
+ rec->refname),
+ errdetail("The tuple structure of a not-yet-assigned record is indeterminate.")));
+ if (!compatible_tupdesc(tupdesc, rec->tupdesc))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("wrong record type supplied in RETURN NEXT")));
+ tuple = rec->tup;
+ }
+ break;
+
+ case PLPGSQL_DTYPE_ROW:
+ {
+ PLpgSQL_row *row = (PLpgSQL_row *) retvar;
+
+ tuple = make_tuple_from_row(estate, row, tupdesc);
+ if (tuple == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("wrong record type supplied in RETURN NEXT")));
+ free_tuple = true;
+ }
+ break;
+
+ default:
+ elog(ERROR, "unrecognized dtype: %d", retvar->dtype);
+ tuple = NULL; /* keep compiler quiet */
+ break;
+ }
}
else if (stmt->expr)
{
Datum retval;
bool isNull;
Oid rettype;
- char nullflag;
if (natts != 1)
ereport(ERROR,
@@ -1826,9 +1822,7 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
tupdesc->attrs[0]->atttypmod,
&isNull);
- nullflag = isNull ? 'n' : ' ';
-
- tuple = heap_formtuple(tupdesc, &retval, &nullflag);
+ tuple = heap_form_tuple(tupdesc, &retval, &isNull);
free_tuple = true;
diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c
index 91d7e1558e3..475d2224bff 100644
--- a/src/pl/plpgsql/src/pl_funcs.c
+++ b/src/pl/plpgsql/src/pl_funcs.c
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.39 2005/02/22 07:18:24 neilc Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.40 2005/04/05 06:22:16 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -857,14 +857,12 @@ dump_return(PLpgSQL_stmt_return *stmt)
{
dump_ind();
printf("RETURN ");
- if (stmt->retrecno >= 0)
- printf("record %d", stmt->retrecno);
- else if (stmt->retrowno >= 0)
- printf("row %d", stmt->retrowno);
- else if (stmt->expr == NULL)
- printf("NULL");
- else
+ if (stmt->retvarno >= 0)
+ printf("variable %d", stmt->retvarno);
+ else if (stmt->expr != NULL)
dump_expr(stmt->expr);
+ else
+ printf("NULL");
printf("\n");
}
@@ -873,12 +871,12 @@ dump_return_next(PLpgSQL_stmt_return_next *stmt)
{
dump_ind();
printf("RETURN NEXT ");
- if (stmt->rec != NULL)
- printf("target = %d %s\n", stmt->rec->recno, stmt->rec->refname);
- else if (stmt->row != NULL)
- printf("target = %d %s\n", stmt->row->rowno, stmt->row->refname);
+ if (stmt->retvarno >= 0)
+ printf("variable %d", stmt->retvarno);
else if (stmt->expr != NULL)
dump_expr(stmt->expr);
+ else
+ printf("NULL");
printf("\n");
}
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index df38351a23e..80bcd9e8fa6 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.57 2005/02/22 07:18:24 neilc Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.58 2005/04/05 06:22:16 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -491,17 +491,15 @@ typedef struct
int cmd_type;
int lineno;
PLpgSQL_expr *expr;
- int retrecno;
- int retrowno;
+ int retvarno;
} PLpgSQL_stmt_return;
typedef struct
{ /* RETURN NEXT statement */
int cmd_type;
int lineno;
- PLpgSQL_rec *rec;
- PLpgSQL_row *row;
PLpgSQL_expr *expr;
+ int retvarno;
} PLpgSQL_stmt_return_next;
typedef struct
@@ -572,6 +570,7 @@ typedef struct PLpgSQL_function
int fn_nargs;
int fn_argvarnos[FUNC_MAX_ARGS];
+ int out_param_varno;
int found_varno;
int new_varno;
int old_varno;
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index 7fec95a2794..ee1c52dfa46 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -1739,6 +1739,125 @@ SELECT * FROM test_ret_rec_dyn(5) AS (a int, b numeric, c text);
(1 row)
--
+-- Test handling of OUT parameters, including polymorphic cases
+--
+-- wrong way to do it:
+create function f1(in i int, out j int) returns int as $$
+begin
+ return i+1;
+end$$ language plpgsql;
+ERROR: RETURN cannot have a parameter in function with OUT parameters at or near "i" at character 74
+LINE 3: return i+1;
+ ^
+create function f1(in i int, out j int) as $$
+begin
+ j := i+1;
+ return;
+end$$ language plpgsql;
+select f1(42);
+ f1
+----
+ 43
+(1 row)
+
+select * from f1(42);
+ f1
+----
+ 43
+(1 row)
+
+create or replace function f1(inout i int) as $$
+begin
+ i := i+1;
+ return;
+end$$ language plpgsql;
+select f1(42);
+ f1
+----
+ 43
+(1 row)
+
+select * from f1(42);
+ f1
+----
+ 43
+(1 row)
+
+drop function f1(int);
+create function f1(in i int, out j int) returns setof int as $$
+begin
+ j := i+1;
+ return next;
+ j := i+2;
+ return next;
+ return;
+end$$ language plpgsql;
+select * from f1(42);
+ f1
+----
+ 43
+ 44
+(2 rows)
+
+drop function f1(int);
+create function f1(in i int, out j int, out k text) as $$
+begin
+ j := i;
+ j := j+1;
+ k := 'foo';
+ return;
+end$$ language plpgsql;
+select f1(42);
+ f1
+----------
+ (43,foo)
+(1 row)
+
+select * from f1(42);
+ j | k
+----+-----
+ 43 | foo
+(1 row)
+
+drop function f1(int);
+create function f1(in i int, out j int, out k text) returns setof record as $$
+begin
+ j := i+1;
+ k := 'foo';
+ return next;
+ j := j+1;
+ k := 'foot';
+ return next;
+ return;
+end$$ language plpgsql;
+select * from f1(42);
+ j | k
+----+------
+ 43 | foo
+ 44 | foot
+(2 rows)
+
+drop function f1(int);
+create function dup(in i anyelement, out j anyelement, out k anyarray) as $$
+begin
+ j := i;
+ k := array[j,j];
+ return;
+end$$ language plpgsql;
+select * from dup(42);
+ j | k
+----+---------
+ 42 | {42,42}
+(1 row)
+
+select * from dup('foo'::text);
+ j | k
+-----+-----------
+ foo | {foo,foo}
+(1 row)
+
+drop function dup(anyelement);
+--
-- test PERFORM
--
create table perform_test (
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
index 607b7f28860..e8079615f1e 100644
--- a/src/test/regress/sql/plpgsql.sql
+++ b/src/test/regress/sql/plpgsql.sql
@@ -1561,6 +1561,89 @@ SELECT * FROM test_ret_rec_dyn(1500) AS (a int, b int, c int);
SELECT * FROM test_ret_rec_dyn(5) AS (a int, b numeric, c text);
--
+-- Test handling of OUT parameters, including polymorphic cases
+--
+
+-- wrong way to do it:
+create function f1(in i int, out j int) returns int as $$
+begin
+ return i+1;
+end$$ language plpgsql;
+
+create function f1(in i int, out j int) as $$
+begin
+ j := i+1;
+ return;
+end$$ language plpgsql;
+
+select f1(42);
+select * from f1(42);
+
+create or replace function f1(inout i int) as $$
+begin
+ i := i+1;
+ return;
+end$$ language plpgsql;
+
+select f1(42);
+select * from f1(42);
+
+drop function f1(int);
+
+create function f1(in i int, out j int) returns setof int as $$
+begin
+ j := i+1;
+ return next;
+ j := i+2;
+ return next;
+ return;
+end$$ language plpgsql;
+
+select * from f1(42);
+
+drop function f1(int);
+
+create function f1(in i int, out j int, out k text) as $$
+begin
+ j := i;
+ j := j+1;
+ k := 'foo';
+ return;
+end$$ language plpgsql;
+
+select f1(42);
+select * from f1(42);
+
+drop function f1(int);
+
+create function f1(in i int, out j int, out k text) returns setof record as $$
+begin
+ j := i+1;
+ k := 'foo';
+ return next;
+ j := j+1;
+ k := 'foot';
+ return next;
+ return;
+end$$ language plpgsql;
+
+select * from f1(42);
+
+drop function f1(int);
+
+create function dup(in i anyelement, out j anyelement, out k anyarray) as $$
+begin
+ j := i;
+ k := array[j,j];
+ return;
+end$$ language plpgsql;
+
+select * from dup(42);
+select * from dup('foo'::text);
+
+drop function dup(anyelement);
+
+--
-- test PERFORM
--
@@ -1917,4 +2000,4 @@ end;$$ language plpgsql;
create function void_return_expr() returns void as $$
begin
return 5;
-end;$$ language plpgsql; \ No newline at end of file
+end;$$ language plpgsql;