В plpgsql вернуть набор составного типа без приведения? - PullRequest
1 голос
/ 17 июня 2019

Это работает, но мне нужны приведения (::name_value_pair). Мне это кажется немного уродливым.Есть ли способ сделать это без приведения?

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', 'foo@example.com')::name_value_pair;
    return next ROW('user_id', 'abc123')::name_value_pair;
    return;
end;
$$ language plpgsql;

Вот ошибка без приведения.Как он не знает, что это text?Я думал, что строковый литерал с одинарными кавычками был text, если вы не приведете его к чему-то другому.

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

Выбор:

=> select * from test1();
  name   |      value      
---------+-----------------
 email   | foo@example.com
 user_id | abc123

Я использую бета-версию PostgreSQL 12.

Ответы [ 2 ]

2 голосов
/ 23 июня 2019

Проблема в вашей функции заключается в том, что конструктор ROW "наносит удар первым", создавая составной тип из двух неизвестных значений, для которых неявное преобразование типов не выполняется.

Вот 5 способов без использования явного приведения :

1. По-прежнему используется предопределенный составной тип / тип строки с явным, подробным синтаксисом (обучающийв основном).При выборе этого параметра для неизвестных типов по умолчанию устанавливается значение text до , они объединяются в тип ROW:

CREATE OR REPLACE FUNCTION test1()
  RETURNS SETOF name_value_pair AS
$func$
BEGIN
   RETURN NEXT (SELECT t FROM (SELECT 'email', 'foo@example.com') t);
   RETURN NEXT (SELECT t FROM (SELECT 'user_id', 'abc123') t);
END
$func$  LANGUAGE plpgsql IMMUTABLE;

SELECT * FROM test1();

2. Использование RETURNS TABLE и RETURN QUERY вместо.Не нужно формировать составные типы для начала, просто позвольте самой функции выполнить этот последний шаг:

CREATE OR REPLACE FUNCTION test2()
  RETURNS TABLE (name text, value text) AS
$func$
BEGIN
   RETURN QUERY SELECT 'email', 'foo@example.com';
   RETURN QUERY SELECT 'user_id', 'abc123';
END
$func$  LANGUAGE plpgsql IMMUTABLE;

SELECT * FROM test2();

3. Или все же использовать составной тип для определения возвращаемого типа.Может быть лучше для вас:

CREATE OR REPLACE FUNCTION test3()
  RETURNS SETOF name_value_pair AS
$func$
BEGIN
   RETURN QUERY SELECT 'email', 'foo@example.com';
   RETURN QUERY SELECT 'user_id', 'abc123';
END
$func$  LANGUAGE plpgsql IMMUTABLE;

SELECT * FROM test3();

4. Хотя это так просто, простое VALUES выражение в простой SQL-функции короче ибыстрее:

CREATE OR REPLACE FUNCTION test4()
  RETURNS SETOF name_value_pair AS
$func$
VALUES
  ('email', 'foo@example.com')
, ('user_id', 'abc123');
$func$  LANGUAGE sql IMMUTABLE;

SELECT * FROM test4();

5. Или, если имеет , будет PL / pgSQL:

CREATE OR REPLACE FUNCTION test5()
  RETURNS SETOF name_value_pair AS
$func$
BEGIN
RETURN QUERY VALUES
  ('email', 'foo@example.com')
, ('user_id', 'abc123');
END
$func$  LANGUAGE plpgsql IMMUTABLE;

SELECT * FROM test5();

дБ <>fiddle здесь

Неявное преобразование из unknown в text для скалярных возвращаемых значений функции было добавлено в Postgres 10 .Postgres 9.6 или старше более строгие и могут вызвать аналогичные ошибки для вариантов 1 - 3. (Только функции 4. и 5. работают в любой версии Postgres.)

ERROR: structure of query does not match function result type
DETAIL: Returned type unknown does not match expected type text in column 1.

Можнообсудить, было ли все еще строгое поведение для типа ROW недосмотром в этом обновлении или намеренно.Я могу предположить, что можно использовать и любой другой случай.

Связанный:

1 голос
/ 17 июня 2019

Проблема в том, что строковые константы, такие как 'email', имеют тип unknown, а не типа text.

. Вы можете переписать свой пример следующим образом:

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, 'foo@example.com'::text);
    return next ROW('user_id'::text, 'abc123'::text);
    return;
end;
$$ language plpgsql;
...