🔝 Retour au Sommaire
Jusqu'à présent dans ce tutoriel, nous avons exploré le SQL standard : les requêtes SELECT, les jointures, les agrégations, les transactions, etc. Mais PostgreSQL offre bien plus qu'un simple langage de requête. Il propose un véritable environnement de programmation côté serveur.
La programmation serveur consiste à écrire du code qui s'exécute directement dans la base de données, plutôt que dans votre application (Python, Java, Node.js, etc.).
Analogie simple :
- SQL standard = Poser une question à quelqu'un : "Combien font 2+2 ?"
- Programmation serveur = Donner une série d'instructions : "Si le résultat est positif, fais ceci, sinon fais cela, puis répète 10 fois..."
Vous vous demandez peut-être : "Pourquoi écrire du code dans PostgreSQL alors que je peux tout faire dans mon application ?"
Voici les raisons principales :
❌ Sans programmation serveur (application) :
┌─────────────┐ ┌──────────────┐
│ Application │ ─── SELECT ligne 1 ────► │ │
│ │ ◄─── Résultat 1 ─────── │ │
│ │ │ │
│ │ ─── SELECT ligne 2 ────► │ PostgreSQL │
│ │ ◄─── Résultat 2 ─────── │ │
│ │ │ │
│ │ ─── SELECT ligne 3 ────► │ │
│ │ ◄─── Résultat 3 ─────── │ │
└─────────────┘ └──────────────┘
1000 requêtes = 1000 allers-retours = LENT !
✅ Avec programmation serveur (fonction/procédure) :
┌─────────────┐ ┌──────────────┐
│ Application │ ─── CALL ma_procedure()─► │ │
│ │ │ Traite les │
│ │ │ 1000 lignes │
│ │ │ en interne │
│ │ ◄─── Résultat final ───── │ │
└─────────────┘ └──────────────┘
1 seul aller-retour = RAPIDE !
Au lieu d'avoir la même logique métier répétée dans chaque application (web, mobile, API), vous la centralisez dans la base de données.
-- Logique de calcul de prix complexe centralisée
CREATE FUNCTION calculer_prix_final(...)
RETURNS NUMERIC
AS $$
-- Règles métier complexes
-- Remises, promotions, TVA, etc.
$$;
-- Toutes vos applications utilisent la même fonction
-- → Cohérence garantieVous pouvez donner accès à une fonction sans donner accès direct aux tables :
-- L'utilisateur ne peut PAS faire : SELECT * FROM salaires;
-- Mais il PEUT faire : SELECT moyenne_salaires_anonymisee();Certaines opérations complexes doivent être atomiques (tout ou rien) et sont mieux gérées côté base :
-- Transfert bancaire : débiter ET créditer en une seule opération atomique
CALL transferer_argent(compte_source, compte_dest, montant);PL/pgSQL signifie Procedural Language/PostgreSQL. C'est un langage de programmation spécialement conçu pour PostgreSQL qui étend le SQL avec :
- ✅ Variables : Stocker des valeurs temporaires
- ✅ Structures de contrôle : IF/ELSE, CASE, boucles (LOOP, WHILE, FOR)
- ✅ Gestion d'erreurs : Capturer et traiter les exceptions
- ✅ Fonctions et procédures : Encapsuler du code réutilisable
- ✅ Triggers : Réagir automatiquement aux changements de données
| Aspect | SQL Standard | PL/pgSQL |
|---|---|---|
| Nature | Déclaratif ("Quoi faire") | Procédural ("Comment le faire") |
| Variables | ❌ Non | ✅ Oui |
| Conditions | ✅ IF/ELSIF/ELSE complet | |
| Boucles | ❌ Non | ✅ LOOP, WHILE, FOR |
| Gestion d'erreurs | ❌ Non | ✅ BEGIN...EXCEPTION...END |
| Exemple | SELECT SUM(prix) FROM produits |
FOR r IN SELECT * FROM produits LOOP ... END LOOP; |
En SQL standard (déclaratif) :
-- Je veux la somme des prix
SELECT SUM(prix) FROM produits WHERE categorie = 'Livres';
-- C'est direct, mais limitéEn PL/pgSQL (procédural) :
DECLARE
total NUMERIC := 0;
prix_courant NUMERIC;
BEGIN
-- Je parcours les produits un par un
FOR prix_courant IN SELECT prix FROM produits WHERE categorie = 'Livres' LOOP
-- J'applique une logique personnalisée
IF prix_courant > 100 THEN
total := total + prix_courant * 0.9; -- 10% de remise
ELSE
total := total + prix_courant;
END IF;
END LOOP;
RETURN total;
END;
-- Plus complexe, mais beaucoup plus flexible !PostgreSQL propose plusieurs types d'objets programmables :
Une fonction retourne toujours une valeur.
CREATE FUNCTION calculer_tva(prix_ht NUMERIC, taux NUMERIC)
RETURNS NUMERIC
LANGUAGE plpgsql
AS $$
BEGIN
RETURN prix_ht * (1 + taux / 100);
END;
$$;
-- Utilisation
SELECT calculer_tva(100, 20); -- Retourne 120Caractéristiques :
- ✅ Retourne obligatoirement une valeur (ou un ensemble de valeurs)
- ✅ Peut être utilisée dans un SELECT
- ✅ S'exécute dans la transaction de l'appelant
- ❌ Ne peut pas faire de COMMIT/ROLLBACK interne
Une procédure ne retourne pas de valeur mais peut contrôler les transactions.
CREATE PROCEDURE archiver_anciennes_commandes()
LANGUAGE plpgsql
AS $$
BEGIN
INSERT INTO archive SELECT * FROM commandes WHERE date < NOW() - INTERVAL '1 year';
COMMIT; -- ✅ Possible dans une procédure !
DELETE FROM commandes WHERE date < NOW() - INTERVAL '1 year';
COMMIT;
END;
$$;
-- Utilisation
CALL archiver_anciennes_commandes();Caractéristiques :
- ❌ Ne retourne pas de valeur (sauf paramètres OUT/INOUT)
- ❌ Ne peut pas être utilisée dans un SELECT
- ✅ Peut faire des COMMIT/ROLLBACK internes
- ✅ Idéale pour les traitements par lots
Un trigger est une fonction qui s'exécute automatiquement lors d'événements sur une table.
-- Fonction trigger
CREATE FUNCTION log_modifications()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
INSERT INTO audit_log (table_name, action, timestamp)
VALUES (TG_TABLE_NAME, TG_OP, NOW());
RETURN NEW;
END;
$$;
-- Associer le trigger à une table
CREATE TRIGGER audit_clients
AFTER INSERT OR UPDATE OR DELETE ON clients
FOR EACH ROW EXECUTE FUNCTION log_modifications(); Caractéristiques :
- ✅ S'exécute automatiquement (INSERT, UPDATE, DELETE)
- ✅ Accès aux anciennes (OLD) et nouvelles (NEW) valeurs
- ✅ Peut modifier les données avant insertion (BEFORE trigger)
- ✅ Utile pour l'audit, validation, synchronisation
Un event trigger réagit aux modifications de structure (DDL) :
CREATE FUNCTION audit_ddl()
RETURNS EVENT_TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
INSERT INTO ddl_history (command, user, timestamp)
VALUES (TG_TAG, current_user, NOW());
END;
$$;
CREATE EVENT TRIGGER log_ddl_commands
ON ddl_command_end
EXECUTE FUNCTION audit_ddl(); Caractéristiques :
- ✅ Surveille CREATE, ALTER, DROP, etc.
- ✅ Audit de changements de schéma
- ✅ Protection contre suppressions accidentelles
- Logique métier complexe nécessitant conditions et boucles
- Traitement de gros volumes (éviter des milliers d'allers-retours)
- Opérations atomiques critiques (tout ou rien)
- Audit et traçabilité automatiques (triggers)
- Calculs intensifs sur des données déjà en base
- Réutilisation de logique entre plusieurs applications
- Sécurité : limiter l'accès direct aux tables
- Logique d'interface utilisateur (mieux dans l'application)
- Logique métier changeante fréquemment (plus facile à déployer côté app)
- Calculs nécessitant des bibliothèques externes (mieux dans l'app)
- Tests unitaires complexes (plus facile côté application)
- Scalabilité horizontale nécessaire (base de données = point unique)
| Avantage | Explication |
|---|---|
| Performance | Pas d'allers-retours réseau pour chaque opération |
| Centralisation | Une seule source de vérité pour la logique métier |
| Sécurité | Contrôle d'accès granulaire via fonctions |
| Cohérence | Garantit l'intégrité des données complexes |
| Réutilisabilité | Toutes les applications utilisent le même code |
| Atomicité | Transactions gérées au plus près des données |
| Inconvénient | Explication |
|---|---|
| Couplage | Applications dépendantes de la structure de la base |
| Débogage | Moins d'outils que pour le code applicatif |
| Versioning | Gestion des versions plus complexe |
| Tests | Tests unitaires moins faciles |
| Portabilité | Code spécifique à PostgreSQL (PL/pgSQL) |
| Scalabilité | Base de données = point de contention potentiel |
PostgreSQL ne se limite pas à PL/pgSQL. Plusieurs langages sont disponibles :
- ✅ Installé par défaut
- ✅ Syntaxe proche du SQL
- ✅ Performance optimale
- ✅ Intégration native
- ✅ Encore plus simple que PL/pgSQL
- ✅ Fonctions très performantes
⚠️ Limité (pas de structures de contrôle)
- ✅ Puissance de Python
- ✅ Accès à des bibliothèques Python
⚠️ Moins performant que PL/pgSQL⚠️ Risques de sécurité
- ✅ Expressions régulières puissantes
- ✅ Manipulation de texte avancée
⚠️ Moins populaire aujourd'hui
- ✅ JavaScript côté serveur
- ✅ Familier pour les développeurs web
⚠️ Maintenance moins active
- ✅ Performance maximale
- ✅ Accès bas niveau
- ❌ Complexe et dangereux
Recommandation pour débutants : Commencez avec PL/pgSQL (couvert dans ce chapitre) ou SQL pur pour les cas simples.
Ce chapitre couvre tous les aspects de la programmation serveur en PostgreSQL :
Comparaison des deux approches et guide de décision.
Optimiser les performances en déclarant correctement vos fonctions.
Maîtriser les procédures et le contrôle des transactions.
Automatiser des actions en réponse aux modifications de données.
Surveiller et contrôler les modifications de structure.
Capturer et gérer les erreurs élégamment.
Aperçu de PL/Python, PL/Perl, et autres alternatives.
Avant de commencer, voici quelques conventions que nous suivrons :
-- ✅ BON : Noms explicites
CREATE FUNCTION calculer_prix_ttc(...)
CREATE PROCEDURE archiver_commandes_anciennes()
-- ❌ ÉVITER : Noms vagues
CREATE FUNCTION calc(...)
CREATE PROCEDURE proc1() CREATE FUNCTION ma_fonction(param INTEGER)
RETURNS TEXT
LANGUAGE plpgsql
AS $$
-- Cette fonction fait ceci et cela
-- Paramètres :
-- param : Description du paramètre
-- Retour :
-- Description de ce qui est retourné
BEGIN
...
END;
$$;
-- Ajouter un commentaire PostgreSQL
COMMENT ON FUNCTION ma_fonction(INTEGER) IS
'Calcule le résultat en fonction du paramètre fourni.';-- ✅ Toujours gérer les erreurs potentielles
BEGIN
-- Code
EXCEPTION
WHEN division_by_zero THEN
RAISE NOTICE 'Division par zéro !';
RETURN NULL;
END;-- ✅ BON : Code indenté et lisible
CREATE FUNCTION exemple()
RETURNS INTEGER
LANGUAGE plpgsql
AS $$
DECLARE
compteur INTEGER := 0;
BEGIN
FOR i IN 1..10 LOOP
IF i % 2 = 0 THEN
compteur := compteur + 1;
END IF;
END LOOP;
RETURN compteur;
END;
$$;Voici un exemple simple mais complet pour illustrer la puissance de PL/pgSQL :
-- Fonction qui calcule des statistiques sur les commandes d'un client
CREATE FUNCTION stats_client(client_id INTEGER)
RETURNS TABLE(
nb_commandes INTEGER,
montant_total NUMERIC,
montant_moyen NUMERIC,
derniere_commande DATE
)
LANGUAGE plpgsql
AS $$
DECLARE
v_nb INTEGER;
v_total NUMERIC;
BEGIN
-- Compter le nombre de commandes
SELECT COUNT(*), COALESCE(SUM(montant), 0)
INTO v_nb, v_total
FROM commandes
WHERE id_client = client_id;
-- Si aucune commande, retourner des valeurs nulles
IF v_nb = 0 THEN
RETURN QUERY SELECT 0, 0::NUMERIC, 0::NUMERIC, NULL::DATE;
RETURN;
END IF;
-- Retourner les statistiques
RETURN QUERY
SELECT
v_nb,
v_total,
v_total / v_nb,
MAX(date_commande)::DATE
FROM commandes
WHERE id_client = client_id;
END;
$$;
-- Utilisation
SELECT * FROM stats_client(42);Ce que cet exemple démontre :
- ✅ Déclaration de variables (
DECLARE) - ✅ Conditions (
IF...THEN...END IF) - ✅ Récupération de valeurs (
INTO) - ✅ Retour de résultats tabulaires (
RETURN QUERY) - ✅ Logique métier (gestion du cas "aucune commande")
-- Créer une fonction
\e -- Ouvre un éditeur pour écrire du code
-- Voir les fonctions
\df -- Liste toutes les fonctions
\df+ ma_fonction -- Détails d'une fonction
-- Voir le code source
\sf ma_fonction- Éditeur de code avec coloration syntaxique
- Débogueur intégré pour fonctions PL/pgSQL
- Navigation dans les objets
- Support multi-bases
- Auto-complétion
- Visualisation des dépendances
PostgreSQLpour la coloration syntaxique- Intégration avec PostgreSQL
- "PostgreSQL: Up and Running" - Regina Obe & Leo Hsu
- "The Art of PostgreSQL" - Dimitri Fontaine
- "Mastering PostgreSQL" - Hans-Jürgen Schönig
- PostgreSQL mailing lists
- Stack Overflow (tag: postgresql)
- Reddit : r/PostgreSQL
- Discord PostgreSQL
Avant de plonger dans les détails techniques des sections suivantes, assurez-vous de bien comprendre :
- La différence entre SQL standard et PL/pgSQL
- Pourquoi programmer côté serveur (performance, centralisation, sécurité)
- Les différents objets : fonctions, procédures, triggers, event triggers
- Quand utiliser la programmation serveur (et quand l'éviter)
- Les avantages et inconvénients de cette approche
- Les bonnes pratiques de nommage et documentation
Vous êtes maintenant prêt à explorer la programmation serveur PostgreSQL !
Les sections suivantes vous guideront pas à pas, du plus simple (fonctions SQL) au plus avancé (triggers et gestion d'exceptions), avec de nombreux exemples pratiques et des explications détaillées pour débutants.
┌────────────────────────────────────────────────────────────────┐
│ PROGRAMMATION SERVEUR POSTGRESQL │
├────────────────────────────────────────────────────────────────┤
│ │
│ POURQUOI ? │
│ ├─ Performance (moins d'allers-retours) │
│ ├─ Centralisation (une seule source de vérité) │
│ ├─ Sécurité (contrôle d'accès fin) │
│ └─ Intégrité (garantir la cohérence) │
│ │
│ QUOI ? │
│ ├─ Fonctions → Retournent des valeurs │
│ ├─ Procédures → Contrôlent les transactions │
│ ├─ Triggers → Réagissent aux changements (données) │
│ └─ Event Triggers → Réagissent aux changements (structure) │
│ │
│ COMMENT ? │
│ ├─ PL/pgSQL (langage procédural PostgreSQL) │
│ ├─ SQL pur (fonctions simples) │
│ └─ Autres langages (Python, Perl, JavaScript...) │
│ │
│ QUAND ? │
│ ✅ Logique métier complexe │
│ ✅ Gros volumes de données │
│ ✅ Opérations critiques atomiques │
│ ❌ Logique d'interface utilisateur │
│ ❌ Déploiements fréquents │
│ │
└────────────────────────────────────────────────────────────────┘
🎓 Prochaine étape :
- 15.1. Fonctions SQL et PL/pgSQL : Différences et cas d'usage
Nous allons maintenant entrer dans le vif du sujet en comparant les deux approches principales de programmation serveur dans PostgreSQL !