Search code examples
sqlpostgresqltypesplpgsqlreturn-type

Return a composite type or multiple columns from a PostgreSQL function


My aim is to write a function that takes in one parameter and returns two values. The query is working perfectly, however, when executed via the function made, I receive an error that a subquery should not return multiple columns.

My function is as follows:

CREATE TYPE double_integer_type AS (p1 integer, p2 integer);

DROP FUNCTION next_dvd_in_queue;

CREATE OR REPLACE FUNCTION next_dvd_in_queue (member_id_p1 integer) RETURNS double_integer_type as $$
BEGIN
RETURN(
    select temp2.dvdid,
       temp2.movie_title
from
    (select temp1.dvdid,
            temp1.movie_title,
            temp1.customer_priority
     from
         (select *
          from rentalqueue
          where rentalqueue.memberid=member_id_p1) temp1
     inner join dvd on dvd.dvdid=temp1.dvdid
     where dvd.dvdquantityonhand>0) temp2
order by temp2.customer_priority asc
limit 1
);
END; $$ LANGUAGE PLPGSQL

Call:

select dvdid from next_dvd_in_queue(3);

The query, when executed with a hard-coded value, is:

select temp2.dvdid,
       temp2.movie_title
from
    (select temp1.dvdid,
            temp1.movie_title,
            temp1.customer_priority
     from
         (select *
          from rentalqueue
          where rentalqueue.memberid=3) temp1
     inner join dvd on dvd.dvdid=temp1.dvdid
     where dvd.dvdquantityonhand>0) temp2
order by temp2.customer_priority asc
limit 1

The above query works fine.

However, when I call the function in the following way:

select * from next_dvd_in_queue(3);

I get the following error:

ERROR:  subquery must return only one column
LINE 1: SELECT (
               ^
QUERY:  SELECT (
    select temp2.dvdid,
       temp2.movie_title
from
    (select temp1.dvdid,
            temp1.movie_title,
            temp1.customer_priority
     from
         (select *
          from rentalqueue
          where rentalqueue.memberid=3) temp1
     inner join dvd on dvd.dvdid=temp1.dvdid
     where dvd.dvdquantityonhand>0) temp2
order by temp2.customer_priority asc
limit 1
)
CONTEXT:  PL/pgSQL function next_dvd_in_queue(integer) line 3 at RETURN

Solution

  • You can fix the syntax error with an explicit cast to the composite type:

    CREATE OR REPLACE FUNCTION next_dvd_in_queue (member_id_p1 integer)
      RETURNS double_integer_type AS
    $func$
    BEGIN
       RETURN (
           SELECT ROW(temp2.dvdid, temp2.movie_title)::double_integer_type
           FROM  ...
       );
    END
    $func$  LANGUAGE plpgsql

    But I would remove the needless complication with the composite type and use OUT parameters instead:

    CREATE OR REPLACE FUNCTION pg_temp.next_dvd_in_queue (member_id_p1 integer
                                                      OUT p1 integer
                                                      OUT p2 varchar(100)) AS
    $func$
    BEGIN
       SELECT INTO p1, p2
              temp2.dvdid, temp2.movie_title
       FROM  ...
    
    END
    $func$  LANGUAGE plpgsql;
    

    Avoid naming collisions between parameter names and column names. I like to stick to a naming convention where I prefix all parameter names with _, so _member_id_p1, _p1, _p2.

    Related: