El ejecutor (executor) toma el plan creado por el planificador/optimizador y lo procesa recursivamente para extraer el conjunto de filas requerido. Esto es esencialmente un mecanismo de tubería de arrastre bajo demanda (demand-pull pipeline). Cada vez que se llama a un nodo del plan, debe entregar una fila más, o informar que ha terminado de entregar filas.
Para proporcionar un ejemplo concreto, supón que el nodo superior es un nodo
MergeJoin. Antes de poder realizar cualquier mezcla, se deben
recuperar dos filas (una de cada subplan). Por lo tanto, el ejecutor se llama a sí mismo
recursivamente para procesar los subplanes (comienza con el subplan adjunto a
lefttree). El nuevo nodo superior (el nodo superior del subplan
izquierdo) es, supongamos, un nodo Sort y de nuevo se necesita
recursividad para obtener una fila de entrada. El nodo hijo del Sort
podría ser un nodo SeqScan, que representa la lectura real de una
tabla. La ejecución de este nodo hace que el ejecutor recupere una fila de la tabla y
la devuelva al nodo llamador. El nodo Sort llamará repetidamente a
su hijo para obtener todas las filas que se van a ordenar. Cuando la entrada se agota
(como lo indica el nodo hijo que devuelve un NULL en lugar de una fila), el código de
Sort realiza la ordenación y, finalmente, es capaz de devolver su
primera fila de salida, es decir, la primera en orden ordenado. Mantiene las filas
restantes almacenadas para poder entregarlas en orden ordenado en respuesta a demandas
posteriores.
El nodo MergeJoin exige de manera similar la primera fila de su
subplan derecho. Luego compara las dos filas para ver si se pueden unir; si es así,
devuelve una fila de unión a su llamador. En la siguiente llamada, o inmediatamente si
no puede unir el par de entradas actual, avanza a la siguiente fila de una tabla o de la
otra (dependiendo de cómo haya resultado la comparación) y vuelve a comprobar si hay una
coincidencia. Eventualmente, uno u otro subplan se agota, y el nodo
MergeJoin devuelve NULL para indicar que no se pueden formar más filas
de unión.
Las consultas complejas pueden involucrar muchos niveles de nodos de plan, pero el enfoque general es el mismo: cada nodo calcula y devuelve su siguiente fila de salida cada vez que se le llama. Cada nodo también es responsable de aplicar cualquier expresión de selección o proyección que le haya sido asignada por el planificador.
El mecanismo del ejecutor se utiliza para evaluar los cinco tipos básicos de consultas
SQL: SELECT, INSERT, UPDATE,
DELETE y MERGE. Para SELECT, el
código del ejecutor de nivel superior solo necesita enviar cada fila devuelta por el
árbol de plan de consulta al cliente. Los comandos INSERT ... SELECT,
UPDATE, DELETE y MERGE son
efectivamente SELECTs bajo un nodo de plan especial de nivel superior
llamado ModifyTable.
El comando INSERT ... SELECT alimenta las filas a
ModifyTable para su inserción. Para UPDATE, el
planificador organiza que cada fila calculada incluya todos los valores de las
columnas actualizadas, más el TID (ID de tupla o ID de fila)
de la fila objetivo original; estos datos se alimentan al nodo ModifyTable,
que utiliza la información para crear una nueva fila actualizada y marcar la fila antigua
como eliminada. Para DELETE, la única columna que realmente
devuelve el plan es el TID, y el nodo ModifyTable simplemente utiliza
el TID para visitar cada fila objetivo y marcarla como eliminada. Para MERGE,
el planificador une las relaciones origen y destino, e incluye todos los valores de
columna requeridos por cualquiera de las cláusulas WHEN, más el TID
de la fila destino; estos datos se alimentan al nodo ModifyTable,
que utiliza la información para determinar qué cláusula WHEN ejecutar
y luego inserta, actualiza o elimina la fila destino, según sea necesario.
Un comando simple INSERT ... VALUES crea un árbol de plan trivial
que consiste en un solo nodo Result, que calcula una sola fila de
resultado, alimentando eso a ModifyTable para realizar la inserción.