O projeto está estruturado da seguinte maneira:
Para pacotes para o arquivo de origem do projeto, é organizado pelo seguinte racional:
Para pressionar a seleção para a posição correta em nosso plano lógico, implementamos a estrutura de dados do Union Encontre para os atributos de agrupamento com a mesma condição de intervalo. A condição de seleção, que contém a expressão de Minormandano EqualSto, Greaterthan, entre uma relação e um número ou entre dois atributos da mesma relação, seria pressionado após a condição de junção. A unidade e outra condição de seleção utilizável seriam empurradas para o operador de junção.
Criamos uma classe chamada SelectionOttimizer para lidar com qual método de seleção usar. Primeiro, verificamos a disponibilidade de indexação para todos os atributos de uma relação envolvida na seleção. Se não houver indexação adequada para esses atributos, escolheríamos o método de varredura simples. Se houver opções de indexação nesses atributos, compararíamos o custo de seus custos e escolheríamos o custo mais baixo.
Tivemos alguns problemas implicando o método de programação dinâmica para escolher o melhor pedido de junção. A solução comprometida que implementamos é escolher o pedido de junção com base no tamanho de cada tabela.
Nossa lógica é usar a união de Merg de classificação, se for aplicável. Se o SMJ não for aplicável, optamos por usar a junção de loop aninhada.
A entrada de nível superior do programa está no client.sqlinerPreter.main ().
O intérprete é responsável por ler o arquivo de entrada e transferir para o avaliador de consultas e, finalmente, produzir o resultado para um arquivo.
Categorizamos as condições da cláusula WHERE em: 1. Comparação entre constantes. 2. Condição de seleção na mesa. 3. Condição de junção cruzada.
No início da análise, todas as condições são extraídas, quebrando recursivamente as expressões. Então, para cada tabela, registramos: 1) suas condições selecionadas e 2) sua condição de junção com todas as tabelas anteriores . Dessa forma, poderíamos avançar ao longo das tabelas e nos concentrar apenas em condições relevantes quando uma nova tabela precisa ser unida à sub-árvore esquerda atual. E, claro, condições constantes são tratadas antes de tudo.
Implementamos o SortOperator conforme as instruções. Se certos elementos ordenados não forem projetados (por exemplo, selecione SA da ordem s por SB) e é necessário distinto, temos outro operador usando o hashset para manter o pedido.
Fornecemos uma classe BPlustee e seu construtor é capaz de criar um arquivo de índice serializado em um atributo perticular a partir dos dados de relação dada. Ele também fornece a funcionalidade de deserialização do índice de árvores quando o usuário precisa usar o operador de digitalização de índice durante a execução. Isso diminuiria o tempo de busca da tupla.
Implementamos um operador de varredura de índice para seleção de índices usando o índice de árvore B+. Ele avalia as expressões contém o atributo de índice e chamaria o deserimento para buscar a primeira entrada de dados saciada no nó foliar da árvore B+. Depois disso, buscaria tuplas linearmente se o arquivo estivesse agrupado, de outra forma, ele buscará continuamente a entrada de dados satisfeita no Deserializer.
Em nosso design anterior, demos ao nosso operador de seleção lógica uma restrição importante de que o filho de um operador de seleção lógica deve ser um operador de varredura. Portanto, quando usamos o padrão do visitante para construir nosso plano físico, precisamos apenas atravessar o operador de seleção no máximo. E quando o visitante atravessar o operador de seleção, a lógica pode decidir que o filho do operador de seleção deve ser operador de varredura completo ou operador de varredura de índice.
Como armazenamos as informações do arquivo de configuração no catálogo. Em primeiro lugar, se o catálogo do banco de dados mostrar que a consulta de índice está ligada, devemos verificar a disponibilidade do índice nesta consulta.
Temos um método chamado hasidxattr que verifica se o índice se aplica à condição de seleção. Ou seja, se a condição de seleção para esta tabela contia maior que , maior que igual a , igual , menor que , menor que igual a no atributo indexado, então somos bons para construir um operador de varredura de índice como criança. Em seguida, calculamos o Lowkey e o Highkey dependem de todas as condições de seleção e, em seguida, construímos o operador.
Se o hasidxattr retornar falso, devemos construir o operador normal de varredura completa.
Finalmente, retornamos ao nível Uppper.
Utilizamos duas estratégias de teste
Para o teste básico, construímos manualmente os operadores e despejamos as tuplas para provar que o operador poderia funcionar em relação ao modelo de iterador.
O teste de ponta a ponta é o teste que leva todo o intérprete como a unidade em teste. O caso de teste pegará o arquivo de consulta de entrada e executará a consulta, finalmente imprima o resultado no arquivo de destino.
Para realizar melhor o teste, escrevemos um script bash que despeja o resultado do banco de dados MySQL como a saída esperada. Portanto, é muito fácil para nós construir casos de teste relativamente complexos.
Além disso, criamos a classe Util Diff para comparar dois arquivos que julgarão automaticamente a correção da saída.