36.11. Информация для оптимизации функций
Сама по себе функция для СУБД является просто «чёрным ящиком», о поведении которого известно очень мало. Это означает, что запросы, вызывающие функции, могут выполняться гораздо менее эффективно, чем могли бы в теории. Поэтому имеется возможность сообщить планировщику дополнительные сведения о функции, которые помогут ему оптимизировать вызовы функций.
Некоторые основные факты передаются декларативным образом в команде CREATE FUNCTION; в их числе один из самых значимых — характеристика изменчивости (IMMUTABLE
, STABLE
или VOLATILE
). Создавая любую функцию, очень важно правильно определить эту характеристику. Также может определяться характеристика распараллеливания (PARALLEL UNSAFE
, PARALLEL RESTRICTED
или PARALLEL SAFE
), если вы рассчитываете, что эта функция будет использоваться в распараллеливаемых запросах. Кроме того, для функции может задаваться примерная стоимость выполнения и/или оценка количества строк, выдаваемого функцией, возвращающей множество. Однако декларативный способ описания двух последних фактов позволяет задать только некоторое постоянное значение, а это полезно далеко не всегда.
Но имеется также возможность связать вспомогательную функцию для планировщика с вызываемой из SQL функцией (она будет целевой функцией для первой), и передать через неё ту информацию о целевой функции, которая слишком сложна для представления в декларативном виде. Вспомогательные функции для планировщика должным быть написаны на C (хотя язык целевых функций может быть любым), что переводит их в категорию расширенных возможностей, и разрабатывать их будут относительно немногие пользователи.
Вспомогательная функция должна иметь в SQL такую сигнатуру:
supportfn(internal) returns internal
Она связывается с целевой функцией с помощью указания SUPPORT
в команде, создающей целевую функцию.
Здесь даётся общее представление о том, что могут делать вспомогательные функции для планировщика. Множество запросов к вспомогательным функциям расширяемое, так что в будущих версиях у них могут появиться и другие возможности.
Некоторые вызовы функций можно упростить во время планирования, в зависимости от особенностей функции. Например, вызов int4mul(n, 1)
можно свести просто к n
. Преобразование такого рода может выполняться вспомогательной функцией, если она обрабатывает запросы типа SupportRequestSimplify
. Эта вспомогательная функция будет вызываться для каждого экземпляра вызова целевой функции, найденного в дереве разобранного запроса. Если она обнаруживает, что этот конкретный вызов можно упростить и привести к другому виду, она может построить и возвратить другое дерево с изменённым выражением. Это будет автоматически работать и для операторов, основанных на функциях, — в данном примере n * 1
будет также упрощено до n
. (Но заметьте, что это просто иллюстрация; конкретно эту оптимизацию стандартный Postgres Pro не производит). При этом не гарантируется, что Postgres Pro никогда не вызовет целевую функцию в случаях, которые может упростить вспомогательная функция. С учётом этого важно обеспечить строгую идентичность упрощённого выражения фактическому выполнению целевой функции.
Для целевой функции, возвращающей значение boolean
, часто бывает полезно оценить, какой процент строк будет выбран предложением WHERE
, в котором вызывается эта функция. Эту оценку позволяет получить вспомогательная функция, обрабатывающая запросы типа SupportRequestSelectivity
.
Если алгоритм работы целевой функции значительно меняется в зависимости от её аргументов, для неё может иметь смысл вычислять переменную оценку стоимости. Это можно сделать, реализовав вспомогательную функцию, которая будет обрабатывать запросы типа SupportRequestCost
.
Для целевой функции, возвращающей множество, часто полезно иметь переменную оценку числа выдаваемых строк. Реализовать это позволяет вспомогательная функция, обрабатывающая запросы типа SupportRequestRows
.
Для целевой функции, возвращающей значение boolean
, может существовать возможность преобразовать вызов функции в условии WHERE
в предложение(я) с индексируемыми операторами. Преобразованные предложения должны быть в точности идентичны условию функции либо могут быть несколько менее строгими (то есть они могут принимать некоторые значения, не удовлетворяющие условию с функцией). В последнем случае условие с индексом считается неточным; оно может использоваться для поиска по индексу, но для каждой строки, полученной при таком поиске, должна вызываться функция, чтобы точно определить, удовлетворяет ли строка условию WHERE
. Для создания таких условий вспомогательная функция должна обрабатывать запросы типа SupportRequestIndexCondition
.