From the PostgreSQL documentation here:

There is another way to declare a function as returning a set, which is to use the syntax RETURNS TABLE(columns). ... This notation is specified in recent versions of the SQL standard, and thus may be more portable than using SETOF.

This sounds as if RETURNS TABLE is a newer, more portable style to return multiple rows. But I am not sure if the two syntaxes are equivalent.

I was wondering if we can actually use RETURNS TABLE to replace RETURNS SETOF?

In particular, one case I haven't figured out is: if we have an existing table foo and its associated composite type, how can we use it in RETURNS TABLE?

Using the example for the above link, can the following be rewritten using RETURNS TABLE:

CREATE TABLE foo (fooid int, foosubid int, fooname text);
    SELECT * FROM foo WHERE fooid = $1;

So far, I tried to use RETURNS TABLE (foo.*) and RETURNS TABLE (foo), which didn't work.


PS: sorry for my comments, I was developing a complex code and I have a small error that seems a stupid PostgreSQL restriction on a "returing table" part... I was stupid, instead of concentrating and solving it, I used the internet (search engine put me here). Now, this wiki-answer is to help other readers, called by search engine and attracted by the title of the question.

Thanks to @dezso (was a correct answer) and, please all readers, you can edit this question to be more didactic, it is a Wiki.

Since PostgreSQL-v8 we can do it! We can RETURNS EXISTING_TABLE_NAME

In its Guide, in all PostgreSQL versions, from since pg v8 to current version, all have a section named "SQL Functions as Table Sources". Let's reproduce the Guide's example with some simplifications:

CREATE TABLE foo (fooid int, foosubid int, fooname text);
INSERT INTO foo VALUES (1, 1, 'Joe'), (1, 2, 'Ed'), (2, 1, 'Mary');

    SELECT * FROM foo WHERE fooid = $1;

SELECT * FROM getfoo(1);

It is running as expected, it is perfect!

 fooid | foosubid | fooname
     1 |        1 | Joe
     1 |        2 | Ed

The question "How to use RETURNS TABLE with an existing table in PostgreSQL?" have a good answer since pg v8... This is the way we do it in the last 15 years, the syntax is:

Use clause TABLE as instantaneous CREATE TABLE for returning

The @tinlyx's confusion, explainded on his question, is about the use of the clause TABLE instead of SETOF... To think using the "PostgreSQL syntax logic", we must first remember that RETURN <EXISTING_TABLE_NAME> is also valid, and it has the same behavior that RETURN <EXISTING_TYPE_NAME>. Is natural to return only one row.

Next step, remember that we declare a tuple with the CREATE TABLE clause (<tuple_description>), therefore, a good syntax to express an "instant-define-tuple table" is RETURN TABLE (<tuple_description>), and it makes sense to return TABLE -type, which is like an array type, will return several instances (TABLE is a set of tuples).

Next step, remember that we declare a tuple with the CREATE TABLE (<tuple_description>) clause, therefore, a good syntax to express an "instantaneous table-definition" is RETURN TABLE (<tuple_description>); and it makes sense to return Table-type, that is like Array-type, they return multiple instances (TABLE is a set of tuples).

The "modern thing" in PostgreSQL (!) is what @ZiggyCrueltyfreeZeitgeister showed, the RETURNS TABLE (LIKE <table_name>) syntax.

CREATE FUNCTION getfoo2(int) RETURNS TABLE (LIKE foo) AS $$ -- working fine!
    SELECT * FROM foo WHERE fooid = $1;
SELECT * FROM getfoo2(1); -- same result as getfoo(1)

Many ways to do the same, a summary:

Expliciting the table name (two ways) or type name:

  • RETURNS TABLE (LIKE <table_name>) (modern and good)
  • RETURNS SETOF <table_name> (old but good)
  • RETURNS SETOF <type_name> (after CREATE TYPE <type_name> (<tuple_description>))

Implicit/generic ways, by anonymous types:

  • RETURNS SETOF RECORD (generic but somethimes a problem)
  • (exist something as?) RETURNS SETOF ROW?

Instantaneous table-definition:

  • RETURNS TABLE (<tuple_description>)
  • (no RETURNS) using OUT in the parameter list.

For the last case, using our example to illustrate:
CREATE FUNCTION getfoo(int, OUT fooid int, OUT foosubid int, OUT fooname text)

For dynamic and/or polymophic input you must check this explanation.

Best practice?

There are many ways to do the same, so, there are a "best one"?

As syntax I prefer the use of RETURNS TABLE (LIKE <table_name>), that is explicit: no confusion with "implicit RECORD", no fear of incompatibilities...

Important for library management, DROP TABLE foo CASCADE will drop also the function: in any syntax (returns table or returns setof) PostgreSQL will do a good job.

drop table foo cascade;
NOTICE:  drop cascades to 2 other objects
DETAIL:  drop cascades to function getfoo(integer)
drop cascades to function getfoo2(integer)

The different modes are interchangeable in some circumstances, but are different. What RETURNS TABLE is good for is described in the sentence you removed from your quote above:

This is equivalent to using one or more OUT parameters plus marking the function as returning SETOF record (or SETOF a single output parameter's type, as appropriate).

This means that CREATE FUNCTION ... RETURNS TABLE is intended to specify a custom return type, i.e. it is equivalent to CREATE TYPE xxx_t AS ([fields...,]); CREATE FUNCTION xxx ... RETURNS SETOF xxx_t, and also CREATE FUNCTION xxx ([in params...,] out [fields...,]) RETURNS SETOF RECORD.

I.e., a function cannot return a table of some other table's type: use RETURNS SETOF directly for that (as you did in the snippet in your question).

  • Hi. We are in 2020! No news? It is impossible to reuse the table as a TYPE? like we do with columns (table_name.column_name%TYPE)... No way to express RETURN table_name%TYPE?? – Peter Krauss Jan 31 '20 at 20:58
  • Note: the RETURNS SETOF RECORD remembered here is not the same thing. – Peter Krauss Jan 31 '20 at 21:03
  • 1
    @PeterKrauss RETURNS SETOF <table_name> or RETURNS TABLE (LIKE <table_name>) – Ezequiel Tolnay Jan 31 '20 at 23:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.