1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-16 06:01:02 +03:00

Implement SQL-spec RETURNS TABLE syntax for functions.

(Unlike the original submission, this patch treats TABLE output parameters
as being entirely equivalent to OUT parameters -- tgl)

Pavel Stehule
This commit is contained in:
Tom Lane
2008-07-18 03:32:53 +00:00
parent a8fb90cf2d
commit 69a785b8bf
23 changed files with 626 additions and 85 deletions

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.170 2008/07/16 16:55:23 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.171 2008/07/18 03:32:51 tgl Exp $ -->
<!-- <!--
Documentation of the system catalogs, directed toward PostgreSQL developers Documentation of the system catalogs, directed toward PostgreSQL developers
--> -->
@ -3680,7 +3680,8 @@
<literal>i</literal> for <literal>IN</> arguments, <literal>i</literal> for <literal>IN</> arguments,
<literal>o</literal> for <literal>OUT</> arguments, <literal>o</literal> for <literal>OUT</> arguments,
<literal>b</literal> for <literal>INOUT</> arguments, <literal>b</literal> for <literal>INOUT</> arguments,
<literal>v</literal> for <literal>VARIADIC</> arguments. <literal>v</literal> for <literal>VARIADIC</> arguments,
<literal>t</literal> for <literal>TABLE</> arguments.
If all the arguments are <literal>IN</literal> arguments, If all the arguments are <literal>IN</literal> arguments,
this field will be null. this field will be null.
Note that subscripts correspond to positions of Note that subscripts correspond to positions of

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.441 2008/07/16 00:48:53 momjian Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.442 2008/07/18 03:32:51 tgl Exp $ -->
<chapter id="functions"> <chapter id="functions">
<title>Functions and Operators</title> <title>Functions and Operators</title>
@ -11563,6 +11563,14 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
<primary>pg_get_ruledef</primary> <primary>pg_get_ruledef</primary>
</indexterm> </indexterm>
<indexterm>
<primary>pg_get_function_arguments</primary>
</indexterm>
<indexterm>
<primary>pg_get_function_result</primary>
</indexterm>
<indexterm> <indexterm>
<primary>pg_get_indexdef</primary> <primary>pg_get_indexdef</primary>
</indexterm> </indexterm>
@ -11636,6 +11644,16 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
<entry>decompile internal form of an expression, assuming that any Vars <entry>decompile internal form of an expression, assuming that any Vars
in it refer to the relation indicated by the second parameter</entry> in it refer to the relation indicated by the second parameter</entry>
</row> </row>
<row>
<entry><literal><function>pg_get_function_arguments</function>(<parameter>func_oid</parameter>)</literal></entry>
<entry><type>text</type></entry>
<entry>get argument list for function</entry>
</row>
<row>
<entry><literal><function>pg_get_function_result</function>(<parameter>func_oid</parameter>)</literal></entry>
<entry><type>text</type></entry>
<entry>get <literal>RETURNS</> clause for function</entry>
</row>
<row> <row>
<entry><literal><function>pg_get_indexdef</function>(<parameter>index_oid</parameter>)</literal></entry> <entry><literal><function>pg_get_indexdef</function>(<parameter>index_oid</parameter>)</literal></entry>
<entry><type>text</type></entry> <entry><type>text</type></entry>
@ -11738,6 +11756,14 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
the same result as the variant that does not have the parameter at all. the same result as the variant that does not have the parameter at all.
</para> </para>
<para>
<function>pg_get_function_arguments</function> returns the argument list
of a function, in the form it would need to appear in within
<command>CREATE FUNCTION</>.
<function>pg_get_function_result</function> similarly returns the
appropriate <literal>RETURNS</> clause for the function.
</para>
<para> <para>
<function>pg_get_serial_sequence</function> returns the name of the <function>pg_get_serial_sequence</function> returns the name of the
sequence associated with a column, or NULL if no sequence is associated sequence associated with a column, or NULL if no sequence is associated

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.132 2008/07/16 01:30:21 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.133 2008/07/18 03:32:51 tgl Exp $ -->
<chapter id="plpgsql"> <chapter id="plpgsql">
<title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title> <title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title>
@ -157,6 +157,8 @@
parameters in place of an explicit specification of the return type. parameters in place of an explicit specification of the return type.
This does not add any fundamental capability to the language, but This does not add any fundamental capability to the language, but
it is often convenient, especially for returning multiple values. it is often convenient, especially for returning multiple values.
The <literal>RETURNS TABLE</> notation can also be used in place
of <literal>RETURNS SETOF</>.
</para> </para>
<para> <para>
@ -468,6 +470,23 @@ $$ LANGUAGE plpgsql;
<literal>RETURNS record</>. <literal>RETURNS record</>.
</para> </para>
<para>
Another way to declare a <application>PL/pgSQL</application> function
is with <literal>RETURNS TABLE</>, for example:
<programlisting>
CREATE FUNCTION extended_sales(p_itemno int) RETURNS TABLE(quantity int, total numeric) AS $$
BEGIN
RETURN QUERY SELECT quantity, quantity * price FROM sales WHERE itemno = p_itemno;
END;
$$ LANGUAGE plpgsql;
</programlisting>
This is exactly equivalent to declaring one or more <literal>OUT</>
parameters and specifying <literal>RETURNS SETOF
<replaceable>sometype</></literal>.
</para>
<para> <para>
When the return type of a <application>PL/pgSQL</application> When the return type of a <application>PL/pgSQL</application>
function is declared as a polymorphic type (<type>anyelement</type>, function is declared as a polymorphic type (<type>anyelement</type>,

View File

@ -1,5 +1,5 @@
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/create_function.sgml,v 1.79 2008/07/16 01:30:21 tgl Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/create_function.sgml,v 1.80 2008/07/18 03:32:52 tgl Exp $
--> -->
<refentry id="SQL-CREATEFUNCTION"> <refentry id="SQL-CREATEFUNCTION">
@ -21,7 +21,8 @@ $PostgreSQL: pgsql/doc/src/sgml/ref/create_function.sgml,v 1.79 2008/07/16 01:30
<synopsis> <synopsis>
CREATE [ OR REPLACE ] FUNCTION CREATE [ OR REPLACE ] FUNCTION
<replaceable class="parameter">name</replaceable> ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) <replaceable class="parameter">name</replaceable> ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] )
[ RETURNS <replaceable class="parameter">rettype</replaceable> ] [ RETURNS <replaceable class="parameter">rettype</replaceable>
| RETURNS TABLE ( <replaceable class="parameter">colname</replaceable> <replaceable class="parameter">coltype</replaceable> [, ...] ) ]
{ LANGUAGE <replaceable class="parameter">langname</replaceable> { LANGUAGE <replaceable class="parameter">langname</replaceable>
| IMMUTABLE | STABLE | VOLATILE | IMMUTABLE | STABLE | VOLATILE
| CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT | CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
@ -49,7 +50,7 @@ CREATE [ OR REPLACE ] FUNCTION
If a schema name is included, then the function is created in the If a schema name is included, then the function is created in the
specified schema. Otherwise it is created in the current schema. specified schema. Otherwise it is created in the current schema.
The name of the new function must not match any existing function The name of the new function must not match any existing function
with the same argument types in the same schema. However, with the same input argument types in the same schema. However,
functions of different argument types can share a name (this is functions of different argument types can share a name (this is
called <firstterm>overloading</>). called <firstterm>overloading</>).
</para> </para>
@ -104,6 +105,9 @@ CREATE [ OR REPLACE ] FUNCTION
The mode of an argument: <literal>IN</>, <literal>OUT</>, The mode of an argument: <literal>IN</>, <literal>OUT</>,
<literal>INOUT</>, or <literal>VARIADIC</>. <literal>INOUT</>, or <literal>VARIADIC</>.
If omitted, the default is <literal>IN</>. If omitted, the default is <literal>IN</>.
Only <literal>OUT</> arguments can follow a <literal>VARIADIC</> one.
Also, <literal>OUT</> and <literal>INOUT</> arguments cannot be used
together with the <literal>RETURNS TABLE</> notation.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
@ -183,6 +187,30 @@ CREATE [ OR REPLACE ] FUNCTION
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><replaceable class="parameter">colname</replaceable></term>
<listitem>
<para>
The name of an output column in the <literal>RETURNS TABLE</>
syntax. This is effectively another way of declaring a named
<literal>OUT</> parameter, except that <literal>RETURNS TABLE</>
also implies <literal>RETURNS SETOF</>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="parameter">coltype</replaceable></term>
<listitem>
<para>
The data type of an output column in the <literal>RETURNS TABLE</>
syntax.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><replaceable class="parameter">langname</replaceable></term> <term><replaceable class="parameter">langname</replaceable></term>
@ -437,7 +465,7 @@ CREATE [ OR REPLACE ] FUNCTION
<productname>PostgreSQL</productname> allows function <productname>PostgreSQL</productname> allows function
<firstterm>overloading</firstterm>; that is, the same name can be <firstterm>overloading</firstterm>; that is, the same name can be
used for several different functions so long as they have distinct used for several different functions so long as they have distinct
argument types. However, the C names of all functions must be input argument types. However, the C names of all functions must be
different, so you must give overloaded C functions different C different, so you must give overloaded C functions different C
names (for example, use the argument types as part of the C names (for example, use the argument types as part of the C
names). names).
@ -541,6 +569,18 @@ CREATE FUNCTION dup(int) RETURNS dup_result
SELECT * FROM dup(42); SELECT * FROM dup(42);
</programlisting> </programlisting>
Another way to return multiple columns is to use a <literal>TABLE</>
function:
<programlisting>
CREATE FUNCTION dup(int) RETURNS TABLE(f1 int, f2 text)
AS $$ SELECT $1, CAST($1 AS text) || ' is text' $$
LANGUAGE SQL;
SELECT * FROM dup(42);
</programlisting>
However, a <literal>TABLE</> function is different from the
preceding examples, because it actually returns a <emphasis>set</>
of records, not just one record.
</para> </para>
</refsect1> </refsect1>

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.131 2008/07/16 01:30:21 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.132 2008/07/18 03:32:52 tgl Exp $ -->
<sect1 id="xfunc"> <sect1 id="xfunc">
<title>User-Defined Functions</title> <title>User-Defined Functions</title>
@ -94,11 +94,12 @@
</para> </para>
<para> <para>
<indexterm><primary>SETOF</><seealso>function</></> Alternatively, Alternatively, an SQL function can be declared to return a set,
an SQL function can be declared to return a set, by specifying the by specifying the function's return type as <literal>SETOF
function's return type as <literal>SETOF <replaceable>sometype</></literal>, or equivalently by declaring it as
<replaceable>sometype</></literal>. In this case all rows of the <literal>RETURNS TABLE(<replaceable>columns</>)</literal>. In this case
last query's result are returned. Further details appear below. all rows of the last query's result are returned. Further details appear
below.
</para> </para>
<para> <para>
@ -117,7 +118,7 @@
other SQL commands. (The only exception is that you cannot put other SQL commands. (The only exception is that you cannot put
<command>BEGIN</>, <command>COMMIT</>, <command>ROLLBACK</>, or <command>BEGIN</>, <command>COMMIT</>, <command>ROLLBACK</>, or
<command>SAVEPOINT</> commands into a <acronym>SQL</acronym> function.) <command>SAVEPOINT</> commands into a <acronym>SQL</acronym> function.)
However, the final command However, the final command
must be a <command>SELECT</command> that returns whatever is must be a <command>SELECT</command> that returns whatever is
specified as the function's return type. Alternatively, if you specified as the function's return type. Alternatively, if you
want to define a SQL function that performs actions but has no want to define a SQL function that performs actions but has no
@ -175,7 +176,7 @@ INSERT INTO $1 VALUES (42);
<para> <para>
The simplest possible <acronym>SQL</acronym> function has no arguments and The simplest possible <acronym>SQL</acronym> function has no arguments and
simply returns a base type, such as <type>integer</type>: simply returns a base type, such as <type>integer</type>:
<screen> <screen>
CREATE FUNCTION one() RETURNS integer AS $$ CREATE FUNCTION one() RETURNS integer AS $$
SELECT 1 AS result; SELECT 1 AS result;
@ -202,7 +203,7 @@ SELECT one();
</para> </para>
<para> <para>
It is almost as easy to define <acronym>SQL</acronym> functions It is almost as easy to define <acronym>SQL</acronym> functions
that take base types as arguments. In the example below, notice that take base types as arguments. In the example below, notice
how we refer to the arguments within the function as <literal>$1</> how we refer to the arguments within the function as <literal>$1</>
and <literal>$2</>. and <literal>$2</>.
@ -226,7 +227,7 @@ SELECT add_em(1, 2) AS answer;
<programlisting> <programlisting>
CREATE FUNCTION tf1 (integer, numeric) RETURNS integer AS $$ CREATE FUNCTION tf1 (integer, numeric) RETURNS integer AS $$
UPDATE bank UPDATE bank
SET balance = balance - $2 SET balance = balance - $2
WHERE accountno = $1; WHERE accountno = $1;
SELECT 1; SELECT 1;
@ -248,7 +249,7 @@ SELECT tf1(17, 100.0);
<programlisting> <programlisting>
CREATE FUNCTION tf1 (integer, numeric) RETURNS numeric AS $$ CREATE FUNCTION tf1 (integer, numeric) RETURNS numeric AS $$
UPDATE bank UPDATE bank
SET balance = balance - $2 SET balance = balance - $2
WHERE accountno = $1; WHERE accountno = $1;
SELECT balance FROM bank WHERE accountno = $1; SELECT balance FROM bank WHERE accountno = $1;
@ -267,7 +268,7 @@ $$ LANGUAGE SQL;
types, we must not only specify which types, we must not only specify which
argument we want (as we did above with <literal>$1</> and <literal>$2</literal>) but argument we want (as we did above with <literal>$1</> and <literal>$2</literal>) but
also the desired attribute (field) of that argument. For example, also the desired attribute (field) of that argument. For example,
suppose that suppose that
<type>emp</type> is a table containing employee data, and therefore <type>emp</type> is a table containing employee data, and therefore
also the name of the composite type of each row of the table. Here also the name of the composite type of each row of the table. Here
is a function <function>double_salary</function> that computes what someone's is a function <function>double_salary</function> that computes what someone's
@ -323,7 +324,7 @@ SELECT name, double_salary(ROW(name, salary*1.1, age, cubicle)) AS dream
<para> <para>
It is also possible to build a function that returns a composite type. It is also possible to build a function that returns a composite type.
This is an example of a function This is an example of a function
that returns a single <type>emp</type> row: that returns a single <type>emp</type> row:
<programlisting> <programlisting>
@ -364,7 +365,7 @@ ERROR: function declared to return emp returns varchar instead of text at colum
</para> </para>
</listitem> </listitem>
</itemizedlist> </itemizedlist>
</para> </para>
<para> <para>
A different way to define the same function is: A different way to define the same function is:
@ -380,7 +381,7 @@ $$ LANGUAGE SQL;
in this situation, but it is a handy alternative in some cases in this situation, but it is a handy alternative in some cases
&mdash; for example, if we need to compute the result by calling &mdash; for example, if we need to compute the result by calling
another function that returns the desired composite value. another function that returns the desired composite value.
</para> </para>
<para> <para>
We could call this function directly in either of two ways: We could call this function directly in either of two ways:
@ -401,7 +402,7 @@ SELECT * FROM new_emp();
The second way is described more fully in <xref The second way is described more fully in <xref
linkend="xfunc-sql-table-functions">. linkend="xfunc-sql-table-functions">.
</para> </para>
<para> <para>
When you use a function that returns a composite type, When you use a function that returns a composite type,
@ -429,7 +430,7 @@ LINE 1: SELECT new_emp().name;
<para> <para>
Another option is to use Another option is to use
functional notation for extracting an attribute. The simple way functional notation for extracting an attribute. The simple way
to explain this is that we can use the to explain this is that we can use the
notations <literal>attribute(table)</> and <literal>table.attribute</> notations <literal>attribute(table)</> and <literal>table.attribute</>
interchangeably. interchangeably.
@ -693,9 +694,14 @@ SELECT *, upper(fooname) FROM getfoo(1) AS t1;
</para> </para>
</sect2> </sect2>
<sect2> <sect2 id="xfunc-sql-functions-returning-set">
<title><acronym>SQL</acronym> Functions Returning Sets</title> <title><acronym>SQL</acronym> Functions Returning Sets</title>
<indexterm>
<primary>function</primary>
<secondary>with SETOF</secondary>
</indexterm>
<para> <para>
When an SQL function is declared as returning <literal>SETOF When an SQL function is declared as returning <literal>SETOF
<replaceable>sometype</></literal>, the function's final <replaceable>sometype</></literal>, the function's final
@ -733,7 +739,7 @@ SELECT * FROM getfoo(1) AS t1;
<programlisting> <programlisting>
CREATE FUNCTION sum_n_product_with_tab (x int, OUT sum int, OUT product int) RETURNS SETOF record AS $$ CREATE FUNCTION sum_n_product_with_tab (x int, OUT sum int, OUT product int) RETURNS SETOF record AS $$
SELECT x + tab.y, x * tab.y FROM tab; SELECT $1 + tab.y, $1 * tab.y FROM tab;
$$ LANGUAGE SQL; $$ LANGUAGE SQL;
</programlisting> </programlisting>
@ -794,6 +800,41 @@ SELECT name, listchildren(name) FROM nodes;
</para> </para>
</sect2> </sect2>
<sect2 id="xfunc-sql-functions-returning-table">
<title><acronym>SQL</acronym> Functions Returning <literal>TABLE</></title>
<indexterm>
<primary>function</primary>
<secondary>RETURNS TABLE</secondary>
</indexterm>
<para>
There is another way to declare a function as returning a set,
which is to use the syntax
<literal>RETURNS TABLE(<replaceable>columns</>)</literal>.
This is equivalent to using one or more <literal>OUT</> parameters plus
marking the function as returning <literal>SETOF record</> (or
<literal>SETOF</> a single output parameter's type, as appropriate).
This notation is specified in recent versions of the SQL standard, and
thus may be more portable than using <literal>SETOF</>.
</para>
<para>
For example, the preceding sum-and-product example could also be
done this way:
<programlisting>
CREATE FUNCTION sum_n_product_with_tab (x int) RETURNS TABLE(sum int, product int) AS $$
SELECT $1 + tab.y, $1 * tab.y FROM tab;
$$ LANGUAGE SQL;
</programlisting>
It is not allowed to use explicit <literal>OUT</> or <literal>INOUT</>
parameters with the <literal>RETURNS TABLE</> notation &mdash; you must
put all the output columns in the <literal>TABLE</> list.
</para>
</sect2>
<sect2> <sect2>
<title>Polymorphic <acronym>SQL</acronym> Functions</title> <title>Polymorphic <acronym>SQL</acronym> Functions</title>

View File

@ -4,7 +4,7 @@
* *
* Copyright (c) 2003-2008, PostgreSQL Global Development Group * Copyright (c) 2003-2008, PostgreSQL Global Development Group
* *
* $PostgreSQL: pgsql/src/backend/catalog/information_schema.sql,v 1.44 2008/07/16 01:30:21 tgl Exp $ * $PostgreSQL: pgsql/src/backend/catalog/information_schema.sql,v 1.45 2008/07/18 03:32:52 tgl Exp $
*/ */
/* /*
@ -1007,6 +1007,7 @@ CREATE VIEW parameters AS
WHEN proargmodes[(ss.x).n] = 'o' THEN 'OUT' WHEN proargmodes[(ss.x).n] = 'o' THEN 'OUT'
WHEN proargmodes[(ss.x).n] = 'b' THEN 'INOUT' WHEN proargmodes[(ss.x).n] = 'b' THEN 'INOUT'
WHEN proargmodes[(ss.x).n] = 'v' THEN 'IN' WHEN proargmodes[(ss.x).n] = 'v' THEN 'IN'
WHEN proargmodes[(ss.x).n] = 't' THEN 'OUT'
END AS character_data) AS parameter_mode, END AS character_data) AS parameter_mode,
CAST('NO' AS character_data) AS is_result, CAST('NO' AS character_data) AS is_result,
CAST('NO' AS character_data) AS as_locator, CAST('NO' AS character_data) AS as_locator,

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.152 2008/07/16 16:55:23 tgl Exp $ * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.153 2008/07/18 03:32:52 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -242,6 +242,7 @@ ProcedureCreate(const char *procedureName,
elog(ERROR, "variadic parameter must be last"); elog(ERROR, "variadic parameter must be last");
break; break;
case PROARGMODE_OUT: case PROARGMODE_OUT:
case PROARGMODE_TABLE:
/* okay */ /* okay */
break; break;
case PROARGMODE_VARIADIC: case PROARGMODE_VARIADIC:

View File

@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.97 2008/07/16 16:55:23 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.98 2008/07/18 03:32:52 tgl Exp $
* *
* DESCRIPTION * DESCRIPTION
* These routines take the parse tree and pick out the * These routines take the parse tree and pick out the
@ -228,9 +228,10 @@ examine_parameter_list(List *parameters, Oid languageOid,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("functions cannot accept set arguments"))); errmsg("functions cannot accept set arguments")));
if (fp->mode != FUNC_PARAM_OUT) /* handle input parameters */
if (fp->mode != FUNC_PARAM_OUT && fp->mode != FUNC_PARAM_TABLE)
{ {
/* only OUT parameters can follow a VARIADIC parameter */ /* other input parameters can't follow a VARIADIC parameter */
if (varCount > 0) if (varCount > 0)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
@ -238,9 +239,10 @@ examine_parameter_list(List *parameters, Oid languageOid,
inTypes[inCount++] = toid; inTypes[inCount++] = toid;
} }
/* handle output parameters */
if (fp->mode != FUNC_PARAM_IN && fp->mode != FUNC_PARAM_VARIADIC) if (fp->mode != FUNC_PARAM_IN && fp->mode != FUNC_PARAM_VARIADIC)
{ {
if (outCount == 0) /* save first OUT param's type */ if (outCount == 0) /* save first output param's type */
*requiredResultType = toid; *requiredResultType = toid;
outCount++; outCount++;
} }

View File

@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.617 2008/07/16 01:30:22 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.618 2008/07/18 03:32:52 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
@ -112,6 +112,8 @@ static Node *doNegate(Node *n, int location);
static void doNegateFloat(Value *v); static void doNegateFloat(Value *v);
static Node *makeAArrayExpr(List *elements); static Node *makeAArrayExpr(List *elements);
static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args); static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args);
static List *mergeTableFuncParameters(List *func_args, List *columns);
static TypeName *TableFuncTypeName(List *columns);
%} %}
@ -253,13 +255,13 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
TableFuncElementList opt_type_modifiers TableFuncElementList opt_type_modifiers
prep_type_clause prep_type_clause
execute_param_clause using_clause returning_clause execute_param_clause using_clause returning_clause
enum_val_list enum_val_list table_func_column_list
%type <range> OptTempTableName %type <range> OptTempTableName
%type <into> into_clause create_as_target %type <into> into_clause create_as_target
%type <defelt> createfunc_opt_item common_func_opt_item %type <defelt> createfunc_opt_item common_func_opt_item
%type <fun_param> func_arg %type <fun_param> func_arg table_func_column
%type <fun_param_mode> arg_class %type <fun_param_mode> arg_class
%type <typnam> func_return func_type %type <typnam> func_return func_type
@ -4118,6 +4120,19 @@ CreateFunctionStmt:
n->withClause = $9; n->withClause = $9;
$$ = (Node *)n; $$ = (Node *)n;
} }
| CREATE opt_or_replace FUNCTION func_name func_args
RETURNS TABLE '(' table_func_column_list ')' createfunc_opt_list opt_definition
{
CreateFunctionStmt *n = makeNode(CreateFunctionStmt);
n->replace = $2;
n->funcname = $4;
n->parameters = mergeTableFuncParameters($5, $9);
n->returnType = TableFuncTypeName($9);
n->returnType->location = @7;
n->options = $11;
n->withClause = $12;
$$ = (Node *)n;
}
| CREATE opt_or_replace FUNCTION func_name func_args | CREATE opt_or_replace FUNCTION func_name func_args
createfunc_opt_list opt_definition createfunc_opt_list opt_definition
{ {
@ -4338,6 +4353,27 @@ opt_definition:
| /*EMPTY*/ { $$ = NIL; } | /*EMPTY*/ { $$ = NIL; }
; ;
table_func_column: param_name func_type
{
FunctionParameter *n = makeNode(FunctionParameter);
n->name = $1;
n->argType = $2;
n->mode = FUNC_PARAM_TABLE;
$$ = n;
}
;
table_func_column_list:
table_func_column
{
$$ = list_make1($1);
}
| table_func_column_list ',' table_func_column
{
$$ = lappend($1, $3);
}
;
/***************************************************************************** /*****************************************************************************
* ALTER FUNCTION * ALTER FUNCTION
* *
@ -9678,7 +9714,7 @@ extractArgTypes(List *parameters)
{ {
FunctionParameter *p = (FunctionParameter *) lfirst(i); FunctionParameter *p = (FunctionParameter *) lfirst(i);
if (p->mode != FUNC_PARAM_OUT) /* keep if IN, INOUT, VARIADIC */ if (p->mode != FUNC_PARAM_OUT && p->mode != FUNC_PARAM_TABLE)
result = lappend(result, p->argType); result = lappend(result, p->argType);
} }
return result; return result;
@ -9861,6 +9897,51 @@ parser_init(void)
QueryIsRule = FALSE; QueryIsRule = FALSE;
} }
/*
* Merge the input and output parameters of a table function.
*/
static List *
mergeTableFuncParameters(List *func_args, List *columns)
{
ListCell *lc;
/* Explicit OUT and INOUT parameters shouldn't be used in this syntax */
foreach(lc, func_args)
{
FunctionParameter *p = (FunctionParameter *) lfirst(lc);
if (p->mode != FUNC_PARAM_IN && p->mode != FUNC_PARAM_VARIADIC)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("OUT and INOUT arguments aren't allowed in TABLE functions")));
}
return list_concat(func_args, columns);
}
/*
* Determine return type of a TABLE function. A single result column
* returns setof that column's type; otherwise return setof record.
*/
static TypeName *
TableFuncTypeName(List *columns)
{
TypeName *result;
if (list_length(columns) == 1)
{
FunctionParameter *p = (FunctionParameter *) linitial(columns);
result = (TypeName *) copyObject(p->argType);
}
else
result = SystemTypeName("record");
result->setof = true;
return result;
}
/* /*
* Must undefine base_yylex before including scan.c, since we want it * Must undefine base_yylex before including scan.c, since we want it
* to create the function base_yylex not filtered_base_yylex. * to create the function base_yylex not filtered_base_yylex.

View File

@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.277 2008/07/16 16:55:23 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.278 2008/07/18 03:32:52 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -135,6 +135,8 @@ static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
int prettyFlags); int prettyFlags);
static char *pg_get_expr_worker(text *expr, Oid relid, char *relname, static char *pg_get_expr_worker(text *expr, Oid relid, char *relname,
int prettyFlags); int prettyFlags);
static int print_function_arguments(StringInfo buf, HeapTuple proctup,
bool print_table_args);
static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
int prettyFlags); int prettyFlags);
static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
@ -1395,6 +1397,147 @@ pg_get_serial_sequence(PG_FUNCTION_ARGS)
} }
/*
* pg_get_function_arguments
* Get a nicely-formatted list of arguments for a function.
* This is everything that would go between the parentheses in
* CREATE FUNCTION.
*/
Datum
pg_get_function_arguments(PG_FUNCTION_ARGS)
{
Oid funcid = PG_GETARG_OID(0);
StringInfoData buf;
HeapTuple proctup;
initStringInfo(&buf);
proctup = SearchSysCache(PROCOID,
ObjectIdGetDatum(funcid),
0, 0, 0);
if (!HeapTupleIsValid(proctup))
elog(ERROR, "cache lookup failed for function %u", funcid);
(void) print_function_arguments(&buf, proctup, false);
ReleaseSysCache(proctup);
PG_RETURN_TEXT_P(string_to_text(buf.data));
}
/*
* pg_get_function_result
* Get a nicely-formatted version of the result type of a function.
* This is what would appear after RETURNS in CREATE FUNCTION.
*/
Datum
pg_get_function_result(PG_FUNCTION_ARGS)
{
Oid funcid = PG_GETARG_OID(0);
StringInfoData buf;
HeapTuple proctup;
Form_pg_proc procform;
int ntabargs = 0;
initStringInfo(&buf);
proctup = SearchSysCache(PROCOID,
ObjectIdGetDatum(funcid),
0, 0, 0);
if (!HeapTupleIsValid(proctup))
elog(ERROR, "cache lookup failed for function %u", funcid);
procform = (Form_pg_proc) GETSTRUCT(proctup);
if (procform->proretset)
{
/* It might be a table function; try to print the arguments */
appendStringInfoString(&buf, "TABLE(");
ntabargs = print_function_arguments(&buf, proctup, true);
if (ntabargs > 0)
appendStringInfoString(&buf, ")");
else
resetStringInfo(&buf);
}
if (ntabargs == 0)
{
/* Not a table function, so do the normal thing */
if (procform->proretset)
appendStringInfoString(&buf, "SETOF ");
appendStringInfoString(&buf, format_type_be(procform->prorettype));
}
ReleaseSysCache(proctup);
PG_RETURN_TEXT_P(string_to_text(buf.data));
}
/*
* Common code for pg_get_function_arguments and pg_get_function_result:
* append the desired subset of arguments to buf. We print only TABLE
* arguments when print_table_args is true, and all the others when it's false.
* Function return value is the number of arguments printed.
*/
static int
print_function_arguments(StringInfo buf, HeapTuple proctup,
bool print_table_args)
{
int numargs;
Oid *argtypes;
char **argnames;
char *argmodes;
int argsprinted;
int i;
numargs = get_func_arg_info(proctup,
&argtypes, &argnames, &argmodes);
argsprinted = 0;
for (i = 0; i < numargs; i++)
{
Oid argtype = argtypes[i];
char *argname = argnames ? argnames[i] : NULL;
char argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
const char *modename;
if (print_table_args != (argmode == PROARGMODE_TABLE))
continue;
switch (argmode)
{
case PROARGMODE_IN:
modename = "";
break;
case PROARGMODE_INOUT:
modename = "INOUT ";
break;
case PROARGMODE_OUT:
modename = "OUT ";
break;
case PROARGMODE_VARIADIC:
modename = "VARIADIC ";
break;
case PROARGMODE_TABLE:
modename = "";
break;
default:
elog(ERROR, "invalid parameter mode '%c'", argmode);
modename = NULL; /* keep compiler quiet */
break;
}
if (argsprinted)
appendStringInfoString(buf, ", ");
appendStringInfoString(buf, modename);
if (argname && argname[0])
appendStringInfo(buf, "%s ", argname);
appendStringInfoString(buf, format_type_be(argtype));
argsprinted++;
}
return argsprinted;
}
/* /*
* deparse_expression - General utility for deparsing expressions * deparse_expression - General utility for deparsing expressions
* *

View File

@ -7,7 +7,7 @@
* Copyright (c) 2002-2008, PostgreSQL Global Development Group * Copyright (c) 2002-2008, PostgreSQL Global Development Group
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.40 2008/07/16 01:30:22 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.41 2008/07/18 03:32:52 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -550,7 +550,7 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYELEMENTOID: case ANYELEMENTOID:
case ANYNONARRAYOID: case ANYNONARRAYOID:
case ANYENUMOID: case ANYENUMOID:
if (argmode == PROARGMODE_OUT) if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
have_anyelement_result = true; have_anyelement_result = true;
else else
{ {
@ -565,7 +565,7 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
} }
break; break;
case ANYARRAYOID: case ANYARRAYOID:
if (argmode == PROARGMODE_OUT) if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
have_anyarray_result = true; have_anyarray_result = true;
else else
{ {
@ -582,7 +582,7 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
default: default:
break; break;
} }
if (argmode != PROARGMODE_OUT) if (argmode != PROARGMODE_OUT && argmode != PROARGMODE_TABLE)
inargno++; inargno++;
} }
@ -848,7 +848,8 @@ get_func_result_name(Oid functionId)
argmodes[i] == PROARGMODE_VARIADIC) argmodes[i] == PROARGMODE_VARIADIC)
continue; continue;
Assert(argmodes[i] == PROARGMODE_OUT || Assert(argmodes[i] == PROARGMODE_OUT ||
argmodes[i] == PROARGMODE_INOUT); argmodes[i] == PROARGMODE_INOUT ||
argmodes[i] == PROARGMODE_TABLE);
if (++numoutargs > 1) if (++numoutargs > 1)
{ {
/* multiple out args, so forget it */ /* multiple out args, so forget it */
@ -999,7 +1000,8 @@ build_function_result_tupdesc_d(Datum proallargtypes,
argmodes[i] == PROARGMODE_VARIADIC) argmodes[i] == PROARGMODE_VARIADIC)
continue; continue;
Assert(argmodes[i] == PROARGMODE_OUT || Assert(argmodes[i] == PROARGMODE_OUT ||
argmodes[i] == PROARGMODE_INOUT); argmodes[i] == PROARGMODE_INOUT ||
argmodes[i] == PROARGMODE_TABLE);
outargtypes[numoutargs] = argtypes[i]; outargtypes[numoutargs] = argtypes[i];
if (argnames) if (argnames)
pname = TextDatumGetCString(argnames[i]); pname = TextDatumGetCString(argnames[i]);

