Конкретная функция, на которую ссылается вызов функции, определяется с помощью следующей процедуры.
Разрешение типов функций
Выбор функций для рассмотрения из
pg_proc системного каталога. Если было использовано имя функции без указания схемы, рассматриваются функции с соответствующим именем и количеством аргументов, видимые в текущем пути поиска (см. Раздел 2.2.10.3). Если было указано уточненное имя функции, рассматриваются только функции в указанной схеме.
Если в пути поиска обнаруживается несколько функций с идентичными типами аргументов, рассматривается только та, которая встречается в пути первой. Функции с различными типами аргументов рассматриваются на равных основаниях независимо от их положения в пути поиска.
Если функция объявлена с VARIADIC параметром-массивом, а
в вызове не используется ключевое слово VARIADIC , то функция обрабатывается так, как если бы параметр-массив был заменен одним или несколькими вхождениями типа его элементов, необходимыми для соответствия вызову. После такого расширения функция может иметь эффективные типы аргументов, идентичные типам некоторой невариативной функции. В этом случае используется функция, расположенная раньше в пути поиска, а если обе функции находятся в одной схеме, предпочтение отдается невариативной функции.
Это создает угрозу безопасности при вызове по уточненному имени
[10],
функции с переменным числом аргументов (variadic function), найденной в схеме, которая позволяет неавторизованным пользователям создавать
объекты. Злоумышленник может перехватить управление и выполнять произвольные SQL-функции от вашего имени. Замените вызов, содержащий VARIADIC ключевое слово, что позволяет избежать данной угрозы. Вызовы,
заполняющие VARIADIC "any" параметры, часто не имеют эквивалентной формулировки с использованием данного VARIADIC ключевого слова. Для безопасного выполнения таких вызовов схема функции должна разрешать создание объектов только доверенным пользователям.
Функции, имеющие значения по умолчанию для параметров, считаются подходящими для любого вызова, в котором пропущены ноль или более позиций параметров, имеющих значения по умолчанию. Если под вызов подпадает более одной такой функции, выбирается та, которая находится выше в пути поиска (search path). Если в одной и той же схеме существуют две или более такие функции с идентичными типами данных параметров в позициях без значений по умолчанию (что возможно при наличии разных наборов параметров со значениями по умолчанию), система не сможет определить наиболее подходящую функцию, и в результате «неоднозначный вызов функции» если для вызова не будет найдено более подходящее соответствие, возникнет ошибка.
Это создает риск нарушения доступности при вызове по уточненному имени[10], любой функции, расположенной в схеме, позволяющей недоверенным пользователям создавать объекты. Злоумышленник может создать функцию с именем существующей функции, продублировав ее параметры и добавив новые параметры со значениями по умолчанию. Это сделает невозможными новые вызовы исходной функции. Чтобы исключить этот риск, следует размещать функции в схемах, где создавать объекты разрешено только доверенным пользователям.
Выполняется поиск функции, принимающей в точности те же типы данных входных аргументов. Если она существует (в рассматриваемом наборе функций может быть только одно точное совпадение), используется именно она. Отсутствие точного совпадения создает угрозу безопасности при вызове по уточненному имени[10], функции, расположенной в схеме, которая позволяет недоверенным пользователям создавать объекты. В таких ситуациях
следует использовать явное приведение типов аргументов для обеспечения точного совпадения. (Случаи, затрагивающие unknown
на данном этапе совпадение найдено не будет.)
Если точное совпадение не найдено, проверяется, не является ли вызов функции специальным запросом на преобразование типа данных. Это происходит в том случае, если вызов функции
имеет только один аргумент, а имя функции совпадает с (внутренним)
именем некоторого типа данных. Кроме того, аргумент функции должен быть либо литералом неизвестного типа, либо типом данных, бинарно совместимым (binary-coercible) с целевым типом данных, либо типом данных, который может быть преобразован в целевой тип данных путем применения функций ввода-вывода этого типа (то есть преобразование осуществляется в один из стандартных строковых типов или из него). При выполнении этих условий
вызов функции рассматривается как форма оператор CAST спецификации.
[11]
Поиск наилучшего соответствия.
Отбросьте функции-кандидаты, для которых входные типы данных не совпадают и не могут быть приведены (посредством неявного приведения типов) к требуемым.
unknown предполагается, что литералы могут быть приведены к любому типу данных для этой цели. Если остался только один
кандидат, используйте его; в противном случае перейдите к следующему шагу.
Если какой-либо входной аргумент относится к типу данных домена, он рассматривается как имеющий базовый тип данных домена на всех последующих этапах. Это гарантирует, что домены ведут себя так же, как их базовые типы данных, при разрешении неоднозначности функций.
Просмотрите всех кандидатов и оставьте тех, у которых наблюдается наибольшее количество точных совпадений по типам данных входных аргументов. Оставьте всех кандидатов, если ни у одного из них нет точных совпадений. Если остался только один кандидат, используйте его; в противном случае перейдите к следующему шагу.
Просмотрите всех кандидатов и оставьте тех, которые принимают предпочтительные типы данных (из категории входного типа данных) в наибольшем количестве позиций, где требуется приведение типов. Сохраните всех кандидатов, если ни один из них не принимает предпочтительные типы данных. Если остался только один кандидат, используйте его; в противном случае перейдите к следующему шагу.
Если какие-либо входные аргументы являются unknown, проверьте категории типов данных, допустимые для данных позиций аргументов в оставшихся функциях-кандидатах. Для каждой позиции
выберите string категорию, если какой-либо из кандидатов принимает эту категорию.
(Такое предпочтение в сторону категории string
обосновано тем, что литерал неизвестного типа выглядит как строка.)
В противном случае, если все оставшиеся кандидаты принимают одну и ту же категорию типов данных,
выберите эту категорию; в противном случае выполнение завершится ошибкой, так как сделать правильный выбор без дополнительных признаков невозможно. Теперь отбросьте кандидатов, которые не принимают выбранную категорию типов данных. Кроме того, если какой-либо кандидат принимает предпочтительный тип данных в этой категории, отбросьте кандидатов, принимающих непредпочтительные типы данных для данного аргумента. Сохраните всех кандидатов, если ни один из них не прошел эти проверки. Если остался только один кандидат, используйте его; в противном случае перейдите к следующему шагу.
Если присутствуют как unknown , так и аргументы известного типа, и при этом все аргументы известного типа имеют один и тот же тип данных, следует предположить, что
unknown аргументы также имеют этот тип, и проверить, какие
кандидаты могут принять этот тип в unknown-позиции аргументов. Если данную проверку проходит ровно один кандидат, используйте его.
В противном случае работа завершится ошибкой.
Обратите внимание, что «наилучшее соответствие» правила выбора идентичны для разрешения типов операторов и функций. Ниже приведено несколько примеров.
Пример 2.7.6. Разрешение типов аргументов функции округления
Существует только одна round функция, которая принимает два
аргумента; она принимает первый аргумент типа данных тип данных numeric и
второй аргумент типа данных integer.
Таким образом, следующий запрос автоматически преобразует
первый аргумент типа данных integer в
тип данных numeric:
SELECT round(4, 4); round -------- 4.0000 (1 строка)
Этот запрос фактически преобразуется парсером в:
SELECT round(CAST (4 AS numeric), 4);
Поскольку числовым константам с десятичной точкой изначально присваивается тип данных тип данных numeric, следующий запрос не потребует приведения типов и, следовательно, может быть немного более эффективным:
SELECT round(4.0, 4);
Пример 2.7.7. Разрешение функций с переменным числом аргументов (variadic)
CREATE FUNCTION public.variadic_example(VARIADIC numeric[]) RETURNS int LANGUAGE sql AS 'SELECT 1'; CREATE FUNCTION
Эта функция поддерживает ключевое слово VARIADIC, но оно не является обязательным. Допускаются аргументы как типа данных integer, так и типа данных numeric:
SELECT public.variadic_example(0),
public.variadic_example(0.0),
public.variadic_example(VARIADIC array[0.0]);
variadic_example | variadic_example | variadic_example
------------------+------------------+------------------
1 | 1 | 1
(1 строка)
Однако при первом и втором вызовах предпочтение будет отдаваться более специфичным функциям, если они доступны:
CREATE FUNCTION public.variadic_example(numeric) RETURNS int
LANGUAGE sql AS 'SELECT 2';
CREATE FUNCTION
CREATE FUNCTION public.variadic_example(int) RETURNS int
LANGUAGE sql AS 'SELECT 3';
CREATE FUNCTION
SELECT public.variadic_example(0),
public.variadic_example(0.0),
public.variadic_example(VARIADIC array[0.0]);
variadic_example | variadic_example | variadic_example
------------------+------------------+------------------
3 | 2 | 1
(1 строка)
При конфигурации по умолчанию и наличии только первой функции первый и второй вызовы являются небезопасными. Любой пользователь может перехватить их, создав вторую или третью функцию. Благодаря точному соответствию типа данных аргумента
и использованию VARIADIC ключевого слова, третий вызов является безопасным.
Пример 2.7.8. Разрешение типов для функции извлечения подстроки
Существует несколько substr функций, одна из которых
принимает типы данных text и integer. Если вызов происходит со строковой константой неопределённого типа, система выбирает ту функцию-кандидат, которая принимает аргумент предпочтительной категории
string (а именно типа данных text).
SELECT substr('1234', 3);
substr
--------
34
(1 строка)
Если строка объявлена с типом данных varchar, как это может быть в случае её получения из таблицы, парсер попытается преобразовать её в text:
SELECT substr(varchar '1234', 3);
substr
--------
34
(1 строка)
Парсер преобразует это выражение, и фактически оно принимает следующий вид:
SELECT substr(CAST (varchar '1234' AS text), 3);
Парсер узнаёт из pg_cast системного каталога, что
text и varchar
являются бинарно-совместимыми; это означает, что один тип данных можно передать функции, принимающей другой тип, без выполнения физического преобразования. Следовательно, в данном случае вызов функции приведения типов фактически не вставляется.
И если функция вызывается с аргументом типа данных integer,
парсер попытается преобразовать его в text:
SELECT substr(1234, 3); ERROR: функция substr(integer, integer) не существует HINT: Ни одна функция не соответствует заданному имени и типам аргументов. Возможно, потребуется добавить явное приведение типов.
Это не работает, так как integer не имеет механизма неявного приведения
к text. Тем не менее, явное приведение типов сработает:
SELECT substr(CAST (1234 AS text), 3);
substr
--------
34
(1 строка)
[10] Данная проблема не возникает при использовании имен, не квалифицированных схемой, поскольку путь поиска, содержащий схемы, в которых недоверенным пользователям разрешено создавать объекты, не представляет собой безопасный шаблон использования схем.
[11] Данный шаг необходим для поддержки спецификаций приведения типов в функциональном стиле в тех случаях, когда фактическая функция приведения типов отсутствует. Если функция приведения типов существует, она по соглашению именуется по названию её выходного типа данных, поэтому необходимость в специальной обработке отсутствует. См. CREATE CAST для получения дополнительных комментариев.