Planificador/optimizador

La tarea del planificador/optimizador es crear un plan de ejecución óptimo. Primero combina todas las posibles vías de barrer (scannear) y cruzar (join) las relaciones que aparecen en una consulta. Todas las rutas creadas conducen al mismo resultado y es el trabajo del optimizador estimar el coste de ejecutar cada una de ellas para encontrar cual es la más económica.

Generando planes posibles

El planificador/optimizador decide qué planes deberían generarse basándose en los tipos de índices definidos sobre las relaciones que aparecen en una consulta. Siempre existe la posibilidad de realizar un barrido secuencial de una relación, de modo que siempre se crea un plan que sólo utiliza barridos secuenciales. Se asume que hay definido un índice en una relación (por ejemplo un índice B-tree) y una consulta contiene la restricción relation.attribute OPR constant. Si relation.attribute acierta a coincidir con la clave del índice B-tree y OPR es distinto de '<>' se crea un plan utilizando el índice B-tree para barrer la relación. Si hay otros índices presentes y las restricciones de la consulta aciertan con una clave de un índice, se considerarán otros planes.

Tras encontrar todos los planes utilizables para revisar relaciones únicas, se crean los planes para cruzar (join) relaciones. El planificador/optimizador considera sólo cruces entre cada dos relaciones para los cuales existe una cláusula de cruce correspondiente (es decir, para las cuales existe una restricción como WHERE rel1.attr1=rel2.attr2) en la cualificación de la WHERE. Se generan todos los posibles planes para cada cruce considerado por el planificador/optimizador. Las tes posibles estrategias son:

Estructura de datos del plan

Daremos ahora una pequeña descripción de los nodos que aparecen en el plan. La figura \ref{plan} muestra el plan producido para la consulta del ejemplo \ref{simple_select}.

El nodo superior del plan es un nodo Cruce Mezclado (MergeJoin) que tiene dos sucesores, uno unido al campo árbol izquierdo (lefttree) y el segundo unido al campo árbol derecho (righttree). Cada uno de los subnodos representa una relación del cruce. Como se mencionó antes, un cruce de mezcla ordenada requiere que cada relación sea ordenada. Por ello encontramos un nodo Sort en cada subplan. La cualificación adicional dada en la consulta (s.sno > 2) se envía tan lejos como es posible y se une al campo qpqual de la rama SeqScan del nodo del correspondiente subplan.

La lista unida al campo mergeclauses del nodo Cruce Mezclado (MergeJoin) contiene información sobre los atributos de cruce. Los valores 65000 y 65001 de los campos varno y los nodos VAR que aparecen en la lista mergeclauses (y también en la lista objetivo) muestran que las tuplas del nodo actual no deben ser consideradas, sino que se deben utilizar en su lugar las tuplas de los siguientes nodos "más profundos" (es decir, los nodos superiores de los subplanes).

Nótese que todos los nodos Sort y SeqScan que aparecen en la figura \ref{plan} han tomado una lista objetivo, pero debido a la falta de espacio sólo se ha dibujado el correspondiente al Cruce Mezclado.

Otra tarea realizada por el planificador/optimizador es fijar los identificadores de operador en los nodos Expr y Oper. Como se mencionó anteriormente, Postgres soporta una variedad de tipos diferentes de datos, e incluso se pueden utilizar tipos definidos por el usuario. Para ser capaz de mantener la gran cantidad de funciones y operadores, es necesario almacenarlos en una tabla del sistema. Cada función y operador toma un identificador de operador único. De acuerdo con los tipos de los atributos usados en las cualificaciones, etc, se utilizan los identificadores de operador apropiados.