Chaque fonction a une classification de volatilité
(volatility) comprenant
VOLATILE
, STABLE
ou IMMUTABLE
.
VOLATILE
est la valeur par défaut si la commande
CREATE FUNCTION
ne
spécifie pas de catégorie. La catégorie de volatilité est une promesse
à l'optimiseur sur le comportement de la fonction :
Une fonction VOLATILE
peut tout faire, y compris modifier
la base de données. Elle peut renvoyer différents
résultats sur des appels successifs avec les mêmes arguments.
L'optimiseur ne fait aucune supposition sur le comportement de telles
fonctions. Une requête utilisant une fonction volatile ré-évaluera la
fonction à chaque ligne où sa valeur est nécessaire.
Une fonction STABLE
ne peut pas modifier la base de
données et est garantie de renvoyer les mêmes résultats si elle est
appelée avec les mêmes arguments pour toutes les lignes à l'intérieur
d'une même instruction. Cette catégorie permet à l'optimiseur d'optimiser
plusieurs appels de la fonction dans une seule requête. En particulier,
vous pouvez utiliser en toute sécurité une expression contenant une
telle fonction dans une condition de parcours d'index (car un parcours
d'index évaluera la valeur de la comparaison une seule fois, pas une
fois pour chaque ligne, utiliser une fonction VOLATILE
dans
une condition de parcours d'index n'est pas valide).
Une fonction IMMUTABLE
ne peut pas modifier la base de
données et est garantie de toujours renvoyer les mêmes résultats si
elle est appelée avec les mêmes arguments. Cette catégorie permet à
l'optimiseur de pré-évaluer la fonction quand une requête l'appelle
avec des arguments constants. Par exemple, une requête comme
SELECT ... WHERE x = 2 + 2
peut être simplifiée pour
obtenir SELECT ... WHERE x = 4
car la fonction sous-jacente
de l'opérateur d'addition est indiquée IMMUTABLE
.
Pour une meilleure optimisation des résultats, vous devez mettre un label sur les fonctions avec la catégorie la plus volatile valide pour elles.
Toute fonction avec des effets de bord doit être indiquée
comme VOLATILE
, de façon à ce que les appels ne puissent pas
être optimisés. Même une fonction sans effets de bord doit être indiquée
comme VOLATILE
si sa valeur peut changer à l'intérieur
d'une seule requête ; quelques exemples sont random()
,
currval()
, timeofday()
.
Un autre exemple important est que la famille de fonctions
current_timestamp
est qualifiée comme
STABLE
car leurs valeurs ne changent pas à l'intérieur
d'une transaction.
Il y a relativement peu de différences entre les catégories
STABLE
et IMMUTABLE
en considérant les requêtes
interactives qui sont planifiées et immédiatement exécutées : il
importe peu que la fonction soit exécutée une fois lors de la
planification ou une fois au lancement de l'exécution de la requête mais
cela fait une grosse différence si le plan est sauvegardé et utilisé plus
tard. Placer un label IMMUTABLE
sur une fonction quand elle
ne l'est pas vraiment pourrait avoir comme conséquence de la considérer
prématurément comme une constante lors de la planification et résulterait en une valeur
erronée lors d'une utilisation ultérieure de ce plan d'exécution.
C'est un danger qui arrive lors de l'utilisation d'instructions préparées
ou avec l'utilisation de langages de fonctions mettant les plans d'exécutions
en cache (comme
PL/pgSQL).
Pour les fonctions écrites en SQL ou dans tout autre langage de procédure
standard, la catégorie de volatibilité détermine une deuxième propriété
importante, à savoir la visibilité de toute modification de données
effectuées par la commande SQL qui a appelé la fonction. Une fonction
VOLATILE
verra les changements, une fonction
STABLE
ou IMMUTABLE
ne les verra pas.
Ce comportement est implantée en utilisant le comportement par images de
MVCC (voir Chapitre 13) : les fonctions
STABLE
et IMMUTABLE
utilisent une
image établie au lancement de la requête appelante alors que les fonctions
VOLATILE
obtiennent une image fraiche au début de chaque
requête qu'elles exécutent.
Les fonctions écrites en C peuvent gérer les images de la façon qu'elles le souhaitent, mais il est préférable de coder les fonctions C de la même façon.
À cause du comportement à base d'images, une fonction contenant seulement des commandes
SELECT
peut être indiquée STABLE
en toute sécurité
même s'il sélectionne des données à partir de tables qui pourraient
avoir subi des modifications entre temps par des requêtes concurrentes.
PostgreSQL exécutera toutes les commandes d'une fonction
STABLE
en utilisant l'image établie par la requête appelante et
n'aura qu'une vision figée de la base de données au cours de la requête.
Ce même comportement d'images est utilisé pour les commandes
SELECT
à l'intérieur de fonctions IMMUTABLE
. Il
est généralement déconseillé de sélectionner des tables de la base de
données à l'intérieur de fonctions IMMUTABLE
car
l'immutabilité sera rompue si le contenu de la table change. Néanmoins,
PostgreSQL ne vous force pas à ne pas le faire.
Une erreur commune est de placer un label sur une fonction
IMMUTABLE
quand son résultat dépend d'un paramètre de
configuration. Par exemple, une fonction qui manipule des types date/heure
pourrait bien avoir des résultats dépendant du paramètre
TimeZone. Pour être sécurisées, de telles
fonctions devraient avoir le label STABLE
à la place.
PostgreSQL requiert que les fonctions
STABLE
et IMMUTABLE
ne contiennent
aucune commande SQL autre que SELECT
pour éviter les
modifications de données (ceci n'a pas été complètement testé car de
telles fonctions pourraient toujours appeler des fonctions
VOLATILE
qui modifient la base de données. Si vous le
faites, vous trouverez que la fonction STABLE
ou
IMMUTABLE
n'est pas au courant des modifications
effectuées sur la base de données par la fonction appelée, car elles
sont cachées depuis son image).