mirror of
https://github.com/postgres/postgres.git
synced 2025-09-03 15:22:11 +03:00
plpgsql does OUT parameters, as per my proposal a few weeks ago.
This commit is contained in:
@@ -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");
|
||||
}
|
||||
|
@@ -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,10 +344,110 @@ 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
|
||||
@@ -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;
|
||||
*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]));
|
||||
}
|
||||
|
||||
deconstruct_array(DatumGetArrayTypeP(argnamesDatum),
|
||||
TEXTOID, -1, false, 'i',
|
||||
&elems, &nelems);
|
||||
/* 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));
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
for (i = 0; i < nargs; i++)
|
||||
result[i] = DatumGetCString(DirectFunctionCall1(textout, elems[i]));
|
||||
|
||||
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));
|
||||
|
||||
/*
|
||||
* 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)
|
||||
/* resolve any polymorphic argument types */
|
||||
plpgsql_resolve_polymorphic_argtypes(procStruct->pronargs,
|
||||
hashkey->argtypes,
|
||||
NULL,
|
||||
fcinfo->flinfo->fn_expr,
|
||||
forValidator,
|
||||
NameStr(procStruct->proname));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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++)
|
||||
@@ -272,36 +270,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
|
||||
*/
|
||||
@@ -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");
|
||||
@@ -566,37 +534,6 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
|
||||
CStringGetDatum(trigdata->tg_trigger->tgargs[i]));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
@@ -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]);
|
||||
|
||||
if (row->rowtupdesc) /* should always be true here */
|
||||
case PLPGSQL_DTYPE_REC:
|
||||
{
|
||||
PLpgSQL_rec *rec = (PLpgSQL_rec *) retvar;
|
||||
|
||||
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;
|
||||
|
||||
|
@@ -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");
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user