fn main() {
println!("Hello World!"); (1)
}
Antoni Boucher
Vérification des erreurs à l’exécution
Dialectes légèrement différents d’une base de données à l’autre
Nécessite d’apprendre ou d’utiliser un langage différent de celui qui est utilisé pour un projet
Sécurité (injections SQL)
Langage utilisé pour un besoin spécifique utilisant la syntaxe du langage hôte
Permet d’effectuer une analyse sémantique
Permet de corriger les problèmes de sécurité
Peut générer du SQL pour différentes bases de données
Pas besoin d’utiliser le SQL
Problème potentiel : dégradation de la performance
Compile du code Rust en SQL à la compilation
Permet de régler le problème de performance
TQL :
Attribut pour la structure des tables
Macro procédurale pour générer le SQL
Conçu par Mozilla, sorti en 2010
Multi-paradigme (impératif, fonctionnel, concurrent)
Typage fort et statique (avec inférence de types)
Sécurisé
Orienté bas niveau
Gestion automatique de la mémoire (sans ramasse-miettes)
Moteur de rendu Servo
Abstractions qui ne coûte rien
Gestion de la concurrence
Sûr :
Pas de pointeurs nuls
Pas d’utilisation de pointeurs après la désallocation
Pas d’utilisation de variables avant leur initialisation
Variables immuables par défaut
Pas de fuite de mémoire
fn main() {
println!("Hello World!"); (1)
}
1 | println! est une macro. |
let nombre: i32 = 42; (1)
let nombre = 42; (2)
let mut nombre = 24; (3)
1 | Variable de type entier signé de 32 bits |
2 | Indiquer le type est facultatif grâce à l’inférence de type |
3 | Variable muable |
struct Personne {
prenom: String,
nom: String,
age: u8, (1)
}
impl Personne {
fn nom_complet(&self) -> String { (2) (3)
self.prenom + " " + &self.nom
}
}
1 | Entier non signé de 8 bits |
2 | self est l’objet courant |
3 | & indique que l’objet courant est reçu par référence |
let personne = Personne {
nom: "Hoare",
prenom: "Graydon",
};
let nom_complet = personne.nom_complet();
#[derive(Clone)]
struct Personne {
prenom: String,
nom: String,
age: u8,
}
match nombre {
1 => println!("un"),
2 => println!("deux"),
3 | 4 => println!("trois ou quatre"),
5 ... 10 => println!("entre 5 et 10"),
_ => println!("autre"),
}
enum Liste {
Vide,
Cons(i32, Box<Liste>),
}
fn somme_liste(liste: &Liste) -> i32 {
match *liste {
Liste::Vide => 0,
Liste::Cons(element, ref reste) =>
element + somme_liste(reste),
}
}
if
)fn est_vide(liste: &Liste) -> bool {
if let Liste::Vide = *liste {
true
}
else {
false
}
}
let texte =
if nombre > 42 { (1)
"plus grand que 42" (2)
}
else {
"plus petit ou égal à 42"
};
1 | if est une expression |
2 | absence du point-virgule |
fn max(nombre1: i32, nombre2: i32) -> i32 {
if nombre1 > nombre2 {
nombre1
}
else {
nombre2
}
}
Création de table
Suppression de table
Sélection
Insertion
Mise à jour
Suppression
SELECT DISTINCT
Pour la création de table :
DEFAULT
UNIQUE
INDEXES
Pour la suppression de données : CASCADE
25 % du SQL est implémenté
80 % des fonctionnalités les plus courantes
L’attribut #[SqlTable]
indique qu’une structure représente une table SQL
#[SqlTable]
struct Personne {
id: PrimaryKey,
nom: String,
prenom: String,
age: i32,
}
#[SqlTable]
struct Article {
id: PrimaryKey,
auteur: String,
titre: String,
revision: i32,
date_ajout: DateTime<UTC>,
}
#[SqlTable]
struct Commentaire {
id: PrimaryKey,
auteur: String,
message: String,
date_post: DateTime<UTC>,
article: ForeignKey<Article>, (1)
}
1 | Clé étrangère vers la table Article |
La macro to_sql!()
converti le code Rust en SQL.
La macro sql!()
exécute en plus la requête.
sql!(Personne.insert(nom = "Hoare", prenom = "Graydon"));
let personnes = sql!(
Personne.filter(nom == "Hoare" && age < 30)
.sort(-age)[10..20]
);
for personne in personnes {
println!("{} {}", personne.prenom, personne.nom);
}
SELECT Personne.id, Personne.nom,
Personne.prenom, Personne.age,
Personne.date_naissance, Personne.poids
FROM Personne
WHERE nom = 'Hoare'
AND age < 30
ORDER BY age DESC
LIMIT 20 OFFSET 10
let personne = sql!(Personne.get("personne_id")).unwrap();
code.rs:10:34: 10:46 error: mismatched types:
expected `i32`,
found `String` [E0308]
code.rs:10 let personne = sql!(Personne.get("personne_id")).unwrap();
^~
code.rs:10:34: 10:46 help: run `rustc --explain E0308` to see a detailed explanation
code.rs:10:34: 10:46 note: in this expansion of sql! (defined in TQL)
Simplification des expressions composées de littéraux
Personne[0 + 10 - 2 .. 50 - (4 + 2)]
SELECT Personne.id, Personne.nom, Personne.prenom, Personne.age
FROM Personne
LIMIT 44
OFFSET 8
Si seul le champ age
est utilisé suite à la requête, la requête suivante :
Personne.all()
pourrait être compilée en :
SELECT Personne.age
FROM Personne
Avantage : diminue le transfert d’informations.
Cette requête :
Personne.filter(nom.contains("oar"))
compile en :
SELECT Personne.id, Personne.nom, Personne.prenom, Personne.age
FROM Personne
WHERE nom LIKE '%' || 'oar' || '%'
pourrait être compilée en :
SELECT Personne.id, Personne.nom, Personne.prenom, Personne.age
FROM Personne
WHERE nom LIKE '%oar%'
for i in (0..5) {
let personnes = sql!(Personne.all());
}
let result = connection.prepare("SELECT Personne.id, Personne.nom,
Personne.prenom, Personne.age, Personne.date_naissance,
Personne.poids FROM Personne").unwrap();
for i in (0..5) {
let personnes = {
result.query(&[]).unwrap().iter().map(|row| {
Personne {
id: row.get(0),
nom: row.get(1),
prenom: row.get(2),
age: row.get(3),
}
}).collect::<Vec<_>>()
};
}
Conversion du code Rust en SQL à la compilation.
Détection des erreurs de types et d’identifiants à la compilation.
Abstraction au SQL.