Partie 11 : Expressions Logiques SQL
Lecture 32 min. âą
Table des matiĂšres
Les articles de la série
- Partie 1 : Construire le REPL
- Partie 2 : Sérialisation des données
- Partie 3 : Database
- Partie 4 : Tables
- Partie 5 : Parser les commandes, création de la boßte à outils
- Partie 6 : Parser les commandes SQL
- Partie 7 : Tuples de données
- Partie 8 : Contraindre les données via un schéma
- Partie 9 : Découper le stockage des tables en pages
- Partie 10 : Clefs primaires
- Partie 11 : Expressions Logiques SQL
- Partie 12 : Scans et filtrage
- Partie 13 : UTF-8 et caractÚres échappés
- Partie 14 : Attributs nullifiables
- Partie 15 : Index secondaires
- Partie 16 : Directive EXPLAIN
- Partie 17 : Logical Plan
- Partie 18 : Exécution du plan logique
- Partie 19 : Suppression d'enregistrements
- Partie 20 : Réindexation et compactation de tables
Bonjour Ă toutes et tous đ
Lâintroduction du concept de clef primaire permet de rĂ©cupĂ©rer une entrĂ©e dans la base de donnĂ©es sans devoir scanner tous les Ă©lĂ©ments un par un jusquâĂ trouver le bon.
Câest pas mal du tout, mais un peu limitĂ©.
GĂ©nĂ©ralement les requĂȘtes sont un peu plus complexes et utiles.
Si lâon reprend la mĂ©taphore de notre annuaire tĂ©lĂ©phonique, câest comme si pour trouver quelquâun, vous nâavez que deux choix: soit vous chercher dans toutes les pages Ă la recherche du bon nom soit vous connaissez dĂ©jĂ le numĂ©ro de page et câest nettement plus simple. Ăa câest le fonctionnement de lâindex primaire et du full-scan.
Maintenant, imaginons que lâon veuille rĂ©cupĂ©rer les numĂ©ros de tous les hommes habitant dans la ville âXâ.
Dans la vraie vie, pour ceux qui nâont jamais vu un, si si ça doit exister maintenant đ . Les entrĂ©es sont dĂ©composĂ© en section pour chaque ville et triĂ© par ordre alphabĂ©tique.
Nous pouvons modĂ©liser notre annuaire au travers dâune table PhoneBook
.
TEXT(50) PRIMARY KEY, city TEXT(50), phone_number TEXT(50), gender TEXT(1));
(name
Notre requĂȘte dans lâannuaire ne peut pas ĂȘtre par clef primaire.
Il faut connaĂźtre les noms des personnes pour les trouver.
SELECT * FROM PhoneBook WHERE name='Alexandre Adams'
SELECT * FROM PhoneBook WHERE name='Amandine Agostini'
SELECT * FROM PhoneBook WHERE name='Arthur Almeida'
SELECT * FROM PhoneBook WHERE name='AnaĂŻs Aubert'
...
Ce nâest pas trĂšs pratique⊠đ
La vraie requĂȘte que lâon veut câest:
SELECT * FROM PhoneBook WHERE city = 'X' AND gender = 'H';
Cette partie de la requĂȘte, nous allons lâappeler âexpressionâ.
city = 'X' AND gender = 'H'
Je vous propose de mettre en place le parse de cette expression dans cette nouvelle partie de notre épopée.
Grammaire
Avant de partir en bataille avec notre parser, nous allons en définir les contours.Expression logique
Expression ::= ColumnExpression | Expression LogicalOperator Expression
ColumnExpression ::= Column BinaryOperator LiteralValue
LogicalOperator ::= 'AND' | 'OR'
BinaryOperator ::= '=' | '!=' | '>' | '>=' | '<' | '<='
Column ::= [A-Za-z_]+
LiteralValue ::= LiteralString | LiteralInteger
LiteralString ::= "'" [^']+ "'"
LiteralInteger ::= [0-9]+
Notre grammaire se complexifie. Nous avons tout une série de nouveaux tokens à rajouter.
AND
OR
!=
=
>
<
>=
<=
Nous avons introduit Ă©galement le concept dâexpression de colonne ColumnExpression
.
On associe un identifiant de colonne et une valeur comparé par un opérateur binaire BinaryOperator
.
id > 12
name != 'Max'
Mais nous avons Ă©galement la possibilitĂ© de dâassocier deux ColumnExpression
via un LogicalOperator
.
Et on peut le faire autant que lâon veut.
id > 12 AND name != 'Max' OR data = 'true'
Ajout des tokens
On rajoute nos différents tokens
On y associe un Matcher
Ainsi que la taille des tokens
Parsing de lâexpression
Comme la grammaire le laisse supposer, parser une Expression est plus complexe que parser les embryon de commandes que nous avons déjà dans notre arsenal.
La principal difficultĂ© Ă©tant quâune Expression est rĂ©cursivement une Expression.
Mais avant de nous attaquer au plat de resistance et de lâavoir sur lâestomac par indigestion. Nous allons dĂ©composer le problĂšmes en sous-problĂšmes et recomposer le puzzle.
Recognizer
On avait dĂ©jĂ un Acceptor qui permettait dâavoir des alternatives de visiteurs, le Forecaster qui donnait le choix de forecast de groupe de token dans le futur.
Il nous manquait la possiblité de choisir une collection de tokens à reconnaßtre.
Avec le Recognizer
câest chose faite !
MĂȘme principe que pour les deux autres, on lui fourni une liste de choses Ă reconnaĂźtre et il tente de reconnaĂźtre le token, sâil nây arrive pas, il passe au suivant, sinon il sâarrĂȘte et propage le token reconnu.
En sorti de méthode finish
on obtient alors une Option du token.
Binary Operator
La premiĂšre chose que lâon va vouloir reconnaĂźtre câest nos opĂ©rateurs binaires.
!=
=
>
<
>=
<=
Nous avons besoin dâune Ă©numĂ©ration pour modĂ©liser ces opĂ©rateurs
Ainsi que dâun TryFrom qui va permettre de passer du token reconnu Ă lâopĂ©rateur.
On peut alors reconnaßtre notre opérateur.
ColumnExpression
On peut désormais représenter notre ColumnExpression
Que lâon peut alors visiter.
Ce qui donne:
Parfait! On peut maintenant parser une ColumnExpression đ€©
Opérateur logique
On fait de mĂȘme pour les tokens:
- AND
- OR
Permettant de reconnaßtre les opérateur logique.
Expression
Notre expression est une suite de ColumnExpression séparé par de LogicalOperator.
Lorsque lâon a une expression du genre:
id > 12 AND name = 'toto'
Cela donne une expression du genre
ColumnExpression LogicalOperator ColumnExpression
Mais si on a quelque chose commen
id > 12 AND name = 'toto' AND gender = 'M'
Cela donne lâexpression suivante
ColumnExpression LogicalOperator ColumnExpression LogicalOperator ColumnExpression
Sauf que lâopĂ©rateur logique nâa que deux paramĂštres:
- un Ă gauche
lhs
- un Ă droite
rhs
Si on redécoupe
ColumnExpression LogicalOperator [ ColumnExpression LogicalOperator ColumnExpression ]
On réduit encore
ColumnExpression LogicalOperator LogicalExpression
Et si lâon veut que le systĂšme soit totalement rĂ©cursif, notre expression doit se rĂ©duire en
LogicalExpression
On en déduit deux choses.
Nous avons besoin dâune Ă©numĂ©ration qui permette de faire une union entre la ColumnExpression et la LogicalExpression
On la visite
LogicalExpression
On peut alors en définir le LogicalExpression
Note
Le boxing
Box<Expression>
est obligatoire car Expression peut contenir une LogicalExpression qui contient lui mĂȘme une Expression, etc âŠCela conduit Ă une taille infinie.
Ăa la stack nâaime pas du tout, la heap nâa pas ce problĂšme.
On en défini également un constructeur
Et finalement un visiteur
Test de parse
On peut finalement parser nos Expressions
Expression simple
Dâabord une simple comparaison
Expression logique
Puis une utilisant un connecteur logique
Expression logiques composites
Puis un chaĂźnage de plusieurs expressions
Et voilĂ ! đ€©
Nous sommes capables de parser des expressions logiques SQL. đđđ
Pas toutes biensĂ»r, mais câest un dĂ©but.
En tout cas ça sera suffisant pour la suite de mes explications.
Conclusion
Oui, câĂ©tait encore un Ă©pisode de parsing, mais que voulez vous, il faut bien des outils pour bĂątir des choses, et le parsing est nĂ©cessaire pour nous ouvir le champ des possibles. đ
Dans la prochaine partie nous utiliserons nos expressions pour requĂȘter notre base donnĂ©es sur autre chose que la clef primaire.
Merci de votre lecture â€ïž