Каждая функция имеет изменчивость категорию изменчивости, при этом
допустимыми значениями являются VOLATILE, STABLE, или
IMMUTABLE. VOLATILE используется по умолчанию, если в
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 (см. Глава 2.10):
STABLE и IMMUTABLE функции используют снимок, созданный в начале вызывающего запроса, тогда как
VOLATILE функции получают новый снимок в начале каждого выполняемого ими запроса.
Функции, написанные на языке C, могут управлять снимками произвольно, но обычно рекомендуется обеспечивать работу функций на языке C аналогичным образом.
Вследствие такого механизма создания снимков данных функция, содержащая только SELECT команды, может быть безопасно помечена STABLE, даже если она выполняет выборку из таблиц, которые могут изменяться параллельными запросами.
Digital Q.DataBase будет выполнять все команды
STABLE функции, используя снимок, созданный для вызывающего запроса, и, таким образом, она будет видеть согласованное состояние базы данных на протяжении всего выполнения этого запроса.
Такой же механизм создания снимков используется для SELECT команд
внутри IMMUTABLE функций. Как правило, нецелесообразно выполнять выборку из таблиц базы данных внутри IMMUTABLE функцию в принципе, поскольку при любом изменении содержимого таблицы свойство неизменяемости будет нарушено. Однако, Digital Q.DataBase не запрещает выполнение таких действий.
Распространенной ошибкой является назначение функции категории IMMUTABLE если её результаты зависят от параметра конфигурации. Например, результаты функции, обрабатывающей метки времени, могут зависеть от настройки
TimeZone параметра. Для обеспечения безопасности такие функции должны быть помечены STABLE вместо этого.
Digital Q.DataBase требует, чтобы STABLE
и IMMUTABLE функции не содержали команд SQL, отличных от SELECT для предотвращения изменения данных. (Данная проверка не является абсолютно надежной, так как подобные функции все же могут вызывать VOLATILE функции, изменяющие базу данных.
В этом случае окажется, что STABLE или
IMMUTABLE функция не фиксирует изменения в базе данных, произведенные вызываемой функцией, поскольку они скрыты от ее снимка данных (snapshot).)