View File

@ -12,7 +12,7 @@
* by PostgreSQL * by PostgreSQL
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.495 2008/07/16 16:55:23 tgl Exp $ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.496 2008/07/18 03:32:52 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -165,7 +165,8 @@ static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
static void getDependencies(void); static void getDependencies(void);
static void getDomainConstraints(TypeInfo *tinfo); static void getDomainConstraints(TypeInfo *tinfo);
static void getTableData(TableInfo *tblinfo, int numTables, bool oids); static void getTableData(TableInfo *tblinfo, int numTables, bool oids);
static char *format_function_arguments(FuncInfo *finfo, int nallargs, static char *format_function_arguments(FuncInfo *finfo, char *funcargs);
static char *format_function_arguments_old(FuncInfo *finfo, int nallargs,
char **allargtypes, char **allargtypes,
char **argmodes, char **argmodes,
char **argnames); char **argnames);
@ -6405,16 +6406,34 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang)
/* /*
* format_function_arguments: generate function name and argument list * format_function_arguments: generate function name and argument list
* *
* This is used when we can rely on pg_get_function_arguments to format
* the argument list.
*/
static char *format_function_arguments(FuncInfo *finfo, char *funcargs)
{
PQExpBufferData fn;
initPQExpBuffer(&fn);
appendPQExpBuffer(&fn, "%s(%s)", fmtId(finfo->dobj.name), funcargs);
return fn.data;
}
/*
* format_function_arguments_old: generate function name and argument list
*
* The argument type names are qualified if needed. The function name * The argument type names are qualified if needed. The function name
* is never qualified. * is never qualified.
* *
* This is used only with pre-8.4 servers, so we aren't expecting to see
* VARIADIC or TABLE arguments.
*
* Any or all of allargtypes, argmodes, argnames may be NULL. * Any or all of allargtypes, argmodes, argnames may be NULL.
*/ */
static char * static char *
format_function_arguments(FuncInfo *finfo, int nallargs, format_function_arguments_old(FuncInfo *finfo, int nallargs,
char **allargtypes, char **allargtypes,
char **argmodes, char **argmodes,
char **argnames) char **argnames)
{ {
PQExpBufferData fn; PQExpBufferData fn;
int j; int j;
@ -6444,9 +6463,6 @@ format_function_arguments(FuncInfo *finfo, int nallargs,
case PROARGMODE_INOUT: case PROARGMODE_INOUT:
argmode = "INOUT "; argmode = "INOUT ";
break; break;
case PROARGMODE_VARIADIC:
argmode = "VARIADIC ";
break;
default: default:
write_msg(NULL, "WARNING: bogus value in proargmodes array\n"); write_msg(NULL, "WARNING: bogus value in proargmodes array\n");
argmode = ""; argmode = "";
@ -6475,7 +6491,7 @@ format_function_arguments(FuncInfo *finfo, int nallargs,
/* /*
* format_function_signature: generate function name and argument list * format_function_signature: generate function name and argument list
* *
* This is like format_function_arguments except that only a minimal * This is like format_function_arguments_old except that only a minimal
* list of input argument types is generated; this is sufficient to * list of input argument types is generated; this is sufficient to
* reference the function, but not to define it. * reference the function, but not to define it.
* *
@ -6527,6 +6543,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
char *proretset; char *proretset;
char *prosrc; char *prosrc;
char *probin; char *probin;
char *funcargs;
char *funcresult;
char *proallargtypes; char *proallargtypes;
char *proargmodes; char *proargmodes;
char *proargnames; char *proargnames;
@ -6559,7 +6577,24 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
selectSourceSchema(finfo->dobj.namespace->dobj.name); selectSourceSchema(finfo->dobj.namespace->dobj.name);
/* Fetch function-specific details */ /* Fetch function-specific details */
if (g_fout->remoteVersion >= 80300) if (g_fout->remoteVersion >= 80400)
{
/*
* In 8.4 and up we rely on pg_get_function_arguments and
* pg_get_function_result instead of examining proallargtypes etc.
*/
appendPQExpBuffer(query,
"SELECT proretset, prosrc, probin, "
"pg_catalog.pg_get_function_arguments(oid) as funcargs, "
"pg_catalog.pg_get_function_result(oid) as funcresult, "
"provolatile, proisstrict, prosecdef, "
"proconfig, procost, prorows, "
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) as lanname "
"FROM pg_catalog.pg_proc "
"WHERE oid = '%u'::pg_catalog.oid",
finfo->dobj.catId.oid);
}
else if (g_fout->remoteVersion >= 80300)
{ {
appendPQExpBuffer(query, appendPQExpBuffer(query,
"SELECT proretset, prosrc, probin, " "SELECT proretset, prosrc, probin, "
@ -6659,9 +6694,19 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset")); proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc")); prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
probin = PQgetvalue(res, 0, PQfnumber(res, "probin")); probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
proallargtypes = PQgetvalue(res, 0, PQfnumber(res, "proallargtypes")); if (g_fout->remoteVersion >= 80400)
proargmodes = PQgetvalue(res, 0, PQfnumber(res, "proargmodes")); {
proargnames = PQgetvalue(res, 0, PQfnumber(res, "proargnames")); funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
proallargtypes = proargmodes = proargnames = NULL;
}
else
{
proallargtypes = PQgetvalue(res, 0, PQfnumber(res, "proallargtypes"));
proargmodes = PQgetvalue(res, 0, PQfnumber(res, "proargmodes"));
proargnames = PQgetvalue(res, 0, PQfnumber(res, "proargnames"));
funcargs = funcresult = NULL;
}
provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile")); provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict")); proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef")); prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
@ -6766,8 +6811,11 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
} }
} }
funcsig = format_function_arguments(finfo, nallargs, allargtypes, if (funcargs)
argmodes, argnames); funcsig = format_function_arguments(finfo, funcargs);
else
funcsig = format_function_arguments_old(finfo, nallargs, allargtypes,
argmodes, argnames);
funcsig_tag = format_function_signature(finfo, false); funcsig_tag = format_function_signature(finfo, false);
/* /*
@ -6777,13 +6825,17 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
fmtId(finfo->dobj.namespace->dobj.name), fmtId(finfo->dobj.namespace->dobj.name),
funcsig); funcsig);
rettypename = getFormattedTypeName(finfo->prorettype, zeroAsOpaque);
appendPQExpBuffer(q, "CREATE FUNCTION %s ", funcsig); appendPQExpBuffer(q, "CREATE FUNCTION %s ", funcsig);
appendPQExpBuffer(q, "RETURNS %s%s", if (funcresult)
(proretset[0] == 't') ? "SETOF " : "", appendPQExpBuffer(q, "RETURNS %s", funcresult);
rettypename); else
free(rettypename); {
rettypename = getFormattedTypeName(finfo->prorettype, zeroAsOpaque);
appendPQExpBuffer(q, "RETURNS %s%s",
(proretset[0] == 't') ? "SETOF " : "",
rettypename);
free(rettypename);
}
appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname)); appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
if (provolatile[0] != PROVOLATILE_VOLATILE) if (provolatile[0] != PROVOLATILE_VOLATILE)

View File

@ -8,7 +8,7 @@
* *
* Copyright (c) 2000-2008, PostgreSQL Global Development Group * Copyright (c) 2000-2008, PostgreSQL Global Development Group
* *
* $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.182 2008/07/16 01:30:23 tgl Exp $ * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.183 2008/07/18 03:32:52 tgl Exp $
*/ */
#include "postgres_fe.h" #include "postgres_fe.h"
@ -189,15 +189,20 @@ describeFunctions(const char *pattern, bool verbose)
printfPQExpBuffer(&buf, printfPQExpBuffer(&buf,
"SELECT n.nspname as \"%s\",\n" "SELECT n.nspname as \"%s\",\n"
" p.proname as \"%s\",\n" " p.proname as \"%s\",\n",
" CASE WHEN p.proretset THEN 'setof ' ELSE '' END ||\n"
" pg_catalog.format_type(p.prorettype, NULL) as \"%s\",\n",
gettext_noop("Schema"), gettext_noop("Schema"),
gettext_noop("Name"), gettext_noop("Name"));
gettext_noop("Result data type"));
if (pset.sversion >= 80100) if (pset.sversion >= 80400)
appendPQExpBuffer(&buf, appendPQExpBuffer(&buf,
" pg_catalog.pg_get_function_result(p.oid) as \"%s\",\n"
" pg_catalog.pg_get_function_arguments(p.oid) as \"%s\"",
gettext_noop("Result data type"),
gettext_noop("Argument data types"));
else if (pset.sversion >= 80100)
appendPQExpBuffer(&buf,
" CASE WHEN p.proretset THEN 'SETOF ' ELSE '' END ||\n"
" pg_catalog.format_type(p.prorettype, NULL) as \"%s\",\n"
" CASE WHEN proallargtypes IS NOT NULL THEN\n" " CASE WHEN proallargtypes IS NOT NULL THEN\n"
" pg_catalog.array_to_string(ARRAY(\n" " pg_catalog.array_to_string(ARRAY(\n"
" SELECT\n" " SELECT\n"
@ -227,10 +232,14 @@ describeFunctions(const char *pattern, bool verbose)
" pg_catalog.generate_series(0, pg_catalog.array_upper(p.proargtypes, 1)) AS s(i)\n" " pg_catalog.generate_series(0, pg_catalog.array_upper(p.proargtypes, 1)) AS s(i)\n"
" ), ', ')\n" " ), ', ')\n"
" END AS \"%s\"", " END AS \"%s\"",
gettext_noop("Argument data types")); gettext_noop("Result data type"),
gettext_noop("Argument data types"));
else else
appendPQExpBuffer(&buf, appendPQExpBuffer(&buf,
" CASE WHEN p.proretset THEN 'SETOF ' ELSE '' END ||\n"
" pg_catalog.format_type(p.prorettype, NULL) as \"%s\",\n"
" pg_catalog.oidvectortypes(p.proargtypes) as \"%s\"", " pg_catalog.oidvectortypes(p.proargtypes) as \"%s\"",
gettext_noop("Result data type"),
gettext_noop("Argument data types")); gettext_noop("Argument data types"));
if (verbose) if (verbose)

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.470 2008/07/16 16:55:23 tgl Exp $ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.471 2008/07/18 03:32:52 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 200807161 #define CATALOG_VERSION_NO 200807171
#endif #endif

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.508 2008/07/16 16:55:24 tgl Exp $ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.509 2008/07/18 03:32:53 tgl Exp $
* *
* NOTES * NOTES
* The script catalog/genbki.sh reads this file and generates .bki * The script catalog/genbki.sh reads this file and generates .bki
@ -2291,6 +2291,10 @@ DATA(insert OID = 1716 ( pg_get_expr PGNSP PGUID 12 1 0 0 f f t f s 2 25 "2
DESCR("deparse an encoded expression"); DESCR("deparse an encoded expression");
DATA(insert OID = 1665 ( pg_get_serial_sequence PGNSP PGUID 12 1 0 0 f f t f s 2 25 "25 25" _null_ _null_ _null_ pg_get_serial_sequence _null_ _null_ _null_ )); DATA(insert OID = 1665 ( pg_get_serial_sequence PGNSP PGUID 12 1 0 0 f f t f s 2 25 "25 25" _null_ _null_ _null_ pg_get_serial_sequence _null_ _null_ _null_ ));
DESCR("name of sequence for a serial column"); DESCR("name of sequence for a serial column");
DATA(insert OID = 2162 ( pg_get_function_arguments PGNSP PGUID 12 1 0 0 f f t f s 1 25 "26" _null_ _null_ _null_ pg_get_function_arguments _null_ _null_ _null_ ));
DESCR("argument list of a function");
DATA(insert OID = 2165 ( pg_get_function_result PGNSP PGUID 12 1 0 0 f f t f s 1 25 "26" _null_ _null_ _null_ pg_get_function_result _null_ _null_ _null_ ));
DESCR("result type of a function");
DATA(insert OID = 1686 ( pg_get_keywords PGNSP PGUID 12 10 400 0 f f t t s 0 2249 "" "{25,18,25}" "{o,o,o}" "{word,catcode,catdesc}" pg_get_keywords _null_ _null_ _null_ )); DATA(insert OID = 1686 ( pg_get_keywords PGNSP PGUID 12 10 400 0 f f t t s 0 2249 "" "{25,18,25}" "{o,o,o}" "{word,catcode,catdesc}" pg_get_keywords _null_ _null_ _null_ ));
DESCR("list of SQL keywords"); DESCR("list of SQL keywords");
@ -4477,5 +4481,6 @@ DESCR("is txid visible in snapshot?");
#define PROARGMODE_OUT 'o' #define PROARGMODE_OUT 'o'
#define PROARGMODE_INOUT 'b' #define PROARGMODE_INOUT 'b'
#define PROARGMODE_VARIADIC 'v' #define PROARGMODE_VARIADIC 'v'
#define PROARGMODE_TABLE 't'
#endif /* PG_PROC_H */ #endif /* PG_PROC_H */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.367 2008/07/16 01:30:23 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.368 2008/07/18 03:32:53 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -1570,7 +1570,8 @@ typedef enum FunctionParameterMode
FUNC_PARAM_IN = 'i', /* input only */ FUNC_PARAM_IN = 'i', /* input only */
FUNC_PARAM_OUT = 'o', /* output only */ FUNC_PARAM_OUT = 'o', /* output only */
FUNC_PARAM_INOUT = 'b', /* both */ FUNC_PARAM_INOUT = 'b', /* both */
FUNC_PARAM_VARIADIC = 'v' /* variadic */ FUNC_PARAM_VARIADIC = 'v', /* variadic */
FUNC_PARAM_TABLE = 't' /* table function output column */
} FunctionParameterMode; } FunctionParameterMode;
typedef struct FunctionParameter typedef struct FunctionParameter

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.318 2008/07/03 20:58:47 tgl Exp $ * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.319 2008/07/18 03:32:53 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -551,6 +551,8 @@ extern Datum pg_get_expr(PG_FUNCTION_ARGS);
extern Datum pg_get_expr_ext(PG_FUNCTION_ARGS); extern Datum pg_get_expr_ext(PG_FUNCTION_ARGS);
extern Datum pg_get_userbyid(PG_FUNCTION_ARGS); extern Datum pg_get_userbyid(PG_FUNCTION_ARGS);
extern Datum pg_get_serial_sequence(PG_FUNCTION_ARGS); extern Datum pg_get_serial_sequence(PG_FUNCTION_ARGS);
extern Datum pg_get_function_arguments(PG_FUNCTION_ARGS);
extern Datum pg_get_function_result(PG_FUNCTION_ARGS);
extern char *deparse_expression(Node *expr, List *dpcontext, extern char *deparse_expression(Node *expr, List *dpcontext,
bool forceprefix, bool showimplicit); bool forceprefix, bool showimplicit);
extern List *deparse_context_for(const char *aliasname, Oid relid); extern List *deparse_context_for(const char *aliasname, Oid relid);

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.127 2008/07/16 01:30:23 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.128 2008/07/18 03:32:53 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -441,7 +441,9 @@ do_compile(FunctionCallInfo fcinfo,
argmode == PROARGMODE_INOUT || argmode == PROARGMODE_INOUT ||
argmode == PROARGMODE_VARIADIC) argmode == PROARGMODE_VARIADIC)
in_arg_varnos[num_in_args++] = argvariable->dno; in_arg_varnos[num_in_args++] = argvariable->dno;
if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_INOUT) if (argmode == PROARGMODE_OUT ||
argmode == PROARGMODE_INOUT ||
argmode == PROARGMODE_TABLE)
out_arg_variables[num_out_args++] = argvariable; out_arg_variables[num_out_args++] = argvariable;
/* Add to namespace under the $n name */ /* Add to namespace under the $n name */

