El proyecto está estructurado de la siguiente manera:
Para los paquetes para el archivo de origen del proyecto, se organiza mediante el siguiente racional:
Para empujar la selección a la posición adecuada en nuestro plan lógico, implementamos la estructura de datos de la Unión para encontrar los atributos de grupo con la misma condición de rango. La condición de selección, que contiene la expresión igual de iguales, grandes y minortistas entre una relación y un número o entre dos atributos de la misma relación, se empujaría hacia abajo después de la condición de unión. Uneqality y otra condición de selección utilizable se empujarían al operador de unión.
Creamos una clase llamada SelectionOptimizer para manejar qué método de selección usar. Primero verificamos la disponibilidad de indexación para todos los atributos de una relación involucrada en la selección. Si no hay una indexación adecuada para estos atributos, elegiríamos el método de exploración simple. Si hay opciones de indexación en estos atributos, compararíamos el costo de sus costos y elegiríamos el que tiene el menor costo.
Tuvimos algunos problemas para implicar el método de programación dinámica para elegir el mejor orden de unión. La solución comprometida que implementamos es elegir el orden de unión en función del tamaño de cada tabla.
Nuestra lógica es utilizar SILD MERG Join si es aplicable. Si SMJ no es aplicable, elegimos usar unión de bucle anidado de bloques.
La entrada de nivel superior del programa está en el cliente.sqlinterpreter.main ().
El intérprete está a cargo de leer el archivo de entrada y transferir al evaluador de consultas, y finalmente emite el resultado a un archivo.
Categorizamos las condiciones de la cláusula WHERE en: 1. Comparación entre constantes. 2. Condición seleccionada en la mesa. 3. Condición de unión cruzada.
Al comienzo del análisis, todas las condiciones se extraen rompiendo recursivamente y las expresiones. Luego, para cada tabla, registramos: 1) sus condiciones selectas, y 2) su condición de unión con todas las tablas anteriores . De esta manera, podríamos avanzar a lo largo de las tablas y centrarnos solo en condiciones relevantes cuando se debe unir una nueva tabla con el subree izquierdo actual. Y, por supuesto, las condiciones constantes se manejan antes que todo.
Implementamos el tipo operador según las instrucciones. Si no se proyectan ciertos elementos ordenados (por ejemplo, seleccione SA de S Order por SB) y se requiere distinto, tenemos otro operador que usa hashset para mantener el orden.
Proporcionamos una clase Bplustree, y su constructor puede construir un archivo de índice serializado en un atributo perticular a partir de los datos de relación dados. También proporciona funcionalidad de deserialización del índice de árbol cuando el usuario necesita usar el operador de escaneo índice durante la ejecución. Esto acortaría el tiempo de búsqueda de tupla.
Implementamos un operador de escaneo de índice para la selección de índice utilizando el índice B+ Tree. Evalúa las expresiones contiene el atributo de índice y llamaría a deserilizante para obtener la primera entrada de datos satificados del nodo hoja del árbol B+. Después de eso, buscaría tuplas linealmente si el archivo está agrupado, otro sabio, obtendrá continuamente la próxima entrada de datos satisfecho del deserializador.
En nuestro diseño anterior, le dimos a nuestro operador de selección lógica una restricción importante de que el hijo de un operador de selección lógica debe ser un operador de escaneo. Por lo tanto, cuando usamos el patrón de visitantes para construir nuestro plan físico, solo necesitamos atravesar al operador de selección como máximo. Y cuando el visitante atraviesa el operador de selección, la lógica podría decidir en lo que el niño del operador de selección debe ser operador de escaneo completo o operador de escaneo de índice.
Como hemos almacenado la información del archivo de configuración en el catálogo. En primer lugar, si el catálogo de la base de datos muestra que la consulta de índice está activada, debemos verificar la disponibilidad del índice en esta consulta.
Tenemos un método llamado Hasidxattr que verifica si el índice se aplica a la condición de selección. Es decir, si la condición de selección para esta tabla contios mayor que , mayor que igual a , es igual , menos que , menos igual a la atraibute indexada, entonces somos buenos para construir un operador de escaneo índice como el niño. Luego calculamos el llave y la tecla alta dependen de todas las condiciones de selección y luego construimos el operador.
Si el hasidxattr devuelve falso, debemos construir el operador de escaneo completo normal.
Finalmente volvemos al nivel de UPPPER.
Hacemos uso de dos estrategias de prueba
Para la prueba básica, construimos manualmente a los operadores y volcamos las tuplas para demostrar que el operador podría funcionar con respecto al modelo de iterador.
La prueba de extremo a extremo es la prueba que toma todo el intérprete como la unidad bajo prueba. El caso de prueba tomará el archivo de consulta de entrada y luego ejecutará la consulta, finalmente imprima el resultado en el archivo de destino.
Para realizar mejor las pruebas, hemos escrito un script bash que arroja el resultado de la base de datos MySQL como la salida esperada. Por lo tanto, es muy fácil para nosotros construir casos de prueba relativamente complejos.
Además, hemos creado el Diff de la clase Util para comparar dos archivos que juzgarán automáticamente la corrección de la salida.