summaryrefslogtreecommitdiff
path: root/src/pl
diff options
context:
space:
mode:
authorTom Lane2002-08-30 00:28:41 +0000
committerTom Lane2002-08-30 00:28:41 +0000
commite107f3a7e3feb7eaef8853ba117465f4f3f8ceed (patch)
treed28fb377b7d00f8171c208cc5ad9ceaec7a623ab /src/pl
parent82ccb420d5c6f62cec1bf042cf0b6472fabdff42 (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/Makefile4
-rw-r--r--src/pl/plpgsql/src/gram.y122
-rw-r--r--src/pl/plpgsql/src/pl_comp.c8
-rw-r--r--src/pl/plpgsql/src/pl_exec.c253
-rw-r--r--src/pl/plpgsql/src/pl_funcs.c30
-rw-r--r--src/pl/plpgsql/src/pl_handler.c9
-rw-r--r--src/pl/plpgsql/src/plpgsql.h25
-rw-r--r--src/pl/plpgsql/src/scan.l46
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;
}