From 470a1048ec145fe16b5baea56b9aef93f9878747 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sun, 1 Sep 2002 16:28:06 +0000 Subject: [PATCH] plpgsql functions can return RECORD, per Neil Conway. --- doc/src/sgml/plpgsql.sgml | 4 +- doc/src/sgml/xfunc.sgml | 23 +++++++++++- src/pl/plpgsql/src/gram.y | 3 +- src/pl/plpgsql/src/pl_comp.c | 14 +++---- src/pl/plpgsql/src/pl_exec.c | 8 ++-- src/test/regress/expected/plpgsql.out | 53 +++++++++++++++++++++++++++ src/test/regress/sql/plpgsql.sql | 35 ++++++++++++++++++ 7 files changed, 123 insertions(+), 17 deletions(-) diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml index 4da3f3c040..998ed72089 100644 --- a/doc/src/sgml/plpgsql.sgml +++ b/doc/src/sgml/plpgsql.sgml @@ -1,5 +1,5 @@ @@ -538,8 +538,6 @@ END; Note that RECORD is not a true data type, only a placeholder. - Thus, for example, one cannot declare a function returning - RECORD. diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml index 9a7b79f0dd..3999bf8176 100644 --- a/doc/src/sgml/xfunc.sgml +++ b/doc/src/sgml/xfunc.sgml @@ -1,5 +1,5 @@ @@ -2119,6 +2119,27 @@ SELECT * FROM vw_getfoo; are all valid statements. + + + In some cases it is useful to define table functions that can return + different column sets depending on how they are invoked. To support this, + the table function can be declared as returning the pseudo-type + record. When such a function is used in a query, the expected + row structure must be specified in the query itself, so that the system + can know how to parse and plan the query. Consider this example: + +SELECT * +FROM dblink('dbname=template1', 'select proname, prosrc from pg_proc') + AS t1(proname name, prosrc text) +WHERE proname LIKE 'bytea%'; + + The dblink function executes a remote query (see + contrib/dblink). It is declared to return record + since it might be used for any kind of query. The actual column set + must be specified in the calling query so that the parser knows, for + example, what * should expand to. + + diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y index 47c8a9c191..7c62d66d24 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.36 2002/08/30 00:28:41 tgl Exp $ + * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.37 2002/09/01 16:28:06 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -1159,7 +1159,6 @@ stmt_return : K_RETURN lno } ; -/* FIXME: this syntax needs work, RETURN NEXT breaks stmt_return */ stmt_return_next: K_RETURN_NEXT lno { PLpgSQL_stmt_return_next *new; diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index 1878c5e396..2ee06acbe0 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.49 2002/08/30 00:28:41 tgl Exp $ + * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.50 2002/09/01 16:28:06 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -211,11 +211,11 @@ plpgsql_compile(Oid fn_oid, int functype) procStruct->prorettype); typeStruct = (Form_pg_type) GETSTRUCT(typeTup); - /* Disallow pseudotype result, except VOID */ - /* XXX someday allow RECORD? */ + /* Disallow pseudotype result, except VOID or RECORD */ if (typeStruct->typtype == 'p') { - if (procStruct->prorettype == VOIDOID) + if (procStruct->prorettype == VOIDOID || + procStruct->prorettype == RECORDOID) /* okay */; else if (procStruct->prorettype == TRIGGEROID || procStruct->prorettype == OPAQUEOID) @@ -227,7 +227,8 @@ plpgsql_compile(Oid fn_oid, int functype) format_type_be(procStruct->prorettype)); } - if (typeStruct->typrelid != InvalidOid) + if (typeStruct->typrelid != InvalidOid || + procStruct->prorettype == RECORDOID) function->fn_retistuple = true; else { @@ -486,8 +487,7 @@ plpgsql_compile(Oid fn_oid, int functype) } /* - * Create the magic found variable indicating if the last FOR or - * SELECT statement returned data + * Create the magic FOUND variable. */ var = malloc(sizeof(PLpgSQL_var)); memset(var, 0, sizeof(PLpgSQL_var)); diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 6f52f2c048..7b7b1c1555 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.61 2002/08/30 23:59:46 tgl Exp $ + * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.62 2002/09/01 16:28:06 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -1588,9 +1588,9 @@ static int exec_stmt_return_next(PLpgSQL_execstate *estate, PLpgSQL_stmt_return_next *stmt) { - TupleDesc tupdesc; - int natts; - HeapTuple tuple; + TupleDesc tupdesc; + int natts; + HeapTuple tuple; bool free_tuple = false; if (!estate->retisset) diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out index 583543262e..e18a1d556a 100644 --- a/src/test/regress/expected/plpgsql.out +++ b/src/test/regress/expected/plpgsql.out @@ -1680,3 +1680,56 @@ select * from test_ret_set_scalar(1,10); 11 (10 rows) +create function test_ret_set_rec_dyn(int) returns setof record as ' +DECLARE + retval RECORD; +BEGIN + IF $1 > 10 THEN + SELECT INTO retval 5, 10, 15; + RETURN NEXT retval; + RETURN NEXT retval; + ELSE + SELECT INTO retval 50, 5::numeric, ''xxx''::text; + RETURN NEXT retval; + RETURN NEXT retval; + END IF; + RETURN; +END;' language 'plpgsql'; +SELECT * FROM test_ret_set_rec_dyn(1500) AS (a int, b int, c int); + a | b | c +---+----+---- + 5 | 10 | 15 + 5 | 10 | 15 +(2 rows) + +SELECT * FROM test_ret_set_rec_dyn(5) AS (a int, b numeric, c text); + a | b | c +----+---+----- + 50 | 5 | xxx + 50 | 5 | xxx +(2 rows) + +create function test_ret_rec_dyn(int) returns record as ' +DECLARE + retval RECORD; +BEGIN + IF $1 > 10 THEN + SELECT INTO retval 5, 10, 15; + RETURN retval; + ELSE + SELECT INTO retval 50, 5::numeric, ''xxx''::text; + RETURN retval; + END IF; +END;' language 'plpgsql'; +SELECT * FROM test_ret_rec_dyn(1500) AS (a int, b int, c int); + a | b | c +---+----+---- + 5 | 10 | 15 +(1 row) + +SELECT * FROM test_ret_rec_dyn(5) AS (a int, b numeric, c text); + a | b | c +----+---+----- + 50 | 5 | xxx +(1 row) + diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql index e6795ed10a..b6607442d7 100644 --- a/src/test/regress/sql/plpgsql.sql +++ b/src/test/regress/sql/plpgsql.sql @@ -1524,3 +1524,38 @@ BEGIN END;' language 'plpgsql'; select * from test_ret_set_scalar(1,10); + +create function test_ret_set_rec_dyn(int) returns setof record as ' +DECLARE + retval RECORD; +BEGIN + IF $1 > 10 THEN + SELECT INTO retval 5, 10, 15; + RETURN NEXT retval; + RETURN NEXT retval; + ELSE + SELECT INTO retval 50, 5::numeric, ''xxx''::text; + RETURN NEXT retval; + RETURN NEXT retval; + END IF; + RETURN; +END;' language 'plpgsql'; + +SELECT * FROM test_ret_set_rec_dyn(1500) AS (a int, b int, c int); +SELECT * FROM test_ret_set_rec_dyn(5) AS (a int, b numeric, c text); + +create function test_ret_rec_dyn(int) returns record as ' +DECLARE + retval RECORD; +BEGIN + IF $1 > 10 THEN + SELECT INTO retval 5, 10, 15; + RETURN retval; + ELSE + SELECT INTO retval 50, 5::numeric, ''xxx''::text; + RETURN retval; + END IF; +END;' language 'plpgsql'; + +SELECT * FROM test_ret_rec_dyn(1500) AS (a int, b int, c int); +SELECT * FROM test_ret_rec_dyn(5) AS (a int, b numeric, c text);