51.6. Ejecutor #

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.