View File

@ -1,7 +1,7 @@
/********************************************************************** /**********************************************************************
* plpython.c - python as a procedural language for PostgreSQL * plpython.c - python as a procedural language for PostgreSQL
* *
* $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.111 2008/07/16 01:30:23 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.112 2008/07/18 03:32:53 tgl Exp $
* *
********************************************************************* *********************************************************************
*/ */
@ -1271,7 +1271,8 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
/* proc->nargs was initialized to 0 above */ /* proc->nargs was initialized to 0 above */
for (i = 0; i < total; i++) for (i = 0; i < total; i++)
{ {
if (modes[i] != PROARGMODE_OUT) if (modes[i] != PROARGMODE_OUT &&
modes[i] != PROARGMODE_TABLE)
(proc->nargs)++; (proc->nargs)++;
} }
} }
@ -1282,7 +1283,9 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
HeapTuple argTypeTup; HeapTuple argTypeTup;
Form_pg_type argTypeStruct; Form_pg_type argTypeStruct;
if (modes && modes[i] == PROARGMODE_OUT) if (modes &&
(modes[i] == PROARGMODE_OUT ||
modes[i] == PROARGMODE_TABLE))
continue; /* skip OUT arguments */ continue; /* skip OUT arguments */
Assert(types[i] == procStruct->proargtypes.values[pos]); Assert(types[i] == procStruct->proargtypes.values[pos]);

