Обсуждение: plpgsql function returning SETOF

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

plpgsql function returning SETOF

От
"Johannes Brügmann"
Дата:
Hello all novices and experts,

I didn't understand how to create a function, which returns a varying
number of records. Worse, it seems, that I didn't understand the SETOF
feature at all... Two examples:

1.)

should give two records in the year 1990: 1990.06.17 and 1990.10.03,
all other years have minimum 1 NULL record (so at some later point I
can filter with WHERE or so; only for timezone = 'CET' and datestyle =
'German')

       CREATE OR REPLACE FUNCTION tagderdeutscheneinheit(TIMESTAMP WITH TIME ZONE)
       RETURNS SETOF TIMESTAMP WITH TIME ZONE
       CALLED ON NULL INPUT STABLE AS $$
       DECLARE
            in_ts timestamp with time zone;
            rest_ts varchar(14);
                yyyy smallint;
       BEGIN
            IF $1 IS NULL
                THEN in_ts := localtimestamp(0);
                ELSE in_ts := $1;
            END IF;

           yyyy := to_number(date_part('year',in_ts),'9999');
           rest_ts := to_char(date_part('year',in_ts),'9999') || to_char(date_part('hour',in_ts),'99')
                  || to_char(date_part('minute',in_ts),'99') || to_char(date_part('seconds',in_ts),'99');

           SELECT CASE WHEN yyyy > 1951 AND yyyy <= 1990 THEN to_timestamp('03.10.' || rest_ts,'DD MM YYYY HH24 MI SS')
                    ELSE NULL
               END
        UNION ALL
        SELECT CASE WHEN yyyy >= 1990 THEN to_timestamp('17.06.' || rest_ts,'DD MM YYYY HH24 MI SS')
                    ELSE NULL
               END;

       END;
       $$ LANGUAGE plpgsql;

what i get:
      ERROR:  SELECT query has no destination for result data

2.)

similar to the above function, the below have been declared; each of
them returns exactly one value of type timestamp with time zone.

      CREATE TYPE feiertag AS (bezeichnung VARCHAR(100), datum TIMESTAMP WITH TIME ZONE);

      CREATE OR REPLACE FUNCTION feiertage(TIMESTAMP WITH TIME ZONE)
      RETURNS SETOF feiertag
      CALLED ON NULL INPUT AS $$
      DECLARE
           in_ts timestamp with time zone;
      BEGIN
           IF $1 IS NULL
               THEN in_ts := localtimestamp(0);
               ELSE in_ts := $1;
           END IF;

              SELECT 'Allerheiligen             ', allerheiligen(in_ts)
          UNION SELECT 'Aschermittwoch            ', aschermittwoch(in_ts)
        ...
          UNION SELECT 'Tag der deutschen Einheit ', tagderdeutscheneinheit(in_ts)
          UNION SELECT 'Zweiter Weihnachtstag     ', zweiterweihnachtstag(in_ts)  ;
      END;
      $$ LANGUAGE plpgsql;


a result "table" like this is what I want:

       bezeichnung     | datum
      -----------------+-------
       Allerheiligen   | ....

what i get:

      bruegmann@traffic_nrw=# select feiertage(NULL);
      ERROR:  SELECT query has no destination for result data
      HINT:  If you want to discard the results, use PERFORM instead.
      CONTEXT:  PL/pgSQL function "feiertage" line 9 at SQL statement

What I don't understand here is: this is a fixed number of records, a
composit type for setof has been declared, so what else is missing or
is wrong?

Thanks for any help,
Johannes


Re: plpgsql function returning SETOF

