mirror of
https://github.com/postgres/postgres.git
synced 2025-09-02 04:21:28 +03:00
PL/pgSQL functions can return sets. Neil Conway's patch, modified so
that the functionality is available to anyone via ReturnSetInfo, rather than hard-wiring it to PL/pgSQL.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# Makefile for the plpgsql shared object
|
||||
#
|
||||
# $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Makefile,v 1.20 2001/11/16 16:32:33 petere Exp $
|
||||
# $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Makefile,v 1.21 2002/08/30 00:28:41 tgl Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@@ -78,7 +78,7 @@ endif
|
||||
|
||||
$(srcdir)/pl_scan.c: scan.l
|
||||
ifdef FLEX
|
||||
$(FLEX) -i $(FLEXFLAGS) -Pplpgsql_base_yy -o'$@' $<
|
||||
$(FLEX) $(FLEXFLAGS) -Pplpgsql_base_yy -o'$@' $<
|
||||
else
|
||||
@$(missing) flex $< $@
|
||||
endif
|
||||
|
@@ -4,7 +4,7 @@
|
||||
* procedural language
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.35 2002/08/28 20:46:24 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.36 2002/08/30 00:28:41 tgl Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
@@ -48,7 +48,7 @@ static PLpgSQL_type *read_datatype(int tok);
|
||||
static PLpgSQL_stmt *make_select_stmt(void);
|
||||
static PLpgSQL_stmt *make_fetch_stmt(void);
|
||||
static PLpgSQL_expr *make_tupret_expr(PLpgSQL_row *row);
|
||||
static void check_assignable(PLpgSQL_datum *datum);
|
||||
static void check_assignable(PLpgSQL_datum *datum);
|
||||
|
||||
%}
|
||||
|
||||
@@ -121,8 +121,8 @@ static void check_assignable(PLpgSQL_datum *datum);
|
||||
%type <stmts> proc_sect, proc_stmts, stmt_else, loop_body
|
||||
%type <stmt> proc_stmt, pl_block
|
||||
%type <stmt> stmt_assign, stmt_if, stmt_loop, stmt_while, stmt_exit
|
||||
%type <stmt> stmt_return, stmt_raise, stmt_execsql, stmt_fori
|
||||
%type <stmt> stmt_fors, stmt_select, stmt_perform
|
||||
%type <stmt> stmt_return, stmt_return_next, stmt_raise, stmt_execsql
|
||||
%type <stmt> stmt_fori, stmt_fors, stmt_select, stmt_perform
|
||||
%type <stmt> stmt_dynexecute, stmt_dynfors, stmt_getdiag
|
||||
%type <stmt> stmt_open, stmt_fetch, stmt_close
|
||||
|
||||
@@ -166,6 +166,7 @@ static void check_assignable(PLpgSQL_datum *datum);
|
||||
%token K_IS
|
||||
%token K_LOG
|
||||
%token K_LOOP
|
||||
%token K_NEXT
|
||||
%token K_NOT
|
||||
%token K_NOTICE
|
||||
%token K_NULL
|
||||
@@ -177,6 +178,7 @@ static void check_assignable(PLpgSQL_datum *datum);
|
||||
%token K_RENAME
|
||||
%token K_RESULT_OID
|
||||
%token K_RETURN
|
||||
%token K_RETURN_NEXT
|
||||
%token K_REVERSE
|
||||
%token K_SELECT
|
||||
%token K_THEN
|
||||
@@ -516,10 +518,8 @@ decl_aliasitem : T_WORD
|
||||
|
||||
plpgsql_convert_ident(yytext, &name, 1);
|
||||
if (name[0] != '$')
|
||||
{
|
||||
plpgsql_error_lineno = yylineno;
|
||||
elog(ERROR, "can only alias positional parameters");
|
||||
}
|
||||
yyerror("can only alias positional parameters");
|
||||
|
||||
plpgsql_ns_setlocal(false);
|
||||
nsi = plpgsql_ns_lookup(name, NULL);
|
||||
if (nsi == NULL)
|
||||
@@ -609,14 +609,11 @@ decl_defval : ';'
|
||||
switch (tok)
|
||||
{
|
||||
case 0:
|
||||
plpgsql_error_lineno = lno;
|
||||
elog(ERROR, "unexpected end of file");
|
||||
yyerror("unexpected end of file");
|
||||
case K_NULL:
|
||||
if (yylex() != ';')
|
||||
{
|
||||
plpgsql_error_lineno = lno;
|
||||
elog(ERROR, "expected ; after NULL");
|
||||
}
|
||||
yyerror("expected ; after NULL");
|
||||
|
||||
free(expr);
|
||||
plpgsql_dstring_free(&ds);
|
||||
|
||||
@@ -628,10 +625,8 @@ decl_defval : ';'
|
||||
while ((tok = yylex()) != ';')
|
||||
{
|
||||
if (tok == 0)
|
||||
{
|
||||
plpgsql_error_lineno = lno;
|
||||
elog(ERROR, "unterminated default value");
|
||||
}
|
||||
yyerror("unterminated default value");
|
||||
|
||||
if (plpgsql_SpaceScanned)
|
||||
plpgsql_dstring_append(&ds, " ");
|
||||
plpgsql_dstring_append(&ds, yytext);
|
||||
@@ -663,7 +658,8 @@ proc_sect :
|
||||
|
||||
proc_stmts : proc_stmts proc_stmt
|
||||
{
|
||||
if ($1->stmts_used == $1->stmts_alloc) {
|
||||
if ($1->stmts_used == $1->stmts_alloc)
|
||||
{
|
||||
$1->stmts_alloc *= 2;
|
||||
$1->stmts = realloc($1->stmts, sizeof(PLpgSQL_stmt *) * $1->stmts_alloc);
|
||||
}
|
||||
@@ -708,6 +704,8 @@ proc_stmt : pl_block ';'
|
||||
{ $$ = $1; }
|
||||
| stmt_return
|
||||
{ $$ = $1; }
|
||||
| stmt_return_next
|
||||
{ $$ = $1; }
|
||||
| stmt_raise
|
||||
{ $$ = $1; }
|
||||
| stmt_execsql
|
||||
@@ -1121,45 +1119,73 @@ stmt_exit : K_EXIT lno opt_exitlabel opt_exitcond
|
||||
stmt_return : K_RETURN lno
|
||||
{
|
||||
PLpgSQL_stmt_return *new;
|
||||
PLpgSQL_expr *expr = NULL;
|
||||
int tok;
|
||||
|
||||
new = malloc(sizeof(PLpgSQL_stmt_return));
|
||||
memset(new, 0, sizeof(PLpgSQL_stmt_return));
|
||||
|
||||
if (plpgsql_curr_compile->fn_retistuple)
|
||||
if (plpgsql_curr_compile->fn_retistuple &&
|
||||
!plpgsql_curr_compile->fn_retset)
|
||||
{
|
||||
new->retistuple = true;
|
||||
new->retrecno = -1;
|
||||
switch (tok = yylex())
|
||||
switch (yylex())
|
||||
{
|
||||
case K_NULL:
|
||||
expr = NULL;
|
||||
new->expr = NULL;
|
||||
break;
|
||||
|
||||
case T_ROW:
|
||||
expr = make_tupret_expr(yylval.row);
|
||||
new->expr = make_tupret_expr(yylval.row);
|
||||
break;
|
||||
|
||||
case T_RECORD:
|
||||
new->retrecno = yylval.rec->recno;
|
||||
expr = NULL;
|
||||
new->expr = NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
yyerror("return type mismatch in function returning table row");
|
||||
yyerror("return type mismatch in function returning tuple");
|
||||
break;
|
||||
}
|
||||
if (yylex() != ';')
|
||||
yyerror("expected ';'");
|
||||
} else {
|
||||
new->retistuple = false;
|
||||
expr = plpgsql_read_expression(';', ";");
|
||||
}
|
||||
else
|
||||
new->expr = plpgsql_read_expression(';', ";");
|
||||
|
||||
new->cmd_type = PLPGSQL_STMT_RETURN;
|
||||
new->lineno = $2;
|
||||
new->expr = expr;
|
||||
|
||||
$$ = (PLpgSQL_stmt *)new;
|
||||
}
|
||||
;
|
||||
|
||||
/* FIXME: this syntax needs work, RETURN NEXT breaks stmt_return */
|
||||
stmt_return_next: K_RETURN_NEXT lno
|
||||
{
|
||||
PLpgSQL_stmt_return_next *new;
|
||||
|
||||
new = malloc(sizeof(PLpgSQL_stmt_return_next));
|
||||
memset(new, 0, sizeof(PLpgSQL_stmt_return_next));
|
||||
|
||||
new->cmd_type = PLPGSQL_STMT_RETURN_NEXT;
|
||||
new->lineno = $2;
|
||||
|
||||
if (plpgsql_curr_compile->fn_retistuple)
|
||||
{
|
||||
int tok = yylex();
|
||||
|
||||
if (tok == T_RECORD)
|
||||
new->rec = yylval.rec;
|
||||
else if (tok == T_ROW)
|
||||
new->row = yylval.row;
|
||||
else
|
||||
yyerror("Incorrect argument to RETURN NEXT");
|
||||
|
||||
if (yylex() != ';')
|
||||
yyerror("Expected ';'");
|
||||
}
|
||||
else
|
||||
new->expr = plpgsql_read_expression(';', ";");
|
||||
|
||||
$$ = (PLpgSQL_stmt *)new;
|
||||
}
|
||||
@@ -1226,7 +1252,7 @@ raise_level : K_EXCEPTION
|
||||
}
|
||||
| K_DEBUG
|
||||
{
|
||||
$$ = DEBUG5;
|
||||
$$ = DEBUG1;
|
||||
}
|
||||
;
|
||||
|
||||
@@ -1377,10 +1403,7 @@ stmt_open : K_OPEN lno cursor_varptr
|
||||
cp += strlen(cp) - 1;
|
||||
|
||||
if (*cp != ')')
|
||||
{
|
||||
plpgsql_error_lineno = yylineno;
|
||||
elog(ERROR, "missing )");
|
||||
}
|
||||
yyerror("missing )");
|
||||
*cp = '\0';
|
||||
}
|
||||
else
|
||||
@@ -1433,10 +1456,8 @@ stmt_close : K_CLOSE lno cursor_variable ';'
|
||||
cursor_varptr : T_VARIABLE
|
||||
{
|
||||
if (yylval.variable->dtype != PLPGSQL_DTYPE_VAR)
|
||||
{
|
||||
plpgsql_error_lineno = yylineno;
|
||||
elog(ERROR, "cursor variable must be a simple variable");
|
||||
}
|
||||
yyerror("cursor variable must be a simple variable");
|
||||
|
||||
if (((PLpgSQL_var *) yylval.variable)->datatype->typoid != REFCURSOROID)
|
||||
{
|
||||
plpgsql_error_lineno = yylineno;
|
||||
@@ -1450,10 +1471,8 @@ cursor_varptr : T_VARIABLE
|
||||
cursor_variable : T_VARIABLE
|
||||
{
|
||||
if (yylval.variable->dtype != PLPGSQL_DTYPE_VAR)
|
||||
{
|
||||
plpgsql_error_lineno = yylineno;
|
||||
elog(ERROR, "cursor variable must be a simple variable");
|
||||
}
|
||||
yyerror("cursor variable must be a simple variable");
|
||||
|
||||
if (((PLpgSQL_var *) yylval.variable)->datatype->typoid != REFCURSOROID)
|
||||
{
|
||||
plpgsql_error_lineno = yylineno;
|
||||
@@ -1906,18 +1925,14 @@ make_fetch_stmt(void)
|
||||
break;
|
||||
|
||||
default:
|
||||
plpgsql_error_lineno = yylineno;
|
||||
elog(ERROR, "syntax error at '%s'", yytext);
|
||||
yyerror("syntax error");
|
||||
}
|
||||
|
||||
if (!have_nexttok)
|
||||
tok = yylex();
|
||||
|
||||
if (tok != ';')
|
||||
{
|
||||
plpgsql_error_lineno = yylineno;
|
||||
elog(ERROR, "syntax error at '%s'", yytext);
|
||||
}
|
||||
yyerror("syntax error");
|
||||
|
||||
fetch = malloc(sizeof(PLpgSQL_stmt_select));
|
||||
memset(fetch, 0, sizeof(PLpgSQL_stmt_fetch));
|
||||
@@ -1976,11 +1991,10 @@ check_assignable(PLpgSQL_datum *datum)
|
||||
/* always assignable? */
|
||||
break;
|
||||
case PLPGSQL_DTYPE_TRIGARG:
|
||||
plpgsql_error_lineno = yylineno;
|
||||
elog(ERROR, "cannot assign to tg_argv");
|
||||
yyerror("cannot assign to tg_argv");
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "check_assignable: unexpected datum type");
|
||||
yyerror("check_assignable: unexpected datum type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@
|
||||
* procedural language
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.48 2002/08/28 20:46:24 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.49 2002/08/30 00:28:41 tgl Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
@@ -37,8 +37,6 @@
|
||||
|
||||
#include "plpgsql.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
@@ -52,9 +50,6 @@
|
||||
#include "catalog/pg_class.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "commands/trigger.h"
|
||||
#include "executor/spi.h"
|
||||
#include "fmgr.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "parser/gramparse.h"
|
||||
#include "parser/parse_type.h"
|
||||
@@ -217,6 +212,7 @@ plpgsql_compile(Oid fn_oid, int functype)
|
||||
typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
|
||||
|
||||
/* Disallow pseudotype result, except VOID */
|
||||
/* XXX someday allow RECORD? */
|
||||
if (typeStruct->typtype == 'p')
|
||||
{
|
||||
if (procStruct->prorettype == VOIDOID)
|
||||
|
@@ -3,7 +3,7 @@
|
||||
* procedural language
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.59 2002/08/29 04:12:03 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.60 2002/08/30 00:28:41 tgl Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
@@ -35,12 +35,6 @@
|
||||
*
|
||||
**********************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
@@ -50,10 +44,8 @@
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "commands/trigger.h"
|
||||
#include "executor/spi.h"
|
||||
#include "executor/spi_priv.h"
|
||||
#include "fmgr.h"
|
||||
#include "funcapi.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "tcop/tcopprot.h"
|
||||
@@ -105,6 +97,8 @@ static int exec_stmt_exit(PLpgSQL_execstate * estate,
|
||||
PLpgSQL_stmt_exit * stmt);
|
||||
static int exec_stmt_return(PLpgSQL_execstate * estate,
|
||||
PLpgSQL_stmt_return * stmt);
|
||||
static int exec_stmt_return_next(PLpgSQL_execstate * estate,
|
||||
PLpgSQL_stmt_return_next * stmt);
|
||||
static int exec_stmt_raise(PLpgSQL_execstate * estate,
|
||||
PLpgSQL_stmt_raise * stmt);
|
||||
static int exec_stmt_execsql(PLpgSQL_execstate * estate,
|
||||
@@ -114,8 +108,9 @@ static int exec_stmt_dynexecute(PLpgSQL_execstate * estate,
|
||||
static int exec_stmt_dynfors(PLpgSQL_execstate * estate,
|
||||
PLpgSQL_stmt_dynfors * stmt);
|
||||
|
||||
static void plpgsql_estate_setup(PLpgSQL_execstate * estate,
|
||||
PLpgSQL_function * func);
|
||||
static void plpgsql_estate_setup(PLpgSQL_execstate *estate,
|
||||
PLpgSQL_function *func,
|
||||
ReturnSetInfo *rsi);
|
||||
static void exec_eval_cleanup(PLpgSQL_execstate * estate);
|
||||
|
||||
static void exec_prepare_plan(PLpgSQL_execstate * estate,
|
||||
@@ -150,6 +145,8 @@ static Datum exec_cast_value(Datum value, Oid valtype,
|
||||
int32 reqtypmod,
|
||||
bool *isnull);
|
||||
static void exec_set_found(PLpgSQL_execstate * estate, bool state);
|
||||
static void exec_init_tuple_store(PLpgSQL_execstate *estate);
|
||||
static void exec_set_ret_tupdesc(PLpgSQL_execstate *estate, List *labels);
|
||||
|
||||
|
||||
/* ----------
|
||||
@@ -211,7 +208,7 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo)
|
||||
/*
|
||||
* Setup the execution state
|
||||
*/
|
||||
plpgsql_estate_setup(&estate, func);
|
||||
plpgsql_estate_setup(&estate, func, (ReturnSetInfo *) fcinfo->resultinfo);
|
||||
|
||||
/*
|
||||
* Make local execution copies of all the datums
|
||||
@@ -332,11 +329,36 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo)
|
||||
* We got a return value - process it
|
||||
*/
|
||||
error_info_stmt = NULL;
|
||||
error_info_text = "while casting return value to functions return type";
|
||||
error_info_text = "while casting return value to function's return type";
|
||||
|
||||
fcinfo->isnull = estate.retisnull;
|
||||
|
||||
if (!estate.retisnull)
|
||||
if (estate.retisset)
|
||||
{
|
||||
ReturnSetInfo *rsi = estate.rsi;
|
||||
|
||||
/* Check caller can handle a set result */
|
||||
if (!rsi || !IsA(rsi, ReturnSetInfo) ||
|
||||
(rsi->allowedModes & SFRM_Materialize) == 0)
|
||||
elog(ERROR, "Set-valued function called in context that cannot accept a set");
|
||||
rsi->returnMode = SFRM_Materialize;
|
||||
|
||||
/* If we produced any tuples, send back the result */
|
||||
if (estate.tuple_store)
|
||||
{
|
||||
MemoryContext oldcxt;
|
||||
|
||||
oldcxt = MemoryContextSwitchTo(estate.tuple_store_cxt);
|
||||
tuplestore_donestoring(estate.tuple_store);
|
||||
rsi->setResult = estate.tuple_store;
|
||||
if (estate.rettupdesc)
|
||||
rsi->setDesc = CreateTupleDescCopy(estate.rettupdesc);
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
}
|
||||
estate.retval = (Datum) 0;
|
||||
fcinfo->isnull = true;
|
||||
}
|
||||
else if (!estate.retisnull)
|
||||
{
|
||||
if (estate.retistuple)
|
||||
{
|
||||
@@ -455,7 +477,7 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
|
||||
/*
|
||||
* Setup the execution state
|
||||
*/
|
||||
plpgsql_estate_setup(&estate, func);
|
||||
plpgsql_estate_setup(&estate, func, NULL);
|
||||
|
||||
/*
|
||||
* Make local execution copies of all the datums
|
||||
@@ -642,6 +664,9 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
|
||||
elog(ERROR, "control reaches end of trigger procedure without RETURN");
|
||||
}
|
||||
|
||||
if (estate.retisset)
|
||||
elog(ERROR, "trigger procedure cannot return a set");
|
||||
|
||||
/*
|
||||
* Check that the returned tuple structure has the same attributes,
|
||||
* the relation that fired the trigger has.
|
||||
@@ -862,6 +887,8 @@ exec_stmt(PLpgSQL_execstate * estate, PLpgSQL_stmt * stmt)
|
||||
save_estmt = error_info_stmt;
|
||||
error_info_stmt = stmt;
|
||||
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
|
||||
switch (stmt->cmd_type)
|
||||
{
|
||||
case PLPGSQL_STMT_BLOCK:
|
||||
@@ -908,6 +935,10 @@ exec_stmt(PLpgSQL_execstate * estate, PLpgSQL_stmt * stmt)
|
||||
rc = exec_stmt_return(estate, (PLpgSQL_stmt_return *) stmt);
|
||||
break;
|
||||
|
||||
case PLPGSQL_STMT_RETURN_NEXT:
|
||||
rc = exec_stmt_return_next(estate, (PLpgSQL_stmt_return_next *) stmt);
|
||||
break;
|
||||
|
||||
case PLPGSQL_STMT_RAISE:
|
||||
rc = exec_stmt_raise(estate, (PLpgSQL_stmt_raise *) stmt);
|
||||
break;
|
||||
@@ -1302,13 +1333,10 @@ exec_stmt_fors(PLpgSQL_execstate * estate, PLpgSQL_stmt_fors * stmt)
|
||||
*/
|
||||
if (stmt->rec != NULL)
|
||||
rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]);
|
||||
else if (stmt->row != NULL)
|
||||
row = (PLpgSQL_row *) (estate->datums[stmt->row->rowno]);
|
||||
else
|
||||
{
|
||||
if (stmt->row != NULL)
|
||||
row = (PLpgSQL_row *) (estate->datums[stmt->row->rowno]);
|
||||
else
|
||||
elog(ERROR, "unsupported target in exec_stmt_fors()");
|
||||
}
|
||||
elog(ERROR, "unsupported target in exec_stmt_fors()");
|
||||
|
||||
/*
|
||||
* Open the implicit cursor for the statement and fetch the initial 10
|
||||
@@ -1499,6 +1527,14 @@ exec_stmt_exit(PLpgSQL_execstate * estate, PLpgSQL_stmt_exit * stmt)
|
||||
static int
|
||||
exec_stmt_return(PLpgSQL_execstate * estate, PLpgSQL_stmt_return * stmt)
|
||||
{
|
||||
/*
|
||||
* If processing a set-returning PL/PgSQL function, the final RETURN
|
||||
* indicates that the function is finished producing tuples. The rest
|
||||
* of the work will be done at the top level.
|
||||
*/
|
||||
if (estate->retisset)
|
||||
return PLPGSQL_RC_RETURN;
|
||||
|
||||
if (estate->retistuple)
|
||||
{
|
||||
/* initialize for null result tuple */
|
||||
@@ -1532,13 +1568,155 @@ exec_stmt_return(PLpgSQL_execstate * estate, PLpgSQL_stmt_return * stmt)
|
||||
return PLPGSQL_RC_RETURN;
|
||||
}
|
||||
|
||||
estate->retval = exec_eval_expr(estate, stmt->expr,
|
||||
&(estate->retisnull),
|
||||
&(estate->rettype));
|
||||
if (estate->fn_rettype == VOIDOID)
|
||||
{
|
||||
/* Special hack for function returning VOID */
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Notes:
|
||||
* - the tuple store must be created in a sufficiently long-lived
|
||||
* memory context, as the same store must be used within the executor
|
||||
* after the PL/PgSQL call returns. At present, the code uses
|
||||
* TopTransactionContext.
|
||||
*/
|
||||
static int
|
||||
exec_stmt_return_next(PLpgSQL_execstate *estate,
|
||||
PLpgSQL_stmt_return_next *stmt)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
bool free_tuple = false;
|
||||
|
||||
if (!estate->retisset)
|
||||
elog(ERROR, "Cannot use RETURN NEXT in a non-SETOF function");
|
||||
|
||||
if (estate->tuple_store == NULL)
|
||||
exec_init_tuple_store(estate);
|
||||
|
||||
if (stmt->rec)
|
||||
{
|
||||
PLpgSQL_rec *rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]);
|
||||
tuple = rec->tup;
|
||||
estate->rettupdesc = rec->tupdesc;
|
||||
}
|
||||
else if (stmt->row)
|
||||
{
|
||||
PLpgSQL_var *var;
|
||||
TupleDesc tupdesc;
|
||||
Datum *dvalues;
|
||||
char *nulls;
|
||||
int natts;
|
||||
int i;
|
||||
|
||||
if (!estate->rettupdesc)
|
||||
exec_set_ret_tupdesc(estate, NIL);
|
||||
|
||||
tupdesc = estate->rettupdesc;
|
||||
natts = tupdesc->natts;
|
||||
dvalues = (Datum *) palloc(natts * sizeof(Datum));
|
||||
nulls = (char *) palloc(natts * sizeof(char));
|
||||
|
||||
MemSet(dvalues, 0, natts * sizeof(Datum));
|
||||
MemSet(nulls, 'n', natts);
|
||||
|
||||
for (i = 0; i < stmt->row->nfields; i++)
|
||||
{
|
||||
var = (PLpgSQL_var *) (estate->datums[stmt->row->varnos[i]]);
|
||||
dvalues[i] = var->value;
|
||||
if (!var->isnull)
|
||||
nulls[i] = ' ';
|
||||
}
|
||||
|
||||
tuple = heap_formtuple(tupdesc, dvalues, nulls);
|
||||
|
||||
pfree(dvalues);
|
||||
pfree(nulls);
|
||||
free_tuple = true;
|
||||
}
|
||||
else if (stmt->expr)
|
||||
{
|
||||
Datum retval;
|
||||
bool isNull;
|
||||
char nullflag;
|
||||
|
||||
if (!estate->rettupdesc)
|
||||
exec_set_ret_tupdesc(estate, makeList1(makeString("unused")));
|
||||
|
||||
retval = exec_eval_expr(estate,
|
||||
stmt->expr,
|
||||
&isNull,
|
||||
&(estate->rettype));
|
||||
|
||||
nullflag = isNull ? 'n' : ' ';
|
||||
|
||||
tuple = heap_formtuple(estate->rettupdesc, &retval, &nullflag);
|
||||
|
||||
free_tuple = true;
|
||||
|
||||
exec_eval_cleanup(estate);
|
||||
}
|
||||
else
|
||||
{
|
||||
elog(ERROR, "Blank RETURN NEXT not allowed");
|
||||
tuple = NULL; /* keep compiler quiet */
|
||||
}
|
||||
|
||||
if (HeapTupleIsValid(tuple))
|
||||
{
|
||||
MemoryContext oldcxt;
|
||||
|
||||
oldcxt = MemoryContextSwitchTo(estate->tuple_store_cxt);
|
||||
tuplestore_puttuple(estate->tuple_store, tuple);
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
|
||||
if (free_tuple)
|
||||
heap_freetuple(tuple);
|
||||
}
|
||||
|
||||
return PLPGSQL_RC_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
exec_set_ret_tupdesc(PLpgSQL_execstate *estate, List *labels)
|
||||
{
|
||||
estate->rettype = estate->fn_rettype;
|
||||
estate->rettupdesc = TypeGetTupleDesc(estate->rettype, labels);
|
||||
|
||||
if (!estate->rettupdesc)
|
||||
elog(ERROR, "Could not produce descriptor for rowtype");
|
||||
}
|
||||
|
||||
static void
|
||||
exec_init_tuple_store(PLpgSQL_execstate *estate)
|
||||
{
|
||||
ReturnSetInfo *rsi = estate->rsi;
|
||||
MemoryContext oldcxt;
|
||||
|
||||
/* Check caller can handle a set result */
|
||||
if (!rsi || !IsA(rsi, ReturnSetInfo) ||
|
||||
(rsi->allowedModes & SFRM_Materialize) == 0)
|
||||
elog(ERROR, "Set-valued function called in context that cannot accept a set");
|
||||
|
||||
estate->tuple_store_cxt = rsi->econtext->ecxt_per_query_memory;
|
||||
|
||||
oldcxt = MemoryContextSwitchTo(estate->tuple_store_cxt);
|
||||
estate->tuple_store = tuplestore_begin_heap(true, SortMem);
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
* exec_stmt_raise Build a message and throw it with
|
||||
@@ -1700,21 +1878,29 @@ exec_stmt_raise(PLpgSQL_execstate * estate, PLpgSQL_stmt_raise * stmt)
|
||||
|
||||
|
||||
/* ----------
|
||||
* Initialize an empty estate
|
||||
* Initialize a mostly empty execution state
|
||||
* ----------
|
||||
*/
|
||||
static void
|
||||
plpgsql_estate_setup(PLpgSQL_execstate * estate,
|
||||
PLpgSQL_function * func)
|
||||
plpgsql_estate_setup(PLpgSQL_execstate *estate,
|
||||
PLpgSQL_function *func,
|
||||
ReturnSetInfo *rsi)
|
||||
{
|
||||
estate->retval = (Datum) 0;
|
||||
estate->retisnull = true;
|
||||
estate->rettype = InvalidOid;
|
||||
|
||||
estate->fn_rettype = func->fn_rettype;
|
||||
estate->retistuple = func->fn_retistuple;
|
||||
estate->rettupdesc = NULL;
|
||||
estate->retisset = func->fn_retset;
|
||||
|
||||
estate->rettupdesc = NULL;
|
||||
estate->exitlabel = NULL;
|
||||
|
||||
estate->tuple_store = NULL;
|
||||
estate->tuple_store_cxt = NULL;
|
||||
estate->rsi = rsi;
|
||||
|
||||
estate->trig_nargs = 0;
|
||||
estate->trig_argv = NULL;
|
||||
|
||||
@@ -2099,13 +2285,10 @@ exec_stmt_dynfors(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynfors * stmt)
|
||||
*/
|
||||
if (stmt->rec != NULL)
|
||||
rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]);
|
||||
else if (stmt->row != NULL)
|
||||
row = (PLpgSQL_row *) (estate->datums[stmt->row->rowno]);
|
||||
else
|
||||
{
|
||||
if (stmt->row != NULL)
|
||||
row = (PLpgSQL_row *) (estate->datums[stmt->row->rowno]);
|
||||
else
|
||||
elog(ERROR, "unsupported target in exec_stmt_fors()");
|
||||
}
|
||||
elog(ERROR, "unsupported target in exec_stmt_dynfors()");
|
||||
|
||||
/*
|
||||
* Evaluate the string expression after the EXECUTE keyword. It's
|
||||
|
@@ -3,7 +3,7 @@
|
||||
* procedural language
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.20 2002/08/29 07:22:30 ishii Exp $
|
||||
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.21 2002/08/30 00:28:41 tgl Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
@@ -35,12 +35,6 @@
|
||||
*
|
||||
**********************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "plpgsql.h"
|
||||
@@ -272,9 +266,7 @@ plpgsql_ns_lookup(char *name, char *label)
|
||||
return ns->items[i];
|
||||
}
|
||||
if (ns_localmode)
|
||||
{
|
||||
return NULL; /* name not found in current namespace */
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -461,6 +453,8 @@ plpgsql_stmt_typename(PLpgSQL_stmt * stmt)
|
||||
return "exit";
|
||||
case PLPGSQL_STMT_RETURN:
|
||||
return "return";
|
||||
case PLPGSQL_STMT_RETURN_NEXT:
|
||||
return "return next";
|
||||
case PLPGSQL_STMT_RAISE:
|
||||
return "raise";
|
||||
case PLPGSQL_STMT_EXECSQL:
|
||||
@@ -500,6 +494,7 @@ static void dump_fors(PLpgSQL_stmt_fors * stmt);
|
||||
static void dump_select(PLpgSQL_stmt_select * stmt);
|
||||
static void dump_exit(PLpgSQL_stmt_exit * stmt);
|
||||
static void dump_return(PLpgSQL_stmt_return * stmt);
|
||||
static void dump_return_next(PLpgSQL_stmt_return_next * stmt);
|
||||
static void dump_raise(PLpgSQL_stmt_raise * stmt);
|
||||
static void dump_execsql(PLpgSQL_stmt_execsql * stmt);
|
||||
static void dump_dynexecute(PLpgSQL_stmt_dynexecute * stmt);
|
||||
@@ -556,6 +551,9 @@ dump_stmt(PLpgSQL_stmt * stmt)
|
||||
case PLPGSQL_STMT_RETURN:
|
||||
dump_return((PLpgSQL_stmt_return *) stmt);
|
||||
break;
|
||||
case PLPGSQL_STMT_RETURN_NEXT:
|
||||
dump_return_next((PLpgSQL_stmt_return_next *) stmt);
|
||||
break;
|
||||
case PLPGSQL_STMT_RAISE:
|
||||
dump_raise((PLpgSQL_stmt_raise *) stmt);
|
||||
break;
|
||||
@@ -839,6 +837,20 @@ dump_return(PLpgSQL_stmt_return * stmt)
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void
|
||||
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);
|
||||
else if (stmt->expr != NULL)
|
||||
dump_expr(stmt->expr);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void
|
||||
dump_raise(PLpgSQL_stmt_raise * stmt)
|
||||
{
|
||||
|
@@ -3,7 +3,7 @@
|
||||
* procedural language
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.11 2002/06/15 19:54:24 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.12 2002/08/30 00:28:41 tgl Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
@@ -35,13 +35,6 @@
|
||||
*
|
||||
**********************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "plpgsql.h"
|
||||
#include "pl.tab.h"
|
||||
|
||||
|
@@ -3,7 +3,7 @@
|
||||
* procedural language
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.25 2002/08/08 01:36:05 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.26 2002/08/30 00:28:41 tgl Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
@@ -40,8 +40,10 @@
|
||||
#include "postgres.h"
|
||||
|
||||
#include "fmgr.h"
|
||||
#include "miscadmin.h"
|
||||
#include "executor/spi.h"
|
||||
#include "commands/trigger.h"
|
||||
#include "utils/tuplestore.h"
|
||||
|
||||
/**********************************************************************
|
||||
* Definitions
|
||||
@@ -90,6 +92,7 @@ enum
|
||||
PLPGSQL_STMT_SELECT,
|
||||
PLPGSQL_STMT_EXIT,
|
||||
PLPGSQL_STMT_RETURN,
|
||||
PLPGSQL_STMT_RETURN_NEXT,
|
||||
PLPGSQL_STMT_RAISE,
|
||||
PLPGSQL_STMT_EXECSQL,
|
||||
PLPGSQL_STMT_DYNEXECUTE,
|
||||
@@ -420,11 +423,18 @@ typedef struct
|
||||
{ /* RETURN statement */
|
||||
int cmd_type;
|
||||
int lineno;
|
||||
bool retistuple;
|
||||
PLpgSQL_expr *expr;
|
||||
int retrecno;
|
||||
} PLpgSQL_stmt_return;
|
||||
|
||||
typedef struct
|
||||
{ /* RETURN NEXT statement */
|
||||
int cmd_type;
|
||||
int lineno;
|
||||
PLpgSQL_rec *rec;
|
||||
PLpgSQL_row *row;
|
||||
PLpgSQL_expr *expr;
|
||||
} PLpgSQL_stmt_return_next;
|
||||
|
||||
typedef struct
|
||||
{ /* RAISE statement */
|
||||
@@ -494,12 +504,19 @@ typedef struct
|
||||
{ /* Runtime execution data */
|
||||
Datum retval;
|
||||
bool retisnull;
|
||||
Oid rettype;
|
||||
Oid rettype; /* type of current retval */
|
||||
|
||||
Oid fn_rettype; /* info about declared function rettype */
|
||||
bool retistuple;
|
||||
TupleDesc rettupdesc;
|
||||
bool retisset;
|
||||
|
||||
TupleDesc rettupdesc;
|
||||
char *exitlabel;
|
||||
|
||||
Tuplestorestate *tuple_store; /* SRFs accumulate results here */
|
||||
MemoryContext tuple_store_cxt;
|
||||
ReturnSetInfo *rsi;
|
||||
|
||||
int trig_nargs;
|
||||
Datum *trig_argv;
|
||||
|
||||
|
@@ -4,7 +4,7 @@
|
||||
* procedural language
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Attic/scan.l,v 1.21 2002/08/08 01:36:05 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Attic/scan.l,v 1.22 2002/08/30 00:28:41 tgl Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
@@ -46,6 +46,8 @@ static int scanner_functype;
|
||||
static int scanner_typereported;
|
||||
static int pushback_token;
|
||||
static bool have_pushback_token;
|
||||
static int lookahead_token;
|
||||
static bool have_lookahead_token;
|
||||
|
||||
int plpgsql_SpaceScanned = 0;
|
||||
|
||||
@@ -134,6 +136,7 @@ into { return K_INTO; }
|
||||
is { return K_IS; }
|
||||
log { return K_LOG; }
|
||||
loop { return K_LOOP; }
|
||||
next { return K_NEXT; }
|
||||
not { return K_NOT; }
|
||||
notice { return K_NOTICE; }
|
||||
null { return K_NULL; }
|
||||
@@ -255,18 +258,50 @@ plpgsql_input(char *buf, int *result, int max)
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the yylex routine called from outside. It exists to provide
|
||||
* a token pushback facility.
|
||||
* This is the yylex routine called from outside. It exists to provide
|
||||
* a pushback facility, as well as to allow us to parse syntax that
|
||||
* requires more than one token of lookahead.
|
||||
*/
|
||||
int
|
||||
plpgsql_yylex(void)
|
||||
{
|
||||
int cur_token;
|
||||
|
||||
if (have_pushback_token)
|
||||
{
|
||||
have_pushback_token = false;
|
||||
return pushback_token;
|
||||
cur_token = pushback_token;
|
||||
}
|
||||
return yylex();
|
||||
else if (have_lookahead_token)
|
||||
{
|
||||
have_lookahead_token = false;
|
||||
cur_token = lookahead_token;
|
||||
}
|
||||
else
|
||||
cur_token = yylex();
|
||||
|
||||
/* Do we need to look ahead for a possible multiword token? */
|
||||
switch (cur_token)
|
||||
{
|
||||
/* RETURN NEXT must be reduced to a single token */
|
||||
case K_RETURN:
|
||||
if (!have_lookahead_token)
|
||||
{
|
||||
lookahead_token = yylex();
|
||||
have_lookahead_token = true;
|
||||
}
|
||||
if (lookahead_token == K_NEXT)
|
||||
{
|
||||
have_lookahead_token = false;
|
||||
cur_token = K_RETURN_NEXT;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return cur_token;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -312,4 +347,5 @@ plpgsql_setinput(char *source, int functype)
|
||||
scanner_typereported = 0;
|
||||
|
||||
have_pushback_token = false;
|
||||
have_lookahead_token = false;
|
||||
}
|
||||
|
Reference in New Issue
Block a user