Есть ли способ отключить перегрузку функций в Postgres

Мои пользователи и я не используем перегрузку функций в PL/pgSQL. У нас всегда есть одна функция для каждого (схема, имя) кортежа. Поэтому мы хотели бы удалить функцию только по имени, изменить ее сигнатуру без необходимости сначала удалять ее и т. Д. Рассмотрим, например, следующую функцию:

CREATE OR REPLACE FUNCTION myfunc(day_number SMALLINT)
RETURNS TABLE(a INT)
AS
$BODY$
BEGIN
  RETURN QUERY (SELECT 1 AS a);
END;
$BODY$
LANGUAGE plpgsql;

Чтобы сэкономить время, мы хотели бы вызвать его следующим образом, не квалифицируя 1 с ::SMALLINTпотому что есть только одна функция с именем myfunc, и она имеет ровно один параметр с именем day_number:

SELECT * FROM myfunc(day_number := 1)

Здесь нет двусмысленности, и значение 1 соответствует SMALLINT типа, но PostgreSQL жалуется:

SELECT * FROM myfunc(day_number := 1);
ERROR:  function myfunc(day_number := integer) does not exist
LINE 12: SELECT * FROM myfunc(day_number := 1);
                       ^
HINT:  No function matches the given name and argument types.
You might need to add explicit type casts.

Когда мы вызываем такие функции из Python, мы используем оболочку, которая просматривает сигнатуры функций и квалифицирует параметры с типами. Этот подход работает, но, кажется, есть потенциал для улучшения.

Есть ли способ вообще отключить перегрузку функций?

3 ответа

Решение

На самом деле это не напрямую связано с перегрузкой функции (которую невозможно "отключить"). Это вопрос разрешения типа функции. (Конечно, этот алгоритм может быть более разрешающим без перегруженных функций.)

Все это будет просто работать:

SELECT * FROM myfunc(day_number := '1');
SELECT * FROM myfunc('1');               -- note the quotes

SELECT * FROM myfunc(1::smallint);
SELECT * FROM myfunc('1'::smallint);

Зачем?

Последние два довольно очевидны, вы уже упоминали об этом в своем вопросе.
Первые два более интересны, объяснение скрыто в Резолюции Типа Функции:

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

И это должно быть простым решением для вас: использовать строковые литералы.

Нетипизированный литерал ('1', с кавычками) или "строковый литерал", как определено в стандарте SQL, по своей природе отличается от любого другого типизированного литерала (или константы).

Числовая константа (1 (без кавычек) сразу преобразуется в числовой тип. По документации:

Числовая константа, которая не содержит ни десятичной точки, ни экспоненты, изначально считается типом integer, если ее значение соответствует типу integer (32 бита); в противном случае предполагается, что это тип bigint если его значение соответствует типу bigint (64 бита); в противном случае он принимается за тип numeric, Константы, которые содержат десятичные точки и / или показатели, всегда изначально считаются типом numeric,

Первоначально назначенный тип данных числовой константы является лишь отправной точкой для алгоритмов разрешения типов. В большинстве случаев константа будет автоматически приведена к наиболее подходящему типу в зависимости от контекста. При необходимости вы можете принудительно интерпретировать числовое значение как определенный тип данных путем его приведения.

Жирный акцент мой.

Назначение в вызове функции (day_number := 1) является частным случаем, тип данных day_number на данный момент неизвестно. Postgres не может получить тип данных из этого назначения и по умолчанию integer,

Следовательно, Postgres ищет функцию, принимающую integer первый Тогда для функций, принимающих тип только неявное приведение от integer, другими словами:

SELECT casttarget::regtype
FROM   pg_cast
WHERE  castsource = 'int'::regtype
AND    castcontext = 'i';

Все они будут найдены - и конфликтуют, если существует более одной функции. Это будет перегрузкой функции, и вы получите другое сообщение об ошибке. С двумя функциями-кандидатами:

SELECT * FROM myfunc(1);
ERROR:  function myfunc(integer) is not unique

Обратите внимание на "целое число" в сообщении: числовая константа была приведена к integer,

Тем не менее, актерский состав из integer в smallint "только" приведение назначения. И на этом путешествие заканчивается:

No function matches the given name and argument types.

SQL Fiddle.

Более подробное объяснение в этих связанных ответах:

Грязная починка

Вы можете исправить это, "улучшив" актерский состав из integer в smallint к неявному приведению:

UPDATE pg_cast
SET    castcontext = 'i'
WHERE  castsource = 'int'::regtype
AND    casttarget  = 'int2'::regtype;

Но я бы настоятельно не рекомендовал вмешиваться в систему кастинга по умолчанию. Только подумайте, если вы точно знаете, что делаете. Вы найдете соответствующие обсуждения в списках Postgres. Он может иметь всевозможные побочные эффекты, начиная с разрешения типа функции, но не заканчивая там.

В сторону

Разрешение типа функции полностью не зависит от используемого языка. Функция SQL будет конкурировать с PL/perl или PL/pgSQL или "внутренними" функциями точно так же. Подпись функции имеет важное значение. Встроенные функции только на первом месте, потому что pg_catalog на первом месте по умолчанию search_path,

Эрвин отправил правильный ответ. Мой следующий ответ связан с возможностью отключить перегрузку.

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

Вы можете просто проверить, есть ли у вас перегруженные функции:

postgres=# select count(*), proname 
               from pg_proc 
              where pronamespace <> 11 
              group by proname 
              having count(*) > 1;
 count | proname 
-------+---------
(0 rows)

Существует множество встроенных функций, которые перегружены, поэтому они просто не будут работать, если вы отключите перегрузку функций.

Другие вопросы по тегам