Есть ли способ отключить перегрузку функций в 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.
Более подробное объяснение в этих связанных ответах:
PostgreSQL ОШИБКА: функция to_tsvector(символ меняется, неизвестно) не существует
Создать серию дат - используя тип даты в качестве входных данных
Грязная починка
Вы можете исправить это, "улучшив" актерский состав из 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)
Существует множество встроенных функций, которые перегружены, поэтому они просто не будут работать, если вы отключите перегрузку функций.