От
"A. Kretschmer"
Дата:
am  21.12.2005, um 16:54:43 +0100 mailte Johannes BrXgmann folgendes:
> Hello all novices and experts,
>
> I didn't understand how to create a function, which returns a varying
> number of records. Worse, it seems, that I didn't understand the SETOF
> feature at all... Two examples:
> 2.)
>
> similar to the above function, the below have been declared; each of
> them returns exactly one value of type timestamp with time zone.
>
>       CREATE TYPE feiertag AS (bezeichnung VARCHAR(100), datum TIMESTAMP WITH TIME ZONE);
>
>       CREATE OR REPLACE FUNCTION feiertage(TIMESTAMP WITH TIME ZONE)
>       RETURNS SETOF feiertag
>       CALLED ON NULL INPUT AS $$
>       DECLARE
>            in_ts timestamp with time zone;
>       BEGIN
>            IF $1 IS NULL
>                THEN in_ts := localtimestamp(0);
>                ELSE in_ts := $1;
>            END IF;
>
>               SELECT 'Allerheiligen             ', allerheiligen(in_ts)
>           UNION SELECT 'Aschermittwoch            ', aschermittwoch(in_ts)
>         ...
>           UNION SELECT 'Tag der deutschen Einheit ', tagderdeutscheneinheit(in_ts)
>           UNION SELECT 'Zweiter Weihnachtstag     ', zweiterweihnachtstag(in_ts)  ;
>       END;
>       $$ LANGUAGE plpgsql;
>
>
> a result "table" like this is what I want:
>
>        bezeichnung     | datum
>       -----------------+-------
>        Allerheiligen   | ....

You should return your rows ;-)

Visit http://www.varlena.com/GeneralBits/26, Chapter ' Returning Sets',
for a example for a SRF written in plpgsql.


HTH, Andreas
--
Andreas Kretschmer    (Kontakt: siehe Header)
Heynitz:  035242/47212,      D1: 0160/7141639
GnuPG-ID 0x3FFF606C http://wwwkeys.de.pgp.net
 ===    Schollglas Unternehmensgruppe    ===

Re: plpgsql function returning SETOF

От
"Johannes Brügmann"
Дата:
Hello Andreas,
hello novices and experts,

first of all thank you very much for your immediate response! The hint
is great but surprising to me.

"A. Kretschmer" <andreas.kretschmer@schollglas.com> writes:

> am  21.12.2005, um 16:54:43 +0100 mailte Johannes BrXgmann folgendes:
>>
>> I didn't understand how to create a function, which returns a varying
>> number of records. Worse, it seems, that I didn't understand the SETOF
>> feature at all...
>>
>> a result "table" like this is what I want:
>>
>>        bezeichnung     | datum
>>       -----------------+-------
>>        Allerheiligen   | ....
>
> You should return your rows ;-)

Thank you very much!

I always had this in mind:

