Eymoutiers, le 20 septembre 2024

Nous lançons ici une nouvelle série d’articles dédiés au développement d’extensions PostgreSQL en général et au framework PGRX en particulier. Chaque article sera conçu comme un mini-atelier avec des rappels théoriques, des objectifs, un exemple concret et des exercices optionnels pour les lectrices et lecteurs qui veulent aller plus loin !

On démarre avec un classique “Hello World” !

Photo Credit Jacob Colvin

Objectifs

Dans cette première partie, nous avons les objectifs suivants:

  • Initialiser un environnement de développement PGRX
  • Créer une extension minimaliste
  • 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 le 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’environnement de développement :

cargo install --locked cargo-pgrx
cargo pgrx init

Suivant la puissance de la machine hôte, cette étape peut être longue (5 à 30 minutes). Elle est va également consommer beaucoup d’espace disque (environ 4Go).

Démarrage avec Docker

Ce chapitre remplace le précédent :)

Vous pouvez directement réaliser les exemples ci-dessous en vous connectant à une machine docker préparée spécialement à cet effet :

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’apparaître 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.

Grâce à PGRX, 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

Une fois la compilation effectuée, nous voici connecté⋅es à une instance Postgres ! Nous pouvons directement créer et tester notre nouvelle extension.

world=# CREATE EXTENSION world;
CREATE EXTENSION

world=# SELECT hello_world();
 hello_world
--------------
 Hello, world
(1 row)

Difficile de faire plus simple, non ?

Ajouter une nouvelle fonction

Dans le fichier world/src/lib.rs, on peut ajouter autant de nouvelles fonctions que nécessaire.

Par exemple, créons une fonction qui prend un paramètre en entrée :

#[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 ouverte, 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)

Écrire 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 chaînes de caractères est bien plus souple en Rust

La suite au prochain épisode !

Jusqu’où Rust nous mènera-t-il ? Rendez-vous dans l’épisode #2 pour le savoir !

PS: Cet article a été rédigé sans l’assistance d’une intelligence artificielle.


DALIBO

DALIBO est le spécialiste français de PostgreSQL®. Nous proposons du support, de la formation et du conseil depuis 2005.