Re: multiple function execute using (func()).*

From: "Merlin Moncure" <mmoncure(at)gmail(dot)com>
To: "PostgreSQL Hackers" <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: multiple function execute using (func()).*
Date: 2008-12-09 14:52:02
Message-ID: b42b73150812090652i1f48ca04tc0e5b76a28ddc40f@mail.gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On Mon, Dec 8, 2008 at 5:15 PM, Merlin Moncure <mmoncure(at)gmail(dot)com> wrote:
> Hello,
>
> I've been bit by this about a million times:
>
> select (func()).* executes the function once per each field in the
> returned tuple. See the example below:
>
> create function foo_func() returns foo as
> $$
> declare f foo;
> begin
> raise notice '!';
> return f;
> end;
> $$ language plpgsql;
>
> postgres=# select (foo_func()).*;
> NOTICE: !
> NOTICE: !
> NOTICE: !
> a | b | c
> ---+---+---
> | |
> (1 row)
>
> This is an anathema to any query trying to use composite types to
> circumvent single field subquery restrictions (for example, when using
> a record aggregate to choose a row). Normally you can work around
> this by writing it like this:
>
> select (foo_func()).*; -> select * from foo_func();
>
> Now, aside from the fact that these to forms should reasonably produce
> the same result, there are a couple of cases where the shorter,
> without 'from' version is easier to write. One example is in 'CREATE
> RULE', since you can't use 'new' in queries using the long form:
>
> postgres=# create or replace rule ins_foo as on insert to foo
> postgres-# do instead select * from add_foo(new);
> ERROR: subquery in FROM cannot refer to other relations of same query level

CTE to the rescue.

my wider problem was that I was trying to set up a rule like this:
create table foo(...);
create table bar(...);
create view foobar as select * from foo join bar using (...);

create function add_foobar(foobar) returns foobar as $$...$$ language plpgsql;
create or replace rule ins_foobar as on insert to foobar
do instead select (add_foobar(new)).*;

The idea is that the rule calls the add function but returns the
adjusted composite so that insertions to foobar behave properly in
queries using 'returning'. This turned out to be quite a bugaboo. I
simply refused on principle to have add_foobar() explicitly list the
fields for foobar, that is, not use the composite type. The longer
form, select * from func(), was completely blocked because of subquery
prohibitions on touching 'new'.

However, this works:
create or replace rule ins_foobar as on insert to foobar
do instead with fb as (select add_foobar(new) as n) select (n).* from fb;

Another great use of the already awesome CTE feature! :-D

merlin

p.s. I still think the SQL standard is wrong, and invalidation events
should re-attempt the source sql (or, the '*' concept needs get to get
pushed into the plan). oh well...

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Pavel Stehule 2008-12-09 14:53:03 Re: WIP: default values for function parameters
Previous Message Magnus Hagander 2008-12-09 14:36:35 Re: new libpq SSL connection option