diff options
| author | Tom Lane | 2002-08-30 00:28:41 +0000 |
|---|---|---|
| committer | Tom Lane | 2002-08-30 00:28:41 +0000 |
| commit | e107f3a7e3feb7eaef8853ba117465f4f3f8ceed (patch) | |
| tree | d28fb377b7d00f8171c208cc5ad9ceaec7a623ab /src/pl | |
| parent | 82ccb420d5c6f62cec1bf042cf0b6472fabdff42 (diff) | |
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.
Diffstat (limited to 'src/pl')
| -rw-r--r-- | src/pl/plpgsql/src/Makefile | 4 | ||||
| -rw-r--r-- | src/pl/plpgsql/src/gram.y | 122 | ||||
| -rw-r--r-- | src/pl/plpgsql/src/pl_comp.c | 8 | ||||
| -rw-r--r-- | src/pl/plpgsql/src/pl_exec.c | 253 | ||||
| -rw-r--r-- | src/pl/plpgsql/src/pl_funcs.c | 30 | ||||
| -rw-r--r-- | src/pl/plpgsql/src/pl_handler.c | 9 | ||||
| -rw-r--r-- | src/pl/plpgsql/src/plpgsql.h | 25 | ||||
| -rw-r--r-- | src/pl/plpgsql/src/scan.l | 46 |
8 files changed, 374 insertions, 123 deletions
diff --git a/src/pl/plpgsql/src/Makefile b/src/pl/plpgsql/src/Makefile index 88bd611402f..cb5b6c21fa5 100644 --- a/src/pl/plpgsql/src/Makefile +++ b/src/pl/plpgsql/src/Makefile @@ -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 diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y index 75d0a0b07a2..47c8a9c1919 100644 --- a/src/pl/plpgsql/src/gram.y +++ b/src/pl/plpgsql/src/gram.y @@ -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; } } diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index 70f1de470c9..1878c5e396e 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -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) diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 363a04839a4..49c88f3a090 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -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 diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c index 33103a9eb4a..deaa2690e31 100644 --- a/src/pl/plpgsql/src/pl_funcs.c +++ b/src/pl/plpgsql/src/pl_funcs.c @@ -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; @@ -840,6 +838,20 @@ dump_return(PLpgSQL_stmt_return * stmt) } 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) { int i; diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c index 3ca0b13ffd1..17b9cf2e42b 100644 --- a/src/pl/plpgsql/src/pl_handler.c +++ b/src/pl/plpgsql/src/pl_handler.c @@ -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" diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index e991aa96ee7..c81b0a3b1bc 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.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; diff --git a/src/pl/plpgsql/src/scan.l b/src/pl/plpgsql/src/scan.l index 5f0f281ada5..3976b542756 100644 --- a/src/pl/plpgsql/src/scan.l +++ b/src/pl/plpgsql/src/scan.l @@ -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; + } + 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 yylex(); + + return cur_token; } /* @@ -312,4 +347,5 @@ plpgsql_setinput(char *source, int functype) scanner_typereported = 0; have_pushback_token = false; + have_lookahead_token = false; } |
