Как использовать смешанные целочисленные и числовые аргументы в функции Postgres 9.1+

Я ищу способ создать функцию icase(), которая работает с любыми типами данных, совместимыми со вторым и третьим параметром.
Я пробовал в Postgres 9.4:

CREATE OR REPLACE FUNCTION public.icase(
    cond1 boolean,
    res1 anyelement,
    conddefault anyelement)
  RETURNS anyelement AS
' SELECT CASE WHEN $1 THEN $2 ELSE $3 END; '
  LANGUAGE sql IMMUTABLE;

Но:

select icase( true, 1.0, 0 )

вызывает ошибку:

ERROR:  function icase(boolean, numeric, integer) does not exist
LINE 9: select icase( true, 1.0, 0 )
               ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.

Как исправить это в 9.1+, чтобы второй и третий аргументы могли быть как целыми, так и числовыми?

Этот метод может быть вызван, если второй и третий параметры имеют типы text, char(n), date, numeric или int.


person Andrus    schedule 19.05.2015    source источник
comment
Проблема в том, что anyelement разрешается только для одного типа данных для каждой функции, а не для каждого использования. Даже если вы избегали anyelement и использовали жестко закодированные типы данных, вы не сможете написать функцию, которая возвращает числовое или целочисленное значение. Вы должны выбрать по одному для каждой функции. Я предполагаю, что если вы измените вызов функции на select icase(true, 1.0, 0.0), он будет работать так, как задумано.   -  person Shawn    schedule 20.05.2015
comment
Другими словами, я бы воспринял это как подсказку, чтобы начать искать способы предварительной обработки вашего ввода, чтобы преобразовать эти вещи в формат, необходимый для работы с postgres.   -  person Shawn    schedule 20.05.2015
comment
Подождите, 2-й и 3-й параметры должны быть одними и теми же в любом случае: оба int или оба numeric или оба дата и т. д.? Ваш заголовок и пример намекают в другом направлении, чем ваше окончательное утверждение ... Кроме того, вам нужно, чтобы результат имел точно такой же тип данных или представление text выполняло бы эту работу?   -  person Erwin Brandstetter    schedule 22.05.2015
comment
второй и третий параметры могут быть: (int,numeric), (numeric,int), (int,int), (numeric,numeric), (date,date), (char(n) , char(n)), (text, char(n)), (char(n), text), (text,text) Для смешанных числовых/целочисленных параметров результат всегда может быть числовым. Для не смешанных параметров результат должен совпадать с типом параметра. Для числовых параметров и параметров даты текстовый результат, вероятно, не может быть возвращен, поскольку он используется в выражении, и prostgres, вероятно, выдаст ошибку, если текст используется в числовом выражении, автоматическое приведение, вероятно, больше не поддерживается в 9.1+   -  person Andrus    schedule 22.05.2015
comment
@Shawn это вызывается из автоматического конвертера, который преобразует выражение VFP в postgres. Одни и те же выражения используются двумя разными языками. Таким образом, выражения уже определены, и их изменение следует избегать.   -  person Andrus    schedule 22.05.2015


Ответы (1)


Полиморфные типы в этот момент строги - в других случаях PostgreSQL пытается привести константы к наиболее распространенному типу, но этот шаг отсутствует для полиморфных типов - поэтому в этом случае, когда вы описали проблему, вы должны явно привести или вы не следует использовать полиморфные типы. План Б закончился перегрузкой функций.

CREATE OR REPLACE FUNCTION public.icase1(cond1 boolean,
                                         res1 integer, conddefault integer)
RETURNS integer AS $$
SELECT CASE WHEN $1 THEN $2 ELSE $3 END;
$$ LANGUAGE sql;

CREATE OR REPLACE FUNCTION public.icase1(cond1 boolean,
                                         res1 numeric, conddefault numeric)
RETURNS numeric AS $$
SELECT CASE WHEN $1 THEN $2 ELSE $3 END;
$$ LANGUAGE sql;

Тогда ваш код будет работать так, как ожидалось:

postgres=> select icase1(true, 1.0, 0);
 icase1 
--------
    1.0
(1 row)

postgres=> select icase1(true, 1.0, 1.0);
 icase1 
--------
    1.0
(1 row)

postgres=> select icase1(true, 1, 0);
 icase1 
--------
      1
(1 row)
person Pavel Stehule    schedule 22.05.2015