Cada función tiene una clasificación de volatilidad,
siendo las opciones posibles VOLATILE, STABLE o
IMMUTABLE. VOLATILE es el valor por defecto si el
comando CREATE FUNCTION
no especifica una categoría. La categoría de volatilidad es una
promesa al optimizador sobre el comportamiento de la función:
Una función VOLATILE puede hacer cualquier cosa, incluyendo modificar
la base de datos. Puede devolver diferentes resultados en llamadas sucesivas con
los mismos argumentos. El optimizador no hace ninguna suposición sobre el
comportamiento de estas funciones. Una consulta que utiliza una función volátil volverá
a evaluar la función en cada fila donde se necesite su valor.
Una función STABLE no puede modificar la base de datos y se
garantiza que devolverá los mismos resultados dados los mismos argumentos
para todas las filas dentro de una sola sentencia. Esta categoría permite al
optimizador optimizar múltiples llamadas de la función a una sola
llamada. En particular, es seguro utilizar una expresión que contenga
una función de este tipo en una condición de escaneo de índice. (Dado que un escaneo de índice
evaluará el valor de comparación solo una vez, no una vez en cada
fila, no es válido utilizar una función VOLATILE en una
condición de escaneo de índice).
Una función IMMUTABLE no puede modificar la base de datos y se
garantiza que devolverá los mismos resultados dados los mismos argumentos para siempre.
Esta categoría permite al optimizador preevaluar la función cuando
una consulta la llama con argumentos constantes. Por ejemplo, una consulta como
SELECT ... WHERE x = 2 + 2 se puede simplificar a primera vista a
SELECT ... WHERE x = 4, porque la función subyacente
al operador de suma de enteros está marcada como IMMUTABLE.
Para obtener los mejores resultados de optimización, debes etiquetar tus funciones con la categoría de volatilidad más estricta que sea válida para ellas.
Cualquier función con efectos secundarios debe etiquetarse como
VOLATILE, para que las llamadas a ella no puedan optimizarse.
Incluso una función sin efectos secundarios debe etiquetarse como
VOLATILE si su valor puede cambiar dentro de una sola consulta;
algunos ejemplos son random(), currval(),
timeofday().
Otro ejemplo importante es que la familia de funciones current_timestamp
califica como STABLE, ya que sus valores no
cambian dentro de una transacción.
Existe relativamente poca diferencia entre las categorías STABLE e
IMMUTABLE cuando se consideran consultas interactivas
simples que se planifican y ejecutan inmediatamente: no importa
mucho si una función se ejecuta una vez durante la planificación o una vez durante el
inicio de la ejecución de la consulta. Pero hay una gran diferencia si el plan se
guarda y se reutiliza más tarde. Etiquetar una función como IMMUTABLE cuando
realmente no lo es podría permitir que se convierta prematuramente en una constante durante
la planificación, lo que daría como resultado la reutilización de un valor obsoleto en usos
posteriores del plan. Esto es un peligro cuando se utilizan sentencias preparadas o cuando
se utilizan lenguajes de función que almacenan en caché los planes (como
PL/pgSQL).
Para las funciones escritas en SQL o en cualquiera de los lenguajes procedimentales
estándar, existe una segunda propiedad importante determinada por la
categoría de volatilidad, a saber, la visibilidad de los cambios de datos que hayan
sido realizados por el comando SQL que está llamando a la función. Una
función VOLATILE verá dichos cambios, una función STABLE
o IMMUTABLE no lo hará. Este comportamiento se implementa
utilizando el comportamiento de toma de instantáneas de MVCC (consulta la Chapter 13):
las funciones STABLE e IMMUTABLE utilizan una instantánea
establecida al inicio de la consulta invocadora, mientras que las
funciones VOLATILE obtienen una instantánea fresca al inicio de
cada consulta que ejecutan.
Las funciones escritas en C pueden gestionar las instantáneas como deseen, pero normalmente es una buena idea hacer que las funciones en C también funcionen de esta manera.
Debido a este comportamiento de instantáneas,
una función que contiene solo comandos SELECT se puede marcar de forma segura
como STABLE, incluso si selecciona de tablas que podrían estar
experimentando modificaciones por consultas concurrentes.
PostgreSQL ejecutará todos los comandos de una
función STABLE utilizando la instantánea establecida para la
consulta invocadora, por lo que verá una vista fija de la base de datos a lo largo
de esa consulta.
El mismo comportamiento de instantáneas se utiliza para los comandos SELECT
dentro de funciones IMMUTABLE. En general, no es recomendable seleccionar
de tablas de la base de datos dentro de una función IMMUTABLE,
ya que la inmutabilidad se romperá si el contenido de la tabla cambia alguna vez.
Sin embargo, PostgreSQL no impide que
hagas eso.
Un error común es etiquetar una función como IMMUTABLE when sus
resultados dependen de un parámetro de configuración. Por ejemplo, una función
que manipula marcas de tiempo podría tener resultados que dependan de la
configuración de TimeZone. Por seguridad, estas funciones deberían
etiquetarse como STABLE en su lugar.
PostgreSQL requiere que las funciones STABLE
e IMMUTABLE no contengan más comandos SQL que
SELECT para evitar la modificación de datos.
(Esta no es una prueba completamente infalible, ya que dichas funciones aún podrían
invocar funciones VOLATILE que modifiquen la base de datos.
Si haces eso, verás que la función STABLE o
IMMUTABLE no nota los cambios en la base de datos
aplicados por la función llamada, ya que están ocultos para su instantánea).