View File

@ -3634,3 +3634,35 @@ NOTICE: non-variadic function called
drop function pleast(numeric[]); drop function pleast(numeric[]);
drop function pleast(numeric); drop function pleast(numeric);
-- test table functions
create function tftest(int) returns table(a int, b int) as $$
begin
return query select $1, $1+i from generate_series(1,5) g(i);
end;
$$ language plpgsql immutable strict;
select * from tftest(10);
a | b
----+----
10 | 11
10 | 12
10 | 13
10 | 14
10 | 15
(5 rows)
create or replace function tftest(a1 int) returns table(a int, b int) as $$
begin
a := a1; b := a1 + 1;
return next;
a := a1 * 10; b := a1 * 10 + 1;
return next;
end;
$$ language plpgsql immutable strict;
select * from tftest(10);
a | b
-----+-----
10 | 11
100 | 101
(2 rows)
drop function tftest(int);

View File

@ -528,3 +528,40 @@ CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE sql; AS 'select $1, array[$1,$1]' LANGUAGE sql;
ERROR: cannot determine result data type ERROR: cannot determine result data type
DETAIL: A function returning a polymorphic type must have at least one polymorphic argument. DETAIL: A function returning a polymorphic type must have at least one polymorphic argument.
--
-- table functions
--
CREATE OR REPLACE FUNCTION foo()
RETURNS TABLE(a int)
AS $$ SELECT a FROM generate_series(1,5) a(a) $$ LANGUAGE sql;
SELECT * FROM foo();
a
---
1
2
3
4
5
(5 rows)
DROP FUNCTION foo();
CREATE OR REPLACE FUNCTION foo(int)
RETURNS TABLE(a int, b int)
AS $$ SELECT a, b
FROM generate_series(1,$1) a(a),
generate_series(1,$1) b(b) $$ LANGUAGE sql;
SELECT * FROM foo(3);
a | b
---+---
1 | 1
1 | 2
1 | 3
2 | 1
2 | 2
2 | 3
3 | 1
3 | 2
3 | 3
(9 rows)
DROP FUNCTION foo(int);

