Créer une extension Postgres en Rust, Episode 1 : Un nouveau monde
Préambule
Cet article fait partie d'une série consacrée au framework PGRX, un environnement de développement qui facilite la conception d'extensions PostgreSQL avec le langage Rust.
Retrouvez les autres articles de la série ci-dessous:
- Episode 1 : Un nouveau monde
- Episode 2 : Demander le luhn
- Episode 3 : Le chant du SIREN
- Episode 4 : Accrocher le parapet
Important: Cette série d'articles n'est pas une introduction au langage Rust ! Il existe d'autres ressources pour ça (notamment ici et là). On se focalisera ici sur la mécanique interne d'une extension Postgres.
Chaque épisode est conçu en 2 parties : un tutoriel de 30 minutes environ, suivi d'exercices pour aller plus loin. Les exercices sont optionnels.
Objectifs
Dans cette première partie, nous avons pour objectifs de:
- Initialiser un environnement de developpement PGRX
- Créer une extension minimale
- Lancer l'extension dans une instance Postgres
Démarrage
Astuce: On peut éviter d'installer Rust et PGRX de manière permanente sur la machine hôte, pour cela voir directement la chapitre suivant Démarrage avec Docker
Avant toutes choses, nous allons installer le framework PGRX.
Pour cela, il faut au préalable mettre en place Rust ( par exemple avec l'installateur rustup ).
Une fois que Rust
et cargo
sont installés, on lance l'installation
de PGRX et l'initialisation de l'environement de développement:
cargo install --locked cargo-pgrx
cargo pgrx init
Suivant la puissance de la machine hôte, cette étape peut être longue. Elle est va également consommer beaucoup d'espace disque.
Démarrage avec Docker
Ce chapitre remplace le précédent :)
docker run -it --volume `pwd`:/pgrx daamien/pgrx
Bonjour le monde
On peut maintenant créer une extension nommée world
avec:
cargo pgrx new world
Un nouveau dossier world
vient d'apparaitre et contient le
strict nécessaire pour générer une extension Postgres, notamment
un fichier source (src/lib.rs
) qui contient la fonction
suivante hello_world
:
#[pg_extern]
fn hello_world() -> &'static str {
"Hello, world"
}
La fonction retourne une valeur statique de type str
( appelé slice de chaînes
de caractères
) qui représente simplement une suite de caractères UTF-8. Cette
valeur de retour sera automatiquement convertie en type TEXT
pour Postgres.
Tous les types essentiels de Postgres sont automatiquement convertis en type Rust La liste complète des conversions est disponible ci-dessous:
https://github.com/pgcentralfoundation/pgrx#mapping-of-postgres-types-to-rust
Lancer l'extension
PGRX va maintenant pouvoir lancer une instance Postgres, charger cette nouvelle extension, créer une base de test et ouvrir une session de travail dessus:
cd world
cargo pgrx run
Attention! Cette commande va compiler une centaine de paquets Rust (
crates
) et peut durer une dizaine de minutes !
world=# CREATE EXTENSION world;
CREATE EXTENSION
world=# SELECT hello_world();
hello_world
--------------
Hello, world
(1 row)
Ajouter une nouvelle fonction
Dans le fichier world/src/lib.rs
, on peut ajouter autant de nouvelles
fonctions que nécessaires.
Par exemple:
#[pg_extern]
fn hello(name: &str) -> String {
format!("Hello, {name}")
}
Note: Par facilité, on utilise ici le type
String
plutot que&str
pour la valeur de retour... Le type String sera également converti en type TEXT pour Postgres
Lançons à nouveau l'extension
cargo pgrx run
Cette fois, seul le module
world
est recompilé !
Une fois dans la nouvelle session psql, on recharge l'extension:
world=# DROP EXTENSION world;
world=# CREATE EXTENSION world;
Et la nouvelle fonction est disponible:
world=# SELECT hello('Bob');
hello
------------
Hello, Bob
(1 row)
Ecrire la même chose en C ?
En guise de comparaison, voici 2 extensions similaires écrites en langage C:
https://github.com/magnusp/pg_hello/blob/master/pg_hello.c
https://github.com/petr-korobeinikov/postgresql-extension-example/blob/master/hello_ext/hello_ext.c
Pour aller plus loin...
Voici quelques propositions d'activités pour poursuivre la découverte:
-
En utilisant la macro panic!, créer une fonction
hello2()
qui renvoie une erreur si la variablename
est vide. -
En utilisant la commande match, dites "Bonjour" si le nom Pierre et "Hola" si le nom est Diego ou Maya.
Un exemple d'implémentation est disponible sur:
https://gitlab.com/daamien/pgrx-notebook/-/blob/main/world/src/lib.rs
Bilan
En quelques minutes, on a pu créer une extension Postgres pour écrire et tester des fonctions internes.
Si l'on fait la comparaison avec le framework classique de développement d'extensions en C ( PGXS ), on remarque déjà plusieurs avantages clairs
- La création de l'environnement de développement est automatisée
- La création de nouvelles fonctions est très simple
- Tous les types de base Postgres sont automatiquement convertis
- La manipulation des chaines de caractères est bien plus souple en Rust
La suite au prochain épisode !
Jusqu'où Rust nous menera-t-il ? Rendez-vous dans l'Episode 2 : Demander le luhn pour le savoir !