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:
parent
a8fb90cf2d
commit
69a785b8bf
@ -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
|
||||
-->
|
||||
@ -3680,7 +3680,8 @@
|
||||
<literal>i</literal> for <literal>IN</> arguments,
|
||||
<literal>o</literal> for <literal>OUT</> 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,
|
||||
this field will be null.
|
||||
Note that subscripts correspond to positions of
|
||||
|
@ -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">
|
||||
<title>Functions and Operators</title>
|
||||
@ -11563,6 +11563,14 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
|
||||
<primary>pg_get_ruledef</primary>
|
||||
</indexterm>
|
||||
|
||||
<indexterm>
|
||||
<primary>pg_get_function_arguments</primary>
|
||||
</indexterm>
|
||||
|
||||
<indexterm>
|
||||
<primary>pg_get_function_result</primary>
|
||||
</indexterm>
|
||||
|
||||
<indexterm>
|
||||
<primary>pg_get_indexdef</primary>
|
||||
</indexterm>
|
||||
@ -11636,6 +11644,16 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
|
||||
<entry>decompile internal form of an expression, assuming that any Vars
|
||||
in it refer to the relation indicated by the second parameter</entry>
|
||||
</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>
|
||||
<entry><literal><function>pg_get_indexdef</function>(<parameter>index_oid</parameter>)</literal></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.
|
||||
</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>
|
||||
<function>pg_get_serial_sequence</function> returns the name of the
|
||||
sequence associated with a column, or NULL if no sequence is associated
|
||||
|
@ -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">
|
||||
<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.
|
||||
This does not add any fundamental capability to the language, but
|
||||
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>
|
||||
@ -468,6 +470,23 @@ $$ LANGUAGE plpgsql;
|
||||
<literal>RETURNS record</>.
|
||||
</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>
|
||||
When the return type of a <application>PL/pgSQL</application>
|
||||
function is declared as a polymorphic type (<type>anyelement</type>,
|
||||
|
@ -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">
|
||||
@ -21,7 +21,8 @@ $PostgreSQL: pgsql/doc/src/sgml/ref/create_function.sgml,v 1.79 2008/07/16 01:30
|
||||
<synopsis>
|
||||
CREATE [ OR REPLACE ] FUNCTION
|
||||
<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>
|
||||
| IMMUTABLE | STABLE | VOLATILE
|
||||
| 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
|
||||
specified schema. Otherwise it is created in the current schema.
|
||||
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
|
||||
called <firstterm>overloading</>).
|
||||
</para>
|
||||
@ -104,6 +105,9 @@ CREATE [ OR REPLACE ] FUNCTION
|
||||
The mode of an argument: <literal>IN</>, <literal>OUT</>,
|
||||
<literal>INOUT</>, or <literal>VARIADIC</>.
|
||||
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>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@ -183,6 +187,30 @@ CREATE [ OR REPLACE ] FUNCTION
|
||||
</listitem>
|
||||
</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>
|
||||
<term><replaceable class="parameter">langname</replaceable></term>
|
||||
|
||||
@ -437,7 +465,7 @@ CREATE [ OR REPLACE ] FUNCTION
|
||||
<productname>PostgreSQL</productname> allows function
|
||||
<firstterm>overloading</firstterm>; that is, the same name can be
|
||||
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
|
||||
names (for example, use the argument types as part of the C
|
||||
names).
|
||||
@ -541,6 +569,18 @@ CREATE FUNCTION dup(int) RETURNS dup_result
|
||||
|
||||
SELECT * FROM dup(42);
|
||||
</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>
|
||||
</refsect1>
|
||||
|
||||
|
@ -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">
|
||||
<title>User-Defined Functions</title>
|
||||
@ -94,11 +94,12 @@
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<indexterm><primary>SETOF</><seealso>function</></> Alternatively,
|
||||
an SQL function can be declared to return a set, by specifying the
|
||||
function's return type as <literal>SETOF
|
||||
<replaceable>sometype</></literal>. In this case all rows of the
|
||||
last query's result are returned. Further details appear below.
|
||||
Alternatively, an SQL function can be declared to return a set,
|
||||
by specifying the function's return type as <literal>SETOF
|
||||
<replaceable>sometype</></literal>, or equivalently by declaring it as
|
||||
<literal>RETURNS TABLE(<replaceable>columns</>)</literal>. In this case
|
||||
all rows of the last query's result are returned. Further details appear
|
||||
below.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@ -117,7 +118,7 @@
|
||||
other SQL commands. (The only exception is that you cannot put
|
||||
<command>BEGIN</>, <command>COMMIT</>, <command>ROLLBACK</>, or
|
||||
<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
|
||||
specified as the function's return type. Alternatively, if you
|
||||
want to define a SQL function that performs actions but has no
|
||||
@ -175,7 +176,7 @@ INSERT INTO $1 VALUES (42);
|
||||
<para>
|
||||
The simplest possible <acronym>SQL</acronym> function has no arguments and
|
||||
simply returns a base type, such as <type>integer</type>:
|
||||
|
||||
|
||||
<screen>
|
||||
CREATE FUNCTION one() RETURNS integer AS $$
|
||||
SELECT 1 AS result;
|
||||
@ -202,7 +203,7 @@ SELECT one();
|
||||
</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
|
||||
how we refer to the arguments within the function as <literal>$1</>
|
||||
and <literal>$2</>.
|
||||
@ -226,7 +227,7 @@ SELECT add_em(1, 2) AS answer;
|
||||
|
||||
<programlisting>
|
||||
CREATE FUNCTION tf1 (integer, numeric) RETURNS integer AS $$
|
||||
UPDATE bank
|
||||
UPDATE bank
|
||||
SET balance = balance - $2
|
||||
WHERE accountno = $1;
|
||||
SELECT 1;
|
||||
@ -248,7 +249,7 @@ SELECT tf1(17, 100.0);
|
||||
|
||||
<programlisting>
|
||||
CREATE FUNCTION tf1 (integer, numeric) RETURNS numeric AS $$
|
||||
UPDATE bank
|
||||
UPDATE bank
|
||||
SET balance = balance - $2
|
||||
WHERE accountno = $1;
|
||||
SELECT balance FROM bank WHERE accountno = $1;
|
||||
@ -267,7 +268,7 @@ $$ LANGUAGE SQL;
|
||||
types, we must not only specify which
|
||||
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,
|
||||
suppose that
|
||||
suppose that
|
||||
<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
|
||||
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>
|
||||
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:
|
||||
|
||||
<programlisting>
|
||||
@ -364,7 +365,7 @@ ERROR: function declared to return emp returns varchar instead of text at colum
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
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
|
||||
— for example, if we need to compute the result by calling
|
||||
another function that returns the desired composite value.
|
||||
</para>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
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
|
||||
linkend="xfunc-sql-table-functions">.
|
||||
</para>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When you use a function that returns a composite type,
|
||||
@ -429,7 +430,7 @@ LINE 1: SELECT new_emp().name;
|
||||
|
||||
<para>
|
||||
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
|
||||
notations <literal>attribute(table)</> and <literal>table.attribute</>
|
||||
interchangeably.
|
||||
@ -693,9 +694,14 @@ SELECT *, upper(fooname) FROM getfoo(1) AS t1;
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<sect2 id="xfunc-sql-functions-returning-set">
|
||||
<title><acronym>SQL</acronym> Functions Returning Sets</title>
|
||||
|
||||
<indexterm>
|
||||
<primary>function</primary>
|
||||
<secondary>with SETOF</secondary>
|
||||
</indexterm>
|
||||
|
||||
<para>
|
||||
When an SQL function is declared as returning <literal>SETOF
|
||||
<replaceable>sometype</></literal>, the function's final
|
||||
@ -733,7 +739,7 @@ SELECT * FROM getfoo(1) AS t1;
|
||||
|
||||
<programlisting>
|
||||
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;
|
||||
</programlisting>
|
||||
|
||||
@ -794,6 +800,41 @@ SELECT name, listchildren(name) FROM nodes;
|
||||
</para>
|
||||
</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 — you must
|
||||
put all the output columns in the <literal>TABLE</> list.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Polymorphic <acronym>SQL</acronym> Functions</title>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
*
|
||||
* 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] = 'b' THEN 'INOUT'
|
||||
WHEN proargmodes[(ss.x).n] = 'v' THEN 'IN'
|
||||
WHEN proargmodes[(ss.x).n] = 't' THEN 'OUT'
|
||||
END AS character_data) AS parameter_mode,
|
||||
CAST('NO' AS character_data) AS is_result,
|
||||
CAST('NO' AS character_data) AS as_locator,
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* 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");
|
||||
break;
|
||||
case PROARGMODE_OUT:
|
||||
case PROARGMODE_TABLE:
|
||||
/* okay */
|
||||
break;
|
||||
case PROARGMODE_VARIADIC:
|
||||
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* 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
|
||||
* 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),
|
||||
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)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||
@ -238,9 +239,10 @@ examine_parameter_list(List *parameters, Oid languageOid,
|
||||
inTypes[inCount++] = toid;
|
||||
}
|
||||
|
||||
/* handle output parameters */
|
||||
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;
|
||||
outCount++;
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* 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
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@ -112,6 +112,8 @@ static Node *doNegate(Node *n, int location);
|
||||
static void doNegateFloat(Value *v);
|
||||
static Node *makeAArrayExpr(List *elements);
|
||||
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
|
||||
prep_type_clause
|
||||
execute_param_clause using_clause returning_clause
|
||||
enum_val_list
|
||||
enum_val_list table_func_column_list
|
||||
|
||||
%type <range> OptTempTableName
|
||||
%type <into> into_clause create_as_target
|
||||
|
||||
%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 <typnam> func_return func_type
|
||||
|
||||
@ -4118,6 +4120,19 @@ CreateFunctionStmt:
|
||||
n->withClause = $9;
|
||||
$$ = (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
|
||||
createfunc_opt_list opt_definition
|
||||
{
|
||||
@ -4338,6 +4353,27 @@ opt_definition:
|
||||
| /*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
|
||||
*
|
||||
@ -9678,7 +9714,7 @@ extractArgTypes(List *parameters)
|
||||
{
|
||||
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);
|
||||
}
|
||||
return result;
|
||||
@ -9861,6 +9897,51 @@ parser_init(void)
|
||||
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
|
||||
* to create the function base_yylex not filtered_base_yylex.
|
||||
|
@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* 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);
|
||||
static char *pg_get_expr_worker(text *expr, Oid relid, char *relname,
|
||||
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,
|
||||
int prettyFlags);
|
||||
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
|
||||
*
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Copyright (c) 2002-2008, PostgreSQL Global Development Group
|
||||
*
|
||||
* 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 ANYNONARRAYOID:
|
||||
case ANYENUMOID:
|
||||
if (argmode == PROARGMODE_OUT)
|
||||
if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
|
||||
have_anyelement_result = true;
|
||||
else
|
||||
{
|
||||
@ -565,7 +565,7 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
|
||||
}
|
||||
break;
|
||||
case ANYARRAYOID:
|
||||
if (argmode == PROARGMODE_OUT)
|
||||
if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
|
||||
have_anyarray_result = true;
|
||||
else
|
||||
{
|
||||
@ -582,7 +582,7 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (argmode != PROARGMODE_OUT)
|
||||
if (argmode != PROARGMODE_OUT && argmode != PROARGMODE_TABLE)
|
||||
inargno++;
|
||||
}
|
||||
|
||||
@ -848,7 +848,8 @@ get_func_result_name(Oid functionId)
|
||||
argmodes[i] == PROARGMODE_VARIADIC)
|
||||
continue;
|
||||
Assert(argmodes[i] == PROARGMODE_OUT ||
|
||||
argmodes[i] == PROARGMODE_INOUT);
|
||||
argmodes[i] == PROARGMODE_INOUT ||
|
||||
argmodes[i] == PROARGMODE_TABLE);
|
||||
if (++numoutargs > 1)
|
||||
{
|
||||
/* multiple out args, so forget it */
|
||||
@ -999,7 +1000,8 @@ build_function_result_tupdesc_d(Datum proallargtypes,
|
||||
argmodes[i] == PROARGMODE_VARIADIC)
|
||||
continue;
|
||||
Assert(argmodes[i] == PROARGMODE_OUT ||
|
||||
argmodes[i] == PROARGMODE_INOUT);
|
||||
argmodes[i] == PROARGMODE_INOUT ||
|
||||
argmodes[i] == PROARGMODE_TABLE);
|
||||
outargtypes[numoutargs] = argtypes[i];
|
||||
if (argnames)
|
||||
pname = TextDatumGetCString(argnames[i]);
|
||||
|
@ -12,7 +12,7 @@
|
||||
* by PostgreSQL
|
||||
*
|
||||
* 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 getDomainConstraints(TypeInfo *tinfo);
|
||||
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 **argmodes,
|
||||
char **argnames);
|
||||
@ -6405,16 +6406,34 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang)
|
||||
/*
|
||||
* 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
|
||||
* 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.
|
||||
*/
|
||||
static char *
|
||||
format_function_arguments(FuncInfo *finfo, int nallargs,
|
||||
char **allargtypes,
|
||||
char **argmodes,
|
||||
char **argnames)
|
||||
format_function_arguments_old(FuncInfo *finfo, int nallargs,
|
||||
char **allargtypes,
|
||||
char **argmodes,
|
||||
char **argnames)
|
||||
{
|
||||
PQExpBufferData fn;
|
||||
int j;
|
||||
@ -6444,9 +6463,6 @@ format_function_arguments(FuncInfo *finfo, int nallargs,
|
||||
case PROARGMODE_INOUT:
|
||||
argmode = "INOUT ";
|
||||
break;
|
||||
case PROARGMODE_VARIADIC:
|
||||
argmode = "VARIADIC ";
|
||||
break;
|
||||
default:
|
||||
write_msg(NULL, "WARNING: bogus value in proargmodes array\n");
|
||||
argmode = "";
|
||||
@ -6475,7 +6491,7 @@ format_function_arguments(FuncInfo *finfo, int nallargs,
|
||||
/*
|
||||
* 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
|
||||
* reference the function, but not to define it.
|
||||
*
|
||||
@ -6527,6 +6543,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
||||
char *proretset;
|
||||
char *prosrc;
|
||||
char *probin;
|
||||
char *funcargs;
|
||||
char *funcresult;
|
||||
char *proallargtypes;
|
||||
char *proargmodes;
|
||||
char *proargnames;
|
||||
@ -6559,7 +6577,24 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
||||
selectSourceSchema(finfo->dobj.namespace->dobj.name);
|
||||
|
||||
/* 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,
|
||||
"SELECT proretset, prosrc, probin, "
|
||||
@ -6659,9 +6694,19 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
||||
proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
|
||||
prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
|
||||
probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
|
||||
proallargtypes = PQgetvalue(res, 0, PQfnumber(res, "proallargtypes"));
|
||||
proargmodes = PQgetvalue(res, 0, PQfnumber(res, "proargmodes"));
|
||||
proargnames = PQgetvalue(res, 0, PQfnumber(res, "proargnames"));
|
||||
if (g_fout->remoteVersion >= 80400)
|
||||
{
|
||||
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"));
|
||||
proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
|
||||
prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
|
||||
@ -6766,8 +6811,11 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
||||
}
|
||||
}
|
||||
|
||||
funcsig = format_function_arguments(finfo, nallargs, allargtypes,
|
||||
argmodes, argnames);
|
||||
if (funcargs)
|
||||
funcsig = format_function_arguments(finfo, funcargs);
|
||||
else
|
||||
funcsig = format_function_arguments_old(finfo, nallargs, allargtypes,
|
||||
argmodes, argnames);
|
||||
funcsig_tag = format_function_signature(finfo, false);
|
||||
|
||||
/*
|
||||
@ -6777,13 +6825,17 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
||||
fmtId(finfo->dobj.namespace->dobj.name),
|
||||
funcsig);
|
||||
|
||||
rettypename = getFormattedTypeName(finfo->prorettype, zeroAsOpaque);
|
||||
|
||||
appendPQExpBuffer(q, "CREATE FUNCTION %s ", funcsig);
|
||||
appendPQExpBuffer(q, "RETURNS %s%s",
|
||||
(proretset[0] == 't') ? "SETOF " : "",
|
||||
rettypename);
|
||||
free(rettypename);
|
||||
if (funcresult)
|
||||
appendPQExpBuffer(q, "RETURNS %s", funcresult);
|
||||
else
|
||||
{
|
||||
rettypename = getFormattedTypeName(finfo->prorettype, zeroAsOpaque);
|
||||
appendPQExpBuffer(q, "RETURNS %s%s",
|
||||
(proretset[0] == 't') ? "SETOF " : "",
|
||||
rettypename);
|
||||
free(rettypename);
|
||||
}
|
||||
|
||||
appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
|
||||
if (provolatile[0] != PROVOLATILE_VOLATILE)
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
* 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"
|
||||
|
||||
@ -189,15 +189,20 @@ describeFunctions(const char *pattern, bool verbose)
|
||||
|
||||
printfPQExpBuffer(&buf,
|
||||
"SELECT n.nspname 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",
|
||||
" p.proname as \"%s\",\n",
|
||||
gettext_noop("Schema"),
|
||||
gettext_noop("Name"),
|
||||
gettext_noop("Result data type"));
|
||||
gettext_noop("Name"));
|
||||
|
||||
if (pset.sversion >= 80100)
|
||||
if (pset.sversion >= 80400)
|
||||
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"
|
||||
" pg_catalog.array_to_string(ARRAY(\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"
|
||||
" ), ', ')\n"
|
||||
" END AS \"%s\"",
|
||||
gettext_noop("Argument data types"));
|
||||
gettext_noop("Result data type"),
|
||||
gettext_noop("Argument data types"));
|
||||
else
|
||||
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\"",
|
||||
gettext_noop("Result data type"),
|
||||
gettext_noop("Argument data types"));
|
||||
|
||||
if (verbose)
|
||||
|
@ -37,7 +37,7 @@
|
||||
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
||||
* 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 */
|
||||
#define CATALOG_VERSION_NO 200807161
|
||||
#define CATALOG_VERSION_NO 200807171
|
||||
|
||||
#endif
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
||||
* 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
|
||||
* 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");
|
||||
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");
|
||||
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_ ));
|
||||
DESCR("list of SQL keywords");
|
||||
@ -4477,5 +4481,6 @@ DESCR("is txid visible in snapshot?");
|
||||
#define PROARGMODE_OUT 'o'
|
||||
#define PROARGMODE_INOUT 'b'
|
||||
#define PROARGMODE_VARIADIC 'v'
|
||||
#define PROARGMODE_TABLE 't'
|
||||
|
||||
#endif /* PG_PROC_H */
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
||||
* 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_OUT = 'o', /* output only */
|
||||
FUNC_PARAM_INOUT = 'b', /* both */
|
||||
FUNC_PARAM_VARIADIC = 'v' /* variadic */
|
||||
FUNC_PARAM_VARIADIC = 'v', /* variadic */
|
||||
FUNC_PARAM_TABLE = 't' /* table function output column */
|
||||
} FunctionParameterMode;
|
||||
|
||||
typedef struct FunctionParameter
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
||||
* 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_userbyid(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,
|
||||
bool forceprefix, bool showimplicit);
|
||||
extern List *deparse_context_for(const char *aliasname, Oid relid);
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* 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_VARIADIC)
|
||||
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;
|
||||
|
||||
/* Add to namespace under the $n name */
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**********************************************************************
|
||||
* 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 */
|
||||
for (i = 0; i < total; i++)
|
||||
{
|
||||
if (modes[i] != PROARGMODE_OUT)
|
||||
if (modes[i] != PROARGMODE_OUT &&
|
||||
modes[i] != PROARGMODE_TABLE)
|
||||
(proc->nargs)++;
|
||||
}
|
||||
}
|
||||
@ -1282,7 +1283,9 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
|
||||
HeapTuple argTypeTup;
|
||||
Form_pg_type argTypeStruct;
|
||||
|
||||
if (modes && modes[i] == PROARGMODE_OUT)
|
||||
if (modes &&
|
||||
(modes[i] == PROARGMODE_OUT ||
|
||||
modes[i] == PROARGMODE_TABLE))
|
||||
continue; /* skip OUT arguments */
|
||||
|
||||
Assert(types[i] == procStruct->proargtypes.values[pos]);
|
||||
|
@ -3634,3 +3634,35 @@ NOTICE: non-variadic function called
|
||||
|
||||
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);
|
||||
|
@ -528,3 +528,40 @@ CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray)
|
||||
AS 'select $1, array[$1,$1]' LANGUAGE sql;
|
||||
ERROR: cannot determine result data type
|
||||
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);
|
||||
|
@ -2925,3 +2925,26 @@ select pleast(10);
|
||||
|
||||
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);
|
||||
|
@ -261,3 +261,21 @@ DROP FUNCTION dup(anyelement);
|
||||
-- fails, no way to deduce outputs
|
||||
CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray)
|
||||
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);
|
||||
|
Loading…
Reference in New Issue
Block a user