Ced
Ced

Reputation: 17327

Return a record from function

I'm trying to return multiple values in plpgsql with a very simplistic test function. The test function takes an integer as input and is supposed to return if it's positive, negative and zero. I've seen some example but they are all over complicated and go over my head.

Here is what i tried:

create or replace function test(v integer)
returns record as $$

BEGIN

    IF v > 0
        THEN return true as positive, false as negative, false as zero;
    ELSEIF v < 0
        THEN return false as positive, true as negative, false as zero;
    ELSE 
        return false as positive, false as negative, true as zero;
    END IF;

END;
$$ LANGUAGE plpgsql;

Also tried this

create or replace function test(v integer)
returns record as $$

DECLARE 
    result record;

BEGIN

    IF v > 0
        THEN 
            result.positive = true;
            result.negative = false;
            result.zero = false;

    ELSEIF v < 0
        THEN 
            result.positive = false;
            result.negative = true;
            result.zero = false;
    ELSE 
            result.positive = false;
            result.negative = false;
            result.zero = true;
    return result;
    END IF;

END;
$$ LANGUAGE plpgsql;

ALSO this:

IF v > 0
    THEN 
        SELECT true, false, false into
        result.positive, result.negative, result.zero;

ELSEIF v < 0
    THEN 
        SELECT false, true, false into
        result.positive, result.negative, result.zero;
ELSE 
        SELECT false, false, true into
        result.positive, result.negative, result.zero;
return result;
END IF;

Upvotes: 1

Views: 1820

Answers (3)

Doc Day
Doc Day

Reputation: 11

Here's a solution to the "return multiple values" problem that works for me.

Frankly, it took WAAAAAY too long to find a solution to such a common need! Like the OP, most examples I've seen are over complicated and go over my head. And of course the only reason I'm even looking for examples is because the docs aren't clear to this beginner. Here's hoping this will help someone.

Paste the code below into a pgAdmin query window, choose one of the invocation solutions, then execute.

create or replace function receive_two_values() returns void language plpgsql as
$main$
declare
    returned record;
begin

    create or replace function return_two_arguments(
            arg1    text,
            arg2    text,
        out return1 text,
        out return2 text)
        language plpgsql as --"Returns record" is implied by multiple outs, so unnecessary here.
    $$
    begin
        return1 := arg1;
        return2 := arg2;
    end;
    $$;

    returned = return_two_arguments('Hello', 'World');
    raise notice 'return1="%"', returned.return1;
    raise notice 'return2="%"', returned.return2;

end;
$main$;

--Invoke in one of at least two ways:

--ONE:
    select receive_two_values();

--TWO:
    do language plpgsql
    $$
    begin
        perform receive_two_values();
    end;
    $$;


--Outputs:
--NOTICE:  return1="Hello"
--NOTICE:  return2="World"

Upvotes: 1

Laurenz Albe
Laurenz Albe

Reputation: 246082

I would solve the problem as follows:

CREATE TYPE sign AS ENUM ('negative', 'zero', 'positive');

CREATE FUNCTION test(integer) RETURNS sign
   LANGUAGE sql IMMUTABLE STRICT AS
$$SELECT CASE
            WHEN $1 < 0
            THEN 'negative'
            WHEN $1 > 0
            THEN 'positive'
            ELSE 'zero'
         END::sign$$;

If you want to return a record like you do in your examples, you'll have to use the ROW() constructor to create a single composite value from the three booleans:

RETURN ROW(true, false, false);

But this solution has its drawbacks: you cannot access the individual fields of the composite type in SQL, because PostgreSQL doesn't know the fields at parse time:

test=> SELECT test(42);
┌─────────┐
│  test   │
├─────────┤
│ (t,f,f) │
└─────────┘
(1 row)

test=> SELECT (test(42)).*;
ERROR:  record type has not been registered

test=> SELECT * FROM test(42);
ERROR:  a column definition list is required for functions returning "record"
LINE 1: SELECT * FROM test(42);
                      ^

You'd have to specify the record definition in the query:

test=> SELECT positive FROM test(42) test(positive boolean, negative boolean, zero boolean);
┌──────────┐
│ positive │
├──────────┤
│ t        │
└──────────┘
(1 row)

But that kind of defies your goal of variable records. This is why such functions are less useful than the answer you referenced tries to make believe.

Upvotes: 1

Ced
Ced

Reputation: 17327

This will return a record, so the result will be ( true, false, false).

CREATE OR REPLACE FUNCTION test(v integer)
  RETURNS record AS $$

DECLARE 
    result record;

BEGIN
        SELECT true , false , false  into result;
return result;

END;

The issue is that the result isn't named so you have this ( true, false, false) and you want to get each value separately. To do so you can do this:

select a, b, c from test(1) AS (a BOOL, b bool, c bool);

Upvotes: 1

Related Questions