Категории изменчивости функций

Все функции делятся по степени изменчивости с возможными вариантами: VOLATILE, STABLE или IMMUTABLE. Если категория не указана в команде CREATE FUNCTION, по умолчанию выбирается VOLATILE. Категория изменчивости — это обещание оптимизатору касательно поведения функции:

  • Изменчивая функция (VOLATILE) может делать все, что угодно, включая изменение базы данных. Она может возвращать разные результаты при последовательных вызовах с одинаковыми аргументами. Оптимизатор не делает никаких предположений о поведении таких функций. Запрос, использующий изменчивую функцию, будет вычислять ее заново для каждой строки, где требуется ее значение.

  • Стабильная функция (STABLE) не может изменять данные и гарантированно возвращает одинаковые результаты, получая одинаковые аргументы для всех строк в рамках одного оператора. Эта категория позволяет оптимизатору заменить несколько вызовов функции с одними и теми же аргументами одним. В частности, выражение, содержащее такую функцию, безопасно использовать в условии поиска по индексу. (Поскольку поиск по индексу вычислит значение для сравнения только один раз, а не для каждой строки, использовать в таком поиске функцию категории VOLATILE нельзя.)

  • Постоянная функция (IMMUTABLE) не может изменять данные и гарантированно возвращает одинаковые результаты, всегда получая одинаковые аргументы. Эта категория позволяет оптимизатору предварительно вычислить функцию, когда запрос вызывает ее с постоянными аргументами. Например, запрос типа SELECT ... WHERE x = 2 + 2 можно упростить до SELECT ... WHERE x = 4, потому что лежащая в его основе функция оператора сложения целых чисел помечена как IMMUTABLE.

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

Любая функция с побочными эффектами должна быть помечена как VOLATILE, чтобы оптимизатор не мог исключить ее вызовы. Даже функция без побочных эффектов должна быть помечена как VOLATILE, если ее значение может измениться в течение выполнения одного запроса, например: random(), currval(), timeofday().

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

Разница между категориями STABLE и IMMUTABLE почти незаметна, если рассматривать простые интерактивные запросы, которые планируются и выполняются немедленно: не имеет большого значения, выполняется ли функция однократно во время планирования или однократно в начале выполнения запроса. Но если план сохраняется для последующего повторного использования, возникает существенная разница. Если пометить функцию как IMMUTABLE, когда она не является постоянной, эта функция может оказаться преждевременно сжатой до константы во время планирования, и в итоге при последующих выполнениях плана будет использоваться неактуальное значение. Это опасно при использовании подготовленных операторов или языков функций, кеширующих планы (например, PL/pgSQL).

У функций, написанных на SQL или на одном из стандартных процедурных языков, имеется еще одно важное свойство, определяемое категорией изменчивости, а именно видимость любых изменений данных, которые были сделаны командой SQL, вызывающей эту функцию. Функция VOLATILE увидит такие изменения, а функция STABLE или IMMUTABLE — нет. Это поведение реализуется с помощью снимков в MVCC (см. главу Управление параллельным доступом): функции STABLE и IMMUTABLE используют снимок, созданный в начале вызывающего запроса, тогда как функции VOLATILE получают новый снимок в начале каждого запроса, который они выполняют.

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

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

Аналогичное поведение со снимками проявляется и для команд SELECT в функциях IMMUTABLE. В целом вообще неразумно выбирать из базы данных таблицы в функциях IMMUTABLE, поскольку постоянство нарушится при первом же изменении содержимого таблицы. Однако QHB не запрещает вам это делать.

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

Примечание
В целях предотвращения модификации данных QHB требует, чтобы функции, помеченные как STABLE или IMMUTABLE, не содержали никаких команд SQL, кроме SELECT. (это не вполне ошибкоустойчиво, так как такие функции все равно могут вызывать функции VOLATILE, модифицирующие базу данных. Если вы это сделаете, то обнаружите, что функция STABLE или IMMUTABLE не замечает изменений в базе данных, сделанных вызванной функцией, так как те не проявляются в ее снимке.)