1
0
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:
Tom Lane
2002-08-30 00:28:41 +00:00
parent 82ccb420d5
commit e107f3a7e3
21 changed files with 957 additions and 408 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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