L'exécuteur prend le plan créé par le planificateur/optimiseur et l'exécute récursivement pour extraire l'ensemble requis de lignes. Il s'agit principalement d'un mécanisme de pipeline en demande-envoi. Chaque fois qu'un nœud du plan est appelé, il doit apporter une ligne supplémentaire ou indiquer qu'il a fini d'envoyer des lignes.
Pour donner un exemple concret, on peut supposer que le nœud
supérieur est un nœud MergeJoin
. Avant de pouvoir faire une
fusion, deux lignes doivent être récupérées (une pour chaque sous-plan).
L'exécuteur s'appelle donc récursivement pour exécuter les sous-plans (en
commençant par le sous-plan attaché à l'arbre gauche
).
Le nouveau nœud supérieur (le nœud supérieur du sous-plan
gauche) est, par exemple, un nœud Sort
(NdT : Tri)
et un appel récursif est une nouvelle fois nécessaire pour obtenir une ligne
en entrée. Le nœud fils de Sort
pourrait être un
nœud SeqScan
, représentant la lecture réelle d'une table.
L'exécution de ce nœud impose à l'exécuteur de récupérer une ligne à
partir de la table et de la retourner au nœud appelant. Le nœud
Sort
appelle de façon répétée son fils pour obtenir
toutes les lignes à trier. Quand l'entrée est épuisée (indiqué par le
nœud fils renvoyant un NULL au lieu d'une ligne), le code de
Sort
est enfin capable de renvoyer sa première ligne en
sortie, c'est-à-dire le premier suivant l'ordre de tri. Il conserve les
lignes restantes en mémoire de façon à les renvoyer dans le bon ordre en
réponse à des demandes ultérieures.
Le nœud MergeJoin
demande de la même façon la
première ligne à partir du sous-plan droit. Ensuite, il compare les deux
lignes pour savoir si elles peuvent être jointes ; si c'est le cas, il
renvoie la ligne de jointure à son appelant. Au prochain appel, ou
immédiatement, s'il ne peut pas joindre la paire actuelle d'entrées, il
avance sur la prochaine ligne d'une des deux tables (suivant le résultat de
la comparaison), et vérifie à nouveau la correspondance. Éventuellement,
un des sous-plans est épuisé et le nœud MergeJoin
renvoie
NULL pour indiquer qu'il n'y a plus de lignes jointes à former.
Les requêtes complexes peuvent nécessiter un grand nombre de niveaux de nœuds pour les plans, mais l'approche générale est la même : chaque nœud est exécuté et renvoie sa prochaine ligne en sortie à chaque fois qu'il est appelé. Chaque nœud est responsable aussi de l'application de toute expression de sélection ou de projection qui lui a été confiée par le planificateur.
Le mécanisme de l'exécuteur est utilisé pour évaluer les cinq types de
requêtes de base en SQL : SELECT
,
INSERT
, UPDATE
,
DELETE
et MERGE
. Pour SELECT
, le code de
l'exécuteur de plus haut niveau a uniquement besoin d'envoyer chaque ligne
retournée par l'arbre plan de la requête vers le client. INSERT
... SELECT
, UPDATE
,
DELETE
et MERGE
sont en réalité des SELECT
sous un nœud de plan haut niveau appelé ModifyTable
.
INSERT ... SELECT
remplit les lignes de
ModifyTable
pour insertion. Pour
UPDATE
, l'optimiseur s'arrange pour que chaque ligne
traitée inclut toutes les valeurs mises à jour des colonnes, plus le
TID (tuple ID, ou
identifiant de ligne) de la ligne cible originale ; cette donnée est
envoyée au nœud ModifyTable
, qui utilise l'information
pour créer un nouveau nœud mis à jour et pour marquer l'ancienne ligne
comme supprimée. Pour DELETE
, la seule colonne qui est
réellement renvoyée par le plan est le TID, et le nœud
ModifyTable
utilise simplement le TID pour visiter
chaque ligne cible et la marquer supprimée.
Pour MERGE
, l'optimiseur joint les relations source et
cible, et inclut toutes les valeurs de colonne requises par chaque clause
WHEN
, plus le TID de la ligne cible ; cette donnée
est fournie au nœud ModifyTable
qui utilise
l'information pour trouver la clause WHEN
à exécuter, puis
insère, met à jour ou supprime la ligne cible, suivant le besoin exprimé.
Une simple commande INSERT ... VALUES
crée un arbre de
plan trivial consistant en un seul nœud Result
, qui
calcule seulement une ligne résultat, en l'envoyant à
ModifyTable
pour réaliser l'insertion.