Le projet est structuré comme suit:
Pour les packages pour le fichier source du projet, le fichier de projet est organisé par le rationnel suivant:
Afin de pousser la sélection à une position appropriée dans notre plan logique, nous avons mis en œuvre la structure de la recherche de données de l'Union aux attributs de groupe avec la même condition de plage. La condition de sélection, qui contient l'expression égale, plus grande et minet, entre une relation et un nombre ou entre deux attributs de la même relation, serait poussé après la condition de jointure. L'usage et une autre condition de sélection utilisable seraient repoussés vers l'opérateur de jointure.
Nous avons créé une classe appelée SelectionOptimizer pour gérer la méthode de sélection à utiliser. Nous vérifions d'abord la disponibilité d'indexation pour tous les attributs d'une relation impliquée dans la sélection. S'il n'y a pas d'indexation appropriée pour ces attributs, nous choisissons la méthode de balayage simple. S'il existe des options d'indexation sur ces attributs, nous comparerions le coût de leurs coûts et choisissons celui avec le coût le plus bas.
Nous avons eu des problèmes en œuvre d'important la méthode de programmation dynamique pour choisir le meilleur ordre de jointure. La solution compromise que nous avons mis en œuvre est de choisir l'ordre de jointure en fonction de chaque taille de table.
Notre logique consiste à utiliser la jointure de Merg de tri si elle est applicable. Si SMJ n'est pas applicable, nous choisissons d'utiliser la jointure de boucle imbriquée de blocs.
L' entrée de niveau supérieur du programme est sur le client.sqlinterpreter.main ().
L'interprète est en charge de lire le fichier d'entrée et de transférer à l'évaluateur de requête, et enfin de sortir le résultat dans un fichier.
Nous catégorisons les conditions de la clause où: 1. Comparaison entre les constantes. 2. Condition de sélection de la table. 3. Condition de jointure à table croisée.
Au début de l'analyse, toutes les conditions sont extraites en brisant récursivement les expressions et. Ensuite, pour chaque tableau, nous enregistrons: 1) ses conditions de sélection et 2) son état de jointure avec toutes les tables précédentes . De cette façon, nous pourrions nous déplacer le long des tables et nous concentrer uniquement sur les conditions pertinentes lorsqu'un nouveau tableau doit être joint au sous-arbre gauche actuel. Et bien sûr, des conditions constantes sont gérées avant tout.
Nous avons mis en œuvre le sort-opérateur conformément aux instructions. Si certains éléments ordonnés ne sont pas projetés (par exemple, sélectionnez SA dans l'ordre par SB) et distinct est requis, nous avons un autre opérateur utilisant HashSet pour maintenir l'ordre.
Nous fournissons une classe BPlustree et son constructeur est en mesure de créer un fichier d'index sérialisé sur un attribut perticulaire à partir des données de relation données. Il fournit également des fonctionnalités de désérialisation d'index d'arborescence lorsque l'utilisateur doit utiliser l'opérateur de numérisation d'index pendant l'exécution. Cela raccourcirait le temps de recherche de tuple.
Nous avons implémenté un opérateur de balayage d'index pour la sélection d'index à l'aide de B + Tree Index. Il évalue les expressions contient l'attribut d'index et appellerait le désériseur pour récupérer la première entrée de données satifiée à partir du nœud feuille de l'arborescence B +. Après cela, il rapporterait les tuples linéairement si le fichier est regroupé, autre, il récupérera continuellement la prochaine entrée de données satisfait du désérialiseur.
Dans notre conception précédente, nous avons donné à notre opérateur de sélection logique une contrainte importante que l'enfant d'un opérateur de sélection logique doit être un opérateur de scan. Par conséquent, lorsque nous utilisons le motif des visiteurs pour construire notre plan physique, nous n'avons qu'à traverser l'opérateur de sélection au plus. Et lorsque le visiteur traverse l'opérateur de sélection, la logique pourrait décider que l'enfant de l'opérateur de sélection doit être un opérateur de balayage complet ou un opérateur de balayage d'index.
Comme nous avons stocké les informations du fichier de configuration dans le catalogue. Premièrement, si le catalogue de la base de données montre que la requête d'index est activée, nous devons vérifier la disponibilité de l'index sur cette requête.
Nous avons une méthode appelée Hasidxattr qui vérifie si l'index s'applique à la condition de sélection. Autrement dit, si la condition de sélection pour ce tableau Contians supérieure à , supérieure à l'égalité , est égal , inférieur , inférieur à l'attaque indexé, alors nous sommes bons de construire un opérateur de balayage d'index en tant qu'enfant. Ensuite, nous calculons le Lowkey et Highkey en dépendant de toutes les conditions de sélection, puis construisons l'opérateur.
Si le HasiDxAttr revient faux, nous devons construire l'opérateur de balayage complet normal.
Enfin, nous revenons au niveau UPPPER.
Nous utilisons deux stratégies de test
Pour le test de base, nous construisons manuellement les opérateurs et jetons les tuples pour prouver que l'opérateur pourrait fonctionner en ce qui concerne le modèle d'itérateur.
Le test de bout en bout est le test qui prend l'ensemble de l'interprète comme unité testée. Le cas de test prendra le fichier de requête d'entrée, puis exécutera la requête, imprimera enfin le résultat dans le fichier cible.
Afin de mieux effectuer les tests, nous avons écrit un script bash qui vide le résultat de la base de données MySQL comme sortie attendue. Par conséquent, il est très facile pour nous de construire des cas de test relativement complexes.
De plus, nous avons créé la classe Util Diff pour comparer deux fichiers qui jugeront automatiquement l'exactitude de la sortie.