Обсуждение: Record with a field consisting of table rows

Поиск
Список
Период
Сортировка

Record with a field consisting of table rows

От
Jon Smark
Дата:
Hi,

I am trying to create a PL/pgSQL function whose return type is a tuple
consisting of an integer and a list of table rows.  I emulate the tuple
by defining a record 'page_t' with the two fields; however, the naïve
approach of doing a SELECT INTO one the record's fields does not work
(see function 'get_page') below.  Am I missing something obvious here?

Thanks in advance!
Jon


create table users
        (
        uid     int4 not null,
        name    text not null,
        age     int4 not null,
        primary key (uid)
        );

create type user_t AS
        (
        uid     int4,
        name    text,
        age     int4
        );

create type page_t AS
        (
        total   int4,
        users   user_t[]
        );

create function get_page ()
returns page_t
language plpgsql as
$$
declare
        _page   page_t;
begin
        _page.total := select count (*) from users;
        select * into _page.users from users limit 10;
        return _page;
end
$$;





Re: Record with a field consisting of table rows

От
Pavel Stehule
Дата:
Hello

try to SELECT INTO ARRAY(SELECT user_t FROM users LIMIT 10) _page.users;

Regards

Pavel Stehule


2011/1/13 Jon Smark <jon.smark@yahoo.com>:
> Hi,
>
> I am trying to create a PL/pgSQL function whose return type is a tuple
> consisting of an integer and a list of table rows.  I emulate the tuple
> by defining a record 'page_t' with the two fields; however, the naïve
> approach of doing a SELECT INTO one the record's fields does not work
> (see function 'get_page') below.  Am I missing something obvious here?
>
> Thanks in advance!
> Jon
>
>
> create table users
>        (
>        uid     int4 not null,
>        name    text not null,
>        age     int4 not null,
>        primary key (uid)
>        );
>
> create type user_t AS
>        (
>        uid     int4,
>        name    text,
>        age     int4
>        );
>
> create type page_t AS
>        (
>        total   int4,
>        users   user_t[]
>        );
>
> create function get_page ()
> returns page_t
> language plpgsql as
> $$
> declare
>        _page   page_t;
> begin
>        _page.total := select count (*) from users;
>        select * into _page.users from users limit 10;
>        return _page;
> end
> $$;
>
>
>
>
>
> --
> Sent via pgsql-general mailing list (pgsql-general@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-general
>

Re: Record with a field consisting of table rows

От
Tom Lane
Дата:
Jon Smark <jon.smark@yahoo.com> writes:
> create type page_t AS
>         (
>         total   int4,
>         users   user_t[]
>         );

> create function get_page ()
> returns page_t
> language plpgsql as
> $$
> declare
>         _page   page_t;
> begin
>         _page.total := select count (*) from users;
>         select * into _page.users from users limit 10;
>         return _page;
> end
> $$;

That is certainly not going to work: that select does not produce an
array, it produces a column of user_t (of which SELECT INTO is only
gonna take the first, anyway).

Untested, but I think you'd have better results with

    _page.users := array(select users from users limit 10);

It'd also be smart to get rid of user_t and rely directly on the "users"
rowtype associated with the users table.

            regards, tom lane

Re: Record with a field consisting of table rows

От
Jon Smark
Дата:
Hi,

> That is certainly not going to work: that select does not produce an
> array, it produces a column of user_t (of which SELECT INTO is only
> gonna take the first, anyway).
>
> Untested, but I think you'd have better results with
>
>     _page.users := array(select users from users limit 10);

The above does work, thanks.  There is however one drawback: the type
associated with _page.users is now an array.  Is there a way to make
it a 'SETOF user_t'?

Best regards,
Jon





Re: Record with a field consisting of table rows

От
Pavel Stehule
Дата:
2011/1/13 Jon Smark <jon.smark@yahoo.com>:
> Hi,
>
>> That is certainly not going to work: that select does not produce an
>> array, it produces a column of user_t (of which SELECT INTO is only
>> gonna take the first, anyway).
>>
>> Untested, but I think you'd have better results with
>>
>>     _page.users := array(select users from users limit 10);
>
> The above does work, thanks.  There is however one drawback: the type
> associated with _page.users is now an array.  Is there a way to make
> it a 'SETOF user_t'?
>

no. PostgreSQL doesn't supports SET. Only arrays are supported.

Regards

Pavel

> Best regards,
> Jon
>
>
>
>
>
> --
> Sent via pgsql-general mailing list (pgsql-general@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-general
>

Re: Record with a field consisting of table rows

От
Alban Hertroys
Дата:
On 13 Jan 2011, at 19:25, Pavel Stehule wrote:

>> The above does work, thanks.  There is however one drawback: the type
>> associated with _page.users is now an array.  Is there a way to make
>> it a 'SETOF user_t'?
>>
>
> no. PostgreSQL doesn't supports SET. Only arrays are supported.


I'm not sure what you mean here, Postgres certainly _does_ support set-returning functions. Maybe you were referring to
somethingin the particular context of the problem the OP is trying to solve? 

It would be kind of bad if people Google for this topic and would come back with the wrong conclusion.

Alban Hertroys

--
Screwing up is an excellent way to attach something to the ceiling.


!DSPAM:737,4d2f4de411871554341128!



Re: Record with a field consisting of table rows

От
Alban Hertroys
Дата:
On 13 Jan 2011, at 17:22, Jon Smark wrote:

> create type page_t AS
>        (
>        total   int4,
>        users   user_t[]
>        );
>
> create function get_page ()
> returns page_t
> language plpgsql as
> $$
> declare
>        _page   page_t;
> begin
>        _page.total := select count (*) from users;
>        select * into _page.users from users limit 10;
>        return _page;
> end
> $$;


I think it would be easier to rewrite that to a set-returning function returning TABLE (...).

Something like this (untested):

create function get_page ()
returns setof table (total int, user users)
language plpgsql as
$$
declare
    _total   int;
begin
    _total := select count (*) from users;

    return query select _total AS total, u from users AS u limit 10;
end
$$;

In general it is considered a bad idea to rely on what * returns though, it's better to return the columns explicitly.

Which makes me wonder, is table cloning supported for these cases? For example:

create function get_page ()
returns setof table (LIKE users, total int)
...



Alban Hertroys

--
Screwing up is an excellent way to attach something to the ceiling.


!DSPAM:737,4d2f50bf11877227918328!



Re: Record with a field consisting of table rows

От
Pavel Stehule
Дата:
2011/1/13 Alban Hertroys <dalroi@solfertje.student.utwente.nl>:
> On 13 Jan 2011, at 19:25, Pavel Stehule wrote:
>
>>> The above does work, thanks.  There is however one drawback: the type
>>> associated with _page.users is now an array.  Is there a way to make
>>> it a 'SETOF user_t'?
>>>
>>
>> no. PostgreSQL doesn't supports SET. Only arrays are supported.
>
>
> I'm not sure what you mean here, Postgres certainly _does_ support set-returning functions. Maybe you were referring
tosomething in the particular context of the problem the OP is trying to solve? 
>

The name of feature "SET RETURNED FUNC" doesn't mean so PostgreSQL
supports SET type in ANSI SQL sense.

Regards

Pavel Stehule

> It would be kind of bad if people Google for this topic and would come back with the wrong conclusion.
>
> Alban Hertroys
>
> --
> Screwing up is an excellent way to attach something to the ceiling.
>
>
> !DSPAM:1030,4d2f4de011871371264419!
>
>
>

Re: Record with a field consisting of table rows

От
Alban Hertroys
Дата:
On 13 Jan 2011, at 20:21, Pavel Stehule wrote:

>> I'm not sure what you mean here, Postgres certainly _does_ support set-returning functions. Maybe you were referring
tosomething in the particular context of the problem the OP is trying to solve? 
>>
>
> The name of feature "SET RETURNED FUNC" doesn't mean so PostgreSQL
> supports SET type in ANSI SQL sense.


I think this is getting off topic, but I don't understand what you're trying to say here. That's probably partially due
toyour odd usage of the word "so" - I think you mean to use it as "that" (which it doesn't mean), and not as "because"
(whichits meaning is much closer to). 
You might also want to elaborate a bit more so that people have enough context to work around your grammar ;)

Anyway, are you saying:
1. PostgreSQL doesn't support SRF's (It does though)? Or that,
2. ANSI SQL doesn't support SRF's? Or that
3. PostgreSQL's implementation of SRF's is not compatible with the ANSI SQL definition?

Or something entirely different?

Alban Hertroys

--
If you can't see the forest for the trees,
cut the trees and you'll see there is no forest.


!DSPAM:737,4d2f54ac11871133719602!



Re: Record with a field consisting of table rows

От
Pavel Stehule
Дата:
2011/1/13 Alban Hertroys <dalroi@solfertje.student.utwente.nl>:
> On 13 Jan 2011, at 20:21, Pavel Stehule wrote:
>
>>> I'm not sure what you mean here, Postgres certainly _does_ support set-returning functions. Maybe you were
referringto something in the particular context of the problem the OP is trying to solve? 
>>>
>>
>> The name of feature "SET RETURNED FUNC" doesn't mean so PostgreSQL
>> supports SET type in ANSI SQL sense.
>
>
> I think this is getting off topic, but I don't understand what you're trying to say here. That's probably partially
dueto your odd usage of the word "so" - I think you mean to use it as "that" (which it doesn't mean), and not as
"because"(which its meaning is much closer to). 
> You might also want to elaborate a bit more so that people have enough context to work around your grammar ;)
>
> Anyway, are you saying:
> 1. PostgreSQL doesn't support SRF's (It does though)? Or that,
> 2. ANSI SQL doesn't support SRF's? Or that
> 3. PostgreSQL's implementation of SRF's is not compatible with the ANSI SQL definition?
>

sorry, my English isn't good. A problem is in meaning of keyword
"SET". ANSI SQL knows a datatype "SET". This datatype isn't supported
by pg. A SRF functions are called table functions in ANSI SQL. But I
can be messed

Pavel



> Or something entirely different?
>
> Alban Hertroys
>
> --
> If you can't see the forest for the trees,
> cut the trees and you'll see there is no forest.
>
>
> !DSPAM:1030,4d2f54a811877157859450!
>
>
>

Re: Record with a field consisting of table rows

От
Jon Smark
Дата:
Hi,

This thread has veered off-topic, but my original problem still remains.
Basically, I want to declare a function that returns a tuple consisting
of an integer and a set of table rows.  Something like the following:

CREATE FUNCTION foobar () RETURNS (int4, SETOF users)

Now, if I understand correctly, the only way to have a function return
a tuple is to create a named record and have the function return that
record:

CREATE TYPE page_t AS
  (
  total  int4
  users  SETOF users
  )

Which is of course not grammatical.  So, how does one solve this problem
in PL/pgSQL?

Thanks in advance!
Jon





Re: Record with a field consisting of table rows

От
Pavel Stehule
Дата:
2011/1/14 Jon Smark <jon.smark@yahoo.com>:
> Hi,
>
> This thread has veered off-topic, but my original problem still remains.
> Basically, I want to declare a function that returns a tuple consisting
> of an integer and a set of table rows.  Something like the following:
>
> CREATE FUNCTION foobar () RETURNS (int4, SETOF users)
>

you have to use a array

CREATE FUNCTION foo(... OUT total int, OUT _users users[])

there are no other way

Regards

Pavel Stehule


> Now, if I understand correctly, the only way to have a function return
> a tuple is to create a named record and have the function return that
> record:
>
> CREATE TYPE page_t AS
>  (
>  total  int4
>  users  SETOF users
>  )
>
> Which is of course not grammatical.  So, how does one solve this problem
> in PL/pgSQL?
>
> Thanks in advance!
> Jon
>
>
>
>
>

Re: Record with a field consisting of table rows

От
Alban Hertroys
Дата:
On 14 Jan 2011, at 16:03, Jon Smark wrote:

> Hi,
>
> This thread has veered off-topic, but my original problem still remains.
> Basically, I want to declare a function that returns a tuple consisting
> of an integer and a set of table rows.  Something like the following:
>
> CREATE FUNCTION foobar () RETURNS (int4, SETOF users)
>
> Now, if I understand correctly, the only way to have a function return
> a tuple is to create a named record and have the function return that
> record:


Nope, see my reply from yesterday around 20:23
You can return a table instead, with the count added as an extra column.

Alban Hertroys

--
Screwing up is an excellent way to attach something to the ceiling.


!DSPAM:737,4d308e2711871421280230!



Re: Record with a field consisting of table rows

От
Jon Smark
Дата:
Hi,

> Nope, see my reply from yesterday around 20:23
> You can return a table instead, with the count added as an
> extra column.

I did see your solution, but note that it does not return a tuple
consisting of an integer and a setof (as I wanted), but instead
returns a setof of a tuple.

I still haven't found a solution to the original problem.  The
best I can do so far is to create a function that returns a tuple
consisting of an int and the first row of table results (see below).
Any more thoughts?

Best regards,
Jon

create table users
        (
        uid     int4 not null,
        name    text not null,
        age     int4 not null,
        primary key (uid)
        );

create type page_t AS
        (
        total   int4,
        users   users
        );


create function get_page ()
returns page_t
language plpgsql as
$$
declare
        _total  int4;
        _users  users;
        _page   page_t;
begin
        select count (*) from users into _total;
        select * from users limit 10 into _users;
        _page := row (_total, _users);
        return _page;
end
$$;





Re: Record with a field consisting of table rows

От
Alban Hertroys
Дата:
On 15 Jan 2011, at 17:01, Jon Smark wrote:

> Hi,
>
>> Nope, see my reply from yesterday around 20:23
>> You can return a table instead, with the count added as an
>> extra column.
>
> I did see your solution, but note that it does not return a tuple
> consisting of an integer and a setof (as I wanted), but instead
> returns a setof of a tuple.

No, of course not. A function cannot return different amounts of different return-values in any language I know of.

You don't seem to grasp the essence of what a set-returning function does; they are similar to Iterators in Java or
functionsthat yield a result (as opposed to returning one) in Python or cursors in SQL. 
You can't mix that with returning a single value, unless you do that each time the function is called for the next
iteration(which happens to be what I did in my earlier example). 

You could do something ugly by collecting the results in memory until you eventually return them in an array, but that
wouldhardly be practical if your data sets get a little large. 

You probably can return a tuple consisting of an int and a refcursor though. You'll need another function to loop
throughthe refcursor to fetch the results, but you would sort of get what you apparently want. I can't see why you'd
wantthat though. 

You may also be able to return the count as an OUT-parameter, but I'm not sure you can mix that with returning a SETOF
somethingelse. I expect you'll find out that isn't possible. Obtaining both results from the function (if it _is_
possible)will be tricky I think. 

Alban Hertroys

--
If you can't see the forest for the trees,
cut the trees and you'll see there is no forest.


!DSPAM:737,4d31da3e11871342110173!



Re: Record with a field consisting of table rows

От
Jon Smark
Дата:
Hi,

> No, of course not. A function cannot return different
> amounts of different return-values in any language I know
> of.

Come on, you make it sound like it's something inconceivable or exotic,
even though pretty much any strongly-typed language with a post-1970s
type-system will allow a function to return tuples of arbitrary values.
Have you never come across Ocaml, Haskell, Scala, and so forth?


> You don't seem to grasp the essence of what a set-returning
> function does; they are similar to Iterators in Java or
> functions that yield a result (as opposed to returning one)
> in Python or cursors in SQL.
>
> You can't mix that with returning a single value, unless
> you do that each time the function is called for the next
> iteration (which happens to be what I did in my earlier
> example).
>
> You could do something ugly by collecting the results in
> memory until you eventually return them in an array, but
> that would hardly be practical if your data sets get a
> little large.
>
> You probably can return a tuple consisting of an int and a
> refcursor though. You'll need another function to loop
> through the refcursor to fetch the results, but you would
> sort of get what you apparently want. I can't see why you'd
> want that though.

Yes, the alternatives are indeed more cumbersome than they're worth.
I might as well split the original function into two separate functions...

Cheers,
Jon





Re: Record with a field consisting of table rows

От
Alban Hertroys
Дата:
On 15 Jan 2011, at 21:57, Jon Smark wrote:

>> No, of course not. A function cannot return different
>> amounts of different return-values in any language I know
>> of.
>
> Come on, you make it sound like it's something inconceivable or exotic,
> even though pretty much any strongly-typed language with a post-1970s
> type-system will allow a function to return tuples of arbitrary values.
> Have you never come across Ocaml, Haskell, Scala, and so forth?


A tuple wouldn't be unusual, but you're not asking for a simple tuple here. What you're asking for is a value and a set
oftuples. That would sort of look like this: 

total | users
---------------------------------------------------------
 3    | foo
      | bar
      | baz

And what's worse, after the first returned tuple you only seem to want the users value, without the total value. I
don'tknow of any language that can do that and I'm pretty sure the languages you mentioned can't do that either. It
makesparsing the return values unnecessary difficult, I bet that's a compelling enough reason that nobody seems to have
implementedit. 


What most languages, including pgsql, _can_ do is this:

total | users
---------------------------------------------------------
 3    | foo
 3    | bar
 3    | baz


Or this:

total | users
---------------------------------------------------------
 3    | [foo, bar, baz]


Alban Hertroys

--
Screwing up is an excellent way to attach something to the ceiling.


!DSPAM:737,4d32398a11872893390349!



Re: Record with a field consisting of table rows

От
Jon Smark
Дата:
Hi,

> A tuple wouldn't be unusual, but you're not asking for a
> simple tuple here. What you're asking for is a value and a
> set of tuples.

Which is in itself a tuple!  This discussion is again veering slightly
off-topic, but note that tuples as mathematical objects do not have such
arbitrary limitations.


> And what's worse, after the first returned tuple you only
> seem to want the users value, without the total value. I
> don't know of any language that can do that and I'm pretty
> sure the languages you mentioned can't do that either. It
> makes parsing the return values unnecessary difficult, I bet
> that's a compelling enough reason that nobody seems to have
> implemented it.

Again, you're making it sound like I'm requesting something exotic,
while any language with a modern type system allows this and much
more without blinking.  In Ocaml, for example, the return type 'page_t'
of my function could be defined as follows:

type user_t = { uid: int; name: string; age: int; }
type page_t = int * user_t list

('user_'t is declared as a record with fields 'uid', 'name', and 'age';
'page_t' is defined as a tuple consisting of an integer and a list of
'user_t').

Moreover, we've already established that PL/pgSQL also allows the return
of a tuple consisting of an integer and an array of tuples.  All I wanted
was to change the latter portion into SETOF tuples...

Best regards,
Jon





Re: Record with a field consisting of table rows

От
Alban Hertroys
Дата:
On 16 Jan 2011, at 17:40, Jon Smark wrote:

> Again, you're making it sound like I'm requesting something exotic,
> while any language with a modern type system allows this and much
> more without blinking.  In Ocaml, for example, the return type 'page_t'
> of my function could be defined as follows:
>
> type user_t = { uid: int; name: string; age: int; }
> type page_t = int * user_t list

That's the refcursor option I gave you earlier.

Alban Hertroys

--
Screwing up is an excellent way to attach something to the ceiling.


!DSPAM:737,4d33222411702065417953!