,----[ ${doc}/postgresql-8.0.3/html/xfunc-sql.html ]
| 31.4.4. SQL Functions Returning Sets
|
| When an SQL function is declared as returning SETOF sometype, the
| function's final SELECT query is executed to completion, and each row
| it outputs is returned as an element of the result set.
`----[ end ]

But this is SQL isn't it, ;-)? And SQL doesn't support timestamp, does
it? (AAaaarrggghhhh...)

A new problem is now, that i still can't get it to work after all:

      CREATE TYPE feiertag AS (bezeichnung VARCHAR(100), datum TIMESTAMP WITH TIME ZONE);

      CREATE OR REPLACE FUNCTION feiertage(TIMESTAMP WITH TIME ZONE)
      RETURNS SETOF feiertag
      CALLED ON NULL INPUT AS $$
      DECLARE
           in_ts timestamp with time zone;
          curr feiertag%ROWTYPE;
          r RECORD;
      BEGIN
           IF $1 IS NULL
               THEN in_ts := localtimestamp(0);
               ELSE in_ts := $1;
           END IF;

              FOR r IN SELECT f.b AS b, f.d AS d
                       FROM (
                             SELECT 'Allerheiligen             ' AS b, allerheiligen(in_ts)          AS d
                         UNION SELECT 'Aschermittwoch            ' AS b, aschermittwoch(in_ts)          AS d
                             ...
                         UNION SELECT 'Tag der deutschen Einheit ' AS b, tagderdeutscheneinheit(in_ts)  AS d
                         UNION SELECT 'Zweiter Weihnachtstag     ' AS b, zweiterweihnachtstag(in_ts)    AS d) AS f
              LOOP
              curr.bezeichnung := r.b;
              curr.datum       := r.d;
              RETURN NEXT curr;
          END LOOP;
          RETURN;

      END;
      $$ LANGUAGE plpgsql;

Where is the bug now?

Thanks a lot!
Johannes


Re: plpgsql function returning SETOF

От
"Johannes Brügmann"
Дата:
Hello Andreas,
hello novices and experts,

> first of all thank you very much for your immediate response! The hint
> is great but surprising to me.
>
> "A. Kretschmer" <andreas.kretschmer@schollglas.com> writes:
>
>> am  21.12.2005, um 16:54:43 +0100 mailte Johannes BrXgmann folgendes:
>>>
>>> I didn't understand how to create a function, which returns a varying
>>> number of records. Worse, it seems, that I didn't understand the SETOF
>>> feature at all...
>>>
>>> a result "table" like this is what I want:
>>>
>>>        bezeichnung     | datum
>>>       -----------------+-------
>>>        Allerheiligen   | ....
>>
>> You should return your rows ;-)
>
> Thank you very much!
>
> I always had this in mind:
>
> ,----[ ${doc}/postgresql-8.0.3/html/xfunc-sql.html ]
> | 31.4.4. SQL Functions Returning Sets
> |
> | When an SQL function is declared as returning SETOF sometype, the
> | function's final SELECT query is executed to completion, and each row
> | it outputs is returned as an element of the result set.
> `----[ end ]
>
> But this is SQL isn't it, ;-)? And SQL doesn't support timestamp, does
> it? (AAaaarrggghhhh...)
>
> A new problem is now, that i still can't get it to work after all:
>
>       CREATE TYPE feiertag AS (bezeichnung VARCHAR(100), datum TIMESTAMP WITH TIME ZONE);
>
>       CREATE OR REPLACE FUNCTION feiertage(TIMESTAMP WITH TIME ZONE)
>       RETURNS SETOF feiertag
>       CALLED ON NULL INPUT AS $$
>       DECLARE
>            in_ts timestamp with time zone;
>           curr feiertag%ROWTYPE;
>           r RECORD;
>       BEGIN
>            IF $1 IS NULL
>                THEN in_ts := localtimestamp(0);
>                ELSE in_ts := $1;
>            END IF;
>
>               FOR r IN SELECT f.b AS b, f.d AS d
>                        FROM (
>                              SELECT 'Allerheiligen             ' AS b, allerheiligen(in_ts)          AS d
>                          UNION SELECT 'Aschermittwoch            ' AS b, aschermittwoch(in_ts)          AS d
>                              ...
>                          UNION SELECT 'Tag der deutschen Einheit ' AS b, tagderdeutscheneinheit(in_ts)  AS d
>                          UNION SELECT 'Zweiter Weihnachtstag     ' AS b, zweiterweihnachtstag(in_ts)    AS d) AS f
>               LOOP
>               curr.bezeichnung := r.b;
>               curr.datum       := r.d;
>               RETURN NEXT curr;
>           END LOOP;
>           RETURN;
>
>       END;
>       $$ LANGUAGE plpgsql;
>
> Where is the bug now?

sorry, i missed the error message:

      bruegmann@traffic_nrw=# select feiertage(NULL);
      ERROR:  set-valued function called in context that cannot accept a set
      CONTEXT:  PL/pgSQL function "feiertage" line 30 at return next

Thanks in advance,
Johannes


Re: plpgsql function returning SETOF