View File

@ -2925,3 +2925,26 @@ select pleast(10);
drop function pleast(numeric[]); drop function pleast(numeric[]);
drop function pleast(numeric); drop function pleast(numeric);
-- test table functions
create function tftest(int) returns table(a int, b int) as $$
begin
return query select $1, $1+i from generate_series(1,5) g(i);
end;
$$ language plpgsql immutable strict;
select * from tftest(10);
create or replace function tftest(a1 int) returns table(a int, b int) as $$
begin
a := a1; b := a1 + 1;
return next;
a := a1 * 10; b := a1 * 10 + 1;
return next;
end;
$$ language plpgsql immutable strict;
select * from tftest(10);
drop function tftest(int);

View File

@ -261,3 +261,21 @@ DROP FUNCTION dup(anyelement);
-- fails, no way to deduce outputs -- fails, no way to deduce outputs
CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray) CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE sql; AS 'select $1, array[$1,$1]' LANGUAGE sql;
--
-- table functions
--
CREATE OR REPLACE FUNCTION foo()
RETURNS TABLE(a int)
AS $$ SELECT a FROM generate_series(1,5) a(a) $$ LANGUAGE sql;
SELECT * FROM foo();
DROP FUNCTION foo();
CREATE OR REPLACE FUNCTION foo(int)
RETURNS TABLE(a int, b int)
AS $$ SELECT a, b
FROM generate_series(1,$1) a(a),
generate_series(1,$1) b(b) $$ LANGUAGE sql;
SELECT * FROM foo(3);
DROP FUNCTION foo(int);