Das Projekt ist wie folgt strukturiert:
Für Pakete für die Projektquelldatei wird von der folgenden rationalen Rationierung angeordnet:
Um die Auswahl in unserem logischen Plan in die richtige Position zu bringen, haben wir die Gewerkschaftsfindungsdatenstruktur in Gruppenattribute mit derselben Reichweite implementiert. Die Auswahlbedingung, die den minorthanischen Ausdruck von Equalsto, Greaterthan zwischen einer Beziehung und einer Zahl oder zwischen zwei Attribut derselben Beziehung enthält, würde nach der Verbindungsbedingung nach unten gedrückt. Uneqality und andere Gebrauchsauswahl -Dienste würden zum Join -Operator weitergegeben.
Wir haben eine Klasse namens SelectionOptimizer erstellt, um die Verwendung der Auswahlmethode zu verarbeiten. Wir überprüfen zunächst die Indexierungsverfügbarkeit für alle Attribute einer Beziehung, die an der Auswahl beteiligt ist. Wenn diese Attribute keine ordnungsgemäße Indexierung vorhanden sind, würden wir die Plain Scan -Methode auswählen. Wenn diese Attribute Indexierungsoptionen haben, würden wir die Kosten ihrer Kosten vergleichen und die mit den niedrigsten Kosten auswählen.
Wir hatten einige Probleme, die die dynamische Programmierungsmethode implizieren, um die beste Join -Bestellung auszuwählen. Die von uns implementierte gefährdete Lösung besteht darin, die Join -Reihenfolge basierend auf jeder Tabellengröße auszuwählen.
Unsere Logik ist es, Sortier -Fusions -Join zu verwenden, wenn sie anwendbar ist. Wenn SMJ nicht anwendbar ist, verwenden wir die Block -verschachtelte Loop -Join.
Der oberste Eintrag des Programms findet am Client.sqLinterPreter.main () statt.
Der Interpreter ist verantwortlich für das Lesen der Eingabedatei und die Überweisung an den Abfrage -Bewerter und geben das Ergebnis schließlich in eine Datei aus.
Wir kategorisieren Bedingungen aus der WHERE -Klausel in: 1. Vergleich zwischen Konstanten. 2. Auswahlbedingung. 3. Cross-table Join-Zustand.
Zu Beginn der Parsen werden alle Bedingungen extrahiert, indem die und Ausdrücke rekursiv aufgebrochen werden. Dann zeichnen wir für jede Tabelle auf: 1) seine ausgewählten Bedingungen und 2) seine Verbindungsbedingung mit allen vorhergehenden Tabellen. Auf diese Weise konnten wir uns aus den Tabellen entlang bewegen und uns nur auf relevante Bedingungen konzentrieren, wenn eine neue Tabelle mit dem aktuellen linken Unterbaum verbunden werden muss. Und natürlich werden ständige Bedingungen vor allem behandelt.
Wir haben den Sortoperator gemäß den Anweisungen implementiert. Wenn bestimmte geordnete Elemente nicht projiziert werden (z. B. SA aus S Order by SB) und eindeutig ist, haben wir einen anderen Betreiber, der Hashset verwendet, um die Reihenfolge aufrechtzuerhalten.
Wir bieten eine Bplustree -Klasse, und sein Konstruktor kann eine serialisierte Indexdatei auf einem pertikulären Attribut aus den angegebenen Beziehungsdaten erstellen. Es bietet auch die Funktionalität des Baumindexdeserialisierungsfunktions, wenn der Benutzer während der Ausführung den Index -Scan -Operator verwenden muss. Dies würde die Tupel -Suchzeit verkürzen.
Wir haben einen Index -Scan -Operator für die Indexauswahl unter Verwendung des B+ -Baumindex implementiert. Es bewertet Ausdrücke das Indexattribut und würde Deserilizer aufrufen, um die erste sattierte Dateneingabe aus dem Blattknoten des B+ -Baums abzurufen. Danach würde es Tupel linear abrufen, wenn die Datei zusammengeklustert ist, andere weise, sie wird die nächste zufriedene Dateneingabe vom Deserializer kontinuierlich abrufen.
In unserem vorherigen Design gaben wir unserem logischen Auswahloperator ein wichtiges Einschränkung dafür, dass das Kind eines logischen Auswahloperators ein Scan -Operator sein muss. Wenn wir das Besuchermuster verwenden, um unseren physischen Plan aufzubauen, müssen wir daher höchstens zum Auswahlbetreiber gehen. Wenn der Besucher zum Auswahloperator fährt, kann die Logik entscheiden, ob das Kind des Auswahloperators einen vollständigen Scan -Operator oder Index -Scan -Operator sein sollte.
Da haben wir die Informationen aus der Konfigurationsdatei in den Katalog gespeichert. Erstens, wenn der Datenbankkatalog zeigt, dass die Indexabfrage eingeschaltet ist, sollten wir nach der Verfügbarkeit des Index für diese Abfrage suchen.
Wir haben eine Methode namens HEIDXATTR , die überprüft, ob der Index für die Auswahlbedingung gilt. Das heißt, wenn die Auswahlbedingung für diese Tabelle, die größer als gleich sind , weniger als gleich , weniger als gleich dem indizierten Attraibut, können wir einen Index -Scan -Operator als Kind erstellen. Anschließend berechnen wir den Lowkey und HighKey hängen von allen Auswahlbedingungen ab und bauen dann den Bediener auf.
Wenn der HaSIDXATTR FALSE zurückgibt, sollten wir den normalen vollständigen Scan -Operator erstellen.
Schließlich kehren wir zum Uppper -Level zurück.
Wir verwenden zwei Teststrategien
Für den Basistest konstruieren wir die Operatoren manuell und entwerfen die Tupel, um zu beweisen, dass der Bediener im Hinblick auf das Iteratormodell arbeiten könnte.
Der End-to-End-Test ist der Test, bei dem der gesamte Dolmetscher als zu testetes Gerät verwendet wird. Der Testfall enthält die Eingabeabfragedatei und führt dann die Abfrage aus und druckte das Ergebnis schließlich in die Zieldatei aus.
Um die Tests besser durchzuführen, haben wir ein Bash -Skript geschrieben, das das Ergebnis der MySQL -Datenbank als erwartete Ausgabe abgelegt hat. Daher ist es für uns sehr einfach, relativ komplexe Testfälle zu erstellen.
Außerdem haben wir die Util -Klasse Diff für den Vergleich von zwei Dateien erstellt, mit denen automatisch die Richtigkeit der Ausgabe beurteilt wird.