Reputation: 16439
This works, but I need the casts (::name_value_pair
) That seems a bit ugly to me. Is there a way to do it without the casts?
create type name_value_pair as (name text, value text);
create or replace function test1()
returns setof name_value_pair
as $$
begin
return next ROW('email', '[email protected]')::name_value_pair;
return next ROW('user_id', 'abc123')::name_value_pair;
return;
end;
$$ language plpgsql;
Here's the error without the cast. How does it not know it's a text
? I thought a single quoted string literal was a text
unless you cast it to something else.
psql: ERROR: returned record type does not match expected record type DETAIL: Returned type unknown does not match expected type text in column 1. CONTEXT: PL/pgSQL function test1() line 4 at RETURN NEXT
The select:
=> select * from test1();
name | value
---------+-----------------
email | [email protected]
user_id | abc123
I'm using PostgreSQL 12 beta.
Upvotes: 2
Views: 1916
Reputation: 658482
The problem in your function is that the ROW constructor "strikes first", creating a composite type of two unknown values, for which there is no implicit type conversion.
Here are 5 ways without using an explicit cast.
Call each with:
SELECT * FROM ...;
Educational mostly. Still using your predefined composite / row type, with explicit, verbose syntax. The subselect makes the unknown types default to text
before they are composed into a ROW type:
CREATE OR REPLACE FUNCTION test1()
RETURNS SETOF name_value_pair
LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE AS
$func$
BEGIN
RETURN NEXT (SELECT t FROM (SELECT 'email', '[email protected]') t);
RETURN NEXT (SELECT t FROM (SELECT 'user_id', 'abc123') t);
END
$func$;
Using RETURNS TABLE
and RETURN QUERY
instead. No need to form composite types to begin with, rather let the function itself do that final step:
CREATE OR REPLACE FUNCTION test2()
RETURNS TABLE (name text, value text)
LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE AS
$func$
BEGIN
RETURN QUERY SELECT 'email', '[email protected]';
RETURN QUERY SELECT 'user_id', 'abc123';
END
$func$;
Or still use the composite type to define the return type. May be a better fit for you:
CREATE OR REPLACE FUNCTION test3()
RETURNS SETOF name_value_pair
LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE AS
$func$
BEGIN
RETURN QUERY SELECT 'email', '[email protected]';
RETURN QUERY SELECT 'user_id', 'abc123';
END
$func$;
While it's that simple, a single VALUES
expression in a plain SQL function is shorter and faster:
CREATE OR REPLACE FUNCTION test4()
RETURNS SETOF name_value_pair
LANGUAGE sql IMMUTABLE PARALLEL SAFE AS
$func$
VALUES
('email', '[email protected]')
, ('user_id', 'abc123')
;
$func$;
If it has to be PL/pgSQL:
CREATE OR REPLACE FUNCTION test5()
RETURNS SETOF name_value_pair
LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE AS
$func$
BEGIN
RETURN QUERY VALUES
('email', '[email protected]')
, ('user_id', 'abc123')
;
END
$func$;
The implicit conversion from unknown
to text
for scalar return values of a function was added in Postgres 10. Postgres 9.6 or older are stricter and would raise a similar error for alternatives 1 - 3. (Only function 4. & 5. work in any Postgres version.)
ERROR: structure of query does not match function result type DETAIL: Returned type unknown does not match expected type text in column 1.
One might debate whether the still strict behavior for the ROW type was an oversight in that update or by design. A case could be made for either I guess.
PARALLEL SAFE
for Postgres 9.6 or later.
Related:
Upvotes: 5
Reputation: 2756
The problem here is that the string constants like 'email' are of type unknown
not of type text
.
You could rewrite your example like this:
create type name_value_pair as (name text, value text);
create or replace function test1()
returns setof name_value_pair
as $$
begin
return next ROW('email'::text, '[email protected]'::text);
return next ROW('user_id'::text, 'abc123'::text);
return;
end;
$$ language plpgsql;
Upvotes: 1