Перегрузка функций

Статья основана на шестнадцатом видео из 31 темы курса SQL 2.0 — PL/pgSQL в PostgreSQL от Аристова Евгения, который является логическим продолжением курса SQL c 0. Ссылки на видео на платформах RUTUBE и VK video.

В данной статье подробно разбираются перезагрузка функции, особенности реализации, сложности отладки и выбора.

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

Презентация и исходники доступны по ссылке.

Перегрузка

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

То есть не нужно задавать уникальные имена и потом их указывать:

CREATE FUNCTION add_int(nt, int) …

CREATE FUNCTION add_real(real, real) …

CREATE FUNCTION add_decimal(decimal, decimal) …


PostgreSQL автоматически выберет нужную функцию. Примеры:

CREATE FUNCTION func(int) …
CREATE FUNCTION func(int, int) …
CREATE FUNCTION func(int, real) …
CREATE FUNCTION func(real) ...

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

Особенности реализации

Две функции считаются совпадающими, если они имеют одинаковые имена и типы входных аргументов, параметры OUT игнорируются. Таким образом, например, эти объявления вызовут конфликт, так как PostgreSQL не сможет выбрать какую функцию вызвать:

CREATE FUNCTION func(int) …
CREATE FUNCTION func(int, out text) …

select func(1) ???

Ограничения

Функции, имеющие разные типы аргументов, не будут считаться конфликтующими в момент создания, но предоставленные для них значения по умолчанию могут вызвать конфликт в момент использования. Например, рассмотрите следующие определения:

CREATE FUNCTION func(int) ...
CREATE FUNCTION func(int, int default 2) …

Вызов foo(100) завершится ошибкой из-за неоднозначности в выборе вызываемой функции, а foo(1,2) вызовется без ошибки.

Сложности выбора

Возможна неоднозначность при вызове:

CREATE FUNCTION process_value(val INTEGER) RETURNS TEXT AS $$ ... $$;
CREATE FUNCTION process_value(val NUMERIC) RETURNS TEXT AS $$ ... $$;

Какую функцию выберет PostgreSQL?

SELECT process_value(10); -- INTEGER или NUMERIC?

Желательно указывать конкретный тип, но минимум функции с одинаковым именем должны реализовывать аналогичную бизнес логику!

Сложности отладки

При ошибке сложно определить, какая именно функция вызвала проблему

ERROR: function process_data(unknown, unknown) does not exist
LINE 1: SELECT process_data('test', 'invalid');
         ^
HINT: No function matches the given name and argument types.

Итоги

Используйте перегрузку когда:

  • Функции выполняют схожую логику с разными типами данных
  • Хотите создать удобный и интуитивный API
  • Нужна обратная совместимость

Избегайте перегрузки когда:

  • Функции выполняют принципиально разную логику
  • Возможна неоднозначность при вызове
  • Работаете с простыми сценариями

Примеры кода:

Напишем функцию, возвращающую большее из двух целых чисел
(Похожая функция есть в SQL и называется greatest, но мы сделаем ее сами):

CREATE OR REPLACE FUNCTION maximum(a integer, b integer) RETURNS integer AS $$
SELECT CASE WHEN a > b THEN a ELSE b END;
$$ LANGUAGE SQL;

Проверим:
SELECT maximum(100,200);

Допустим, мы решили сделать аналогичную функцию для трех чисел. 
Благодаря перегрузке, не надо придумывать для нее какое-то новое название:

CREATE OR REPLACE FUNCTION maximum(a integer, b integer, c integer) RETURNS integer AS $$
SELECT CASE WHEN a > b THEN maximum(a,c) ELSE maximum(b,c) END;
$$ LANGUAGE SQL;

Теперь у нас две функции с одним именем, но разным числом параметров:
\df maximum

И обе работают:
SELECT maximum(10,20), maximum(10,20,-100);

Больше примеров доступно на гитхабе и в видео.

В следующей статье мы разберём перегрузку функций.


Опубликовано

в

Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

один + двадцать =