От
Andreas Kretschmer
Дата:
Johannes Brügmann <johannes@jottbee.org> schrieb:
> I always had this in mind:
>
> ,----[ ${doc}/postgresql-8.0.3/html/xfunc-sql.html ]
> | 31.4.4. SQL Functions Returning Sets
> |
> | When an SQL function is declared as returning SETOF sometype, the
> | function's final SELECT query is executed to completion, and each row
> | it outputs is returned as an element of the result set.
> `----[ end ]

If you write the function in the language 'sql', then this is right.
But in your function there are things like 'if...', so you need plpgsql.

>
> But this is SQL isn't it, ;-)? And SQL doesn't support timestamp, does
> it? (AAaaarrggghhhh...)

???
timestamp and timestamptz are valid types in SQL.


> A new problem is now, that i still can't get it to work after all:
>
>       CREATE TYPE feiertag AS (bezeichnung VARCHAR(100), datum TIMESTAMP WITH TIME ZONE);
>
>       CREATE OR REPLACE FUNCTION feiertage(TIMESTAMP WITH TIME ZONE)
> ...
>       END;
>       $$ LANGUAGE plpgsql;
>
> Where is the bug now?

I can't see a error-message, sorry ;-)


Btw.: we have a german mailing list too ;-)

HTH, Andreas
--
Really, I'm not out to destroy Microsoft. That will just be a completely
unintentional side effect. (Linus Torvalds)
Kaufbach, Saxony, Germany, Europe.              N 51.05082°, E 13.56889°

Re: plpgsql function returning SETOF

От
Tom Lane
Дата:
"Johannes Br�gmann" <johannes@jottbee.org> writes:
> sorry, i missed the error message:

>       bruegmann@traffic_nrw=# select feiertage(NULL);
>       ERROR:  set-valued function called in context that cannot accept a set
>       CONTEXT:  PL/pgSQL function "feiertage" line 30 at return next

You need to do
    select * from your_function(...);
not just
    select your_function(...);

            regards, tom lane

Re: plpgsql function returning SETOF

От
"Johannes Brügmann"
Дата:
Hello Andreas,
hello novices and experts,

thanks again for your immediate help!

Andreas Kretschmer <akretschmer@spamfence.net> writes:

> Johannes Brügmann <johannes@jottbee.org> schrieb:
>> I always had this in mind:
>>
>> ,----[ ${doc}/postgresql-8.0.3/html/xfunc-sql.html ]
>> | 31.4.4. SQL Functions Returning Sets
>> |
>> | When an SQL function is declared as returning SETOF sometype, the
>> | function's final SELECT query is executed to completion, and each row
>> | it outputs is returned as an element of the result set.
>> `----[ end ]
>
> If you write the function in the language 'sql', then this is right.
> But in your function there are things like 'if...', so you need plpgsql.

This is what i tried after understanding it with your help, but i got
a syntax error in the DECLARE part just before timestamptz, so i
thought the type wouldn't be supported by language 'sql'. I didn't
know that the IF control expressions where the bad guys. Maybe I try
it again in language 'sql', because IF should be replacable in what i
want.

>> But this is SQL isn't it, ;-)? And SQL doesn't support timestamp, does
>> it? (AAaaarrggghhhh...)
>
> ???
> timestamp and timestamptz are valid types in SQL.

yes, i wrote it before i thought about it; sorry.

>> A new problem is now, that i still can't get it to work after all:
>>
>>       CREATE TYPE feiertag AS (bezeichnung VARCHAR(100), datum TIMESTAMP WITH TIME ZONE);
>>
>>       CREATE OR REPLACE FUNCTION feiertage(TIMESTAMP WITH TIME ZONE)
>> ...
>>       END;
>>       $$ LANGUAGE plpgsql;
>>
>> Where is the bug now?
>
> I can't see a error-message, sorry ;-)

yes, i recognized my mistake already.. The other posting contains the solution.

> Btw.: we have a german mailing list too ;-)

Aah, next time!

Thanks you very much for your patience and the immediate help
Johannes


Re: plpgsql function returning SETOF

От
"Johannes Brügmann"
Дата:
Hello Tom,
hello novices and experts,

Tom Lane <tgl@sss.pgh.pa.us> writes:

> "Johannes Brügmann" <johannes@jottbee.org> writes:
>> sorry, i missed the error message:
>
>>       bruegmann@traffic_nrw=# select feiertage(NULL);
>>       ERROR:  set-valued function called in context that cannot accept a set
>>       CONTEXT:  PL/pgSQL function "feiertage" line 30 at return next
>
> You need to do
>     select * from your_function(...);
> not just
>     select your_function(...);

yes, that's it! Now it works, yeaaaah!

Thanks a lot to you, Tom, for your immediate help!
Johannes