Context
When a function returns a TABLE
or a SETOF composite-type
, like this sample function:
CREATE FUNCTION func(n int) returns table(i int, j bigint) as $$
BEGIN
RETURN QUERY select 1,n::bigint
union all select 2,n*n::bigint
union all select 3,n*n*n::bigint;
END
$$ language plpgsql;
the results can be accessed by various methods:
1) select * from func(3)
will produce these output columns :
i | j ---+--- 1 | 3 2 | 9 3 | 27
2) select func(3)
will produce only one output column of ROW type.
func ------- (1,3) (2,9) (3,27)
3) select (func(3)).*
will produce like #1:
i | j ---+--- 1 | 3 2 | 9 3 | 27
When the function argument comes from a table or a subquery, the syntax #3 is the only possible one, as in:
select N, (func(N)).* from (select 2 as N union select 3 as N) s;
or as in this related answer. If we had LATERAL JOIN
we could use that, but until PostgreSQL 9.3 is out, it's not supported, and the previous versions will still be used for years anyway.
Problem
Now the problem with syntax #3 is that the function is called as many times as there are columns in the result. There's no apparent reason for that, but it happens.
We can see it in version 9.2 by adding a RAISE NOTICE 'called for %', n
in the function. With the query above, it outputs:
NOTICE: called for 2 NOTICE: called for 2 NOTICE: called for 3 NOTICE: called for 3
Now if the function is changed to return 4 columns, like this:
CREATE FUNCTION func(n int) returns table(i int, j bigint,k int, l int) as $$
BEGIN
raise notice 'called for %', n;
RETURN QUERY select 1,n::bigint,1,1
union all select 2,n*n::bigint,1,1
union all select 3,n*n*n::bigint,1,1;
END
$$ language plpgsql stable;
then the same query outputs:
NOTICE: called for 2 NOTICE: called for 2 NOTICE: called for 2 NOTICE: called for 2 NOTICE: called for 3 NOTICE: called for 3 NOTICE: called for 3 NOTICE: called for 3
2 function calls were needed, 8 were actually made. The ratio is the number of output columns.
With syntax #2 that produces the same result except for the output columns layout, these multiple calls don't happen:
select N,func(N) from (select 2 as N union select 3 as N) s;
gives:
NOTICE: called for 2 NOTICE: called for 3
followed by the 6 resulting rows:
n | func ---+------------ 2 | (1,2,1,1) 2 | (2,4,1,1) 2 | (3,8,1,1) 3 | (1,3,1,1) 3 | (2,9,1,1) 3 | (3,27,1,1)
Questions
Is there a syntax or a construct with 9.2 that would achieve the expected result by doing only the minimum required function calls?
Bonus question: why do the multiple evaluations happen at all?