Créer une extension Postgres en Rust, Episode 1 : Un nouveau monde

Photo Credit Jacob Colvin

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:

Important: Cette série d'articles n'est pas une introduction au langage Rust ! Il existe d'autres ressources pour ça (notamment ici et ). 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 variable name 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 !