PostgreSQL pour DBA expérimentés

12 mars 2025

Dalibo SCOP

Sur ce document

Formation Formation DBAADM
Titre PostgreSQL pour DBA expérimentés
Révision 25.03
ISBN N/A
PDF https://dali.bo/dbaadm_pdf
EPUB https://dali.bo/dbaadm_epub
HTML https://dali.bo/dbaadm_html
Slides https://dali.bo/dbaadm_slides

Vous trouverez en ligne les différentes versions complètes de ce document. Les solutions de TP ne figurent pas forcément dans la version imprimée, mais sont dans les versions numériques (PDF ou HTML).


Chers lectrices & lecteurs,

Nos formations PostgreSQL sont issues de nombreuses années d’études, d’expérience de terrain et de passion pour les logiciels libres. Pour Dalibo, l’utilisation de PostgreSQL n’est pas une marque d’opportunisme commercial, mais l’expression d’un engagement de longue date. Le choix de l’Open Source est aussi le choix de l’implication dans la communauté du logiciel.

Au‑delà du contenu technique en lui‑même, notre intention est de transmettre les valeurs qui animent et unissent les développeurs de PostgreSQL depuis toujours : partage, ouverture, transparence, créativité, dynamisme… Le but premier de nos formations est de vous aider à mieux exploiter toute la puissance de PostgreSQL mais nous espérons également qu’elles vous inciteront à devenir un membre actif de la communauté en partageant à votre tour le savoir‑faire que vous aurez acquis avec nous.

Nous mettons un point d’honneur à maintenir nos manuels à jour, avec des informations précises et des exemples détaillés. Toutefois malgré nos efforts et nos multiples relectures, il est probable que ce document contienne des oublis, des coquilles, des imprécisions ou des erreurs. Si vous constatez un souci, n’hésitez pas à le signaler via l’adresse !

À propos de DALIBO

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

Retrouvez toutes nos formations sur https://dalibo.com/formations

Remerciements

Ce manuel de formation est une aventure collective qui se transmet au sein de notre société depuis des années. Nous remercions chaleureusement ici toutes les personnes qui ont contribué directement ou indirectement à cet ouvrage, notamment :

Alexandre Anriot, Jean‑Paul Argudo, Carole Arnaud, Alexandre Baron, David Bidoc, Sharon Bonan, Franck Boudehen, Arnaud Bruniquel, Pierrick Chovelon, Damien Clochard, Christophe Courtois, Marc Cousin, Gilles Darold, Ronan Dunklau, Vik Fearing, Stefan Fercot, Dimitri Fontaine, Pierre Giraud, Nicolas Gollet, Florent Jardin, Virginie Jourdan, Luc Lamarle, Denis Laxalde, Guillaume Lelarge, Alain Lesage, Benoit Lobréau, Jean‑Louis Louër, Thibaut Madelaine, Adrien Nayrat, Alexandre Pereira, Flavie Perette, Robin Portigliatti, Thomas Reiss, Maël Rimbault, Jehan-Guillaume de Rorthais, Julien Rouhaud, Stéphane Schildknecht, Julien Tachoires, Nicolas Thauvin, Be Hai Tran, Christophe Truffier, Arnaud de Vathaire, Cédric Villemain, Thibaud Walkowiak, Frédéric Yhuel.

Forme de ce manuel

Les versions PDF, EPUB ou HTML de ce document sont structurées autour des slides de nos formations. Le texte suivant chaque slide contient le cours et de nombreux détails qui ne peuvent être données à l’oral.

Licence Creative Commons CC-BY-NC-SA

Cette formation est sous licence CC-BY-NC-SA. Vous êtes libre de la redistribuer et/ou modifier aux conditions suivantes :

  • Paternité
  • Pas d’utilisation commerciale
  • Partage des conditions initiales à l’identique

Vous n’avez pas le droit d’utiliser cette création à des fins commerciales.

Si vous modifiez, transformez ou adaptez cette création, vous n’avez le droit de distribuer la création qui en résulte que sous un contrat identique à celui-ci.

Vous devez citer le nom de l’auteur original de la manière indiquée par l’auteur de l’œuvre ou le titulaire des droits qui vous confère cette autorisation (mais pas d’une manière qui suggérerait qu’ils vous soutiennent ou approuvent votre utilisation de l’œuvre). À chaque réutilisation ou distribution de cette création, vous devez faire apparaître clairement au public les conditions contractuelles de sa mise à disposition. La meilleure manière de les indiquer est un lien vers cette page web. Chacune de ces conditions peut être levée si vous obtenez l’autorisation du titulaire des droits sur cette œuvre. Rien dans ce contrat ne diminue ou ne restreint le droit moral de l’auteur ou des auteurs.

Le texte complet de la licence est disponible sur http://creativecommons.org/licenses/by-nc-sa/2.0/fr/legalcode

Cela inclut les diapositives, les manuels eux-mêmes et les travaux pratiques. Cette formation peut également contenir quelques images et schémas dont la redistribution est soumise à des licences différentes qui sont alors précisées.

Marques déposées

PostgreSQL® Postgres® et le logo Slonik sont des marques déposées par PostgreSQL Community Association of Canada.

Versions de PostgreSQL couvertes

Ce document ne couvre que les versions supportées de PostgreSQL au moment de sa rédaction, soit les versions 13 à 17.

Sur les versions précédentes susceptibles d’être encore rencontrées en production, seuls quelques points très importants sont évoqués, en plus éventuellement de quelques éléments historiques.

Sauf précision contraire, le système d’exploitation utilisé est Linux.

PostgreSQL : historique & communauté

PostgreSQL

Préambule

  • Quelle histoire !
    • parmi les plus vieux logiciels libres
    • et les plus sophistiqués
  • Souvent cité comme exemple
    • qualité du code
    • indépendance des développeurs
    • réactivité de la communauté

L’histoire de PostgreSQL est longue, riche et passionnante. Au côté des projets libres Apache et Linux, PostgreSQL est l’un des plus vieux logiciels libres en activité et fait partie des SGBD les plus sophistiqués à l’heure actuelle.

Au sein des différentes communautés libres, PostgreSQL est souvent cité comme exemple à différents niveaux :

  • qualité du code ;
  • indépendance des développeurs et gouvernance du projet ;
  • réactivité de la communauté ;
  • stabilité et puissance du logiciel.

Tous ces atouts font que PostgreSQL est désormais reconnu et adopté par des milliers de grandes sociétés de par le monde.


Au menu

  • Origines et historique du projet
  • Versions et feuille de route
  • Projets satellites
  • Sponsors et références
  • La communauté

Cette première partie est un tour d’horizon pour découvrir les multiples facettes du système de gestion de base de données libre PostgreSQL.

Les deux premières parties expliquent la genèse du projet et détaillent les différences entre les versions successives du logiciel. PostgreSQL est un des plus vieux logiciels libres ! Comprendre son histoire permet de mieux réaliser le chemin parcouru et les raisons de son succès.

Nous verrons ensuite certains projets satellites et nous listerons plusieurs utilisateurs renommés et cas d’utilisations remarquables.

Enfin, nous terminerons par une découverte de la communauté.


Un peu d’histoire…

  • La licence
  • L’origine du nom
  • Les origines du projet
  • Les principes

Licence

  • Licence PostgreSQL
  • Droit, sans coûts de licence, de :
    • utiliser, copier, modifier, distribuer (et même revendre)
  • Reconnue par l’Open Source Initiative
  • Utilisée par un grand nombre de projets de l’écosystème

PostgreSQL est distribué sous une licence spécifique, la licence PostgreSQL, combinant la licence BSD et la licence MIT. Elle est reconnue comme une licence libre par l’Open Source Initiative.

Cette licence vous donne le droit de distribuer PostgreSQL, de l’installer, de le modifier… et même de le vendre. Certaines sociétés, comme EnterpriseDB et PostgresPro, produisent leur version propriétaire de PostgreSQL de cette façon.

PostgreSQL n’est pas pour autant complètement gratuit : il peut y avoir des frais et du temps de formation, des projets de migration depuis d’autres bases, ou d’intégration des différents outils périphériques indispensables en production.

Cette licence a ensuite été reprise par de nombreux projets de la communauté : pgAdmin, pgCluu, pgstat, etc.


PostgreSQL ?!?!

  • 1985 : Michael Stonebraker recode Ingres
  • post « ingres » postingres postgres
  • postgres PostgreSQL

PostgreSQL a une origine universitaire.

L’origine du nom PostgreSQL remonte au système de gestion de base de données Ingres, développé à l’université de Berkeley par Michael Stonebraker. En 1985, il prend la décision de reprendre le développement à partir de zéro et nomme ce nouveau logiciel Postgres, comme raccourci de post-Ingres.

En 1995, avec l’ajout du support du langage SQL, Postgres fut renommé Postgres95 puis PostgreSQL.

Aujourd’hui, le nom officiel est « PostgreSQL » (prononcé « post - gresse - Q - L »). Cependant, le nom « Postgres » reste accepté.

Pour aller plus loin :


Principes fondateurs

  • Sécurité des données (ACID)
  • Respect des normes (ISO SQL)
  • Portabilité
  • Fonctionnalités intéressant le plus grand nombre
  • Performances
    • si pas de péril pour les données
  • Simplicité du code
  • Documentation
  • Tests fonctionnels

Depuis son origine, PostgreSQL a toujours privilégié la stabilité et le respect des standards plutôt que les performances.

La sécurité des données est un point essentiel. En premier lieu, un utilisateur doit être certain qu’à partir du moment où il a exécuté l’ordre COMMIT d’une transaction, les données modifiées relatives à cette transaction se trouvent bien sur disque et que même un crash ne pourra pas les faire disparaître. PostgreSQL est très attaché à ce concept et fait son possible pour forcer le système d’exploitation à ne pas conserver les données en cache, mais à les écrire sur disque dès l’arrivée d’un COMMIT.

L’intégrité des données, et le respect des contraintes fonctionnelles et techniques qui leur sont imposées, doivent également être garanties par le moteur à tout moment, quoi que fasse l’utilisateur. Par exemple, insérer 1000 caractères dans un champ contraint à 200 caractères maximum doit mener à une erreur explicite et non à l’insertion des 200 premiers caractères en oubliant les autres, comme cela s’est vu ailleurs. De même, un champ avec le type date ne contiendra jamais un 31 février, et un champ NOT NULL ne sera jamais vide. Tout ceci est formalisé par les propriétés (ACID) que possèdent toute bonne base de données relationnelle.

Le respect des normes est un autre principe au cœur du projet. Les développeurs de PostgreSQL cherchent à coller à la norme SQL le plus possible. PostgreSQL n’est pas compatible à cette norme à 100 %, aucun moteur ne l’est, mais il cherche à s’en approcher. Tout nouvel ajout d’une syntaxe ne sera accepté que si la syntaxe de la norme est ajoutée. Des extensions sont acceptées pour différentes raisons (performances, fonctionnalités en avance sur le comité de la norme, facilité de transition d’un moteur de bases de données à un autre) mais si une fonctionnalité existe dans la norme, une syntaxe différente ne peut être acceptée que si la syntaxe de la norme est elle-aussi présente.

La portabilité est importante : PostgreSQL tourne sur l’essentiel des systèmes d’exploitation : Linux (plate-forme à privilégier), macOS, les Unix propriétaires, Windows… Tout est fait pour que cela soit encore le cas dans le futur.

Ajouter des fonctionnalités est évidemment l’un des buts des développeurs de PostgreSQL. Cependant, comme il s’agit d’un projet libre, rien n’empêche un développeur de proposer une fonctionnalité, de la faire intégrer, puis de disparaître laissant aux autres la responsabilité de la corriger le cas échéant. Comme le nombre de développeurs de PostgreSQL est restreint, il est important que les fonctionnalités ajoutées soient vraiment utiles au plus grand nombre pour justifier le coût potentiel du débogage. Donc ne sont ajoutées dans PostgreSQL que ce qui est vraiment le cœur du moteur de bases de données et que ce qui sera utilisé vraiment par le plus grand nombre. Une fonctionnalité qui ne sert que une à deux personnes aura très peu de chances d’être intégrée. (Le système des extensions offre une élégante solution aux problèmes très spécifiques.)

Les performances ne viennent qu’après tout ça. En effet, rien ne sert d’avoir une modification du code qui permet de gagner énormément en performances si cela met en péril le stockage des données. Cependant, les performances de PostgreSQL sont excellentes et le moteur permet d’opérer des centaines de tables, des milliards de lignes pour plusieurs téraoctets de données, sur une seule instance, pour peu que la configuration matérielle soit correctement dimensionnée.

La simplicité du code est un point important. Le code est relu scrupuleusement par différents contributeurs pour s’assurer qu’il est facile à lire et à comprendre. En effet, cela facilitera le débogage plus tard si cela devient nécessaire.

La documentation est là aussi un point essentiel dans l’admission d’une nouvelle fonctionnalité. En effet, sans documentation, peu de personnes pourront connaître cette fonctionnalité. Très peu sauront exactement ce qu’elle est supposée faire, et il serait donc très difficile de déduire si un problème particulier est un manque actuel de cette fonctionnalité ou un bug.

Enfin, les tests fonctionnels suite à la compilation sont un prérequis depuis quelques années. Ils permettent de tester les fonctionnalités proposées et de ne pas retomber dans des bugs déjà corrigés.

Tous ces points sont vérifiés à chaque relecture d’un patch (nouvelle fonctionnalité ou correction).


Origines

  • Années 1970 : Michael Stonebraker développe Ingres à Berkeley
  • 1985 : Postgres succède à Ingres
  • 1995 : Ajout du langage SQL
  • 1996 : Libération du code : Postgres devient PostgreSQL
  • 1996 : Création du PostgreSQL Global Development Group

L’histoire de PostgreSQL remonte au système de gestion de base de données Ingres, développé dès 1973 à l’Université de Berkeley (Californie) par Michael Stonebraker.

Lorsque ce dernier décide en 1985 de recommencer le développement de zéro, il nomme le logiciel Postgres, comme raccourci de post-Ingres. Des versions commencent à être diffusées en 1989, puis commercialisées.

Postgres utilise alors un langage dérivé de QUEL, hérité d’Ingres, nommé POSTQUEL1. En 1995, lors du remplacement par le langage SQL par Andrew Yu and Jolly Chen, deux étudiants de Berkeley, Postgres est renommé Postgres95.

En 1996, Bruce Momijan et Marc Fournier convainquent l’Université de Berkeley de libérer complètement le code source. Est alors fondé le PGDG (PostgreSQL Development Group), entité informelle — encore aujourd’hui — regroupant l’ensemble des contributeurs. Le développement continue donc hors tutelle académique (et sans son fondateur historique Michael Stonebraker) : PostgreSQL 6.0 est publié début 1997.

Plus d’informations :


Apparition de la communauté internationale

  • ~ 2000: Communauté japonaise (JPUG)
  • 2004 : PostgreSQLFr
  • 2006 : SPI
  • 2007 : Communauté italienne
  • 2008 : PostgreSQL Europe et US
  • 2009 : Boom des PGDay
  • 2011 : Postgres Community Association of Canada
  • 2017 : Community Guidelines
  • …et ça continue

Les années 2000 voient l’apparition de communautés locales organisées autour d’association ou de manière informelle. Chaque communauté organise la promotion, la diffusion d’information et l’entraide à son propre niveau.

En 2000 apparaît la communauté japonaise (JPUG). Elle dispose déjà d’un grand groupe, capable de réaliser des conférences chaque année, d’éditer des livres et des magazines. Elle compte, au dernier recensement connu, plus de 3000 membres.

En 2004 naît l’association française (loi 1901) appelée PostgreSQL Fr. Cette association a pour but de fournir un cadre légal pour pouvoir participer à certains événements comme Solutions Linux, les RMLL ou d’en organiser comme le pgDay.fr (qui a déjà eu lieu à Toulouse, Nantes, Lyon, Toulon, Marseille). Elle permet aussi de récolter des fonds pour aider à la promotion de PostgreSQL.

En 2006, le PGDG intègre Software in the Public Interest, Inc.(SPI), une organisation à but non lucratif chargée de collecter et redistribuer des financements. Elle a été créée à l’initiative de Debian et dispose aussi de membres comme LibreOffice.org.

Jusque là, les événements liés à PostgreSQL apparaissaient plutôt en marge de manifestations, congrès, réunions… plus généralistes. En 2008, douze ans après la création du projet, des associations d’utilisateurs apparaissent pour soutenir, promouvoir et développer PostgreSQL à l’échelle internationale. PostgreSQL UK organise une journée de conférences à Londres, PostgreSQL Fr en organise une à Toulouse. Des « sur-groupes » apparaissent aussi pour aider les groupes locaux : PGUS rassemble les différents groupes américains, plutôt organisés géographiquement, par État ou grande ville. De même, en Europe, est fondée PostgreSQL Europe, association chargée d’aider les utilisateurs de PostgreSQL souhaitant mettre en place des événements. Son principal travail est l’organisation d’un événement majeur en Europe tous les ans : pgconf.eu, d’abord à Paris en 2009, puis dans divers pays d’Europe jusque Milan en 2019. Cependant, elle aide aussi les communautés allemande, française et suédoise à monter leur propre événement (respectivement PGConf.DE, pgDay Paris et Nordic PGday).

Dès 2010, nous dénombrons plus d’une conférence par mois consacrée uniquement à PostgreSQL dans le monde. Ce mouvement n’est pas prêt de s’arrêter :

En 2011, l’association Postgres Community Association of Canada voit le jour. Elle est créée par quelques membres de la Core Team pour gérer le nom déposé PostgreSQL, le logo, le nom de domaine sur Internet, etc.

Vu l’émergence de nombreuses communautés internationales, la communauté a décidé d’écrire quelques règles pour ces communautés. Il s’agit des Community Guidelines, apparues en 2017, et disponibles sur le site officiel.


Progression du code

  • 1,9 millions de lignes
    • ¼ de commentaires
    • le reste surtout en C
  • Nombres de commit par mois :
Évolution du nombre de commit dans le dépôt PostgreSQL

Le dépôt principal de PostgreSQL a été un dépôt CVS, passé depuis à git. Il est en accès public en lecture.

Le graphe ci-dessus (source) représente l’évolution du nombre de commit dans les sources de PostgreSQL. L’activité ne se dément pas. Le plus intéressant est certainement de noter que l’évolution est constante. Il n’y a pas de gros pic, ni dans un sens, ni dans l’autre.

Fin 2024, le code de PostgreSQL contient 1,9 millions de lignes,dont un quart de commentaires. Ce ratio montre que le code est très commenté, très documenté, facile à lire, et donc pratique à déboguer. Et le ratio ne change pas au fil des ans. Le code est essentiellement en C, avec un peu de SQL et de Perl. pour environ 200 développeurs actifs, à environ 200 commits par mois ces dernières années. Et ce, sans compter les contributions aux outils périphériques.


Les versions de PostgreSQL

Quelle version utiliser ?

  • Historique
  • Numérotation
  • Mises à jour mineures et majeures
  • Les versions courantes
  • Quelle version en production ?
  • Les forks & dérivés

Historique


Versions & fonctionnalités

  • 1996 : v6.0 -> première version publiée
  • 2003 : v7.4 -> première version réellement stable
  • 2005 : v8.0 -> arrivée sur Windows
  • 2008 : v8.3 -> performances et fonctionnalités, organisation (commitfests)
  • 2010 : v9.0 -> réplication physique
  • 2016 : v9.6 -> parallélisation
  • 2017 : v10 -> réplication logique, partitionnement déclaratif
  • 2024 : v17 -> performances, fonctionnalités, administration…

La version 7.4 est la première version réellement stable. La gestion des journaux de transactions a été nettement améliorée, et de nombreuses optimisations ont été apportées au moteur.

La version 8.0 marque l’entrée tant attendue de PostgreSQL dans le marché des SGDB de haut niveau, en apportant des fonctionnalités telles que les tablespaces, les routines stockées en Java, le Point In Time Recovery, ainsi qu’une version native pour Windows.

La version 8.3 se focalise sur les performances et les nouvelles fonctionnalités. C’est aussi la version qui a causé un changement important dans l’organisation du développement pour encourager les contributions : gestion des commitfests, création de l’outil web associé, etc.

Les versions 9.x sont axées réplication physique. La 9.0 intègre un système de réplication asynchrone asymétrique. La version 9.1 ajoute une réplication synchrone et améliore de nombreux points sur la réplication (notamment pour la partie administration et supervision). La version 9.2 apporte la réplication en cascade. La 9.3 et la 9.4 ajoutent quelques améliorations supplémentaires. La version 9.4 intègre surtout les premières briques pour l’intégration de la réplication logique dans PostgreSQL. La version 9.6 apporte la parallélisation, ce qui était attendu par de nombreux utilisateurs.

La version 10 propose beaucoup de nouveautés, comme une amélioration nette de la parallélisation et du partitionnement (le partitionnement déclaratif complète l’ancien partitionnement par héritage), mais aussi l’ajout de la réplication logique.

Les améliorations des versions 11 à 17 sont plus incrémentales, et portent sur tous les plans. Le partitionnement déclaratif et la réplication logique sont progressivement améliorés, en performances comme en facilité de développement et maintenance. Les performances s’améliorent encore grâce à la compilation Just In Time, la parallélisation de plus en plus d’opérations, les index couvrants, l’affinement des statistiques. La facilité d’administration s’améliore : nouvelles vues système, rôles supplémentaires pour réduire l’utilisation du superutilisateur, outillage pour la réplication logique et la sauvegarde physique, activation des sommes de contrôle sur une instance existante.

Il est toujours possible de télécharger les sources depuis la version 1.0 jusqu’à la version courante sur postgresql.org.


Numérotation

  • Version récentes (10+)
    • X : version majeure (10, 11, … 17)
    • X.Y : version mineure (14.8, 17.4)
  • Avant la version 10 (toutes périmées !)
    • X.Y : version majeure (9.4, 9.6)
    • X.Y.Z : version mineure (9.6.24)

Une version majeure apporte de nouvelles fonctionnalités, des changements de comportement, etc. Une version majeure sort généralement tous les ans à l’automne. Une migration majeure peut se faire directement depuis n’importe quelle version précédente. Le numéro est incrémenté chaque année (version 12 en 2019, version 16 en 2023).

Une version mineure ne comporte que des corrections de bugs ou de failles de sécurité. Les publications de versions mineures sont plus fréquentes que celles de versions majeures, avec un rythme de sortie trimestriel, sauf bug majeur ou faille de sécurité. Chaque bug est corrigé dans toutes les versions stables alors maintenues par le projet. Le numéro d’une version mineure porte deux nombres. Par exemple, en février 2025 sont sorties les versions 17.4, 16.8, 15.12, 14.17, et 13.20. La version 12 n’étant plus supportée, il n’y aura pas d’autre version mineure sur cette branche après la 12.22.

Avant la version 10, les versions majeures annuelles portaient deux chiffres : 9.0 en 2010, 9.6 en 2016. Les mineures avaient un numéro de plus (par exemple 9.6.24). Cela a entraîné quelques confusions, d’où le changement de numérotation. Il va sans dire que ces versions sont totalement périmées et ne sont plus supportées, mais beaucoup continuent de fonctionner.


Mises à jour mineure

De M.m à M.m+n :

  • En général chaque trimestre
  • Et sans souci
    • Release notes
    • tests
    • mise à jour des binaires
    • redémarrage

Une mise à jour mineure consiste à mettre à jour vers une nouvelle version de la même branche majeure, par exemple de 14.8 à 14.9, ou de 17.3 à 17.4 (mais pas d’une version 14.x à une version 17.x). Les mises à jour des versions mineures sont cumulatives : vous pouvez mettre à jour une instance 17.0 en version 17.4 sans passer par les versions 17.1 à 17.3 intermédiaires.

En général, les mises à jour mineures se font sans souci et ne nécessitent que le remplacement des binaires et un redémarrage (et donc une courte interruption). Les fichiers de données conservent le même format. Des opérations supplémentaires sont possibles mais rarissimes. Mais comme pour toute mise à jour, il convient d’être prudent sur d’éventuels effets de bord. En particulier, il faudra lire les Release Notes et, si possible, effectuer les tests ailleurs qu’en production.


Versions courantes

  • 1 version majeure par an
    • maintenue 5 ans
  • Dernières mises à jour mineures
  • Prochaine sortie de versions mineures prévue : 8 mai 2025

La philosophie générale des développeurs de PostgreSQL peut se résumer ainsi :

« Notre politique se base sur la qualité, pas sur les dates de sortie. »

Toutefois, même si cette philosophie reste très présente parmi les développeurs, en pratique une version stable majeure paraît tous les ans, habituellement à l’automne. Pour ne pas sacrifier la qualité des versions, toute fonctionnalité supposée insuffisamment stable est repoussée à la version suivante si des correctifs satisfaisants ne peuvent être trouvés à temps. Il est aussi arrivé que la sortie de la version majeure soit repoussée de quelques semaines à cause de bugs inacceptables mais corrigeables rapidement.

La tendance actuelle est de garantir un support pour chaque version majeure pendant une durée minimale de 5 ans. Ainsi ne sont plus supportées les versions 11 depuis novembre 2023 et 12 depuis novembre 2024. Il n’y aura pour elles plus aucune mise à jour mineure, donc plus de correction de bug ou de faille de sécurité. Le support de la version 13 s’arrêtera en novembre 2025. Le support de la dernière version majeure, la 17, devrait durer jusqu’en 2029.

Pour plus de détails :


Versions 9.4 à 11

  • jsonb
  • Row Level Security
  • Index BRIN, bloom
  • Fonctions OLAP
  • Parallélisation
  • SQL/MED : accès distants
  • Réplication logique
  • Partitionnement déclaratif
  • Réduction des inconvénients de MVCC
  • JIT
  • Index couvrants

Ces versions ne sont plus supportées !

La version 9.4 (décembre 2014) a apporté le type jsonb, binaire, facilitant la manipulation des objets en JSON.

La 9.5 parue en janvier 2016 apportait notamment les index BRIN et des possibilités OLAP plus avancées que GROUP BY. Pour plus de détails :

En 9.6, la nouvelle fonctionnalité majeure est certainement la parallélisation de certaines parties de l’exécution d’une requête. Le VACUUM FREEZE devient beaucoup moins gênant.

En version 10, les fonctionnalités majeures sont l’intégration de la réplication logique et le partitionnement déclaratif, longtemps attendus, améliorés dans les versions suivantes. Sont notables aussi les tables de transition ou les améliorations sur la parallélisation.

La version 10 a aussi été l’occasion de renommer plusieurs répertoires et fonctions système, et même des outils. Attention donc si vous rencontrez des requêtes ou des scripts adaptés aux versions précédentes. Entre autres :

  • le répertoire pg_xlog est devenu pg_wal ;
  • le répertoire pg_clog est devenu pg_xact ;
  • dans les noms de fonctions, xlog a été remplacé par wal (par exemple pg_switch_xlog est devenue pg_switch_wal) ;
  • toujours dans les fonctions, location a été remplacé par lsn.

Pour plus de détails :

La version 11 (octobre 2018) améliore le partitionnement de la version 10, le parallélisme, la réplication logique… et de nombreux autres points. Elle comprend aussi une première version du JIT (Just In Time compilation) pour accélérer les requêtes les plus lourdes en CPU, ou encore les index couvrants.

Pour plus de détails :


Version 12

  • Octobre 2019 - Novembre 2024 (n’est plus supportée !)
  • Amélioration du partitionnement déclaratif
  • Amélioration des performances
    • sur la gestion des index
    • sur les CTE (option MATERIALIZED)
  • Colonnes générées
  • Nouvelles vues de visualisation de la progression des commandes
  • Refonte de la configuration de la réplication

La version 12 sortie en 2019 n’est plus supportée depuis novembre 2024. Elle améliore de nouveau le partitionnement et elle fait surtout un grand pas au niveau des performances et de la supervision.

Le fichier recovery.conf (pour la réplication et les restaurations physiques) est intégré au fichier postgresql.conf. Une source fréquente de ralentissement disparaît, avec l’intégration des CTE (clauses WITH) dans la requête principale. Des colonnes d’une table peuvent être automatiquement générées à partir d’autres colonnes.

Pour plus de détails :


Version 13

  • Septembre 2020 - Septembre 2025
  • Améliorations :
    • partitionnement déclaratif
    • réplication logique
  • Amélioration des performances :
    • index B-tree, objet statistique, tri et agrégat
  • Amélioration de l’autovacuum et du VACUUM :
    • gestion complète des tables en insertion seule
    • traitement parallélisé des index lors d’un VACUUM
  • Amélioration des sauvegardes :
    • génération d’un fichier manifeste, outil pg_verifybackup
  • Nouvelles vues de progression de commandes :
    • pg_stat_progress_basebackup, pg_stat_progress_analyze

La version 13 sortie en 2020 ne sera plus supportée dès novembre 2025. Elle est remplie de nombreuses petites améliorations sur différents domaines : partitionnement déclaratif, autovacuum, sauvegarde, etc. Les performances sont aussi améliorées grâce à un gros travail sur l’optimiseur, ou la réduction notable de la taille de certains index.

Pour plus de détails :


Version 14

  • Septembre 2021 - Novembre 2026
  • Nouvelles vues système & améliorations
    • pg_stat_progress_copy, pg_stat_wal, pg_lock.waitstart, query_id
  • Lecture asynchrone des tables distantes
  • Paramétrage par défaut adapté aux machines plus récentes
  • Améliorations diverses :
    • réplications physique et logique
    • quelques facilités de syntaxe (triggers, tableaux en PL/pgSQL)
  • Performances :
    • connexions en lecture seule plus nombreuses
    • index…

La version 14 est remplie de nombreuses petites améliorations sur différents domaines listés ci-dessus.

Pour plus de détails :


Version 15

  • Octobre 2022 - Novembre 2027
  • Nombreuses améliorations incrémentales
    • dont en réplication logique
  • Commande MERGE
  • Performances :
    • DISTINCT parallélisable
    • pg_dump & sauvegardes, recovery, partitionnement
  • Changements notables :
    • public n’est plus accessible en écriture à tous
    • sauvegarde PITR exclusive disparaît

La version 15 est également une mise à jour sans grande nouveauté fracassante, mais contenant de très nombreuses améliorations et optimisations sur de nombreux plans, comme par exemple la commande MERGE ou l’accélération du recovery sur une reprise de restauration.

Signalons deux changements de comportement importants : pour renforcer la sécurité, le schéma public n’est plus accessible en écriture par défaut à tous les utilisateurs ; et la sauvegarde physique en mode exclusif n’est plus disponible.

Pour plus de détails :


Version 16

  • Septembre 2023 - Novembre 2028
  • Plus de tris incrémentaux (DISTINCT…)
  • Réplication logique depuis un secondaire
  • Expressions régulières dans pg_hba.conf
  • Vues systèmes améliorées : pg_stat_io
  • Compression lz4 ou zstd pour pg_dump
  • Optimisation et améliorations diverses (parallélisation…)

La version 16 est parue le 14 septembre 2023. Là encore, les améliorations sont incrémentales.

On notera la possibilité de rajouter des expressions régulières dans pg_hba.conf pour faciliter la gestion des accès. En réplication logique, un abonnement peut se faire auprès d’un serveur secondaire. La réplication logique peut devenir parallélisable. pg_dump acquiert des algorithmes de compression plus modernes. Le travail de parallélisation de nouveaux nœuds se poursuit. Une nouvelle vue de suivi des entrées-sorties apparaît : pg_stat_io. Le VACUUM peut être accéléré en lui permettant d’utiliser plus de mémoire dans les shared buffers.

Pour plus de détails :


Version 17

  • Septembre 2024 - Novembre 2029
  • Améliorations du VACUUM
  • Planificateur : IN et CTE
  • BRIN : création parallélisée
  • JSON_TABLE
  • Sauvegarde incrémentale (pg_basebackup + pg_combinebackup)
  • Améliorations en réplication logique (pg_createsubscriber)
  • pg_dump --filter
  • Imports (COPY peut rejeter des lignes, MERGE)
  • Améliorations diverses

La version 17, parue le 26 septembre 2024, contient quelques nouveautés intéressantes parmi ses 2635 commits.

Le VACUUM est encore amélioré en terme de sélectivité, de suivi, de mémoire maximum utilisable, tout en réduisant celle consommée. GRANT MAINTAIN et pg_maintain autorisent son lancement sur une table sans en être propriétaire. Le planificateur comprend quelques améliorations (pour les IN et les CTE entre autres). La création des index BRIN peut être parallélisée. Le chargement en masse et la transformation de données via MERGE et COPY ont également évolué en performances et fonctionnalités (MERGE … RETURNING). COPY peut enfin ignorer quelques lignes en erreur. Le travail de fond sur le partitionnement continue, avec le support natif des contraintes d’exclusions et des colonnes d’identité.

PostgreSQL inclut désormais un nouveau fournisseur de collation immutable (builtin), en plus de la collation de l’OS et de ICU. JSON et JSONPath voient apparaître de nouvelles fonctions (notamment JSON_TABLE).

La réplication logique permet enfin la gestion du failover du primaire, et la possibilité de créer un réplica logique via la réplication physique, avec l’outil pg_createsubscriber. pg_basebackup permet enfin de créer des sauvegardes incrémentales à recombiner avec pg_combinebackup. pg_dump a une clause --filter plus flexible.

Quelques nouveaux paramètres apparaissent, comme transaction_timeout, allow_alter_system, ou ceux permettant de gérer quelques caches spécifiques.

En supervision, entre autres, des éléments de pg_stat_bgwriter sont remplacés par d’autres dans la nouvelle vue pg_stat_checkpointer.

Pour plus de détails :


Petit résumé

  • Versions 7.x :
    • fondations
    • durabilité
  • Versions 8.x :
    • fonctionnalités
    • performances
  • Versions 9.x :
    • réplication physique
    • extensibilité
  • Versions 10 à 17 :
    • réplication logique
    • parallélisation
    • partitionnement
    • maintenabilité

Si nous essayons de voir cela avec de grosses mailles, les développements des versions 7 ciblaient les fondations d’un moteur de bases de données stable et durable. Ceux des versions 8 avaient pour but de rattraper les gros acteurs du marché en fonctionnalités et en performances. Enfin, pour les versions 9, on est plutôt sur la réplication et l’extensibilité.

La version 10 est une version mémorable grâce à la réplication logique, la parallélisation et le partitionnement. Les versions 11 à 17 améliorent ces deux points, entre mille autres améliorations en différents points du moteur, notamment les performances et la facilité d’administration.


Quelle version utiliser en production ?

Au premier semestre 2025 :

  • 12 et inférieures
    • Danger !
    • planifier une migration urgemment !
  • 13, 14, 15, 16
    • OK pour la production
    • ne pas oublier les mises à jour mineures
  • Nouvelles installations
    • 17
  • Nouveaux développements
    • 17
  • https://www.postgresql.org/about/featurematrix

La version 12 n’est plus supportée depuis novembre 2024.

Si vous avez une version 12 ou inférieure, planifiez le plus rapidement possible une migration vers une version plus récente, comme la 17.

Les versions non supportées fonctionneront toujours aussi bien, mais il n’y aura plus de correction de bug, y compris pour les failles de sécurité ! Si vous utilisez ces versions, il est impératif d’étudier une migration de version dès que possible.

Les versions 13 à 16 restent recommandables pour une production. Le plus important est d’appliquer les mises à jour correctives.

La version 17 est conseillée pour les nouvelles installations en production.

Par expérience, quand une version x.0 paraît à l’automne, elle est généralement stable. Nombre de DBA préfèrent prudemment attendre les premières mises à jour mineures (en novembre généralement) pour la mise en production. Cette prudence est à mettre en balance avec l’intérêt pour les nouvelles fonctionnalités.

Le développement de la version 18 a déjà débuté. Les paquets du PGDG contiennent déjà une version 18 utilisable à titre de test (mais attention ! il n’est pas garanti que toutes les fonctionnalités ajoutées soient finalement conservées). La version 18 bêta est prévue pour mai 2025, et il est probable que la 18.0 paraîtra à l’automne 2025.

Pour les nouveaux développements, démarrez en version 17.

Pour plus de détails sur les fonctionnalités de chaque version, voir le tableau comparatif des versions.


Versions dérivées / Forks

Entre de nombreux autres :

  • Compatibilité Oracle :
    • EnterpriseDB
  • Data warehouse :
    • Greenplum, Netezza
  • Forks :
    • Amazon RedShift, Aurora, Neon…
    • attention : support, extensions…
  • Extensions :
    • Citus
    • timescaledb
  • Packages avec des outils & support
  • Bases compatibles

Il existe de nombreuses versions dérivées de PostgreSQL. Elles sont en général destinées à des cas d’utilisation très spécifiques et offrent des fonctionnalités non proposées par la version communautaire. Leur code est souvent fermé et nécessite l’acquisition d’une licence payante. La licence de PostgreSQL permet cela, et le phénomène existait déjà dès les années 1990 avec divers produits commerciaux comme Illustra.

Modifier le code de PostgreSQL a plusieurs conséquences négatives. Certaines fonctionnalités de PostgreSQL peuvent être désactivées. Il est donc difficile de savoir ce qui est réellement utilisable. De plus, chaque nouvelle version mineure demande une adaptation de leur ajout de code. Chaque nouvelle version majeure demande une adaptation encore plus importante de leur code. C’est un énorme travail, qui n’apporte généralement pas suffisamment de plus-value à la société éditrice pour qu’elle le réalise. La seule société qui le fait de façon complète est EnterpriseDB, qui arrive à proposer des mises à jour régulièrement. Par contre, si on revient sur l’exemple de Greenplum, ils sont restés bloqués pendant un bon moment sur la version 8.0. Ils ont cherché à corriger cela. Fin 2021, Greenplum 6.8 est au niveau de la version 9.4, version considérée alors comme obsolète par la communauté depuis plus de deux ans. En janvier 2023, Greenplum 7.0 bêta n’est toujours parvenu qu’au niveau de PostgreSQL 12.12. Depuis, le dépôt n’est plus public…

Rien ne dit non plus que la société ne va pas abandonner son fork. Par exemple, il a existé quelques forks créés lorsque PostgreSQL n’était pas disponible en natif sous Windows : ces forks ont majoritairement disparu lors de l’arrivée de la version 8.0, qui supportait officiellement Windows dans la version communautaire.

Il y a eu aussi quelques forks créés pour gérer la réplication. Là aussi, la plupart de ces forks ont été abandonnés (et leurs clients avec) quand PostgreSQL a commencé à proposer de la réplication en version 9.0.

Il faut donc bien comprendre qu’à partir du moment où un utilisateur choisit une version dérivée, il dépend fortement (voire uniquement) de la bonne volonté de la société éditrice pour maintenir son produit, le mettre à jour avec les dernières corrections et les dernières nouveautés de la version communautaire, et le rendre compatible avec la myriade d’extensions existantes. Pour éviter ce problème, certaines sociétés ont décidé de transformer leur fork en une extension. C’est beaucoup plus simple à maintenir et n’enferme pas leurs utilisateurs. C’est le cas par exemple de citusdata (racheté par Microsoft) pour son extension de sharding ; ou encore de TimescaleDB, avec leur extension spécialisée dans les séries temporelles.

Dans les exemples de forks dédiés aux entrepôts de données, les plus connus historiquement sont Greenplum, de Pivotal (racheté par WMWare), et Netezza (racheté par IBM). Autant Greenplum tente de se raccrocher au PostgreSQL communautaire toutes les quelques années, autant ce n’est pas le cas de Netezza, optimisé pour du matériel dédié, et qui a forké de PostgreSQL 7.2.

Amazon, avec notamment les versions Redshift ou Aurora, a la particularité de modifier profondément PostgreSQL pour l’adapter à son infrastructure, mais ne diffuse pas ses modifications. Même si certaines incompatibilités sont listées, il est très difficile de savoir où ils en sont et l’impact qu’a leurs modifications.

Neon.tech est un autre fork ayant réécrit la couche de stockage et permettant de dupliquer des bases rapidement, notamment à l’usage des développeurs.

EDB Postgres Advanced Server est une distribution PostgreSQL d’EnterpriseDB. Elle permet de faciliter la migration depuis Oracle. Son code est propriétaire et soumis à une licence payante. Certaines fonctionnalités finissent par atterrir dans le code communautaire (une fois qu’EnterpriseDB le souhaite et que la communauté a validé l’intérêt de cette fonctionnalité et sa possible intégration).

Supabase est un exemple de société intégrant PostgreSQL dans une plateforme plus vaste pour du développement web.

BDR, anciennement de 2nd Quadrant, maintenant EnterpriseDB, est un fork visant à fournir une version multimaître de PostgreSQL, mais le code a été refermé dans les dernières versions. Il est très difficile de savoir où ils en sont. Son utilisation implique de prendre le support chez eux.

La société russe Postgres Pro, tout comme EnterpriseDB, propose diverses fonctionnalités dans sa version propre, tout en proposant souvent leur inclusion dans la version communautaire — ce qui n’est pas automatique.

Face au leadership de PostgreSQL, une tendance récente pour certaines bases de données est de se revendiquer « compatibles PostgreSQL », par exemple YugabyteDB. Certains éditeurs de solutions de bases de données distribuées propriétaires disent que leur produit peut remplacer PostgreSQL sans modification de code côté application. Il convient de rester critique et prudent face à cette affirmation, car ces produits n’ont parfois rien à voir avec PosgreSQL. Leurs évolutions n’intégreront sans doute pas la version communautaire.

(Cet historique provient en partie de la liste exhaustive des « forks », ainsi de que cette conférence de Josh Berkus de 2009 et des références en bibliographie.)

Sauf cas très précis, il est recommandé d’utiliser la version officielle, libre et gratuite. Vous savez exactement ce qu’elle propose et vous choisissez librement vos partenaires (pour les formations, pour le support, pour les audits, etc). Vous profitez aussi de tous les outils de l’écosystème.


Quelques projets satellites

PostgreSQL n’est que le moteur ! Besoin d’outils pour :

  • Administration
  • Sauvegarde
  • Supervision
  • Migration
  • SIG

PostgreSQL n’est qu’un moteur de bases de données. Quand vous l’installez, vous n’avez que ce moteur. Vous disposez de quelques outils en ligne de commande (détaillés dans nos modules « Outils graphiques et consoles » et « Tâches courantes ») mais aucun outil graphique n’est fourni.

Du fait de ce manque, certaines personnes ont décidé de développer ces outils graphiques. Ceci a abouti à une grande richesse grâce à la grande variété de projets « satellites » qui gravitent autour du projet principal.

Par choix, nous ne présenterons ici que des logiciels libres et gratuits. Pour chaque problématique, il existe aussi des solutions propriétaires. Ces solutions peuvent parfois apporter des fonctionnalités inédites. Il faut néanmoins considérer que l’offre de la communauté Open-Source répond à la plupart des besoins des utilisateurs de PostgreSQL.


Administration, Développement, Modélisation

Entre autres, dédiés ou pas :

  • Administration :
    • pgAdmin4
    • temBoard
  • Développement :
    • DBeaver
  • Modélisation :
    • pgModeler

Il existe différents outils graphiques pour l’administration, le développement et la modélisation. Une liste plus exhaustive est disponible sur le wiki PostgreSQL.

pgAdmin4 est un outil d’administration dédié à PostgreSQL, qui permet aussi de requêter. (La version 3 est considérée comme périmée.)

temBoard est une console d’administration plus complète. temBoard intègre de la supervision, des tableaux de bord, la gestion des sessions en temps réel, du bloat, de la configuration et l’analyse des performances.

DBeaver est un outil de requêtage courant, utilisable avec de nombreuses bases de données différentes, et adapté à PostgreSQL.

Pour la modélisation, pgModeler est dédié à PostgreSQL. Il permet la modélisation, la rétro-ingénierie d’un schéma existant, la génération de scripts de migration.


Sauvegardes

Les outils listés ci-dessus sont les outils principaux et que nous recommandons pour la réalisation des sauvegardes et la gestion de leur rétention.

Ils se basent sur les outils standards de PostgreSQL de sauvegarde physique ou logique.

Liens :

Il existe plusieurs outils propriétaires, notamment pour une sauvegarde par snapshot au niveau de la baie.


Supervision

  • Nagios/Icinga2 :
    • check_pgactivity
    • check_postgres
  • Prometheus : postgres_exporter
  • PoWA

Pour ne citer que quelques projets libres et matures :

check_pgactivity est une sonde Nagios pouvant récupérer un grand nombre de statistiques d’activités renseignées par PostgreSQL. Il faut de ce fait un serveur Nagios (ou un de ses nombreux forks ou surcharges) pour gérer les alertes et les graphes. Il existe aussi check_postgres.

postgres_exporter est l’exporteur de métriques pour Prometheus.

PoWA est composé d’une extension qui historise les statistiques récupérées par l’extension pg_stat_statements et d’une application web qui permet de récupérer les requêtes et leur statistiques facilement.


Audit

pgBadger est l’outil de base pour les analyses (à posteriori) des traces de PostgreSQL, dont notamment les requêtes.

pgCluu permet une analyse du système et de PostgreSQL.


Migration

Il existe de nombreux outils pour migrer vers PostgreSQL une base de données utilisant un autre moteur. Ce qui pose le plus problème en pratique est le code applicatif (procédures stockées).

Ces outils ont été développé au fil du temps par des sociétés proposant de la prestation de service autour de la migration, ou par de grosses entreprises pour leurs besoins internes. Il sont plus ou moins avancés, efficaces, finis, maintenus, et généralement propriétaires.

Les fonctionnalités incluent généralement la migration du schéma, parfois le transfert des données de manière plus ou moins optimisée, l’estimation de la charge de migration, ou encore la migration du code applicatif (PL/SQL d’Oracle notamment). Ce dernier point est généralement le plus complexe et doit généralement être repris ou traité par des humains.

Les plus importants parmi les outils libres actifs sont :

  • Ora2Pg, de Gilles Darold, convertit le schéma de données, migre les données, et tente même de convertir le code PL/SQL en PL/pgSQL. Il convertit aussi des bases MySQL ou SQL Server.

  • pgloader, de Dimitri Fontaine, permet de migrer depuis MySQL, SQLite ou MS SQL Server, et importe les fichiers CSV, DBF (dBase) ou IXF (fichiers d’échange indépendants de la base).


PostGIS

Logo Postgis
  • Projet indépendant, GPL, https://postgis.net/
  • Module spatial pour PostgreSQL
    • Extension pour types géométriques/géographiques & outils
    • La référence des bases de données spatiales
    • « quelles sont les routes qui coupent le Rhône ? »
    • « quelles sont les villes adjacentes à Toulouse ? »
    • « quels sont les restaurants situés à moins de 3 km de la Nationale 12 ? »

PostGIS ajoute le support d’objets géographiques à PostgreSQL. C’est un projet totalement indépendant développé par la société Refractions Research sous licence GPL, soutenu par une communauté active, utilisée par des spécialistes du domaine géospatial (IGN, BRGM, AirBNB, Mappy, Openstreetmap, Agence de l’eau…), mais qui peut convenir pour des projets plus modestes.

Techniquement, c’est une extension transformant PostgreSQL en serveur de données spatiales, qui sera utilisé par un Système d’Information Géographique (SIG), tout comme le SDE de la société ESRI ou bien l’extension Oracle Spatial. PostGIS se conforme aux directives du consortium OpenGIS et a été certifié par cet organisme comme tel, ce qui est la garantie du respect des standards par PostGIS.

PostGIS permet d’écrire des requêtes de ce type :

SELECT restaurants.geom, restaurants.name FROM restaurants
  WHERE EXISTS (SELECT 1 FROM routes
                   WHERE ST_DWithin(restaurants.geom, routes.geom, 3000)
                AND route.name = 'Nationale 12')

PostGIS fournit les fonctions d’indexation qui permettent d’accéder rapidement aux objets géométriques, au moyen d’index GiST. La requête ci-dessus n’a évidemment pas besoin de parcourir tous les restaurants à la recherche de ceux correspondant aux critères de recherche.

La liste des fonctionnalités comprend le support des coordonnées géodésiques ; des projections et reprojections dans divers systèmes de coordonnées locaux (Lambert93 en France par exemple) ; des opérateurs d’analyse géométrique (enveloppe convexe, simplification…)

PostGIS est intégré aux principaux serveurs de carte, ETL, et outils de manipulation.

La version 3.0 apporte la gestion du parallélisme, un meilleur support de l’indexation SP-GiST et GiST, ainsi qu’un meilleur support du type GeoJSON.


Sponsors & Références

Au-delà de ses qualités, PostgreSQL suscite toujours les mêmes questions récurrentes :

  • qui finance les développements ? (et pourquoi ?)
  • qui utilise PostgreSQL ?

Sponsors principaux

  • Sociétés se consacrant à PostgreSQL :
    • Crunchy Data (USA) : Tom Lane, Stephen Frost, Joe Conway…
    • EnterpriseDB (USA) : Bruce Momjian, Robert Haas, Peter Eisentraut…
    • PostgresPro (Russie) : Oleg Bartunov, Teodor Sigaev
    • Cybertec (Autriche), Dalibo (France), Redpill Linpro (Suède), Credativ (Allemagne)…
  • Sociétés vendant un fork ou une extension :
    • Citusdata (Microsoft), Pivotal (VMWare), TimescaleDB

La liste des sponsors de PostgreSQL contribuant activement au développement figure sur la liste officielle des sponsors. Ce qui suit n’est qu’un aperçu.

EnterpriseDB est une société américaine qui a décidé de fournir une version de PostgreSQL propriétaire fournissant une couche de compatibilité avec Oracle. Ils emploient plusieurs développeurs importants du projet PostgreSQL (dont trois font partie de la Core Team), et reversent un certain nombre de leurs travaux au sein du moteur communautaire. Ils ont aussi un poids financier qui leur permet de sponsoriser la majorité des grands événements autour de PostgreSQL : PGEast et PGWest aux États-Unis, PGDay en Europe.

En 2020, EnterpriseDB rachète 2nd Quadrant, une société anglaise fondée par le regretté Simon Riggs, développeur PostgreSQL de longue date. 2nd Quadrant développe de nombreux outils autour de PostgreSQL comme pglogical, des versions dérivées comme Postgres-XL ou BDR, ou des outils annexes comme barman ou repmgr.

Crunchy Data offre sa propre version certifiée et finance de nombreux développements.

De nombreuses autres sociétés dédiées à PostgreSQL existent dans de nombreux pays. Parmi les sponsors officiels, nous pouvons compter Cybertec en Autriche ou Redpill Linpro en Suède. En Russie, PostgresPro maintient une version locale et reverse aussi de nombreuses contributions à la communauté.

En Europe francophone, Dalibo participe pleinement à la communauté. La société est Major Sponsor du projet PostgreSQL, ce qui indique un support de longue date. Elle développe et maintient plusieurs outils plébiscités par la communauté, comme autrefois Open PostgreSQL Monitoring (OPM) ou la sonde check_pgactivity, plus récemment la console d’administration temBoard, avec de nombreux autres projets en cours, et une participation active au développement de patchs pour PostgreSQL. Dalibo sponsorise également des événements comme les PGDay français et européens, ainsi que la communauté francophone.

Des sociétés comme Citusdata (racheté par Microsoft), Pivotal (VMWare) ou TimescaleDB proposent ou ont proposé leur version dérivée sous une forme ou une autre, mais « jouent le jeu » et participent au développement de la version communautaire, notamment en cherchant à ce que leur produit n’en diverge pas.


Autres sponsors

  • Autres sociétés :
    • VMWare, Rackspace, Heroku, Conova, Red Hat, Microsoft
    • NTT (streaming replication), Fujitsu, NEC
  • Cloud
    • nombreuses

Contribuent également à PostgreSQL nombre de sociétés non centrées autour des bases de données.

NTT a financé de nombreux patchs pour PostgreSQL.

Fujitsu a participé à de nombreux développements aux débuts de PostgreSQL, et emploie Amit Kapila.

VMWare a longtemps employé le développeur finlandais Heikki Linnakangas, parti ensuite un temps chez Pivotal. VMWare emploie aussi Michael Paquier ou Julien Rouhaud.

Red Hat a longtemps employé Tom Lane à plein temps pour travailler sur PostgreSQL. Il a pu dédier une très grande partie de son temps de travail à ce projet, bien qu’il ait eu d’autres affectations au sein de Red Hat. Tom Lane a travaillé également chez SalesForce, ensuite il a rejoint Crunchy Data Solutions fin 2015.

Il y a déjà plus longtemps, Skype a offert un certain nombre d’outils très intéressants : PgBouncer (pooler de connexion), Londiste (réplication par trigger), etc. Ce sont des outils utilisés en interne et publiés sous licence BSD comme retour à la communauté. Malgré le rachat par Microsoft, certains sont encore utiles et maintenus.

Zalando est connu pour l’outil de haute disponibilité patroni.

De nombreuses sociétés liées au cloud figurent aussi parmi les sponsors, comme Conova (Autriche), Heroku ou Rackspace (États-Unis), ou les mastodontes Google, Amazon Web Services et, à nouveau, Microsoft.


Références

  • Météo France
  • IGN
  • RATP, SNCF
  • CNAF
  • MAIF, MSA
  • Le Bon Coin
  • Air France-KLM
  • Société Générale
  • Carrefour, Leclerc, Leroy Merlin
  • Instagram, Zalando, TripAdvisor
  • Yandex
  • CNES
  • …et plein d’autres

Météo France utilise PostgreSQL depuis plus d’une décennie pour l’essentiel de ses bases, dont des instances critiques de plusieurs téraoctets (témoignage sur postgresql.fr).

L’IGN utilise PostGIS et PostgreSQL depuis 2006.

La RATP a fait ce choix depuis 2007 également.

La Caisse Nationale d’Allocations Familiales a remplacé ses mainframes par des instances PostgreSQL dès 2010 (4 To et 1 milliard de requêtes par jour).

Instagram utilise PostgreSQL depuis le début.

Zalando a décrit plusieurs fois son infrastructure PostgreSQL et annonçait en 2018 utiliser pas moins de 300 bases de données en interne et 650 instances dans un cloud AWS. Zalando contribue à la communauté, notamment par son outil de haute disponibilité patroni.

Le DBA de TripAdvisor témoigne de leur utilisation de PostgreSQL dans l’interview suivante.

Dès 2009, Leroy Merlin migrait vers PostgreSQL des milliers de logiciels de caisse.

Yandex, équivalent russe de Google a décrit en 2016 la migration des 300 To de données de Yandex.Mail depuis Oracle vers PostgreSQL.

La Société Générale a publié son outil de migration d’Oracle à PostgreSQL.

Autolib à Paris utilisait PostgreSQL. Le logiciel est encore utilisé dans les autres villes où le service continue. Ils ont décrit leur infrastructure au PG Day 2018 à Marseille.

De nombreuses autres sociétés participent au Groupe de Travail Inter-Entreprises de PostgreSQLFr : Air France, Carrefour, Leclerc, le CNES, la MSA, la MAIF, PeopleDoc, EDF…

Cette liste ne comprend pas les innombrables sociétés qui n’ont pas communiqué sur le sujet. PostgreSQL étant un logiciel libre, il n’existe nulle part de dénombrement des instances actives.


Le Bon Coin

  • Site de petites annonces
  • 4è site le plus consulté en France (2017)
  • 27 millions d’annonces en ligne, 800 000 nouvelles chaque jour
  • Instance PostgreSQL principale : 3 To de volume, 3 To de RAM
  • 20 serveurs secondaires

PostgreSQL tient la charge sur de grosses bases de données et des serveurs de grande taille.

Le Bon Coin privilégie des serveurs physiques dans ses propres datacenters.

Pour plus de détails et l’évolution de la configuration, voir les témoignages de ses directeurs technique (témoignage de juin 2012) et infrastructure (juin 2017), ou la conférence de son DBA Flavio Gurgel au pgDay Paris 2019.

Ce dernier s’appuie sur les outils classiques fournis par la communauté : pg_dump (pour archivage, car ses exports peuvent être facilement restaurés), barman, pg_upgrade.


À la rencontre de la communauté

  • Cartographie du projet
  • Pourquoi participer
  • Comment participer

PostgreSQL, un projet mondial

Carte des hackers

On le voit, PostgreSQL compte des contributeurs sur tous les continents.

Le projet est principalement anglophone. Les core hackers sont surtout répartis en Amérique, Europe, Asie (Japon surtout).

Il existe une très grande communauté au Japon, et de nombreux développeurs en Russie.

La communauté francophone est très dynamique, s’occupe beaucoup des outils, mais il n’y a que quelques développeurs réguliers du core francophones : Michael Paquier, Julien Rouhaud, Fabien Coelho…

La communauté hispanophone est naissante.


PostgreSQL Core Team

Core team

Le terme Core Hackers désigne les personnes qui sont dans la communauté depuis longtemps. Ces personnes désignent directement les nouveaux membres.

NB : Le terme hacker peut porter à confusion, il s’agit bien ici de la définition « universitaire » : https://fr.wikipedia.org/wiki/Hacker_(programmation)

La Core Team est un ensemble de personnes doté d’un pouvoir assez limité. Ils ne doivent pas appartenir en majorité à la même société. Ils peuvent décider de la date de sortie d’une version. Ce sont les personnes qui sont immédiatement au courant des failles de sécurité du serveur PostgreSQL. Exceptionnellement, elles tranchent certains débats si un consensus ne peut être atteint dans la communauté. Tout le reste des décisions est pris par la communauté dans son ensemble après discussion, généralement sur la liste pgsql-hackers.

Les membres actuels de la Core Team sont :

  • Tom Lane (Crunchy Data, Pittsburgh, États-Unis) : certainement le développeur le plus aguerri avec la vision la plus globale, notamment sur l’optimiseur ;
  • Bruce Momjian (EDB, Pennsylvanie, États-Unis) : a lancé le projet en 1995, écrit du code (pg_upgrade notamment) et s’est beaucoup occupé de la promotion ;
  • Magnus Hagander (Redpill Linpro, Stockholm, Suède) : développeur, a participé notamment au portage Windows, à l’outil pg_basebackup, à l’authentification, à l’administration des serveurs, président de PostgreSQL Europe ;
  • Andres Freund (Microsoft, San Francisco, États-Unis) : contributeur depuis des années de nombreuses fonctionnalités (JIT, réplication logique, performances…) ;
  • Dave Page (pgEdge, Oxfordshire, Royaume-Uni) : leader du projet pgAdmin, version Windows, administration des serveurs, secrétaire de PostgreSQL Europe ;
  • Peter Eisentraut (EDB, Dresde, Allemagne) : développement du moteur (internationalisation, SQL/Med…), participe à la définition du standard SQL, etc. ;
  • Jonathan Katz (Amazon Web Services, New York, États-Unis) : promotion du projet, modération, revues de patchs.

Contributeurs

Contributeurs

Actuellement, les « contributeurs » se répartissent quotidiennement les tâches suivantes :

  • développement des projets satellites (pgAdmin…) ;
  • promotion du logiciel ;
  • administration de l’infrastructure des serveurs ;
  • rédaction de documentation ;
  • conférences ;
  • traductions ;
  • organisation de groupes locaux.

Le PGDG a fêté son 10e anniversaire à Toronto en juillet 2006. Ce « PostgreSQL Anniversary Summit » a réuni pas moins de 80 membres actifs du projet. La photo ci-dessus a été prise à l’occasion.

PGCon2009 a réuni 180 membres actifs à Ottawa, et environ 220 en 2018 et 2019.

Voir la liste des contributeurs officiels.


Qui contribue du code ?

  • Principalement des personnes payées par leur société
  • 30 committers
    • Tom Lane
    • Andres Freund
    • Peter Eisentraut
    • Nikita Glukhov
    • Álvaro Herrera
    • Michael Paquier
    • Robert Haas
    • …et beaucoup d’autres
  • Commitfests : tous les 2 mois

À l’automne 2024, on compte 30 committers, c’est-à-dire personnes pouvant écrire dans tout ou partie du dépôt de PostgreSQL. Il ne s’agit pas que de leur travail, mais pour une bonne partie de patchs d’autres contributeurs après discussion et validation des fonctionnalités mais aussi des standards propres à PostgreSQL, de la documentation, de la portabilité, de la simplicité, de la sécurité, etc. Ces autres contributeurs peuvent être potentiellement n’importe qui. En général, un patch est relu par plusieurs personnes avant d’être transmis à un committer.

Les discussions quant au développement ont lieu principalement (mais pas uniquement) sur la liste pgsql-hackers. Les éventuels bugs sont transmis à la liste pgsql-bugs. Puis les patchs en cours sont revus au moins tous les deux mois lors des Commitfests. Il n’y a pas de bug tracker car le fonctionnement actuel est jugé satisfaisant.

Robert Haas publie chaque année une analyse sur les contributeurs de code et les participants aux discussions sur le développement de PostgreSQL sur la liste pgsql-hackers :


Répartition des développeurs

Répartition des développeurs

Voici une répartition des différentes sociétés qui ont contribué aux améliorations de la version 13. On y voit qu’un grand nombre de sociétés prend part à ce développement. La plus importante est EDB, mais même elle n’est responsable que d’un petit tiers des contributions.

(Source : Future Postgres Challenges, Bruce Momjian, 2021)


Utilisateurs

  • Vous !
  • Le succès d’un logiciel libre dépend de ses utilisateurs.

Il est impossible de connaître précisément le nombre d’utilisateurs de PostgreSQL. Toutefois ce nombre est en constante augmentation.

Il existe différentes manières de s’impliquer dans une communauté Open-Source. Dans le cas de PostgreSQL, vous pouvez :

  • déclarer un bug ;
  • tester les versions bêta ;
  • témoigner.

Pourquoi participer

  • Rapidité des corrections de bugs
  • Préparer les migrations / tester les nouvelles versions
  • Augmenter la visibilité du projet
  • Créer un réseau d’entraide

Au-delà de motivations idéologiques ou technologiques, il y a de nombreuses raisons objectives de participer au projet PostgreSQL.

Envoyer une description d’un problème applicatif aux développeurs est évidemment le meilleur moyen d’obtenir sa correction. Attention toutefois à être précis et complet lorsque vous déclarez un bug sur pgsql-bugs ! Assurez-vous que vous pouvez le reproduire.

Tester les versions « candidates » dans votre environnement (matériel et applicatif) est la meilleure garantie que votre système d’information sera compatible avec les futures versions du logiciel.

Les retours d’expérience et les cas d’utilisations professionnelles sont autant de preuves de la qualité de PostgreSQL. Ces témoignages aident de nouveaux utilisateurs à opter pour PostgreSQL, ce qui renforce la communauté.

S’impliquer dans les efforts de traductions, de relecture ou dans les forums d’entraide ainsi que toute forme de transmission en général est un très bon moyen de vérifier et d’approfondir ses compétences.


Ressources web de la communauté

Le site officiel de la communauté se trouve sur https://www.postgresql.org/. Ce site contient des informations sur PostgreSQL, la documentation des versions maintenues, les archives des listes de discussion, etc.

Le site « Planet PostgreSQL » est un agrégateur réunissant les blogs des Core Hackers, des contributeurs, des traducteurs et des utilisateurs de PostgreSQL.

Le site PGXN est l’équivalent pour PostgreSQL du CPAN de Perl, une collection en ligne de librairies et extensions accessibles depuis la ligne de commande.


Documentation officielle

La documentation officielle sur https://www.postgresql.org/docs/current est maintenue au même titre que le code du projet, et sert aussi au quotidien, pas uniquement pour des cas obscurs.

Elle est versionnée pour chaque version majeure.

La traduction française suit de près les mises à jour de la documentation officielle : https://docs.postgresql.fr/.


Serveurs francophones

Le site postgresql.fr est le site de l’association des utilisateurs francophones du logiciel. La communauté francophone se charge de la traduction de toutes les documentations.


Listes de discussions / Listes d’annonces

  • pgsql-announce
  • pgsql-general
  • pgsql-admin
  • pgsql-sql
  • pgsql-performance
  • pgsql-fr-generale
  • pgsql-advocacy
  • pgsql-bugs

Les mailing-lists sont les outils principaux de gouvernance du projet. Toute l’activité de la communauté (bugs, promotion, entraide, décisions) est accessible par ce canal. Les développeurs principaux du projets répondent parfois eux-mêmes. Si vous avez une question ou un problème, la réponse se trouve probablement dans les archives ! Pour s’inscrire ou consulter les archives : https://www.postgresql.org/list/.

Si vous pensez avoir trouvé un bug, vous pouvez le remonter sur la liste anglophone pgsql-bugs, par le formulaire dédié. Pour faciliter la tâche de ceux qui tenteront de vous répondre, suivez bien les consignes sur les rapports de bug : informations complètes, reproductibilité…


IRC

  • Réseau LiberaChat
  • IRC anglophone :
    • #postgresql
    • #postgresql-eu
  • IRC francophone :
    • #postgresqlfr

Le point d’entrée principal pour le réseau LiberaChat est le serveur irc.libera.chat. La majorité des développeurs sont disponibles sur IRC et peuvent répondre à vos questions.

Des canaux de discussion spécifiques à certains projets connexes sont également disponibles, comme par exemple #slony.

Attention ! Vous devez poser votre question en public et ne pas solliciter de l’aide par message privé.


Wiki

Le wiki est un outil de la communauté qui met à disposition une véritable mine d’informations.

Au départ, le wiki avait pour but de récupérer les spécifications écrites par des développeurs pour les grosses fonctionnalités à développer à plusieurs. Cependant, peu de développeurs l’utilisent dans ce cadre. L’utilisation du wiki a changé en passant plus entre les mains des utilisateurs qui y intègrent un bon nombre de pages de documentation (parfois reprises dans la documentation officielle). Le wiki est aussi utilisé par les organisateurs d’événements pour y déposer les slides des conférences. Elle n’est pas exhaustive et, hélas, souffre fréquemment d’un manque de mises à jour.


L’avenir de PostgreSQL

  • PostgreSQL est devenu la base de données de référence
  • Grandes orientations :
    • réplication logique
    • meilleur parallélisme
    • gros volumes
  • Prochaine version, la 18
  • Stabilité économique
  • De plus en plus de (gros) clients
  • Le futur de PostgreSQL dépend de vous !

Le projet avance grâce à de plus en plus de contributions. Les grandes orientations actuelles sont :

  • une réplication de plus en plus sophistiquée ;
  • une gestion plus étendue du parallélisme ;
  • une volumétrie acceptée de plus en plus importante ;
  • etc.

PostgreSQL est là pour durer. Le nombre d’utilisateurs, de toutes tailles, augmente tous les jours. Il n’y a pas qu’une seule entreprise derrière ce projet. Il y en a plusieurs, petites et grosses sociétés, qui s’impliquent pour faire avancer le projet, avec des modèles économiques et des marchés différents, garants de la pérennité du projet.


Conclusion

  • Un projet de grande ampleur
  • Un SGBD complet
  • Souplesse, extensibilité
  • De belles références
  • Une solution stable, ouverte, performante et éprouvée
  • Pas de dépendance envers UN éditeur

Certes, la licence PostgreSQL implique un coût nul (pour l’acquisition de la licence), un code source disponible et aucune contrainte de redistribution. Toutefois, il serait erroné de réduire le succès de PostgreSQL à sa gratuité.

Beaucoup d’acteurs font le choix de leur SGBD sans se soucier de son prix. En l’occurrence, ce sont souvent les qualités intrinsèques de PostgreSQL qui séduisent :

  • sécurité des données (reprise en cas de crash et résistance aux bogues applicatifs) ;
  • facilité de configuration ;
  • montée en puissance et en charge progressive ;
  • gestion des gros volumes de données ;
  • pas de dépendance envers un unique éditeur ou prestataire.

Bibliographie

  • Documentation officielle (préface)
  • Articles fondateurs de M. Stonebracker (1987)
  • Présentation du projet PostgreSQL (Guillaume Lelarge, 2008)
  • Looking back at PostgreSQL (J.M. Hellerstein, 2019)

Quelques références :

Iconographie : La photo initiale est le logo officiel de PostgreSQL.


Questions

N’hésitez pas, c’est le moment !


Quiz

Découverte des fonctionnalités

PostgreSQL

Au menu

  • Fonctionnalités du moteur

  • Objets SQL

  • Connaître les différentes fonctionnalités et possibilités

  • Découvrir des exemples concrets

Ce module propose un tour rapide des fonctionnalités principales du moteur : ACID, MVCC, transactions, journaux de transactions… ainsi que des objets SQL gérés (schémas, index, tablespaces, triggers…). Ce rappel des concepts de base permet d’avancer plus facilement lors des modules suivants.


Fonctionnalités du moteur

  • Standard SQL
  • ACID : la gestion transactionnelle
  • Niveaux d’isolation
  • Journaux de transactions
  • Administration
  • Sauvegardes
  • Réplication
  • Supervision
  • Sécurité
  • Extensibilité

Cette partie couvre les différentes fonctionnalités d’un moteur de bases de données. Il ne s’agit pas d’aller dans le détail de chacune, mais de donner une idée de ce qui est disponible. Les modules suivants de cette formation et des autres formations détaillent certaines de ces fonctionnalités.


Respect du standard SQL

  • Excellent support du SQL ISO
  • Objets SQL
    • tables, vues, séquences, routines, triggers
  • Opérations
    • jointures, sous-requêtes, requêtes CTE, requêtes de fenêtrage, etc.

La dernière version du standard SQL est SQL:2023. À ce jour, aucun SGBD ne la supporte complètement, mais :

  • PostgreSQL progresse et s’en approche au maximum, au fil des versions ;
  • la majorité de la norme est supportée, parfois avec des syntaxes différentes ;
  • PostgreSQL est le SGDB le plus respectueux du standard.

ACID

Gestion transactionnelle : la force des bases de données relationnelles :

  • Atomicité (Atomic)
  • Cohérence (Consistent)
  • Isolation (Isolated)
  • Durabilité (Durable)

Les propriétés ACID sont le fondement même de toute bonne base de données. Il s’agit de l’acronyme des quatre règles que toute transaction (c’est-à-dire une suite d’ordres modifiant les données) doit respecter :

  • A : Une transaction est appliquée en « tout ou rien ».
  • C : Une transaction amène la base d’un état stable à un autre.
  • I : Les transactions n’agissent pas les unes sur les autres.
  • D : Une transaction validée sera conservée de manière permanente.

Les bases de données relationnelles les plus courantes depuis des décennies (PostgreSQL bien sûr, mais aussi Oracle, MySQL, SQL Server, SQLite…) se basent sur ces principes, même si elles font chacune des compromis différents suivant leurs cas d’usage, les compromis acceptés à chaque époque avec la performance et les versions.

Atomicité :

Une transaction doit être exécutée entièrement ou pas du tout, et surtout pas partiellement, même si elle est longue et complexe, même en cas d’incident majeur sur la base de données. L’exemple basique est une transaction bancaire : le montant d’un virement doit être sur un compte ou un autre, et en cas de problème ne pas disparaître ou apparaître en double. Ce principe garantit que les données modifiées par des transactions valides seront toujours visibles dans un état stable, et évite nombre de problèmes fonctionnels comme techniques.

Cohérence :

Un état cohérent respecte les règles de validité définies dans le modèle, c’est-à-dire les contraintes définies dans le modèle : types, plages de valeurs admissibles, unicité, liens entre tables (clés étrangères), etc. Le non-respect de ces règles par l’applicatif entraîne une erreur et un rejet de la transaction.

Isolation :

Des transactions simultanées doivent agir comme si elles étaient seules sur la base. Surtout, elles ne voient pas les données non validées des autres transactions. Ainsi une transaction peut travailler sur un état stable et fixe, et durer assez longtemps sans risque de gêner les autres transactions.

Il existe plusieurs « niveaux d’isolation » pour définir précisément le comportement en cas de lectures ou écritures simultanées sur les mêmes données et pour arbitrer avec les contraintes de performances ; le niveau le plus contraignant exige que tout se passe comme si toutes les transactions se déroulaient successivement.

Durabilité :

Une fois une transaction validée par le serveur (typiquement : COMMIT ne retourne pas d’erreur, ce qui valide la cohérence et l’enregistrement physique), l’utilisateur doit avoir la garantie que la donnée ne sera pas perdue ; du moins jusqu’à ce qu’il décide de la modifier à nouveau. Cette garantie doit valoir même en cas d’événement catastrophique : plantage de la base, perte d’un disque… C’est donc au serveur de s’assurer autant que possible que les différents éléments (disque, système d’exploitation…) ont bien rempli leur office. C’est à l’humain d’arbitrer entre le niveau de criticité requis et les contraintes de performances et de ressources adéquates (et fiables) à fournir à la base de données.

NoSQL :

À l’inverse, les outils de la mouvance (« NoSQL », par exemple MongoDB ou Cassandra), ne fournissent pas les garanties ACID. C’est le cas de la plupart des bases non-relationnelles, qui reprennent le modèle BASE (Basically Available, Soft State, Eventually Consistent, soit succintement : disponibilité d’abord ; incohérence possible entre les réplicas ; cohérence… à terme, après un délai). Un intérêt est de débarrasser le développeur de certaines lourdeurs apparentes liées à la modélisation assez stricte d’une base de données relationnelle. Cependant, la plupart des applications ont d’abord besoin des garanties de sécurité et cohérence qu’offrent un moteur transactionnel classique, et la décision d’utiliser un système ne les garantissant pas ne doit pas être prise à la légère ; sans parler d’autres critères comme la fragmentation du domaine par rapport au monde relationnel et son SQL (à peu près) standardisé. Avec le temps, les moteurs transactionnels ont acquis des fonctionnalités qui faisaient l’intérêt des bases NoSQL (en premier lieu la facilité de réplication et le stockage de JSON), et ces dernières ont tenté d’intégrer un peu plus de sécurité dans leur modèle.


MVCC

  • MultiVersion Concurrency Control
  • Le « noyau » de PostgreSQL
  • Garantit les propriétés ACID
  • Permet les accès concurrents sur la même table
    • une lecture ne bloque pas une écriture
    • une écriture ne bloque pas une lecture
    • une écriture ne bloque pas les autres écritures…
    • …sauf pour la mise à jour de la même ligne

MVCC (Multi Version Concurrency Control) est le mécanisme interne de PostgreSQL utilisé pour garantir la cohérence des données lorsque plusieurs processus accèdent simultanément à la même table.

MVCC maintient toutes les versions nécessaires de chaque ligne, ainsi chaque transaction voit une image figée de la base (appelée snapshot). Cette image correspond à l’état de la base lors du démarrage de la requête ou de la transaction, suivant le niveau d’isolation demandé par l’utilisateur à PostgreSQL pour la transaction.

MVCC fluidifie les mises à jour en évitant les blocages trop contraignants (verrous sur UPDATE) entre sessions et par conséquent de meilleures performances en contexte transactionnel.

C’est notamment MVCC qui permet d’exporter facilement une base à chaud et d’obtenir un export cohérent alors même que plusieurs utilisateurs sont potentiellement en train de modifier des données dans la base.

C’est la qualité de l’implémentation de ce système qui fait de PostgreSQL un des meilleurs SGBD au monde : chaque transaction travaille dans son image de la base, cohérent du début à la fin de ses opérations. Par ailleurs, les écrivains ne bloquent pas les lecteurs et les lecteurs ne bloquent pas les écrivains, contrairement aux SGBD s’appuyant sur des verrous de lignes. Cela assure de meilleures performances, moins de contention et un fonctionnement plus fluide des outils s’appuyant sur PostgreSQL.


Transactions

  • Une transaction = ensemble atomique d’opérations
  • « Tout ou rien »
  • BEGIN obligatoire pour grouper des modifications
  • COMMIT pour valider
    • y compris le DDL
  • Perte des modifications si :
    • ROLLBACK / perte de la connexion / arrêt (brutal ou non) du serveur
  • SAVEPOINT pour sauvegarde des modifications d’une transaction à un instant t
  • Pas de transactions imbriquées

L’exemple habituel et très connu des transactions est celui du virement d’une somme d’argent du compte de Bob vers le compte d’Alice. Le total du compte de Bob ne doit pas montrer qu’il a été débité de X euros tant que le compte d’Alice n’a pas été crédité de X euros. Nous souhaitons en fait que les deux opérations apparaissent aux yeux du reste du système comme une seule opération unitaire. D’où l’emploi d’une transaction explicite. En voici un exemple :

BEGIN;
UPDATE comptes SET solde=solde-200 WHERE proprietaire='Bob';
UPDATE comptes SET solde=solde+200 WHERE proprietaire='Alice';
COMMIT;

Contrairement à d’autres moteurs de bases de données, PostgreSQL accepte aussi les instructions DDL dans une transaction. En voici un exemple :

BEGIN;
CREATE TABLE capitaines (id serial, nom text, age integer);
INSERT INTO capitaines VALUES (1, 'Haddock', 35);
SELECT age FROM capitaines;
 age
---
  35
ROLLBACK;
SELECT age FROM capitaines;
ERROR:  relation "capitaines" does not exist
LINE 1: SELECT age FROM capitaines;
                        ^

Nous voyons que la table capitaines a existé à l’intérieur de la transaction. Mais puisque cette transaction a été annulée (ROLLBACK), la table n’a pas été créée au final.

Cela montre aussi le support du DDL transactionnel au sein de PostgreSQL : PostgreSQL n’effectue aucun COMMIT implicite sur des ordres DDL tels que CREATE TABLE, DROP TABLE ou TRUNCATE TABLE. De ce fait, ces ordres peuvent être annulés au sein d’une transaction.

Un point de sauvegarde est une marque spéciale à l’intérieur d’une transaction qui autorise l’annulation de toutes les commandes exécutées après son établissement, restaurant la transaction dans l’état où elle était au moment de l’établissement du point de sauvegarde.

BEGIN;
CREATE TABLE capitaines (id serial, nom text, age integer);
INSERT INTO capitaines VALUES (1, 'Haddock', 35);
SAVEPOINT insert_sp;
UPDATE capitaines SET age = 45 WHERE nom = 'Haddock';
ROLLBACK TO SAVEPOINT insert_sp;
COMMIT;
SELECT age FROM capitaines WHERE nom = 'Haddock';
 age
---
  35

Malgré le COMMIT après l’UPDATE, la mise à jour n’est pas prise en compte. En effet, le ROLLBACK TO SAVEPOINT a permis d’annuler cet UPDATE mais pas les opérations précédant le SAVEPOINT.

À partir de la version 12, il est possible de chaîner les transactions avec COMMIT AND CHAIN ou ROLLBACK AND CHAIN. Cela veut dire terminer une transaction et en démarrer une autre immédiatement après avec les mêmes propriétés (par exemple, le niveau d’isolation).


Niveaux d’isolation

  • Chaque transaction (et donc session) est isolée à un certain point
    • elle ne voit pas les opérations des autres
    • elle s’exécute indépendamment des autres
  • Nous pouvons spécifier le niveau d’isolation au démarrage d’une transaction
    • BEGIN ISOLATION LEVEL xxx;
  • Niveaux d’isolation supportés
    • read commited (défaut)
    • repeatable read
    • serializable

Chaque transaction, en plus d’être atomique, s’exécute séparément des autres. Le niveau de séparation demandé sera un compromis entre le besoin applicatif (pouvoir ignorer sans risque ce que font les autres transactions) et les contraintes imposées au niveau de PostgreSQL (performances, risque d’échec d’une transaction).

Le standard SQL spécifie quatre niveaux, mais PostgreSQL n’en supporte que trois (il n’y a pas de read uncommitted : les lignes non encore committées par les autres transactions sont toujours invisibles).


Fiabilité : journaux de transactions (1)

Principe de la journalisation

Fiabilité : journaux de transactions (2)

  • Write Ahead Logs (WAL)
  • Chaque donnée est écrite 2 fois sur le disque !
  • Avantages :
    • sécurité infaillible (après COMMIT), intégrité, durabilité
    • écriture séquentielle rapide, et un seul sync sur le WAL
    • fichiers de données écrits en asynchrone
    • sauvegarde PITR et réplication fiables

Les journaux de transactions (appelés souvent WAL) sont une garantie contre les pertes de données. Il s’agit d’une technique standard de journalisation appliquée à toutes les transactions, pour garantir l’intégrité (la base reste cohérente quoiqu’il arrive) et la durabilité (ce qui est validé ne sera pas perdu).

Ainsi lors d’une modification de donnée, l’écriture au niveau du disque se fait généralement en deux temps :

  • écriture des modifications dans le journal de transactions, avec écriture forcée sur disque (« synchronisation ») lors d’un COMMIT ;
  • écriture dans le fichier de données bien plus tard, lors d’un « checkpoint ».

Ainsi en cas de crash :

  • PostgreSQL redémarre ;
  • il vérifie s’il reste des données non intégrées aux fichiers de données dans les journaux (mode recovery) ;
  • si c’est le cas, ces données sont recopiées dans les fichiers de données afin de retrouver un état stable et cohérent ;
  • PostgreSQL peut alors s’ouvrir sans perte des transactions validées lors du crash (une transaction interrompue, et donc jamais validée, est perdue).

Les écritures dans le journal se font de façon séquentielle, donc sans grand déplacement de la tête d’écriture (sur un disque dur classique, c’est l’opération la plus coûteuse). De plus, comme nous n’écrivons que dans un seul fichier de transactions, la synchronisation sur disque, lors d’un COMMIT, peut se faire sur ce seul fichier, si le système de fichiers le supporte. Concrètement, ces journaux sont des fichiers de 16 Mo par défaut, avec des noms comme 0000000100000026000000AF, dans le répertoire pg_wal/ de l’instance PostgreSQL (répertoire souvent sur une partition dédiée).

L’écriture définitive dans les fichiers de données est asynchrone, et généralement lissée, ce qui est meilleur pour les performances qu’une écriture immédiate. Cette opération est appelée « checkpoint » et périodique (5 minutes par défaut, ou plus).

Divers paramètres et fonctionnalités peuvent altérer ce comportement par défaut, par exemple pour des raisons de performances.

À côté de la sécurité et des performances, le mécanisme des journaux de transactions est aussi utilisé pour des fonctionnalités très intéressantes, comme le PITR et la réplication physique, basés sur le rejeu des informations stockées dans ces journaux.

Pour plus d’informations :


Sauvegardes

  • Sauvegarde des fichiers à froid
    • outils système
  • Import/Export logique
    • pg_dump, pg_dumpall, pg_restore
  • Sauvegarde physique à chaud
    • pg_basebackup
    • sauvegarde PITR

PostgreSQL supporte différentes solutions pour la sauvegarde.

La plus simple revient à sauvegarder à froid tous les fichiers des différents répertoires de données mais cela nécessite d’arrêter le serveur, ce qui occasionne une mise hors production plus ou moins longue, suivant la volumétrie à sauvegarder.

L’export logique se fait avec le serveur démarré. Plusieurs outils sont proposés : pg_dump pour sauvegarder une base, pg_dumpall pour sauvegarder toutes les bases. Suivant le format de l’export, l’import se fera avec les outils psql ou pg_restore. Les sauvegardes se font à chaud et sont cohérentes sans blocage de l’activité (seuls la suppression des tables et le changement de leur définition sont interdits).

Enfin, il est possible de sauvegarder les fichiers à chaud. Cela nécessite de mettre en place l’archivage des journaux de transactions. L’outil pg_basebackup est conseillé pour ce type de sauvegarde.

Il est à noter qu’il existe un grand nombre d’outils développés par la communauté pour faciliter encore plus la gestion des sauvegardes avec des fonctionnalités avancées comme le PITR (Point In Time Recovery) ou la gestion de la rétention, notamment pg_back (sauvegarde logique), pgBackRest ou barman (sauvegarde physique).


Réplication

  • Réplication physique
    • instance complète
    • même architecture
  • Réplication logique (PG 10+)
    • table par table / colonne par colonne avec ou sans filtre (PG 15)
    • voire opération par opération
  • Asynchrone ou synchrone
  • Asymétrique

PostgreSQL dispose de la réplication depuis de nombreuses années.

Le premier type de réplication intégrée est la réplication physique. Il n’y a pas de granularité, c’est forcément l’instance complète (toutes les bases de données), et au niveau des fichiers de données. Cette réplication est asymétrique : un seul serveur primaire effectue lectures comme écritures, et les serveurs secondaires n’acceptent que des lectures.

Le deuxième type de réplication est bien plus récent vu qu’il a été ajouté en version 10. Il s’agit d’une réplication logique, où les données elles-mêmes sont répliquées. Cette réplication est elle aussi asymétrique. Cependant, ceci se configure table par table (et non pas au niveau de l’instance comme pour la réplication physique). Avec la version 15, il devient possible de choisir quelles colonnes sont publiées et de filtrer les lignes à publier.

La réplication logique n’est pas intéressante quand nous voulons un serveur sur lequel basculer en cas de problème sur le primaire. Dans ce cas, il vaut mieux utiliser la réplication physique. Par contre, c’est le bon type de réplication pour une réplication partielle ou pour une mise à jour de version majeure.

Dans les deux cas, les modifications sont transmises en asynchrone (avec un délai possible). Il est cependant possible de la configurer en synchrone pour tous les serveurs ou seulement certains.


Extensibilité

  • Extensions
    • CREATE EXTENSION monextension ;
    • nombreuses : contrib, packagées… selon provenance
    • notion de confiance (v13+)
    • dont langages de procédures stockées !
  • Système des hooks
  • Background workers

Faute de pouvoir intégrer toutes les fonctionnalités demandées dans PostgreSQL, ses développeurs se sont attachés à permettre à l’utilisateur d’étendre lui-même les fonctionnalités sans avoir à modifier le code principal.

Ils ont donc ajouté la possibilité de créer des extensions. Une extension contient un ensemble de types de données, de fonctions, d’opérateurs, etc. en un seul objet logique. Il suffit de créer ou de supprimer cet objet logique pour intégrer ou supprimer tous les objets qu’il contient. Cela facilite grandement l’installation et la désinstallation de nombreux objets. Les extensions peuvent être codées en différents langages, généralement en C ou en PL/SQL. Elles ont eu un grand succès.

La possibilité de développer des routines dans différents langages en est un exemple : perl, python, PHP, Ruby ou JavaScript sont disponibles. PL/pgSQL est lui-même une extension à proprement parler, toujours présente.

Autre exemple : la possibilité d’ajouter des types de données, des routines et des opérateurs a permis l’émergence de la couche spatiale de PostgreSQL (appelée PostGIS).

Les provenances, rôle et niveau de finition des extensions sont très variables. Certaines sont des utilitaires éprouvés fournis avec PostgreSQL (parmi les « contrib »). D’autres sont des utilitaires aussi complexes que PostGIS ou un langage de procédures stockées. Des éditeurs diffusent leur produit comme une extension plutôt que forker PostgreSQL (Citus, timescaledb…). Beaucoup d’extensions peuvent être installées très simplement depuis des paquets disponibles dans les dépôts habituels (de la distribution ou du PGDG), ou le site du concepteur. Certaines sont diffusées comme code source à compiler. Comme tout logiciel, il faut faire attention à en vérifier la source, la qualité, la réputation et la pérennité.

Une fois les binaires de l’extension en place sur le serveur, l’ordre CREATE EXTENSION suffit généralement dans la base cible, et les fonctionnalités sont immédiatement exploitables.

Les extensions sont habituellement installées par un administrateur (un utilisateur doté de l’attribut SUPERUSER). À partir de la version 13, certaines extensions sont déclarées de confiance trusted). Ces extensions peuvent être installées par un utilisateur standard (à condition qu’il dispose des droits de création dans la base et le ou les schémas concernés).

Les développeurs de PostgreSQL ont aussi ajouté des hooks pour accrocher du code à exécuter sur certains cas. Cela a permis entre autres de créer l’extension pg_stat_statements qui s’accroche au code de l’exécuteur de requêtes pour savoir quelles sont les requêtes exécutées et pour récupérer des statistiques sur ces requêtes.

Enfin, les background workers ont vu le jour. Ce sont des processus spécifiques lancés par le serveur PostgreSQL lors de son démarrage et stoppés lors de son arrêt. Cela a permis la création de PoWA (outil qui historise les statistiques sur les requêtes) et une amélioration très intéressante de pg_prewarm (sauvegarde du contenu du cache disque à l’arrêt de PostgreSQL, restauration du contenu au démarrage).

Des exemples d’extensions sont décrites dans nos modules Extensions PostgreSQL pour l’utilisateur, Extensions PostgreSQL pour la performance, Extensions PostgreSQL pour les DBA.


Sécurité

  • Fichier pg_hba.conf
  • Filtrage IP
  • Authentification interne (MD5, SCRAM-SHA-256)
  • Authentification externe (identd, LDAP, Kerberos…)
  • Support natif de SSL

Le filtrage des connexions se paramètre dans le fichier de configuration pg_hba.conf. Nous pouvons y définir quels utilisateurs (déclarés auprès de PostgreSQL) peuvent se connecter à quelles bases, et depuis quelles adresses IP.

L’authentification peut se baser sur des mots de passe chiffrés propres à PostgreSQL (md5 ou le plus récent et plus sécurisé scram-sha-256 en version 10), ou se baser sur une méthode externe (auprès de l’OS, ou notamment LDAP ou Kerberos qui couvre aussi Active Directory).

Si PostgreSQL interroge un service de mots de passe centralisé, vous devez toujours créer les rôle dans PostgreSQL. Seule l’option WITH PASSWORD est inutile. Pour créer, configurer mais aussi supprimer les rôles depuis un annuaire, l’outil ldap2pg existe.

L’authentification et le chiffrement de la connexion par SSL sont couverts.


Objets SQL

  • Instances
  • Objets globaux :
    • Bases
    • Rôles
    • Tablespaces
  • Objets locaux :
    • Schémas
    • Tables
    • Vues
    • Index
    • Routines

Le but de cette partie est de passer en revue les différents objets logiques maniés par un moteur de bases de données PostgreSQL.

Nous allons donc aborder la notion d’instance, les différents objets globaux et les objets locaux. Tous ne seront pas vus, mais le but est de donner une idée globale des objets et des fonctionnalités de PostgreSQL.


Organisation logique

Organisation logique d’une instance

Il est déjà important de bien comprendre une distinction entre les objets. Une instance est un ensemble de bases de données, de rôles et de tablespaces. Ces objets sont appelés des objets globaux parce qu’ils sont disponibles quelque soit la base de données de connexion. Chaque base de données contient ensuite des objets qui lui sont propres. Ils sont spécifiques à cette base de données et accessibles uniquement lorsque l’utilisateur est connecté à la base qui les contient. Il est donc possible de voir les bases comme des conteneurs hermétiques en dehors des objets globaux.


Instances

  • Une instance
    • un répertoire de données
    • un port TCP
    • une configuration
    • plusieurs bases de données
  • Plusieurs instances possibles sur un serveur

Une instance est un ensemble de bases de données. Après avoir installé PostgreSQL, il est nécessaire de créer un répertoire de données contenant un certain nombre de répertoires et de fichiers qui permettront à PostgreSQL de fonctionner de façon fiable. Le contenu de ce répertoire est créé initialement par la commande initdb. Ce répertoire stocke ensuite tous les objets des bases de données de l’instance, ainsi que leur contenu.

Chaque instance a sa propre configuration. Il n’est possible de lancer qu’un seul postmaster par instance, et ce dernier acceptera les connexions à partir d’un port TCP spécifique.

Il est possible d’avoir plusieurs instances sur le même serveur, physique ou virtuel. Dans ce cas, chaque instance aura son répertoire de données dédié et son port TCP dédié. Ceci est particulièrement utile quand l’on souhaite disposer de plusieurs versions de PostgreSQL sur le même serveur (par exemple pour tester une application sur ces différentes versions).


Rôles

  • Utilisateurs / Groupes
    • Utilisateur : Permet de se connecter
  • Différents attributs et droits

Une instance contient un ensemble de rôles. Certains sont prédéfinis et permettent de disposer de droits particuliers (lecture de fichier avec pg_read_server_files, annulation d’une requête avec pg_signal_backend, etc). Cependant, la majorité est composée de rôles créés pour permettre la connexion des utilisateurs.

Chaque rôle créé peut être utilisé pour se connecter à n’importe quelle base de l’instance, à condition que ce rôle en ait le droit. Ceci se gère directement avec l’attribution du droit LOGIN au rôle, et avec la configuration du fichier d’accès pg_hba.conf.

Chaque rôle peut être propriétaire d’objets, auquel cas il a tous les droits sur ces objets. Pour les objets dont il n’est pas propriétaire, il peut se voir donner des droits, en lecture, écriture, exécution, etc par le propriétaire.

Nous parlons aussi d’utilisateurs et de groupes. Un utilisateur est un rôle qui a la possibilité de se connecter aux bases alors qu’un groupe ne le peut pas. Un groupe sert principalement à gérer plus simplement les droits d’accès aux objets.


Tablespaces

  • Répertoire physique contenant les fichiers de données de l’instance
  • Une base peut
    • se trouver sur un seul tablespace
    • être répartie sur plusieurs tablespaces
  • Permet de gérer l’espace disque et les performances
  • Pas de quota

Toutes les données des tables, vues matérialisées et index sont stockées dans le répertoire de données principal. Cependant, il est possible de stocker des données ailleurs que dans ce répertoire. Il faut pour cela créer un tablespace. Un tablespace est tout simplement la déclaration d’un autre répertoire de données utilisable par PostgreSQL pour y stocker des données :

CREATE TABLESPACE chaud LOCATION '/SSD/tbl/chaud';

Il est possible d’avoir un tablespace par défaut pour une base de données, auquel cas tous les objets logiques créés dans cette base seront enregistrés physiquement dans le répertoire lié à ce tablespace. Il est aussi possible de créer des objets en indiquant spécifiquement un tablespace, ou de les déplacer d’un tablespace à un autre. Un objet spécifique ne peut appartenir qu’à un seul tablespace (autrement dit, un index ne pourra pas être enregistré sur deux tablespaces). Cependant, pour les objets partitionnés, le choix du tablespace peut se faire partition par partition.

Le but des tablespaces est de fournir une solution à des problèmes d’espace disque ou de performances. Si la partition où est stocké le répertoire des données principal se remplit fortement, il est possible de créer un tablespace dans une autre partition et donc d’utiliser l’espace disque de cette partition. Si de nouveaux disques plus rapides sont à disposition, il est possible de placer les objets fréquemment utilisés sur le tablespace contenant les disques rapides. Si des disques SSD sont à disposition, il est très intéressant d’y placer les index, les fichiers de tri temporaires, des tables de travail…

Par contre, contrairement à d’autres moteurs de bases de données, PostgreSQL n’a pas de notion de quotas. Les tablespaces ne peuvent donc pas être utilisés pour contraindre l’espace disque utilisé par certaines applications ou certains rôles.


Bases

  • Conteneur hermétique
  • Un rôle ne se connecte pas à une instance
    • il se connecte forcément à une base
  • Une fois connecté, il ne voit que les objets de cette base
    • contournement : foreign data wrappers, dblink

Une base de données est un conteneur hermétique. En dehors des objets globaux, le rôle connecté à une base de données ne voit et ne peut interagir qu’avec les objets contenus dans cette base. De même, il ne voit pas les objets locaux des autres bases. Néanmoins, il est possible de lui donner le droit d’accéder à certains objets d’une autre base (de la même instance ou d’une autre instance) en utilisant les Foreign Data Wrappers (postgres_fdw) ou l’extension dblink.

Un rôle ne se connecte pas à l’instance. Il se connecte forcément à une base spécifique.


Schémas

  • Espace de noms
  • Sous-ensemble de la base
  • Non lié à un utilisateur
  • Résolution des objets : search_path
  • pg_catalog, information_schema
    • pour catalogues système (lecture seule !)

Les schémas sont des espaces de noms à l’intérieur d’une base de données permettant :

  • de grouper logiquement les objets d’une base de données ;
  • de séparer les utilisateurs entre eux ;
  • de contrôler plus efficacement les accès aux données ;
  • d’éviter les conflits de noms dans les grosses bases de données.

Un schéma n’a à priori aucun lien avec un utilisateur donné.

Un schéma est un espace logique sans lien avec les emplacements physiques des données (ne pas confondre avec les tablespaces).

Un utilisateur peut avoir accès à tous les schémas ou à un sous-ensemble, tout dépend des droits dont il dispose. Depuis la version 15, un nouvel utilisateur n’a le droit de créer d’objet nulle part. Dans les versions précédentes, il avait accès au schéma public de chaque base et pouvait y créer des objets.

Lorsque le schéma n’est pas indiqué explicitement pour les objets d’une requête, PostgreSQL recherche les objets dans les schémas listés par le paramètre search_path valable pour la session en cours .

Voici un exemple d’utilisation des schémas :

-- Création de deux schémas
CREATE SCHEMA s1;
CREATE SCHEMA s2;

-- Création d'une table sans spécification du schéma
CREATE TABLE t1 (id integer);

-- Comme le montre la méta-commande \d, la table est créée dans le schéma public
postgres=# \d
                List of relations
 Schema |       Name        |   Type   |  Owner
--------+-------------------+----------+----------
 public | capitaines        | table    | postgres
 public | capitaines_id_seq | sequence | postgres
 public | t1                | table    | postgres
-- Ceci est dû à la configuration par défaut du paramètre search_path
-- modification du search_path
SET search_path TO s1;

-- création d'une nouvelle table sans spécification du schéma
CREATE TABLE t2 (id integer);

-- Cette fois, le schéma de la nouvelle table est s1
-- car la configuration du search_path est à s1
-- Nous pouvons aussi remarquer que les tables capitaines et s1
-- ne sont plus affichées
-- Ceci est dû au fait que le search_path ne contient que le schéma s1 et
-- n'affiche donc que les objets de ce schéma.
postgres=# \d
        List of relations
 Schema | Name | Type  |  Owner
--------+------+-------+----------
 s1     | t2   | table | postgres
-- Nouvelle modification du search_path
SET search_path TO s1, public;

-- Cette fois, les deux tables apparaissent
postgres=# \d
                List of relations
 Schema |       Name        |   Type   |  Owner
--------+-------------------+----------+----------
 public | capitaines        | table    | postgres
 public | capitaines_id_seq | sequence | postgres
 public | t1                | table    | postgres
 s1     | t2                | table    | postgres
-- Création d'une nouvelle table en spécifiant cette fois le schéma
CREATE TABLE s2.t3 (id integer);

-- changement du search_path pour voir la table
SET search_path TO s1, s2, public;

-- La table apparaît bien, et le schéma d'appartenance est bien s2
postgres=# \d
                List of relations
 Schema |       Name        |   Type   |  Owner
--------+-------------------+----------+----------
 public | capitaines        | table    | postgres
 public | capitaines_id_seq | sequence | postgres
 public | t1                | table    | postgres
 s1     | t2                | table    | postgres
 s2     | t3                | table    | postgres
-- Création d'une nouvelle table en spécifiant cette fois le schéma
-- attention, cette table a un nom déjà utilisé par une autre table
CREATE TABLE s2.t2 (id integer);

-- La création se passe bien car, même si le nom de la table est identique,
-- le schéma est différent
-- Par contre, \d ne montre que la première occurence de la table
-- ici, nous ne voyons t2 que dans s1
postgres=# \d
                List of relations
 Schema |       Name        |   Type   |  Owner
--------+-------------------+----------+----------
 public | capitaines        | table    | postgres
 public | capitaines_id_seq | sequence | postgres
 public | t1                | table    | postgres
 s1     | t2                | table    | postgres
 s2     | t3                | table    | postgres
-- Changeons le search_path pour placer s2 avant s1
SET search_path TO s2, s1, public;

-- Maintenant, la seule table t2 affichée est celle du schéma s2
postgres=# \d
                List of relations
 Schema |       Name        |   Type   |  Owner
--------+-------------------+----------+----------
 public | capitaines        | table    | postgres
 public | capitaines_id_seq | sequence | postgres
 public | t1                | table    | postgres
 s2     | t2                | table    | postgres
 s2     | t3                | table    | postgres

Tous ces exemples se basent sur des ordres de création de table. Cependant, le comportement serait identique sur d’autres types de commande (SELECT, INSERT, etc) et sur d’autres types d’objets locaux.

Pour des raisons de sécurité, il est très fortement conseillé de laisser le schéma public en toute fin du search_path. En effet, avant la version 15, s’il est placé au début, comme tout le monde avait le droit de créer des objets dans public, quelqu’un de mal intentionné pouvait placer un objet dans le schéma public pour servir de proxy à un autre objet d’un schéma situé après public. Même si la version 15 élimine ce risque, il reste la bonne pratique d’adapter le search_path pour placer les schémas applicatifs en premier.

Les schémas pg_catalog et information_schema contiennent des tables utilitaires (« catalogues système ») et des vues. Les catalogues système représentent l’endroit où une base de données relationnelle stocke les métadonnées des schémas, telles que les informations sur les tables, et les colonnes, et des données de suivi interne. Dans PostgreSQL, ce sont de simples tables. Un simple utilisateur lit fréquemment ces tables, plus ou moins directement, mais n’a aucune raison d’y modifier des données. Toutes les opérations habituelles pour un utilisateur ou administrateur sont disponibles sous la forme de commandes SQL.

Ne modifiez jamais directement les tables et vues système dans les schémas pg_catalog et information_schema ; n’y ajoutez ni n’y effacez jamais rien !

Même si cela est techniquement possible, seules des exceptions particulièrement ésotériques peuvent justifier une modification directe des tables systèmes (par exemple, une correction de vue système, suite à un bug corrigé dans une version mineure). Ces tables n’apparaissent d’ailleurs pas dans une sauvegarde logique (pg_dump).


Tables

Par défaut, une table est :

  • Permanente
    • si temporaire, vivra le temps de la session (ou de la transaction)
  • Journalisée
    • si unlogged, perdue en cas de crash, pas de réplication
  • Non partitionnée
    • partitionnement possible par intervalle, valeur ou hachage

Par défaut, les tables sont permanentes, journalisées et non partitionnées.

Il est possible de créer des tables temporaires (CREATE TEMPORARY TABLE). Celles-ci ne sont visibles que par la session qui les a créées et seront supprimées par défaut à la fin de cette session. Il est aussi possible de les supprimer automatiquement à la fin de la transaction qui les a créées. Il n’existe pas dans PostgreSQL de notion de table temporaire globale. Cependant, une extension existe pour combler leur absence.

Pour des raisons de performance, il est possible de créer une table non journalisée (CREATE UNLOGGED TABLE). La définition de la table est journalisée mais pas son contenu. De ce fait, en cas de crash, il est impossible de dire si la table est corrompue ou non, et donc, au redémarrage du serveur, PostgreSQL vide la table de tout contenu. De plus, n’étant pas journalisée, la table n’est pas présente dans les sauvegardes PITR, ni repliquée vers d’éventuels serveurs secondaires.

Enfin, depuis la version 10, il est possible de partitionner les tables suivant un certain type de partitionnement : par intervalle, par valeur ou par hachage.


Vues

  • Masquer la complexité
    • structure : interface cohérente vers les données, même si les tables évoluent
    • sécurité : contrôler l’accès aux données de manière sélective
  • Vues matérialisées
    • à rafraîchir à une certaine fréquence

Le but des vues est de masquer une complexité, qu’elle soit du côté de la structure de la base ou de l’organisation des accès. Dans le premier cas, elles permettent de fournir un accès qui ne change pas même si les structures des tables évoluent. Dans le second cas, elles permettent l’accès à seulement certaines colonnes ou certaines lignes. De plus, les vues étant exécutées avec les mêmes droits que l’utilisateur qui les a créées, cela permet un changement temporaire des droits d’accès très appréciable dans certains cas.

Voici un exemple d’utilisation :

SET search_path TO public;

-- création de l'utilisateur guillaume
-- il n'aura pas accès à la table capitaines
-- par contre, il aura accès à la vue capitaines_anon
CREATE ROLE guillaume LOGIN;

-- ajoutons une colonne à la table capitaines
-- et ajoutons-y des données
ALTER TABLE capitaines ADD COLUMN num_cartecredit text;
INSERT INTO capitaines (nom, age, num_cartecredit)
  VALUES ('Robert Surcouf', 20, '1234567890123456');

-- création de la vue
CREATE VIEW capitaines_anon AS
  SELECT nom, age, substring(num_cartecredit, 0, 10) || '******' AS num_cc_anon
  FROM capitaines;

-- ajout du droit de lecture à l'utilisateur guillaume
GRANT SELECT ON TABLE capitaines_anon TO guillaume;

-- connexion en tant qu'utilisateur guillaume
SET ROLE TO guillaume;

-- vérification qu'on lit bien la vue mais pas la table
SELECT * FROM capitaines_anon WHERE nom LIKE '%Surcouf';
      nom       | age |   num_cc_anon
----------------+-----+-----------------
 Robert Surcouf |  20 | 123456789******
-- tentative de lecture directe de la table
SELECT * FROM capitaines;
ERROR:  permission denied for relation capitaines

Il est possible de modifier une vue en lui ajoutant des colonnes à la fin, au lieu de devoir les détruire et recréer (ainsi que toutes les vues qui en dépendent, ce qui peut être fastidieux).

Par exemple :

SET ROLE postgres;

CREATE OR REPLACE VIEW capitaines_anon AS SELECT
  nom,age,substring(num_cartecredit,0,10)||'******' AS num_cc_anon,
  md5(substring(num_cartecredit,0,10)) AS num_md5_cc
  FROM capitaines;
SELECT * FROM capitaines_anon WHERE nom LIKE '%Surcouf';
      nom       | age |   num_cc_anon   |            num_md5_cc
----------------+-----+-----------------+----------------------------------
 Robert Surcouf |  20 | 123456789****** | 25f9e794323b453885f5181f1b624d0b

Nous pouvons aussi modifier les données au travers des vues simples, sans ajout de code et de trigger :

UPDATE capitaines_anon SET nom = 'Nicolas Surcouf' WHERE nom = 'Robert Surcouf';
SELECT * from capitaines_anon WHERE nom LIKE '%Surcouf';
       nom       | age |   num_cc_anon   |            num_md5_cc
-----------------+-----+-----------------+----------------------------------
 Nicolas Surcouf |  20 | 123456789****** | 25f9e794323b453885f5181f1b624d0b
UPDATE capitaines_anon SET num_cc_anon = '123456789xxxxxx'
  WHERE nom = 'Nicolas Surcouf';
ERROR:  cannot update column "num_cc_anon" of view "capitaines_anon"
DETAIL:  View columns that are not columns of their base relation
         are not updatable.

PostgreSQL gère le support natif des vues matérialisées (CREATE MATERIALIZED VIEW nom_vue_mat AS SELECT …). Les vues matérialisées sont des vues dont le contenu est figé sur disque, permettant de ne pas recalculer leur contenu à chaque appel. De plus, il est possible de les indexer pour accélérer leur consultation. Il faut cependant faire attention à ce que leur contenu reste synchrone avec le reste des données.

Les vues matérialisées ne sont pas mises à jour automatiquement, il faut demander explicitement le rafraîchissement (REFRESH MATERIALIZED VIEW). Avec la clause CONCURRENTLY, s’il y a un index d’unicité, le rafraîchissement ne bloque pas les sessions lisant en même temps les données d’une vue matérialisée.

-- Suppression de la vue
DROP VIEW capitaines_anon;

-- Création de la vue matérialisée
CREATE MATERIALIZED VIEW capitaines_anon AS
  SELECT nom,
    age,
    substring(num_cartecredit, 0, 10) || '******' AS num_cc_anon
  FROM capitaines;
-- Les données sont bien dans la vue matérialisée
SELECT * FROM capitaines_anon WHERE nom LIKE '%Surcouf';
       nom       | age |   num_cc_anon
-----------------+-----+-----------------
 Nicolas Surcouf |  20 | 123456789******
-- Mise à jour d'une ligne de la table
-- Cette mise à jour est bien effectuée, mais la vue matérialisée
-- n'est pas impactée
UPDATE capitaines SET nom = 'Robert Surcouf' WHERE nom = 'Nicolas Surcouf';
SELECT * FROM capitaines WHERE nom LIKE '%Surcouf';
 id |      nom       | age | num_cartecredit
----+----------------+-----+------------------
  1 | Robert Surcouf |  20 | 1234567890123456
SELECT * FROM capitaines_anon WHERE nom LIKE '%Surcouf';
       nom       | age |   num_cc_anon
-----------------+-----+-----------------
 Nicolas Surcouf |  20 | 123456789******
-- Le résultat est le même mais le plan montre bien que PostgreSQL ne passe
-- plus par la table mais par la vue matérialisée :
EXPLAIN SELECT * FROM capitaines_anon WHERE nom LIKE '%Surcouf';
                         QUERY PLAN
-----------------------------------------------------------------
 Seq Scan on capitaines_anon  (cost=0.00..20.62 rows=1 width=68)
   Filter: (nom ~~ '%Surcouf'::text)
-- Après un rafraîchissement explicite de la vue matérialisée,
-- cette dernière contient bien les bonnes données
REFRESH MATERIALIZED VIEW capitaines_anon;

SELECT * FROM capitaines_anon WHERE nom LIKE '%Surcouf';
      nom       | age |   num_cc_anon
----------------+-----+-----------------
 Robert Surcouf |  20 | 123456789******
-- Pour rafraîchir la vue matérialisée sans bloquer les autres sessions :

REFRESH MATERIALIZED VIEW CONCURRENTLY capitaines_anon;
ERROR:  cannot refresh materialized view "public.capitaines_anon" concurrently
HINT:  Create a unique index with no WHERE clause on one or more columns
       of the materialized view.
-- En effet, il faut un index d'unicité pour faire un rafraîchissement
-- sans bloquer les autres sessions.
CREATE UNIQUE INDEX ON capitaines_anon(nom);
REFRESH MATERIALIZED VIEW CONCURRENTLY capitaines_anon;

Index

  • Algorithmes supportés
    • B-tree (par défaut)
    • Hash
    • GiST / SP-GiST
    • GIN
    • BRIN
    • Bloom
  • Type
    • Mono ou multicolonne
    • Partiel
    • Fonctionnel
    • Couvrant

PostgreSQL propose plusieurs algorithmes d’index.

Pour une indexation standard, nous utilisons en général un index B-tree, de par ses nombreuses possibilités et ses très bonnes performances.

Les index hash sont peu utilisés, essentiellement dans la comparaison d’égalité de grandes chaînes de caractères.

Moins simples d’abord, les index plus spécifiques (GIN, GIST) sont spécialisés pour les grands volumes de données complexes et multidimensionnelles : indexation textuelle, géométrique, géographique, ou de tableaux de données par exemple.

Les index BRIN sont des index très compacts destinés aux grandes tables où les données sont fortement corrélées par rapport à leur emplacement physique sur les disques.

Les index bloom sont des index probabilistes visant à indexer de nombreuses colonnes interrogées simultanément. Ils nécessitent l’ajout d’une extension (nommée bloom). Contrairement aux index btree, les index bloom ne dépendent pas de l’ordre des colonnes.

Le module pg_trgm permet l’utilisation d’index dans des cas habituellement impossibles, comme les expressions rationnelles et les LIKE '%...%'.

Généralement, l’indexation porte sur la valeur d’une ou plusieurs colonnes. Il est néanmoins possible de n’indexer qu’une partie des lignes (index partiel) ou le résultat d’une fonction sur une ou plusieurs colonnes en paramètre. Enfin, il est aussi possible de modifier les index de certaines contraintes (unicité et clé primaire) pour inclure des colonnes supplémentaires.

Plus d’informations :


Types de données

  • Types de base
    • natif : int, float
    • standard SQL : numeric, char, varchar, date, time, timestamp, bool
  • Type complexe
    • tableau
    • JSON (jsonb), XML
    • vecteur (données LLM, FTS)
  • Types métier
    • réseau, géométrique, etc.
  • Types créés par les utilisateurs
    • structure SQL, C, Domaine, Enum

PostgreSQL dispose d’un grand nombre de types de base, certains natifs (comme la famille des integer et celle des float), et certains issus de la norme SQL (numeric, char, varchar, date, time, timestamp, bool).

Il dispose aussi de types plus complexes. Les tableaux (array) permettent de lister un ensemble de valeurs discontinues. Les intervalles (range) permettent d’indiquer toutes les valeurs comprises entre une valeur de début et une valeur de fin. Ces deux types dépendent évidemment d’un type de base : tableau d’entiers, intervalle de dates, etc. Existent aussi les types complexes, les données XML et JSON (préférer le type optimisé jsonb).

PostgreSQL sait travailler avec des vecteurs pour des calculs avancé. De base, le type tsvector permet la recherche plein texte, avec calcul de proximité de mots dans un texte, pondération des résultats, etc. L’extension pgvector permet de stocker et d’indexer des vecteurs utilisé par les algorithmes LLM implémentés dans les IA génératives.

Enfin, il existe des types métiers ayant trait principalement au réseau (adresse IP, masque réseau), à la géométrie (point, ligne, boite). Certains sont apportés par des extensions.

Tout ce qui vient d’être décrit est natif. Il est cependant possible de créer ses propres types de données, soit en SQL soit en C. Les possibilités et les performances ne sont évidemment pas les mêmes.

Voici comment créer un type en SQL :

CREATE TYPE serveur AS (
  nom             text,
  adresse_ip      inet,
  administrateur  text
);

Ce type de données va pouvoir être utilisé dans tous les objets SQL habituels : table, routine, opérateur (pour redéfinir l’opérateur + par exemple), fonction d’agrégat, contrainte, etc.

Voici un exemple de création d’un opérateur :

CREATE OPERATOR + (
    leftarg = stock,
    rightarg = stock,
    procedure = stock_fusion,
    commutator = +
);

(Il faut au préalable avoir défini le type stock et la fonction stock_fusion.)

Il est aussi possible de définir des domaines. Ce sont des types créés par les utilisateurs à partir d’un type de base et en lui ajoutant des contraintes supplémentaires.

En voici un exemple :

CREATE DOMAIN code_postal_francais AS text CHECK (value ~ '^\d{5}$');
ALTER TABLE capitaines ADD COLUMN cp code_postal_francais;
UPDATE capitaines SET cp = '35400' WHERE nom LIKE '%Surcouf';
UPDATE capitaines SET cp = '1420' WHERE nom = 'Haddock';
ERROR:  value for domain code_postal_francais violates check constraint
        "code_postal_francais_check"
UPDATE capitaines SET cp = '01420' WHERE nom = 'Haddock';
SELECT * FROM capitaines;
 id |      nom       | age | num_cartecredit  |  cp
----+----------------+-----+------------------+-------
  1 | Robert Surcouf |  20 | 1234567890123456 | 35400
  1 | Haddock        |  35 |                  | 01420

Les domaines permettent d’intégrer la déclaration des contraintes à la déclaration d’un type, et donc de simplifier la maintenance de l’application si ce type peut être utilisé dans plusieurs tables : si la définition du code postal est insuffisante pour une évolution de l’application, il est possible de la modifier par un ALTER DOMAIN, et définir de nouvelles contraintes sur le domaine. Ces contraintes seront vérifiées sur l’ensemble des champs ayant le domaine comme type avant que la nouvelle version du type ne soit considérée comme valide.

Le défaut par rapport à des contraintes CHECK classiques sur une table est que l’information ne se trouvant pas dans la table, les contraintes sont plus difficiles à lister sur une table.

Enfin, il existe aussi les enums. Ce sont des types créés par les utilisateurs composés d’une liste ordonnée de chaînes de caractères.

En voici un exemple :

CREATE TYPE jour_semaine
  AS ENUM ('Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi',
  'Samedi', 'Dimanche');

ALTER TABLE capitaines ADD COLUMN jour_sortie jour_semaine;

UPDATE capitaines SET jour_sortie = 'Mardi' WHERE nom LIKE '%Surcouf';
UPDATE capitaines SET jour_sortie = 'Samedi' WHERE nom LIKE 'Haddock';

SELECT * FROM capitaines WHERE jour_sortie >= 'Jeudi';
 id |   nom   | age | num_cartecredit | cp | jour_sortie
----+---------+-----+-----------------+----+-------------
  1 | Haddock |  35 |                 |    | Samedi

Les enums permettent de déclarer une liste de valeurs statiques dans le dictionnaire de données plutôt que dans une table externe sur laquelle il faudrait rajouter des jointures : dans l’exemple, nous aurions pu créer une table jour_de_la_semaine, et stocker la clé associée dans planning. Nous aurions pu tout aussi bien positionner une contrainte CHECK, mais nous n’aurions plus eu une liste ordonnée.

Exemple d’utilisation :


Contraintes

  • CHECK
    • prix > 0
  • NOT NULL
    • id_client NOT NULL
  • Unicité
    • id_client UNIQUE
  • Clés primaires
    • UNIQUE NOT NULL ==> PRIMARY KEY (id_client)
  • Clés étrangères
    • produit_id REFERENCES produits(id_produit)
  • EXCLUDE
    • EXCLUDE USING gist (room WITH =, during WITH &&)

Les contraintes sont la garantie de conserver des données de qualité ! Elles permettent une vérification qualitative des données, beaucoup plus fine qu’en définissant uniquement un type de données.

Les exemples ci-dessus reprennent :

  • un prix qui doit être strictement positif ;
  • un identifiant qui ne doit pas être vide (sinon des jointures filtreraient des lignes) ;
  • une valeur qui doit être unique (comme des numéros de clients ou de facture) ;
  • une clé primaire (unique non nulle), qui permet d’identifier précisément une ligne ;
  • une clé étrangère vers la clé primaire d’une autre table (là encore pour garantir l’intégrité des jointures) ;
  • une contrainte d’exclusion interdisant que deux plages temporelles se recouvrent dans la réservation de la même salle de réunion.

Les contraintes d’exclusion permettent un test sur plusieurs colonnes avec différents opérateurs (et non uniquement l’égalité, comme dans le cas d’une contrainte unique, qui n’est qu’une contrainte d’exclusion très spécialisée). Si le test se révèle positif, la ligne est refusée.

Une contrainte peut porter sur plusieurs champs et un champ peut être impliqué dans plusieurs contraintes :

CREATE TABLE commandes (
    no_commande     varchar(16) CHECK (no_commande ~ '^[A-Z0-9]*$'),
    id_entite_commerciale int REFERENCES entites_commerciales,
    id_client       int       REFERENCES clients,
    date_commande   date      NOT NULL,
    date_livraison  date      CHECK (date_livraison >= date_commande),
    PRIMARY KEY (no_commande, id_entite_commerciale)
);
\d commandes
                                Table « public.commandes »
        Colonne        |         Type          | … | NULL-able | Par défaut 
-----------------------+-----------------------+---+-----------+------------
 no_commande           | character varying(16) |   | not null  | 
 id_entite_commerciale | integer               |   | not null  | 
 id_client             | integer               |   |           | 
 date_commande         | date                  |   | not null  | 
 date_livraison        | date                  |   |           | 
Index :
    "commandes_pkey" PRIMARY KEY, btree (no_commande, id_entite_commerciale)
Contraintes de vérification :
    "commandes_check" CHECK (date_livraison >= date_commande)
    "commandes_no_commande_check" CHECK (no_commande::text ~ '^[A-Z0-9]*$'::text)
Contraintes de clés étrangères :
    "commandes_id_client_fkey" FOREIGN KEY (id_client) REFERENCES clients(id_client)
    "commandes_id_entite_commerciale_fkey" FOREIGN KEY (id_entite_commerciale) REFERENCES entites_commerciales(id_entite_commerciale)

Les contraintes doivent être vues comme la dernière ligne de défense de votre application face aux bugs. En effet, le code d’une application change beaucoup plus souvent que le schéma, et les données survivent souvent à l’application, qui peut être réécrite entretemps. Quoi qu’il se passe, des contraintes judicieuses garantissent qu’il n’y aura pas d’incohérence logique dans la base.

Si elles sont gênantes pour le développeur (car elles imposent un ordre d’insertion ou de mise à jour), il faut se rappeler que les contraintes peuvent être « débrayées » le temps d’une transaction :

BEGIN;
SET CONSTRAINTS ALL DEFERRED ;

COMMIT ;

Les contraintes ne seront validées qu’au COMMIT.

Sur le sujet, voir par exemple Constraints: a Developer’s Secret Weapon de Will Leinweber (pgDay Paris 2018) (slides, vidéo).

Du point de vue des performances, les contraintes permettent au planificateur d’optimiser les requêtes. Par exemple, le planificateur sait ne pas prendre en compte certaines jointures, notamment grâce à l’existence d’une contrainte d’unicité. (Sur ce point, la version 15 améliore les contraintes d’unicité en permettant de choisir si la valeur NULL est considérée comme unique ou pas. Par défaut et historiquement, une valeur NULL n’étant pas égale à une valeur NULL, les valeurs NULL sont considérées distinctes, et donc on peut avoir plusieurs valeurs NULL dans une colonne ayant une contrainte d’unicité.)


Colonnes à valeur générée

  • Valeur calculée à l’insertion
  • DEFAULT
  • Identité
    • GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY
  • Expression
    • GENERATED ALWAYS AS ( generation_expr ) STORED

Une colonne a par défaut la valeur NULL si aucune valeur n’est fournie lors de l’insertion de la ligne. Il existe néanmoins trois cas où le moteur peut substituer une autre valeur.

Le plus connu correspond à la clause DEFAULT. Dans ce cas, la valeur insérée correspond à la valeur indiquée avec cette clause si aucune valeur n’est indiquée pour la colonne. Si une valeur est précisée, cette valeur surcharge la valeur par défaut. L’exemple suivant montre cela :

CREATE TABLE t2 (c1 integer, c2 integer, c3 integer DEFAULT 10);
INSERT INTO t2 (c1, c2, c3) VALUES (1, 2, 3);
INSERT INTO t2 (c1) VALUES (2);
SELECT * FROM t2;
 c1 | c2 | c3
----+----+----
  1 |  2 |  3
  2 |    | 10

La clause DEFAULT ne peut pas être utilisée avec des clauses complexes, notamment des clauses comprenant des requêtes.

Pour aller un peu plus loin, à partir de PostgreSQL 12, il est possible d’utiliser GENERATED ALWAYS AS ( expression ) STORED. Cela permet d’avoir une valeur calculée pour la colonne, valeur qui ne peut pas être surchargée, ni à l’insertion, ni à la mise à jour (mais qui est bien stockée sur le disque).

Comme exemple, nous allons reprendre la table capitaines et lui ajouter une colonne ayant comme valeur la version modifiée du numéro de carte de crédit :

ALTER TABLE capitaines
  ADD COLUMN num_cc_anon text
  GENERATED ALWAYS AS (substring(num_cartecredit, 0, 10) || '******') STORED;

SELECT nom, num_cartecredit, num_cc_anon FROM capitaines;
      nom       | num_cartecredit  |   num_cc_anon
----------------+------------------+-----------------
 Robert Surcouf | 1234567890123456 | 123456789******
 Haddock        |                  |
INSERT INTO capitaines VALUES
  (2, 'Joseph Pradere-Niquet', 40, '9876543210987654', '44000', 'Lundi', 'test');
ERROR:  cannot insert into column "num_cc_anon"
DETAIL:  Column "num_cc_anon" is a generated column.
INSERT INTO capitaines VALUES
  (2, 'Joseph Pradere-Niquet', 40, '9876543210987654', '44000', 'Lundi');
SELECT nom, num_cartecredit, num_cc_anon FROM capitaines;
          nom          | num_cartecredit  |   num_cc_anon
-----------------------+------------------+-----------------
 Robert Surcouf        | 1234567890123456 | 123456789******
 Haddock               |                  |
 Joseph Pradere-Niquet | 9876543210987654 | 987654321******

Enfin, GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY permet d’obtenir une colonne d’identité, bien meilleure que ce que le pseudo-type serial propose. Si ALWAYS est indiqué, la valeur n’est pas modifiable.

ALTER TABLE capitaines
  ADD COLUMN id2 integer GENERATED ALWAYS AS IDENTITY;
SELECT nom, id2 FROM capitaines;
          nom          | id2
-----------------------+-----
 Robert Surcouf        |   1
 Haddock               |   2
 Joseph Pradere-Niquet |   3
INSERT INTO capitaines (nom) VALUES ('Tom Souville');
SELECT nom, id2 FROM capitaines;
          nom          | id2
-----------------------+-----
 Robert Surcouf        |   1
 Haddock               |   2
 Joseph Pradere-Niquet |   3
 Tom Souville          |   4

Le type serial est remplacé par le type integer et une séquence comme le montre l’exemple suivant. C’est un problème dans la mesure où la déclaration qui est faite à la création de la table produit un résultat différent en base et donc dans les exports de données.

CREATE TABLE tserial(s serial);
                            Table "public.tserial"
 Column |  Type   | Collation | Nullable |              Default
--------+---------+-----------+----------+------------------------------------
 s      | integer |           | not null | nextval('tserial_s_seq'::regclass)

Langages

  • Procédures & fonctions en différents langages
  • Par défaut : SQL, C et PL/pgSQL
  • Extensions officielles : Perl, Python
  • Mais aussi Java, Ruby, Javascript…
  • Intérêts : fonctionnalités, performances

Les langages officiellement supportés par le projet sont :

Voici une liste non exhaustive des langages procéduraux disponibles, à différents degrés de maturité :

Le wiki PostgreSQL contient un tableau des langages supportés.

Pour qu’un langage soit utilisable, il doit être activé au niveau de la base où il sera utilisé. Les trois langages activés par défaut sont le C, le SQL et le PL/pgSQL. Les autres doivent être ajoutés à partir des paquets de la distribution ou du PGDG, ou compilés à la main, puis l’extension installée dans la base :

CREATE EXTENSION plperl ;
CREATE EXTENSION plpython3u ;
-- etc.

Ces fonctions peuvent être utilisées dans des index fonctionnels et des triggers comme toute fonction SQL ou PL/pgSQL.

Chaque langage a ses avantages et inconvénients. Par exemple, PL/pgSQL est très simple à apprendre mais n’est pas performant quand il s’agit de traiter des chaînes de caractères. Pour ce traitement, il est souvent préférable d’utiliser PL/Perl, voire PL/Python. Évidemment, une routine en C aura les meilleures performances mais sera beaucoup moins facile à coder et à maintenir, et ses bugs seront susceptibles de provoquer un plantage du serveur.

Par ailleurs, les procédures peuvent s’appeler les unes les autres quel que soit le langage. S’ajoute l’intérêt de ne pas avoir à réécrire en PL/pgSQL des fonctions existantes dans d’autres langages ou d’accéder à des modules bien établis de ces langages.


Fonctions & procédures

  • Fonction
    • renvoie une ou plusieurs valeurs
    • SETOF ou TABLE pour plusieurs lignes
  • Procédure (v11+)
    • ne renvoie rien
    • peut gérer le transactionnel dans certains cas

Historiquement, PostgreSQL ne proposait que l’écriture de fonctions. Depuis la version 11, il est aussi possible de créer des procédures. Le terme « routine » est utilisé pour signifier procédure ou fonction.

Une fonction renvoie une donnée. Cette donnée peut comporter une ou plusieurs colonnes. Elle peut aussi avoir plusieurs lignes dans le cas d’une fonction SETOF ou TABLE.

Une procédure ne renvoie rien. Elle a cependant un gros avantage par rapport aux fonctions dans le fait qu’elle peut gérer le transactionnel. Elle peut valider ou annuler la transaction en cours. Dans ce cas, une nouvelle transaction est ouverte immédiatement après la fin de la transaction précédente.


Opérateurs

  • Dépend d’un ou deux types de données
  • Utilise une fonction prédéfinie :
CREATE OPERATOR //
  (FUNCTION=division0,
  LEFTARG=integer,
  RIGHTARG=integer);

Il est possible de créer de nouveaux opérateurs sur un type de base ou sur un type utilisateur. Un opérateur exécute une fonction, soit à un argument pour un opérateur unitaire, soit à deux arguments pour un opérateur binaire.

Voici un exemple d’opérateur acceptant une division par zéro sans erreur :

-- définissons une fonction de division en PL/pgSQL
CREATE FUNCTION division0 (p1 integer, p2 integer) RETURNS integer
LANGUAGE plpgsql
AS $$
BEGIN
  IF p2 = 0 THEN
      RETURN NULL;
  END IF;

  RETURN p1 / p2;
END
$$;

-- créons l'opérateur
CREATE OPERATOR // (FUNCTION = division0, LEFTARG = integer, RIGHTARG = integer);

-- une division normale se passe bien

SELECT 10/5;
 ?column?
----------
        2
SELECT 10//5;
 ?column?
----------
        2
-- une division par 0 ramène une erreur avec l'opérateur natif
SELECT 10/0;
ERROR:  division by zero
-- une division par 0 renvoie NULL avec notre opérateur
SELECT 10//0;
 ?column?
----------

(1 row)

Triggers

  • Opérations : INSERT, UPDATE, DELETE, TRUNCATE
  • Trigger sur :
    • une table, voire une colonne, et/ou avec condition
    • une vue
    • une opération DDL
    • une connexion
  • Tables de transition
  • Effet sur :
    • l’ensemble de la requête (FOR STATEMENT)
    • chaque ligne impactée (FOR EACH ROW)
  • N’importe quel langage supporté

Les triggers peuvent être exécutés avant (BEFORE) ou après (AFTER) une opération.

Il est possible de les déclencher pour chaque ligne impactée (FOR EACH ROW) ou une seule fois pour l’ensemble de la requête (FOR STATEMENT). Dans le premier cas, il est possible d’accéder à la ligne impactée (ancienne et nouvelle version). Dans le deuxième cas, des tables de transition donnent à l’utilisateur une vision des lignes avant et après modification.

Par ailleurs, les triggers peuvent être écrits dans n’importe lequel des langages de routine supportés par PostgreSQL (C, PL/pgSQL, PL/Perl, etc. )

Exemple :

ALTER TABLE capitaines ADD COLUMN salaire integer;

CREATE FUNCTION verif_salaire()
RETURNS trigger AS $verif_salaire$
BEGIN
  -- Nous verifions que les variables ne sont pas vides
  IF NEW.nom IS NULL THEN
    RAISE EXCEPTION 'Le nom ne doit pas être null.';
  END IF;

  IF NEW.salaire IS NULL THEN
    RAISE EXCEPTION 'Le salaire ne doit pas être null.';
  END IF;

  -- pas de baisse de salaires !
  IF NEW.salaire < OLD.salaire THEN
    RAISE EXCEPTION 'Pas de baisse de salaire !';
  END IF;

  RETURN NEW;
END;
$verif_salaire$ LANGUAGE plpgsql;

CREATE TRIGGER verif_salaire BEFORE INSERT OR UPDATE ON capitaines
  FOR EACH ROW EXECUTE PROCEDURE verif_salaire();

UPDATE capitaines SET salaire = 2000 WHERE nom = 'Robert Surcouf';
UPDATE capitaines SET salaire = 3000 WHERE nom = 'Robert Surcouf';
UPDATE capitaines SET salaire = 2000 WHERE nom = 'Robert Surcouf';
ERROR:  pas de baisse de salaire !
CONTEXTE : PL/pgSQL function verif_salaire() line 13 at RAISE

Questions

N’hésitez pas, c’est le moment !


Quiz

Installation de PostgreSQL

PostgreSQL

Introduction

  • Pré-requis
  • Installation depuis les sources
  • Installation depuis les binaires
    • installation à partir des paquets
    • installation sous Windows
  • Installation dans les conteneurs
  • Premiers réglages
  • Mises à jours

Il existe trois façons d’installer PostgreSQL :

  • Les installeurs graphiques :
    • uniquement Windows et macOS ;
    • avantages : installation facile, idéale pour les nouveaux venus ;
    • inconvénients : pas d’intégration avec le système de paquets du système d’exploitation.
  • les paquets du système :
    • avantages : meilleure intégration avec les autres logiciels, idéal pour un serveur en production ;
    • inconvénients : aucun ?
  • Le code source :
    • avantages : configuration très fine, ajout de patchs, intéressant pour les utilisateurs expérimentés et les testeurs, ou pour embarquer PostgreSQL au sein d’un ensemble de logiciels ;
    • inconvénients : nécessite un environnement de compilation, ainsi que de configurer utilisateurs et script de démarrage.

Nous allons maintenant détailler chaque façon d’installer PostgreSQL.


Pré-requis minimaux pour une instance PostgreSQL


Pré-requis minimaux

  • À peu près n’importe quel OS actuel
    • Linux (conseillé)
    • Unix propriétaires (dont macOS), FreeBSD
    • Windows
  • N’importe quelle machine
    • …selon les besoins
    • 64 bits conseillé
  • Stockage fiable
  • Pas d’antivirus !

Il n’existe pas de configuration minimale pour une installation de PostgreSQL. La configuration de base est très conservatrice (128 Mo de cache). PostgreSQL peut fonctionner sur n’importe quelle machine actuelle, sur x86, 32 ou 64 bits. La configuration dépend plutôt des performances et volumétries attendues.

Les plate-formes officiellement supportées incluent les principaux dérivés d’Unix, en premier lieu Linux, mais aussi les FreeBSD, OpenBSD, macOS, Solaris, ou illumos ; ainsi que Windows.

Linux 64 bits est de loin la plate-forme privilégiée par les développeurs, celle disposant du plus d’outils annexes, et est donc recommandée pour faire tourner PostgreSQL.

Les versions 32 bits fonctionnent mais sont de plus en plus ignorées par les développeurs d’extensions ou les mainteneurs, et ne se voient plus vraiment en production.

Debian et Red Hat et leurs dérivés sont les principales distributions vues en production (à côté d’Alpine pour les images docker).

Si vous devez absolument installer un antivirus ou un outil anti-intrusion, il faut impérativement exclure de son analyse tous les répertoires, fichiers et processus de PostgreSQL. L’interaction avec des antivirus a régulièrement mené à des problèmes de performance, voire de corruption.


Installation à partir des sources


Étapes d’une installation à partir des sources

  • Téléchargement
  • Vérification des prérequis
  • Compilation
  • Installation

Les utilisateurs compilent rarement PostgreSQL, et nous recommandons d’utiliser les paquets précompilés du PGDG. Certaines utilisations le nécessitent toutefois. C’est aussi l’occasion de voir quelques concepts techniques importants.

Nous allons aborder ici les différentes étapes à réaliser pour installer PostgreSQL à partir des sources :

  • trouver les fichiers sources ;
  • préparer le serveur pour accueillir PostgreSQL ;
  • compiler le serveur ;
  • vérifier le résultat de la compilation ;
  • installer les fichiers compilés.

Téléchargement

Les fichiers sources et les instructions de compilation sont disponibles sur le site officiel du projet (ou plus directement https://www.postgresql.org/ftp/source/ ou https://ftp.postgresql.org/pub/source). Le nom du fichier à télécharger se présente toujours sous la forme postgresql-<version>.tar.bz2<version> représente la version de PostgreSQL (par exemple : https://ftp.postgresql.org/pub/source/v17.0/postgresql-17.0.tar.bz2)

Lorsque la future version de PostgreSQL paraît en phase de test (versions bêta), souvent à partir de mai, les sources sont accessibles à l’adresse suivante : https://www.postgresql.org/developer/beta.

Il existe bien sûr un dépôt git officiel, avec un miroir sur Github.


Phases de compilation/installation

# au choix
tar xvfj postgresql-17.0.tar.bz2
git clone --branch REL_17_0 https://git.postgresql.org/git/postgresql.git
./configure --prefix=/usr/local/pgsql   # beaucoup d'options !
make -j8
cd contrib/ && make -j8        # ne pas oublier les contrib
make check

sudo make install && cd contrib/ && sudo make install
pg_config --configure

La compilation de PostgreSQL sous Linux/Unix suit un processus classique.

Une fois l’archive ou le dépôt Git récupéré, si les librairies habituelles de développement sont présentes, la compilation est extrêmement classique. Le répertoire contrib contient des modules et extensions gérés par le projet, dont l’installation est chaudement conseillée. La compilation s’effectue de la même manière.

./configure

Des options courantes pour une machine de production sont :

  • --prefix=répertoire : répertoire d’installation personnalisé (par défaut, il s’agit de /usr/local/pgsql) ;
  • --with-pgport=port : port par défaut (si différent de 5432) ;
  • --with-openssl : support d’OpenSSL pour bénéficier de connexions chiffrées ;
  • --enable-nls : le support de la langue utilisateur pour les messages provenant du serveur et des applications ;
  • --with-perl, --with-python : installe les langages PL correspondants.

On voudra généralement activer ces trois dernières options… et beaucoup d’autres.

make --jobs=8 all
(cd contrib/ && make --jobs=8 all)
make --jobs=8 check
sudo make install && cd contrib/ && sudo make install

(Le make de GNU se nomme gmake sur certaines systèmes différents de Linux). Cette phase est la plus longue, mais ne dure que quelques minutes sur du matériel récent en parallélisant.

̀make check lance des tests de non régression pour vérifier que PostgreSQL fonctionne correctement sur la machine cible.

Pour les mises à jour, il est important de connaître les options utilisées lors de la précédente compilation. L’outil pg_config le permet. Un PostgreSQL 17.0 sur Debian 12 affichera ce qui suit (les distributions ont tendance à intégrer toutes les options) :

$ /usr/lib/postgresql/17/bin/pg_config --configure
 '--build=x86_64-linux-gnu' '--prefix=/usr' '--includedir=${prefix}/include'
 '--mandir=${prefix}/share/man' '--infodir=${prefix}/share/info'
 '--sysconfdir=/etc' '--localstatedir=/var' '--disable-option-checking'
 '--disable-silent-rules' '--libdir=${prefix}/lib/x86_64-linux-gnu'
 '--runstatedir=/run'
 '--disable-maintainer-mode' '--disable-dependency-tracking'
 '--with-tcl' '--with-perl' '--with-python' '--with-pam' '--with-openssl'
 '--with-libxml' '--with-libxslt' '--mandir=/usr/share/postgresql/17/man'
 '--docdir=/usr/share/doc/postgresql-doc-17'
 '--sysconfdir=/etc/postgresql-common' '--datarootdir=/usr/share/'
 '--datadir=/usr/share/postgresql/17'
 '--bindir=/usr/lib/postgresql/17/bin'
 '--libdir=/usr/lib/x86_64-linux-gnu/'
 '--libexecdir=/usr/lib/postgresql/'
 '--includedir=/usr/include/postgresql/'
 '--with-extra-version= (Debian 17.0-1.pgdg120+1)'
 '--enable-nls' '--enable-thread-safety' '--enable-debug' '--disable-rpath'
 '--with-uuid=e2fs' '--with-gnu-ld' '--with-gssapi' '--with-ldap'
 '--with-pgport=5432' '--with-system-tzdata=/usr/share/zoneinfo'
 'AWK=mawk' 'MKDIR_P=/bin/mkdir -p' 'PROVE=/usr/bin/prove'
 'PYTHON=/usr/bin/python3' 'TAR=/bin/tar' 'XSLTPROC=xsltproc --nonet'
 'CFLAGS=-g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fno-omit-frame-pointer' 'LDFLAGS=-Wl,-z,relro -Wl,-z,now'
 '--enable-tap-tests' '--with-icu'
 '--with-llvm' 'LLVM_CONFIG=/usr/bin/llvm-config-16' 'CLANG=/usr/bin/clang-16'
 '--with-lz4' '--with-zstd'
 '--with-systemd' '--with-selinux' '--enable-dtrace'
 'build_alias=x86_64-linux-gnu'
 'CPPFLAGS=-Wdate-time -D_FORTIFY_SOURCE=2'
 'CXXFLAGS=-g -O2 -fstack-protector-strong -Wformat -Werror=format-security'

Il existe aussi une vue pg_config accessible sur un serveur démarré :

 TABLE pg_config ;
       name        |  setting
-------------------+-----------------------------------------------------------
 BINDIR            | /usr/lib/postgresql/17/bin
 DOCDIR            | /usr/share/doc/postgresql-doc-17
 HTMLDIR           | /usr/share/doc/postgresql-doc-17
 INCLUDEDIR        | /usr/include/postgresql
 PKGINCLUDEDIR     | /usr/include/postgresql
 INCLUDEDIR-SERVER | /usr/include/postgresql/17/server
 LIBDIR            | /usr/lib/x86_64-linux-gnu
 PKGLIBDIR         | /usr/lib/postgresql/17/lib
 LOCALEDIR         | /usr/share/locale
 MANDIR            | /usr/share/postgresql/17/man
 SHAREDIR          | /usr/share/postgresql/17
 SYSCONFDIR        | /etc/postgresql-common
 PGXS              | /usr/lib/postgresql/17/lib/pgxs/src/makefiles/pgxs.mk
 CONFIGURE         |  '--build=x86_64-linux-gnu' '--prefix=/usr' '--includedir=${prefix}/include' '--mandir=${prefix}/share/man' '--infodir=${prefix}/share/info' '--sysconfdir=/etc' '--localstatedir=/var' '--disable-option-checking' '--disable-silent-rules' '--libdir=${prefix}/lib/x86_64-linux-gnu' '--runstatedir=/run' '--disable-maintainer-mode' '--disable-dependency-tracking' '--with-tcl' '--with-perl' '--with-python' '--with-pam' '--with-openssl' '--with-libxml' '--with-libxslt' '--mandir=/usr/share/postgresql/17/man' '--docdir=/usr/share/doc/postgresql-doc-17' '--sysconfdir=/etc/postgresql-common' '--datarootdir=/usr/share/' '--datadir=/usr/share/postgresql/17' '--bindir=/usr/lib/postgresql/17/bin' '--libdir=/usr/lib/x86_64-linux-gnu/' '--libexecdir=/usr/lib/postgresql/' '--includedir=/usr/include/postgresql/' '--with-extra-version= (Debian 17.0-1.pgdg120+1)' '--enable-nls' '--enable-thread-safety' '--enable-debug' '--disable-rpath' '--with-uuid=e2fs' '--with-gnu-ld' '--with-gssapi' '--with-ldap' '--with-pgport=5432' '--with-system-tzdata=/usr/share/zoneinfo' 'AWK=mawk' 'MKDIR_P=/bin/mkdir -p' 'PROVE=/usr/bin/prove' 'PYTHON=/usr/bin/python3' 'TAR=/bin/tar' 'XSLTPROC=xsltproc --nonet' 'CFLAGS=-g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fno-omit-frame-pointer' 'LDFLAGS=-Wl,-z,relro -Wl,-z,now' '--enable-tap-tests' '--with-icu' '--with-llvm' 'LLVM_CONFIG=/usr/bin/llvm-config-16' 'CLANG=/usr/bin/clang-16' '--with-lz4' '--with-zstd' '--with-systemd' '--with-selinux' '--enable-dtrace' 'build_alias=x86_64-linux-gnu' 'CPPFLAGS=-Wdate-time -D_FORTIFY_SOURCE=2' 'CXXFLAGS=-g -O2 -fstack-protector-strong -Wformat -Werror=format-security'
 CC                | gcc
 CPPFLAGS          | -Wdate-time -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2
 CFLAGS            | -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wshadow=compatible-local -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fno-omit-frame-pointer
 CFLAGS_SL         | -fPIC
 LDFLAGS           | -Wl,-z,relro -Wl,-z,now -L/usr/lib/llvm-16/lib -Wl,--as-needed
 LDFLAGS_EX        | 
 LDFLAGS_SL        | 
 LIBS              | -lpgcommon -lpgport -lselinux -lzstd -llz4 -lxslt -lxml2 -lpam -lssl -lcrypto -lgssapi_krb5 -lz -lreadline -lm 
 VERSION           | PostgreSQL 17.0 (Debian 17.0-1.pgdg120+1)
(23 lignes)

Utilisateur dédié de PostgreSQL

  • Jamais root
  • Utilisateur dédié
    • propriétaire des répertoires et fichiers
    • peut lancer PostgreSQL
    • traditionnellement : postgres
  • Variables d’environnement (défaut) :
export PATH=/usr/local/pgsql/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/pgsql/lib:$LD_LIBRARY_PATH
export MANPATH=$MANPATH:/usr/local/pgsql/share/man
export PGDATA=/usr/local/pgsql/data    # Données

Le serveur PostgreSQL ne peut pas être exécuté par l’utilisateur root, pour des raisons de sécurité. Un utilisateur système sans droits particuliers suffit. Il sera le seul propriétaire des répertoires et fichiers gérés par le serveur PostgreSQL. Il sera aussi le compte qui permettra de lancer PostgreSQL (directement ou indirectement via les outils système comme systemd).

Cet utilisateur système est habituellement appelé postgres (et les paquets d’installation utilisent ce nom), mais ce n’est absolument pas une obligation.

Il est nécessaire de positionner un certain nombre de variables d’environnement dans ${HOME}/.profile ou dans /etc/profile. Avec le chemin par défaut de la compilation, elles valent :

export PATH=/usr/local/pgsql/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/pgsql/lib:$LD_LIBRARY_PATH
export MANPATH=$MANPATH:/usr/local/pgsql/share/man
export PGDATA=/usr/local/pgsql/data

Chemins des binaires :

  • /usr/local/pgsql/bin est le chemin par défaut (ou le chemin indiqué à l’option --prefix lors de l’étape configure). L’ajouter à PATH permet de rendre l’exécution de PostgreSQL possible depuis n’importe quel répertoire ;
  • LD_LIBRARY_PATH indique au système où trouver les différentes bibliothèques nécessaires à l’exécution des programmes ;
  • MANPATH indique le chemin de recherche des pages de manuel.

Emplacement des données :

PDGATA est une spécificité de PostgreSQL : cette variable indique le répertoire des fichiers de données de PostgreSQL, c’est-à-dire les données de l’utilisateur. (Plus rigoureusement : elle pointe l’emplacement du fichier postgresql.conf, généralement dans ce répertoire de données. Les paquets pour Debian/Ubuntu représentent une grosse exception, avec un postgresql.conf dans /etc, qui contient le paramètre data_directory, lequel pointe vers le vrai répertoire de données.) PostgreSQL n’impose de lui-même aucune contrainte sur le chemin.


Création du répertoire de données de l’instance

$ initdb -D /usr/local/pgsql/data
  • Une seule instance sur le répertoire !
  • Pas à la racine d’un point de montage
  • Options d’emplacement :
    • --data pour les fichiers de données
    • --waldir pour les journaux de transactions
  • Autres options :
    • --data-checksums : sommes de contrôle (conseillé !)
    • et : chemin des journaux, mot de passe, encodage…

La commande initdb doit être exécutée sous le compte de l’utilisateur système PostgreSQL décrit dans la section précédente (généralement postgres). (Les paquets d’installation fournissent d’autres outils, mais tous utilisent initdb.)

Répertoire :

initdb crée les fichiers d’une nouvelle instance avec une première base de données dans le répertoire indiqué. Si le répertoire n’existe pas, initdb tentera de le créer, s’il a les droits pour le faire. S’il existe, il doit être vide.

Typiquement, les chemins par défaut sont ceux-ci :

# chemin par défaut d'une version compilée sans --prefix
/usr/local/pgsql/data
# chemin par défaut des paquets Debian/Ubuntu (première instance v17)
/var/lib/postgresql/17/main
# chemin par défaut des paquets RPM du PGDG (première instance v17)
/var/lib/pgsql/17/data

Vous pouvez décider de placer le PGDATA n’importe où pourvu que l’utilisateur sous lequel tourne PostgreSQL puisse y accéder ; il y a cependant quelques conseils à connaître.

Attention : pour des raisons de sécurité et de fiabilité, les répertoires choisis pour les données de votre instance ne doivent pas être à la racine d’un point de montage. Que ce soit le répertoire PGDATA, le répertoire pg_wal ou les éventuels tablespaces. Si un ou plusieurs points de montage sont dédiés à l’utilisation de PostgreSQL, positionnez toujours les données dans un sous-répertoire, voire deux niveaux en-dessous du point de montage, couramment : point de montage/version majeure/nom instance. Exemples :

# Si /mnt/donnees est le point de montage
/mnt/donnees/17/dbprod
# Si /var/lib/postgresql est une partition
/var/lib/postgresql/17/main
# Si /var/lib/pgsql est une partition
/var/lib/pgsql/17/data

À ce propos, voir :

Enfin :

Un répertoire de données ne doit être utilisé que par une seule instance (processus) à la fois !

PostgreSQL vérifie au démarrage qu’aucune autre instance du même serveur n’utilise les fichiers indiqués, mais cette protection n’est pas absolue, notamment avec des accès depuis des systèmes différents. Faites donc bien attention à ne lancer PostgreSQL qu’une seule fois sur un répertoire de données.

Si plusieurs instances cohabitent sur le serveur, elles devront avoir chacune leur répertoire.

Lancement de initdb :

Voici ce qu’affiche cette commande :

$ initdb --data /usr/local/pgsql/data --data-checksums

The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.

The database cluster will be initialized with locale "fr_FR.UTF-8".
The default database encoding has accordingly been set to "UTF8".
The default text search configuration will be set to "french".

Data page checksums are enabled.

fixing permissions on existing directory /usr/local/pgsql/data ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default "max_connections" ... 100
selecting default "shared_buffers" ... 128MB
selecting default time zone ... Europe/Paris
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok

initdb: warning: enabling "trust" authentication for local connections
initdb: hint: You can change this by editing pg_hba.conf or using the option -A, or --auth-local and --auth-host, the next time you run initdb.

Success. You can now start the database server using:

    pg_ctl -D /usr/local/pgsql/data -l logfile start

Nous pouvons ainsi suivre les actions de initdb :

  • détection de l’utilisateur, de l’encodage et de la locale ;
  • prise en compte des sommes de contrôle ;
  • création ou vérification des droits du répertoire PGDATA (/usr/local/pgsql/data ici) ;
  • création des sous-répertoires ;
  • création et modification des fichiers de configuration ;
  • exécution du script bootstrap ;
  • synchronisation sur disque ;
  • affichage de quelques informations supplémentaires (noter les droits par défaut très laxistes d’une version compilée).

Authentification par défaut :

Il est possible de changer la méthode d’authentification par défaut avec les paramètres en ligne de commande --auth, --auth-host et --auth-local. Les options --pwprompt ou --pwfile permettent d’assigner un mot de passe à l’utilisateur postgres.

Sommes de contrôle :

Il est conseillé d’activer les sommes de contrôle des fichiers de données avec l’option --data-checksums. Ces sommes de contrôle sont vérifiées à chaque lecture d’un bloc. Leur activation est chaudement recommandée pour détecter une corruption physique le plus tôt possible. Tous les processeurs Intel/AMD pas trop anciens supportent le jeu d’instruction SSE 4.2 (voir dans /proc/cpuinfo), il ne devrait pas y avoir d’impact notable sur les performances. Les journaux générés seront cependant plus nombreux. Il est possible de vérifier, activer ou désactiver toutes les sommes de contrôle grâce à l’outil pg_checksums. Cependant, l’opération d’activation sur une instance existante impose un arrêt et une réécriture complète des fichiers. (L’opération était même impossible avant PostgreSQL 12.) Pensez-y donc dès l’installation, y compris si vous installez par les paquets.

Emplacement des journaux :

L’option --waldir indique l’emplacement des journaux de transaction. Par défaut, ils sont dans le sous-répertoire pg_wal/ dans le PGDATA. Si vous désignez un autre répertoire, pg_wal sera un lien symbolique vers le répertoire désigné.

Taille des segments :

Les fichiers des journaux de transaction ont une taille par défaut de 16 Mo. Augmenter leur taille avec --wal-segsize n’a d’intérêt que pour les très grosses installations générant énormément de journaux pour optimiser leur archivage.


Lancement et arrêt

  • Avec le script de l’OS (recommandé) ou pg_ctl :
systemctl [action] postgresql     # systemd
/etc/init.d/postgresql [action]   # SysV Init
service postgresql [action]       # idem
$ pg_ctl --pgdata /usr/local/pgsql/data --log logfile [action]
         --mode [smart|fast|immediate]
  • [action] dépend du besoin :
    • start / stop / restart
    • reload pour recharger la configuration
    • status
    • promote, logrotate, kill

(Re)démarrage et arrêt :

La méthode recommandée est d’utiliser un script de démarrage adapté à l’OS, (voir plus bas les outils les commandes systemd ou celles propres à Debian) , surtout si l’on a installé PostgreSQL par les paquets. Au besoin, des scripts d’exemple existent dans le répertoire des sources (contrib/start-scripts/) pour Linux, FreeBSD et macOS. Ces scripts sont à exécuter en tant qu’utilisateur root. Sinon, il est possible d’exécuter pg_ctl avec l’utilisateur créé précédemment.

Les deux méthodes partagent certaines des actions présentées ci-dessus : start, stop, restart (aux sens évidents), ou reload (pour recharger la configuration sans redémarrer PostgreSQL ni couper les connexions).

L’option --mode permet de préciser le mode d’arrêt parmi les trois disponibles :

  • smart : pour vider le cache de PostgreSQL sur disque, interdire de nouvelles connexions et attendre la déconnexion des clients et d’éventuelles sauvegardes ;
  • fast (par défaut) : pour vider le cache sur disque et déconnecter les clients sans attendre (les transactions en cours sont annulées) ;
  • immediate : équivalent à un arrêt brutal : tous les processus serveur sont tués et donc, au redémarrage, le serveur devra rejouer ses journaux de transactions.

Rechargement de la configuration :

Pour recharger la configuration après changement du paramétrage, la commande :

pg_ctl reload -D /repertoire_pgdata

est équivalente à cet ordre SQL :

SELECT pg_reload_conf() ;

Il faut aussi savoir que quelques paramètres nécessitent un redémarrage de PostgreSQL et non un simple rechargement, ils sont indiqués dans les commentaires de postgresql.conf.


Installation à partir des paquets Linux


Paquets

  • Packages Debian
  • Packages RPM

Pour une utilisation en environnement de production, il est généralement préférable d’installer les paquets binaires préparés spécialement pour la distribution utilisée. Les paquets sont préparés par des personnes différentes, suivant les recommendations officielles de la distribution. Il y a donc des différences, parfois importantes, entre les paquets.


Paquets Debian officiels

  • Nombreux paquets disponibles :
    • serveur, client, contrib, docs
    • extensions, outils
  • apt install postgresql-<version majeure>
    • installe les binaires
    • crée l’utilisateur postgres
    • exécute initdb
    • démarre le serveur

Sur Debian et les versions dérivées (Ubuntu notamment), l’installation de PostgreSQL a été découpée en plusieurs paquets (ici pour la version majeure 17) :

  • le serveur : postgresql-17 ;
  • les clients : postgresql-client-17 ;
  • la documentation : postgresql-doc-17.

Ce paquet correspond toujours à la dernière version mineure trimestrielle (par exemple 17.1).

La version majeure dans le nom des paquets (9.6,10,13,17…) permet d’installer plusieurs versions majeures sur le même serveur physique ou virtuel.

Par défaut, sans autre dépôt, une seule version majeure sera disponible dans une version de distribution. Par exemple, apt install postgresql sur Debian 12 installera en fait postgresql-15 (il est en dépendance).

Il existe aussi des paquets pour les outils, les extensions, etc. Certains langages de procédures stockées sont disponibles dans des paquets séparés :

  • PL/python dans postgresql-plpython3-17
  • PL/perl dans postgresql-plperl-17
  • PL/Tcl dans postgresql-pltcl-17
  • etc.

Pour compiler des outils liés à PostgreSQL, il est recommandé d’installer également les bibliothèques de développement qui font partie du paquet postgresql-server-dev-17.

Quand le paquet postgresql-17 est installé, plusieurs opérations sont immédiatement exécutées :

  • téléchargement du paquet (dans la dernière version mineure) ;
  • installation des binaires contenus dans le paquet ;
  • création de l’utilisateur postgres (s’il n’existe pas déjà) ;
  • paramétrage d’une première instance nommée main ;
  • création du répertoire des données, lancement de l’instance.

Les exécutables sont installés dans :

/usr/lib/postgresql/17/bin

Chaque instance porte un nom, qui se retrouve dans le paramètre cluster_name, et permet d’identifier les processus dans un ps ou un top. Le nom de la première instance de chaque version majeure est par défaut main. Pour cette instance :

  • les données sont dans :
/var/lib/postgresql/17/main
  • les fichiers de configuration (pas tous ! certains restent dans le répertoire des données) sont dans :
/etc/postgresql/17/main
  • les traces sont gérées par l’OS sous ce nom :
/var/log/postgresql/postgresql-17-main.log
  • un fichier PID, la socket d’accès local, et l’espace de travail temporaire des statistiques d’activité figurent dans /var/run/postgresql.

Tout ceci vise à respecter le plus possible la norme FHS (Filesystem Hierarchy Standard).

En cas de mise à jour d’un paquet, le serveur PostgreSQL est redémarré après mise à jour des binaires.


Paquets Debian : spécificités

  • Plusieurs versions majeures installables
  • Wrappers/scripts pour la gestion des différentes instances :
    • pg_lsclusters
    • pg_ctlcluster
      • ou : systemctl stop|start postgresql-15@main
    • pg_createcluster
    • etc.
  • Respect de la FHS
  • Configuration dans /etc/postgresql/

Numéroter les paquets permet d’installer plusieurs versions majeures de PostgreSQL (mais chacune seulement dans sa dernière version mineure) au besoin sur le même système, si les dépôts les contiennent.

Les mainteneurs des paquets Debian ont écrit des scripts pour faciliter la création, la suppression et la gestion de différentes instances sur le même serveur. Les principaux sont :

  • pg_lsclusters liste les instances ;
  • pg_createcluster <version majeure> <nom instance> crée une instance de la version majeure et du nom voulu ;
  • pg_dropcluster <version majeure> <nom instance> détruit l’instance désignée ;
  • /etc/postgresql-common/createcluster.conf permet de centraliser les paramètres par défaut des instances ;
  • la gestion d’une instance est réalisée avec la commande pg_ctlcluster :
pg_ctlcluster <version majeure> <nom instance> start|stop|reload|status|promote

Ce dernier script interagit avec systemd, qui peut être utilisé pour arrêter ou démarrer séparément chaque instance. Ces deux commandes sont équivalentes :

sudo pg_ctlcluster 17 main start
sudo systemctl start postgresql@17-main

Paquets Debian communautaires

  • La communauté met des paquets Debian à disposition :
  • Synchrone avec le projet PostgreSQL
  • Ajout du dépôt dans /etc/apt/sources.list.d/pgdg.list
  • Utilisation chaudement conseillée

La distribution Debian préfère des paquets testés et validés, y compris sur des versions assez anciennes, que d’adopter la dernière version dès qu’elle est disponible. Par exemple, Debian 11 ne contiendra jamais que PostgreSQL 13 et ses versions mineures, et Debian 12 ne contiendra que PostgreSQL 15.

Pour faciliter les mises à jour, la communauté PostgreSQL met à disposition son propre dépôt de paquets Debian. Elle en assure le maintien et le support. Les paquets de la communauté ont la même provenance et le même contenu que les paquets officiels Debian, avec d’ailleurs les mêmes mainteneurs. La seule différence est que apt.postgresql.org est mis à jour plus fréquemment, en liaison directe avec la communauté PostgreSQL, et contient beaucoup plus d’outils et extensions.

Le wiki indique quelle est la procédure, qui peut se résumer à :

sudo apt install -y postgresql-common
sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh

Setting up postgresql-common (248) ...
Creating config file /etc/postgresql-common/createcluster.conf with new version
Building PostgreSQL dictionaries from installed myspell/hunspell packages...
Removing obsolete dictionary files:
Created symlink /etc/systemd/system/multi-user.target.wants/postgresql.service → /lib/systemd/system/postgresql.service.
Processing triggers for man-db (2.11.2-2) ...
This script will enable the PostgreSQL APT repository on apt.postgresql.org on
your system. The distribution codename used will be bookworm-pgdg.
Press Enter to continue, or Ctrl-C to abort.

Si l’on continue, ce dépôt sera ajouté au système (ici sous Debian 12) :

$ cat /etc/apt/sources.list.d/pgdg.sources
Types: deb
Architectures: amd64
URIs: https://apt.postgresql.org/pub/repos/apt
Suites: bookworm-pgdg
Components: main
Signed-By: /usr/share/postgresql-common/pgdg/apt.postgresql.org.gpg

et la version choisie de PostgreSQL sera immédiatement installable :

sudo apt install postgresql-17

Paquets Red Hat communautaires : yum.postgresql.org

Les versions majeures de Red Hat et de ses dérivés (Rocky Linux, AlmaLinux, Fedora, CentOS, Scientific Linux…) sont très espacées, et la version par défaut de PostgreSQL n’est parfois plus supportée. Même les versions disponibles en AppStream (avec dnf module) sont parfois en retard ou ne contiennent que certaines versions majeures. Les dépôts de la communauté sont donc fortement conseillés. Ils contiennent aussi beaucoup plus d’utilitaires, toutes les versions majeures supportées de PostgreSQL simultanément, et collent au plus près des versions publiées par la communauté.

Ce dépôt convient pour les dérivés de Red Hat comme Fedora, CentOS, Rocky Linux…


Paquets Red Hat communautaires : installation

sudo dnf install -y \
     https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm
sudo dnf -qy module disable postgresql

sudo dnf install -y postgresql17-server
# dont : utilisateur postgres

sudo /usr/pgsql-17/bin/postgresql-17-setup initdb
sudo systemctl enable postgresql-17
sudo systemctl start postgresql-17

L’installation de la configuration du dépôt de la communauté est très simple. Les commandes peuvent même être générées en fonction des versions sur https://www.postgresql.org/download/linux/redhat/. L’exemple ci-dessus installe PostgreSQL 17 sur Rocky 9.


Paquets Red Hat communautaires : spécificités

  • Paquets séparés serveur, client, contrib
  • /usr/pgsql-XX/bin: binaires
  • Initialisation manuelle (postgresql-17-setup initdb)
    • vers : /var/lib/pgsql/XX/data
  • Gestion par systemd
  • Particularités :
    • plusieurs versions majeures installables
    • configuration : dans le répertoire de données

Sous Red Hat et les versions dérivées, les dépôts communautaires ont été découpés en plusieurs paquets, disponibles pour chacune des versions majeures supportées :

  • le serveur : postgresqlXX-server ;
  • les clients : postgresqlXX ;
  • les modules contrib : postgresqlXX-contrib ;
  • la documentation : postgresqlXX-docs.

où XX est la version majeure (par exemple 13 ou 17).

Il existe aussi des paquets pour les outils, les extensions, etc. Certains langages de procédures stockées sont disponibles dans des paquets séparés :

  • PL/python dans postgresqlXX-plpython3 ;
  • PL/perl dans postgresqlXX-plperl ;
  • PL/Tcl dans postgresqlXX-pltcl ;
  • etc.

Pour compiler des outils liés à PostgreSQL, il est recommandé d’installer également les bibliothèques de développement qui font partie du paquet postgresqlXX-devel.

Ce nommage sous-entend qu’il est possible d’installer plusieurs versions majeures sur le même serveur physique ou virtuel. Les exécutables sont installés dans le répertoire /usr/pgsql-XX/bin, les traces dans /var/lib/pgsql/XX/data/log (utilisation du logger process de PostgreSQL), les données dans /var/lib/pgsql/XX/data. Ce dernier est le répertoire par défaut des données, mais il est possible de le surcharger.

Sur un système type Red Hat sans dépôt communautaire, les noms des paquets ne comportent pas le numéro de version et installent tous les binaires cités ici dans /usr/bin.

Quand le paquet serveur est installé, plusieurs opérations sont exécutées : téléchargement du paquet, installation des binaires contenus dans le paquet, et création de l’utilisateur postgres (s’il n’existe pas déjà).

Le répertoire des données n’est pas créé. Cela reste une opération à réaliser par la personne qui a installé PostgreSQL sur le serveur. Lancer le script /usr/psql-XX/bin/postgresqlXX-setup en tant que root :

PGSETUP_INITDB_OPTIONS="--data-checksums" \
 /usr/pgsql-17/bin/postgresql-17-setup initdb

Plutôt que de respecter la norme FHS (Filesystem Hierarchy Standard), les mainteneurs ont fait le choix de respecter l’emplacement des fichiers utilisé par défaut par les développeurs PostgreSQL. La configuration de l’instance (postgresql.conf entre autres) est donc directement dans le PGDATA.

Pour installer plusieurs instances, il faudra créer manuellement des services systemd différents.

En cas de mise à jour d’un paquet, le serveur PostgreSQL n’est pas redémarré après mise à jour des binaires.


Industrialisation avec pglift


pglift : présentation rapide

  • Déploiement et infrastructure as code :
    • ligne de commande
    • collections Ansible
  • Couverture fonctionnelle :
    • Sauvegardes avec pgBackRest
    • Supervision avec Prometheus
    • Administration avec temBoard
    • Analyse avec PoWA
    • Haute disponibilité avec Patroni
    • Intégration système avec systemd ou rsyslog

pglift est un outil permettant de déployer et d’exploiter PostgreSQL à grande échelle. Le projet fournit à la fois une interface en ligne de commande pour gérer le cycle de vie des instances et une collection de modules Ansible pour piloter une infrastructure-as-code dans un contexte de production.

L’élément fondamental de pglift est l’instance. Celle-ci est constituée d’une instance PostgreSQL et inclut des composants satellites, facultatifs, permettant d’exploiter PostgreSQL à grande échelle. Dans sa version 1.0, pglift supporte les composants suivants :

pgBackRest permet de prendre en charge les sauvegardes physiques PITR (Point In Time Recovery) de PostgreSQL.

postgres_exporter est un service de supervision permettant de remonter des informations à l’outil de surveillance Prometheus.

temBoard est une console web de supervision et d’administration dédiée aux instances PostgreSQL.

PoWA est une console web permettant d’analyser l’activité des instances PostgreSQL en direct.

Patroni est un outil permettant de construire un agrégat d’instances PostgreSQL résilient offrant un service de haute disponibilité.

Tous les composants satellites supportés par pglift sont des logiciels libres. Le projet pglift est lui aussi nativement open source, sous licence GPLv3. Son développement se passe en public sur https://gitlab.com/dalibo/pglift/ pour l’API Python et l’interface en ligne de commande et sur https://gitlab.com/dalibo/pglift-ansible/ pour la collection Ansible dalibo.pglift.

Références :


pglift : fichier de configuration

prefix: /srv # fichier /etc/pglift/settings.yaml
postgresql:
  auth:
    host: scram-sha-256
prometheus:
  execpath: /usr/bin/prometheus-postgres-exporter
pgbackrest:
  repository:
    mode: path
    path: /srv/pgsql-backups
powa: {}
systemd: {}
rsyslog: {}

À côté de PostgreSQL, l’instance inclut un ensemble d’outils nécessaires à son utilisation. L’intégration de ces outils satellites est configurée localement via un fichier YAML settings.yaml.

Ce fichier définit comment les différents composants de l’instance sont configurés, installés et exécutés. Il permet aussi de définir quels composants satellites facultatifs supportés par pgLift sont inclus dans l’instance. Si un élément est listé dans ce fichier sans paramètre associé, il sera exploité dans sa configuration par défaut.

Dans l’exemple ci-dessus, temBoard et Patroni ne sont pas installés, et PoWA est laissé à sa configuration par défaut.


pglift : exemples de commandes

  • Initialisation
pglift instance create main --pgbackrest-stanza=main
  • Modification de configuration
pglift pgconf -i main set log_connections=on
  • Sauvegarde physique
pglift instance backup main
  • Utilisation des outils de l’instance
pglift instance exec main -- psql
pglift instance exec main -- pgbackrest info

Interface impérative en ligne de commande :

La commande suivante permet la création d’une instance pglift :

$ pglift instance create main --pgbackrest-stanza=main
INFO     initializing PostgreSQL
INFO     configuring PostgreSQL authentication
INFO     configuring PostgreSQL
INFO     starting PostgreSQL 16-main
INFO     creating role 'powa'
INFO     creating role 'prometheus'
INFO     creating role 'backup'
INFO     altering role 'backup'
INFO     creating 'powa' database in 16/main
INFO     creating extension 'btree_gist' in database powa
INFO     creating extension 'pg_qualstats' in database powa
INFO     creating extension 'pg_stat_statements' in database powa
INFO     creating extension 'pg_stat_kcache' in database powa
INFO     creating extension 'powa' in database powa
INFO     configuring Prometheus postgres_exporter 16-main
INFO     configuring pgBackRest stanza 'main' for
         pg1-path=/srv/pgsql/16/main/data
INFO     creating pgBackRest stanza main
INFO     starting Prometheus postgres_exporter 16-main

L’instance pglift inclut l’instance PostgreSQL ainsi que l’ensemble des modules définis dans le fichier de configuration settings.yaml. pglift gère aussi l’intégration au système avec systemd ou rsyslog comme dans notre exemple. Tout ceci fonctionne sans privilège root pour une meilleure séparation des responsabilités et une meilleure sécurité.

pglift permet de récupérer l’état d’une instance à un moment donné :

$ pglift instance get main -o json
{
  "name": "main",
  "version": "16",
  "port": 5432,
  "settings": {
    "unix_socket_directories": "/run/user/1000/pglift/postgresql",
    "shared_buffers": "1 GB",
    "wal_level": "replica",
    "archive_mode": true,
    "archive_command": "/usr/bin/pgbackrest --config-path=/etc/pgbackrest \
    --stanza=main --pg1-path=/srv/pgsql/16/main/data archive-push %p",
    "effective_cache_size": "4 GB",
    "log_destination": "syslog",
    "logging_collector": true,
    "log_directory": "/var/log/postgresql",
    "log_filename": "16-main-%Y-%m-%d_%H%M%S.log",
    "syslog_ident": "postgresql-16-main",
    "cluster_name": "main",
    "lc_messages": "C",
    "lc_monetary": "C",
    "lc_numeric": "C",
    "lc_time": "C",
    "shared_preload_libraries": "pg_qualstats, pg_stat_statements, pg_stat_kcache"
  },
  "data_checksums": false,
  "locale": "C",
  "encoding": "UTF8",
  "standby": null,
  "state": "started",
  "pending_restart": false,
  "wal_directory": "/srv/pgsql/16/main/wal",
  "prometheus": {
    "port": 9187
  },
  "data_directory": "/srv/pgsql/16/main/data",
  "powa": {},
  "pgbackrest": {
    "stanza": "main"
  }
}

ou de modifier l’instance :

# activation du paramètre log_connections
$ pglift pgconf -i main set log_connections=on
INFO     configuring PostgreSQL
INFO     instance 16/main needs reload due to parameter changes: log_connections
INFO     reloading PostgreSQL configuration for 16-main
log_connections: None -> True
# changement du port prometheus
$ pglift instance alter main --prometheus-port 8188
INFO     configuring PostgreSQL
INFO     reconfiguring Prometheus postgres_exporter 16-main
INFO     instance 16/main needs reload due to parameter changes: log_connections
INFO     reloading PostgreSQL configuration for 16-main
INFO     starting Prometheus postgres_exporter 16-main
$ pglift instance get main # vérification
 name  version  port  data_checksums  locale  encoding  pending_restart  prometheus  pgbackrest
 main  16       5432  False           C       UTF8      False            port: 8188  stanza: main

Les instances et objets PostgreSQL peuvent être manipulés à l’aide des outils natifs de PostgreSQL depuis la ligne de commande :

$ pglift instance exec main -- pgbench -i bench
creating tables...
generating data (client-side)...
100000 of 100000 tuples (100%) done (elapsed 0.06 s, remaining 0.00 s)
vacuuming...
creating primary keys...
done in 0.18 s (drop tables 0.00 s, create tables 0.01 s, ... vacuum 0.04 s, primary keys 0.05 s).
$ pglift instance exec main -- pgbench bench
pgbench (16.0 (Debian 16.0-1.pgdg120+1))
starting vacuum...end.
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 1
query mode: simple
number of clients: 1
number of threads: 1
maximum number of tries: 1
number of transactions per client: 10
number of transactions actually processed: 10/10
number of failed transactions: 0 (0.000%)
latency average = 1.669 ms
initial connection time = 4.544 ms
tps = 599.125277 (without initial connection time)

Ceci s’applique aussi à des outils tiers, par exemple avec pgBackRest :

$ pglift instance exec main -- pgbackrest info
stanza: main
    status: ok
    cipher: none

    db (current)
        wal archive min/max (16): 000000010000000000000001/000000010000000000000007

        full backup: 20231016-092726F
            timestamp start/stop: 2023-10-16 09:27:26+02 / 2023-10-16 09:27:31+02
            wal start/stop: 000000010000000000000004 / 000000010000000000000004
            database size: 32.0MB, database backup size: 32.0MB
            repo1: backup set size: 4.2MB, backup size: 4.2MB

        diff backup: 20231016-092726F_20231016-092821D
            timestamp start/stop: 2023-10-16 09:28:21+02 / 2023-10-16 09:28:24+02
            wal start/stop: 000000010000000000000007 / 000000010000000000000007
            database size: 54.5MB, database backup size: 22.6MB
            repo1: backup set size: 6MB, backup size: 1.8MB
            backup reference list: 20231016-092726F

Le tutoriel de la ligne de commande de la documentation recense tous les exemples d’utilisation des commandes usuelles de pglift.

Interface déclarative avec Ansible :

pglift comporte une collection de modules Ansible, sous l’espace de noms dalibo.pglift.Voici un exemple de playbook illustrant ses capacités :

- name: Set up database instances
hosts: dbserver
tasks:
  - name: main instance
    dalibo.pglift.instance:
      name: main
      state: started
      port: 5444
      settings:
        max_connections: 100
        shared_buffers: 1GB
        shared_preload_libraries: 'pg_stat_statements, passwordcheck'
      surole_password: ''
      pgbackrest:
        stanza: main
        password: ''
      prometheus:
        password: ''
        port: 9186
      roles:
        - name: admin
          login: true
          password: ''
          connection_limit: 10
          validity: '2025-01-01T00:00'
          in_roles:
            - pg_read_all_stats
            - pg_signal_backend
      databases:
        - name: main
          owner: admin
          settings:
            work_mem: 3MB
          extensions:
            - name: unaccent
              schema: public

Le module dalibo.pglift.instance permet de gérer l’instance, et les objets reliés comme des rôles ou des bases de données. Les données sensibles peuvent être prises en charge par Ansible vault. Les modules Ansible permettent un contrôle plus important que la ligne de commandes, grâce aux champs imbriqués, pouvant inclure la configuration de l’instance, des bases de données, des extensions, etc.

Comme tout module Ansible en général, cette interface est complètement déclarative, idempotente et sans état. Ces modules fonctionnent avec d’autres modules Ansible, tels que community.postgresql. Le tutoriel Ansible de la documentation recense davantage d’exemples d’utilisation.


Utiliser PostgreSQL dans un conteneur


Docker

docker run --name pg17 -e POSTGRES_PASSWORD=mdpsuperfort -d postgres
docker exec -it pg17 psql -U postgres
  • Image officielle Docker Inc (pas PGDG !) : https://hub.docker.com/_/postgres
  • Très pratique pour le développement
  • Beaucoup de possibilités avancées avec docker-compose
  • Ne jamais lancer 2 conteneurs Docker sur un même PGDATA
  • Une base de données est-elle faite pour du docker ?
    • supervision système délicate

Les conteneurs comme docker et assimilés (podman, containerd, etc.) permettent d’isoler l’installation de PostgreSQL sur un poste sans avoir à gérer de machine virtuelle. Une fois configuré, le conteneur permet d’avoir un contexte d’exécution de PostgreSQL identique peu importe son support. C’est idéal dans le cas de machines « jetables », par exemple les chaînes de CI/CD.

Exemple :

À titre d’exemple, voici les étapes nécessaires à l’installation d’une instance PostgreSQL sous docker.

D’abord, récupérer l’image de la dernière version de PostgreSQL maintenue par la communauté PostgreSQL docker :

docker pull postgres

La commande permet de créer et de lancer un nouveau conteneur à partir d’une image donnée, ici postgres:17.0. Certaines options sont passées en paramètres à initdb :

docker run \
--network net17 \
--name pg17 \
-p 127.0.0.1:16501:5432 \
--env-file password.env \
-e POSTGRES_INITDB_ARGS='--data-checksums --wal-segsize=1' \
-v '/var/lib/postgresql/docker/17/pg17':'/var/lib/postgresql/data' \
-d postgres:17.0 \
-c 'work_mem=10MB' \
-c 'shared_buffers=256MB' \
-c 'cluster_name=pg17'

Cette commande permet au conteneur d’être attaché à un réseau dédié. Celui-ci doit avoir été créé au préalable avec la commande :

docker network create --subnet=192.168.122.0/24 net17

L’option --name permet de nommer le conteneur pour le rendre plus facilement accessible.

L’option -p permet de faire suivre le port 5432 ouvert par le container sur le port 16501 du serveur.

L’option -d permet de faire de l’image un démon en arrière-plan.

Les options -e définissent des variables d’environnement, moyen systématique de passer des informations au conteneur. Ici l’option est utilisée pour le mot de passe et certaines des options d’initdb. On préférera utiliser un fichier .env pour docker compose ou docker run --env-file pour éviter de définir un secret dans la ligne de commande.

L’option --env-file permet de passer en paramètre un fichier contenant des variables d’environnement. Ici nous passons un fichier contenant le mot de passe dont le contenu est le suivant :

# fichier password.env
POSTGRES_PASSWORD=mdpsuperfort

L’option -v '/var/lib/postgresql/docker/17/pg17':'/var/lib/postgresql/data' lie un répertoire du disque sur le PGDATA du container : ainsi les fichiers de la base survivront à la disparition du conteneur.

Les paramètres de PostgreSQL dans les -c sont transmis directement à la ligne de commande du postmaster.

Une fois l’image téléchargée et le conteneur instancié, il est possible d’accéder directement à psql via la commande suivante :

docker exec -it pg17 psql -U postgres

Ou directement via le serveur hôte s’il dispose de psql :

psql -h 127.0.0.1 -p 16501 -U postgres

En production, il est recommandé de dédier un serveur à chaque instance PostgreSQL. De ce fait, docker permet de reproduire cette isolation pour des cas d’usage de développement et prototypage où dédier une machine à PostgreSQL est soit trop coûteux, soit trop complexe.

Exemple avec docker compose :

Il est évidement possible de passer par l’outil docker compose pour déployer une instance PostgreSQL conteneurisée. Voici la commande docker run précédente portée sous docker compose dans un fichier YAML.

Fichier docker-compose.yml

version: '3.3'

networks:
  net16:
    ipam:
      driver: default
      config:
        - subnet: 192.168.122.0/24

services:
    pg16:
      networks:
          - net17
      container_name: pg17
      ports:
          - '127.0.0.1:16501:5432'
      env_file:
          - password.env
      environment:
          - POSTGRES_INITDB_ARGS=--data-checksums --wal-segsize=1
      volumes:
          - /var/lib/postgresql/docker/17/pg17:/var/lib/postgresql/data'
      image: postgres:17.0
      command: [
      -c, work_mem=10MB,
      -c, shared_buffers=256MB,
      ]

La commande docker compose --file docker-compose.yml up -d permet la création, ou le lancement, des objets définis dans le fichier docker-compose.yml. L’option -d permet de lancer les conteneurs en arrière-plan.

# Au premier lancement
docker compose --file docker-compose.yml up -d
Creating network "postgresql_net17" with the default driver
Creating pg17 ... done 

# Aux lancements suivants
docker compose --file docker-compose.yml up -d
Starting pg17 ... done

Pour arrêter, il faut utiliser l’option stop :

docker compose --file docker-compose.yml stop
Stopping pg17 ... done

Limites de l’utilisation de PostgreSQL sous docker :

Ne lancez jamais 2 instances de PostgreSQL sous docker sur le même PGDATA ! Les sécurités habituelles de PostgreSQL ne fonctionnent pas et les deux instances écriront toutes les deux dans les fichiers. La corruption est garantie.

En production, si l’utilisation de PostgreSQL sous docker est de nos jours très fréquente, ce n’est pas toujours une bonne idée. Une base de données est l’inverse total d’une machine sans état et jetable, et pour les performances, il vaut mieux en première intention gonfler la base (scale up) que multiplier les instances (scale out), qui sont de toute façon toutes dépendantes de l’instance primaire. Si le choix est fait de fonctionner sous docker, voire Kubernetes, pour des raisons architecturales, la base de données est sans doute le dernier composant à migrer.

De plus, pour les performances, la supervision au niveau système devient très compliquée, et le DBA est encore plus aveugle qu’avec une virtualisation classique. L’ajout d’outillage ou d’extensions non fournies avec PostgreSQL devient un peu plus compliquée (docker stats n’est qu’un bon début).


Kubernetes

  • Dans les très grands environnements
    • mise à l’échelle automatique
    • complexité à assumer
  • Réclame de l’expérience sur PG et K8s
  • Réservez un worker à PostgreSQL
  • Quel opérateur ?
    • CrunchyData, CloudNativePG, Percona…
    • nombreux outils, HA…

La taille de certains SI, ou le besoin très important d’automatisation, peuvent justifier le déploiement d’un outil comme Kubernetes. Il permet entre autres l’autoscaling lors de variations de charges importantes.

Comme déjà dit ci-dessus : les bases de données sont tout l’inverse de machines jetables. Il est toutefois possible de les déployer sur Kubernetes, mais vos bases PostgreSQL sont sans doute les derniers éléments de votre infrastructure à migrer vers Kubernetes, si même leur criticité le permet. En effet, PostgreSQL est plutôt adapté à une scalabilité verticale (ajout de mémoire ou processeurs) qu’horizontale, même si la multiplication de secondaires en lecture seule ou du sharding peut être envisageable. Par contre, des bases de données non critiques et/ou à courte durée de vie (par exemple pour du développement), et surtout standardisées, ont intérêt à être gérées par Kubernetes.

L’utilisation de Kubernetes apporte une couche d’abstraction supplémentaire et demande nécessairement des connaissances à avoir sur son fonctionnement. La manière de travailler avec PostgreSQL changera aussi. On peut par exemple citer la modification des paramètres de configuration qui pourra, selon la façon dont est déployée l’instance dans Kubernetes, être très différente de la manière « traditionnelle ». Kubernetes est une plateforme de déploiement d’applications et ne répondra pas à votre place aux considérations liées à PostgreSQL :

  • les problèmes de performance dus au SQL ou aux disques trop lents, existeront toujours ;
  • les montées de versions seront toujours à faire régulièrement ;
  • comprendre les concepts essentiels de PostgreSQL (instances, bases, schéma, etc) reste obligatoire ;
  • le besoin d’arbitrage entre peu de grosses instances multibases, et de nombreuses petites instances séparée, reste présent ; même si la facilité de déploiement et la légèreté de PostgreSQL favorise les instances monobases.

Même si en théorie on peut s’en passer, il est fortement conseillé de passer par un opérateur Kubernetes dédié à PostgreSQL, un composant qui facilite énormément l’administration de PostgreSQL dans Kubernetes. Plusieurs fournisseurs en proposent un, chacun avec ses avantages, inconvénients et limites. Ils proposent notamment :

  • des images docker toutes prêtes, basées sur les paquets du PGDG, ou des images maison, avec ou sans outils annexes (comme le pooler pgBouncer), et plus ou moins d’extensions ;
  • la mise en place de sauvegarde PITR avec des outils spécifiques ;
  • une mise en place automatique de haute disponibilité, avec la construction automatique d’instances en réplication (lecture seule) et des bascules automatiques, le tout étant basé sur Patroni ou sur les mécanismes de Kubernetes ;
  • des mécanismes de surveillance à destination de Kubernetes pour la connaissance de la santé des instances ;
  • des outils de supervision ;
  • des mécanismes de mises à jour mineure et majeure ;
  • et bien d’autres choses encore.

Citons notamment les opérateurs suivants :

  • postgres-operator, de Zalando (avec WAL-E et Patroni, image docker propre) ;
  • postgres-operator, de Crunchy Data (avec pgBackRest et Patroni, dépôts propres) ;
  • CloudNativePG, initié à l’origine par EDB, le plus récent et peut-être le plus « à la mode » et « cloud native » (avec barman) ;
  • Percona Operator for PostgreSQL (avec pgBackRest, dépôts propres) ;
  • StackGres (avec Patroni)

Pour choisir un opérateur, les points d’attention sont :

  • la licence (opérateur et images docker), car certains fournisseurs poussent bien sûr à la version payante ;
  • la disponibilité et la transparence du code source ;
  • la pérennité de l’opérateur ;
  • la documentation ;
  • la rapidité de mise à jour des versions de PostgreSQL et, à l’inverse, le support de vieilles versions de PostgreSQL que vous seriez obligés de conserver ;
  • le niveau de capacité de l’opérateur (Level III, IV, V) ;
  • le comportement lors des mises à jour pour limiter les interruptions de service ;
  • la facilité d’adaptation de l’image docker sous-jacente (par exemple avec une extension supplémentaire) ;
  • les outils intégrés (sauvegarde, supervision…), leur facilité d’emploi, leur intégration avec l’existant (chaque éditeur a tendance à pousser ses outils propres) ;
  • la présence de télémétrie, rarement bienvenue.

Le sujet Kubernetes est trop vaste et évolue trop vite pour être développé ici. Ajoutons juste qu’un conseil fréquent est de réserver un nœud (une machine physique ou virtuelle) à chaque instance PostgreSQL, notamment pour s’assurer que l’instance aura toujours à disposition des capacités RAM et CPU. Dédier un espace de stockage aux bases est également important, car une base de données est très sensible aux performances du stockage.


Installation sous Windows


Comment installer sous Windows ?

  • Un seul installeur graphique disponible, proposé par EnterpriseDB
  • Ou archive des binaires

Le portage de PostgreSQL sous Windows a justifié à lui seul le passage de la branche 7 à la branche 8 du projet. Le système de fichiers NTFS est obligatoire car, contrairement à la VFAT, il gère les liens symboliques (appelés jonctions sous Windows).

L’installateur n’existe plus qu’en version 64 bits depuis PostgreSQL 11.

Étant donné la quantité de travail nécessaire pour le développement et la maintenance de l’installeur graphique, la communauté a abandonné l’installeur graphique qu’elle a proposé un temps. EntrepriseDB a continué de proposer gratuitement le sien, pour la version communautaire comme pour leur version payante. D’autres installateurs ont été proposés par d’autres éditeurs.

Il contient le serveur PostgreSQL avec les modules contrib ainsi que pgAdmin 4, et aussi un outil appelé StackBuilder permettant d’installer d’autres outils comme des pilotes JDBC, ODBC, C#, ou PostGIS.

Pour installer PostgreSQL sans installateur ni compilation, EBD propose aussi une archive des binaires compilés, même de versions aussi anciennes que la 9.3.


Installeur graphique

Installeur graphique - bienvenue

Son utilisation est tout à fait classique. Il y a plusieurs écrans de saisie d’informations :

  • le répertoire d’installation des binaires ;
  • le choix des outils (copie d’écran ci-dessus), notamment des outils en ligne de commande (à conserver impérativement), des pilotes et de pgAdmin 4 ;
  • le répertoire des données de la première instance ;
  • le mot de passe de l’utilisateur postgres ;
  • le numéro de port ;
  • la locale par défaut.

Le répertoire d’installation a une valeur par défaut généralement convenable car il n’existe pas vraiment de raison d’installer les binaires PostgreSQL en dehors du répertoire Program Files.

Par contre, le répertoire des données de l’instance PostgreSQL. n’a pas à être dans ce même répertoire Program Files ! Il est souvent modifié pour un autre disque que le disque système.

Le numéro de port est par défaut le 5432, sauf si d’autres instances sont déjà installées. Dans ce cas, l’installeur propose un numéro de port non utilisé.

Le mot de passe est celui de l’utilisateur postgres au sein de PostgreSQL. En cas de mise à jour, il faut saisir l’ancien mot de passe. Le service lui-même et tous ses processus tourneront avec le compte système générique NETWORK SERVICE (Compte de service réseau).

La commande initdb est exécutée pour créer le répertoire des données. Un service est ajouté pour lancer automatiquement le serveur au démarrage de Windows.

Un sous-menu du menu Démarrage contient le nécessaire pour interagir avec le serveur PostgreSQL, comme une icône pour recharger la configuration et surtout pgAdmin 4, qui se lancera dans un navigateur.

L’outil StackBuilder, lancé dans la foulée, permet de télécharger et d’installer d’autres outils : pilotes pour différents langages (Npgsql pour C#, pgJDBC, psqlODBC), PostGIS… installés dans le répertoire de l’utilisateur en cours.

L’installateur peut être utilisé uniquement en ligne de commande (voir les options avec --help).

Cet installeur existe aussi sous macOS. Autant il est préférable de passer par les paquets de sa distribution Linux, autant il est recommandé d’utiliser cet installeur sous macOS.


Premiers réglages


Au menu

  • Sécurité
  • Configuration
    • accès
    • connexions
    • mémoire
    • journaux
    • traces
    • tâches de fond

Sécurité

  • Politique d’accès :
    • pour l’utilisateur postgres système
    • pour le rôle postgres
  • Règles d’accès à l’instance dans pg_hba.conf

Selon l’environnement et la politique de sécurité interne à l’entreprise, il faut potentiellement initialiser un mot de passe pour l’utilisateur système postgres :

$ passwd postgres

Sans mot de passe, il faudra passer par un système comme sudo pour pouvoir exécuter des commandes en tant qu’utilisateur postgres, ce qui sera nécessaire au moins au début.

Le fait de savoir qu’un utilisateur existe sur un serveur permet à un utilisateur hostile de tenter de forcer une connexion par force brute. Par exemple, ce billet de blog, montre que l’utilisateur postgres est dans le top 10 des logins attaqués.

La meilleure solution pour éviter ce type d’attaque est de ne pas définir de mot de passe pour l’utilisateur OS postgres et de se connecter uniquement par des échanges de clés SSH.

Il est conseillé de ne fixer aucun mot de passe pour l’utilisateur système. Il en va de même pour le rôle postgres dans l’instance. Une fois connecté au système, nous pourrons utiliser le mode d’authentification local peer pour nous connecter au rôle postgres. Ce mode permet de limiter la surface d’attaque sur son instance.

En cas de besoin d’accès distant en mode superutilisateur, il sera possible de créer des rôles supplémentaires avec des droits superutilisateur. Ces noms ne doivent pas être facile à deviner par de potentiels attaquants. Il faut donc éviter les rôles admin ou root.

Si vous avez besoin de créer des mots de passe, ils doivent bien sûr être longs et complexes (par exemple en les générant avec les utilitaires pwgen ou apg).

Si vous avez utilisé l’installeur proposé par EnterpriseDB, l’utilisateur système et le rôle PostgreSQL ont déjà un mot de passe, celui demandé par l’installeur. Il n’est donc pas nécessaire de leur en configurer un autre.

Enfin, il est important de vérifier les règles d’accès au serveur contenues dans le fichier pg_hba.conf. Ces règles définissent les accès à l’instance en se basant sur plusieurs paramètres : utilisation du réseau ou du socket fichier, en SSL ou non, depuis quel réseau, en utilisant quel rôle, pour quelle base de données et avec quelle méthode d’authentification.


Configuration minimale

  • Fichier postgresql.conf
  • Configuration du moteur
  • Plus de 300 paramètres
  • Quelques paramètres essentiels

La configuration du moteur se fait via un seul fichier, le fichier postgresql.conf. Il se trouve généralement dans le répertoire des données du serveur PostgreSQL. Sous certaines distributions (Debian et affiliés principalement), il est déplacé dans /etc/postgresql/.

Ce fichier contient beaucoup de paramètres, plus de 300, mais seuls quelques-uns sont essentiels à connaître pour avoir une instance fiable et performante.


Précédence des paramètres

Ordre de précédence des paramètres

PostgreSQL offre une certaine granularité dans sa configuration, ainsi certains paramètres peuvent être surchargés par rapport au fichier postgresql.conf. Il est utile de connaître l’ordre de précédence. Par exemple, un utilisateur peut spécifier un paramètre dans sa session avec l’ordre SET, celui-ci sera prioritaire par rapport à la configuration présente dans le fichier postgresql.conf.


Configuration des connexions : accès au serveur

  • listen_addresses = '*' (systématique)
  • port = 5432
  • password_encryption = scram-sha-256 (v10+)

Ouvrir les accès :

Par défaut, une instance PostgreSQL n’écoute que sur l’interface de boucle locale (localhost) et pas sur les autres interfaces réseaux. Pour autoriser les connexions externes à PostgreSQL, il faut modifier le paramètre listen_addresses, en général ainsi :

listen_addresses = '*'

La valeur * est un joker indiquant que PostgreSQL doit écouter sur toutes les interfaces réseaux disponibles au moment où il est lancé. Il est aussi possible d’indiquer les interfaces, une à une, en les séparant avec des virgules. Cette méthode est intéressante lorsqu’on veut éviter que l’instance écoute sur une interface donnée. Par prudence il est possible de se limiter aux interfaces destinées à être utilisées :

listen_addresses = 'localhost, 10.1.123.123'

La restriction par listen_addresses est un premier niveau de sécurité. Elle est complémentaire de la méthode plus fine par pg_hba.conf, par les IP clientes, utilisateur et base, qu’il faudra de toute façon déployer. De plus, modifier listen_addresses impose de redémarrer l’instance.

Port :

Le port par défaut des connexions TCP/IP est le 5432. C’est la valeur traditionnelle et connue de tous les outils courants.

La modifier n’a d’intérêt que si vous voulez exécuter plusieurs instances PostgreSQL sur le même serveur (physique ou virtuel). En effet, plusieurs instances sur une même machine ne peuvent pas écouter sur le même couple adresse IP et port.

Une instance PostgreSQL n’écoute jamais que sur ce seul port, et tous les clients se connectent dessus. Il n’existe pas de notion de listener ou d’outil de redirection comme sur d’autres bases de données concurrentes, du moins sans outil supplémentaire (par exemple le pooler PgBouncer).

S’il y a plusieurs instances dans une même machine, elles devront posséder chacune un couple adresse IP/port unique. En pratique, il vaut mieux attribuer un port par instance. Bien sûr, PostgreSQL refusera de démarrer s’il voit que le port est déjà occupé.

Ne confondez pas la connexion à localhost (soit ::1 en IPv6 ou 127.0.0.1 en IPv4), qui utilise les ports TCP/IP, et la connexion dite local, passant par les sockets de l’OS (par défaut /var/run/postgresql/.s.PGSQL.5432 sur les distributions les plus courantes). La distinction est importante dans pg_hba.conf notamment.

Chiffrement des mots de passe :

À partir de la version 10 et avant la version 14, le paramètre password_encryption est à modifier dès l’installation. Il définit l’algorithme de chiffrement utilisé pour le stockage des mots de passe. La valeur scram-sha-256 permettra d’utiliser la nouvelle norme, plus sécurisée que l’ancien md5. Ce n’est plus nécessaire à partir de la version 14 car c’est la valeur par défaut. Avant toute modification, vérifiez quand même que vos outils clients sont compatibles. Au besoin, vous pouvez revenir à md5 pour un utilisateur donné.


Configuration du nombre de connexions

  • max_connections = 100
  • 1 connexion = 1 processus serveur
  • Compromis entre
    • CPU / nombre requêtes actives / RAM / complexité
  • Danger si trop haut !
    • performances (même avec des connexions inactives)
    • risque de saturation
  • Possibilité de réserver quelques connexions pour l’administration

Le nombre de connexions simultanées est limité par le paramètre max_connections. Dès que ce nombre est atteint, les connexions suivantes sont refusées avec un message d’erreur, et ce jusqu’à ce qu’un utilisateur connecté se déconnecte.

max_connections vaut par défaut 100, et c’est généralement suffisant en première intention.

Noter qu’il existe un paramètre superuser_reserved_connections (à 3 par défaut) qui réserve quelques connexions au superutilisateur pour qu’il puisse se connecter malgré une saturation. Depuis PostgreSQL 16, il existe un autre paramètre nommé reserved_connections, (à 0 par défaut) pour réserver quelques connexions aux utilisateurs à qui l’on aura attribué un rôle spécifique, nommé pg_use_reserved_connections. Ce peut être utile pour des utilisateurs non applicatifs (supervision et sauvegarde notamment) à qui l’on ne veut ou peut pas donner le rôle SUPERUSER.

Il peut être intéressant de diminuer max_connections pour interdire d’avoir trop de connexions actives. Cela permet de soulager les entrées-sorties, ou de monter work_mem (la mémoire de tri). À l’inverse, il est possible d’augmenter max_connections pour qu’un plus grand nombre d’utilisateurs ou d’applications puisse se connecter en même temps.

Au niveau mémoire, un processus consomme par défaut 2 Mo de mémoire vive. Cette consommation peut augmenter suivant son activité.

Il faut surtout savoir qu’à chaque connexion se voit associée un processus sur le serveur, processus qui n’est vraiment actif qu’à l’exécution d’une requête. Il s’agit donc d’arbitrer entre :

  • le nombre de requêtes à exécuter à un instant T ;
  • le nombre de CPU disponibles ;
  • la complexité et la longueur des requêtes ;
  • et même le nombre de processus que peut gérer l’OS.

L’établissement a un certain coût également. Il faut éviter qu’une application se connecte et se déconnecte sans cesse.

Il ne sert à rien d’autoriser des milliers de connexions s’il n’y a que quelques processeurs, ou si les requêtes sont lourdes. Si le nombre de requêtes réellement actives augmente fortement, le serveur peut s’effondrer. Restreindre les connexions permet de préserver le serveur, même si certaines connexions sont refusées.

Le paramétrage est compliqué par le fait qu’une même requête peut mobiliser plusieurs processeurs si elle est parallélisée. Certaines requêtes seront limitées par le CPU, d’autres par la bande passante des disques.

Enfin, même si une connexion inactive ne consomme pas de CPU et peu de RAM, elle a tout de même un impact. En effet, une connexion active va générer assez fréquemment ce qu’on appelle un snapshot (ou une image) de l’état des transactions de la base. La durée de création de ce snapshot dépend principalement du nombre de connexions, actives ou non, sur le serveur. Donc une connexion active consommera plus de CPU s’il y a 399 autres connexions, actives ou non, que s’il y a 9 connexions, actives ou non. Ce comportement est partiellement corrigé avec la version 14. Mais il vaut mieux éviter d’avoir des milliers de connexions ouvertes « au cas où ».

Intercaler un « pooler » comme PgBouncer entre les clients et l’instance peut se justifier dans certains cas :

  • connexions/déconnexions très fréquentes ;
  • centaines, voire milliers, de connexions généralement inactives ;
  • limitation du nombre de connexions actives avec mise en attente au niveau du pooler (sans erreur).

Configuration de la mémoire partagée

  • shared_buffers = (?)GB
    • 25 % de la RAM généralement
    • max 40 %
    • complémentaire du cache OS

Shared buffers :

Chaque fois que PostgreSQL a besoin de lire ou d’écrire des données, il les charge d’abord dans son cache interne. Ce cache ne sert qu’à ça : stocker des blocs disques qui sont accessibles à tous les processus PostgreSQL, ce qui permet d’éviter de trop fréquents accès disques car ces accès sont lents. La taille de ce cache dépend d’un paramètre appelé shared_buffers.

La documentation officielle conseille ceci pour dimensionner shared_buffers :

Un bon point de départ est 25 % de la mémoire vive totale. Ne pas dépasser 40 %, car le cache du système d’exploitation est aussi utilisé.

Sur une machine dédiée de 32 Go de RAM, cela donne donc :

shared_buffers = 8GB

Le défaut de 128 Mo n’est donc pas adapté à un serveur sur une machine récente.

Suivant les cas, une valeur inférieure ou supérieure à 25 % sera encore meilleure pour les performances, mais il faudra tester avec votre charge (en lecture, en écriture, et avec le bon nombre de clients).

Le cache système limite la plupart du temps l’impact d’un mauvais paramétrage de shared_buffers, et il est moins grave de sous-dimensionner un peu shared_buffers que de le sur-dimensionner.

Attention : une valeur élevée de shared_buffers (au-delà de 8 Go) nécessite de paramétrer finement le système d’exploitation (Huge Pages notamment) et d’autres paramètres liés aux journaux et checkpoints comme max_wal_size. Il faut aussi s’assurer qu’il restera de la mémoire pour le reste des opérations (tri…) et donc adapter work_mem.

Modifier shared_buffers impose de redémarrer l’instance.


Configuration : mémoire des processus

  • work_mem

    • par processus, voire nœud
    • valeur très dépendante de la charge et des requêtes
    • fichiers temporaires vs saturation RAM
  • × hash_mem_multiplier

  • maintenance_work_mem

  • Pas de limite stricte à la consommation mémoire des sessions

    • Augmenter prudemment & superviser

Les processus de PostgreSQL ont accès à la mémoire partagée, définie principalement par shared_buffers, mais ils ont aussi leur mémoire propre. Cette mémoire n’est utilisable que par le processus l’ayant allouée.

  • Le paramètre le plus important est work_mem, qui définit la taille maximale de la mémoire de travail d’un ORDER BY, de certaines jointures, pour la déduplication… que peut utiliser un processus sur un nœud de requête, principalement lors d’opérations de tri ou regroupement.
  • Autre paramètre capital, maintenance_work_mem qui est la mémoire utilisable pour les opérations de maintenance lourdes : VACUUM, CREATE INDEX, REINDEX, ajouts de clé étrangère…

Cette mémoire liée au processus est rendue immédiatement après la fin de l’ordre concerné.

  • Il existe aussi logical_decoding_work_mem (défaut : 64 Mo), utilisable pour chacun des flux de réplication logique (s’il y en a, ils sont rarement nombreux).

Opérations de maintenance & maintenance_work_mem :

maintenance_work_mem peut être monté à 256 Mo à 1 Go, voire plus sur les machines récentes, car il concerne des opérations lourdes (indexation, nettoyage des index par VACUUM…). Leurs consommations de RAM s’additionnent, mais en pratique ces opérations sont rarement exécutées plusieurs fois simultanément.

Monter au-delà de 1 Go n’a d’intérêt que pour la création ou la réindexation de très gros index.

autovacuum_work_mem est la mémoire que s’autorise à prendre l’autovacuum pour les nettoyages d’index, et ce pour chaque worker de l’autovacuum (3 maximum par défaut). Par défaut, ce paramètre reprend maintenance_work_mem, et est généralement laissé tel quel.

Paramétrage de work_mem :

Pour work_mem, c’est beaucoup plus compliqué.

Si work_mem est trop bas, beaucoup d’opérations de tri, y compris nombre de jointures, ne s’effectueront pas en RAM. Par exemple, si une jointure par hachage impose d’utiliser 100 Mo en mémoire, mais que work_mem vaut 10 Mo, PostgreSQL écrira des dizaines de Mo sur disque à chaque appel de la jointure. Si, par contre, le paramètre work_mem vaut 120 Mo, aucune écriture n’aura lieu sur disque, ce qui accélérera forcément la requête.

Trop de fichiers temporaires peuvent ralentir les opérations, voire saturer le disque. Un work_mem trop bas peut aussi contraindre le planificateur à choisir des plans d’exécution moins optimaux.

Par contre, si work_mem est trop haut, et que trop de requêtes le consomment simultanément, le danger est de saturer la RAM. Il n’existe en effet pas de limite à la consommation des sessions de PostgreSQL, ni globalement ni par session !

Or le paramétrage de l’overcommit sous Linux est par défaut très permissif, le noyau ne bloquera rien. La première conséquence de la saturation de mémoire est l’assèchement du cache système (complémentaire de celui de PostgreSQL), et la dégradation des performances. Puis le système va se mettre à swapper, avec à la clé un ralentissement général et durable. Enfin le noyau, à court de mémoire, peut être amené à tuer un processus de PostgreSQL. Cela mène à l’arrêt de l’instance, ou plus fréquemment à son redémarrage brutal avec coupure de toutes les connexions et requêtes en cours.

Toutefois, si l’administrateur paramètre correctement l’overcommit (voir https://dali.bo/j1_html#configuration-de-la-surréservation-mémoire), Linux refusera d’allouer la RAM et la requête tombera en erreur, mais le cache système sera préservé, et PostgreSQL ne tombera pas.

Suivant la complexité des requêtes, il est possible qu’un processus utilise plusieurs fois work_mem (par exemple si une requête fait une jointure et un tri, ou qu’un nœud est parallélisé). À l’inverse, beaucoup de requêtes ne nécessitent aucune mémoire de travail.

La valeur de work_mem dépend donc beaucoup de la mémoire disponible, des requêtes et du nombre de connexions actives.

Si le nombre de requêtes simultanées est important, work_mem devra être faible. Avec peu de requêtes simultanées, work_mem pourra être augmenté sans risque.

Il n’y a pas de formule de calcul miracle. Une première estimation courante, bien que très conservatrice, peut être :

work_mem = mémoire / max_connections

On obtient alors, sur un serveur dédié avec 16 Go de RAM et 200 connexions autorisées :

work_mem = 80MB

Mais max_connections est fréquemment surdimensionné, et beaucoup de sessions sont inactives. work_mem est alors sous-dimensionné.

Plus finement, Christophe Pettus propose en première intention :

work_mem = 4 × mémoire libre / max_connections

Soit, pour une machine dédiée avec 16 Go de RAM, donc 4 Go de shared buffers, et 200 connections :

work_mem = 240MB

Dans l’idéal, si l’on a le temps pour une étude, on montera work_mem jusqu’à voir disparaître l’essentiel des fichiers temporaires dans les traces, tout en restant loin de saturer la RAM lors des pics de charge.

En pratique, le défaut de 4 Mo est très conservateur, souvent insuffisant. Généralement, la valeur varie entre 10 et 100 Mo. Au-delà de 100 Mo, il y a souvent un problème ailleurs : des tris sur de trop gros volumes de données, une mémoire insuffisante, un manque d’index (utilisés pour les tris), etc. Des valeurs vraiment grandes ne sont valables que sur des systèmes d’infocentre.

Augmenter globalement la valeur du work_mem peut parfois mener à une consommation excessive de mémoire. Il est possible de ne la modifier que le temps d’une session pour les besoins d’une requête ou d’un traitement particulier :

SET work_mem TO '30MB' ;

hash_mem_multiplier :

hash_mem_multiplier est un paramètre multiplicateur, qui peut s’appliquer à certaines opérations (le hachage, lors de jointures ou agrégations). Par défaut, il vaut 1 en versions 13 et 14, et 2 à partir de la 15. Le seuil de consommation fixé par work_mem est alors multiplié d’autant. hash_mem_multiplier permet de donner plus de RAM à ces opérations sans augmenter globalement work_mem. Il peut lui aussi être modifié dans une session.

Il existe d’autres paramètres influant sur les besoins en mémoires, moins importants pour une première approche.


Configuration des journaux de transactions 1/2

fsync = on (si vous tenez à vos données)

À chaque fois qu’une transaction est validée (COMMIT), PostgreSQL écrit les modifications qu’elle a générées dans les journaux de transactions.

Afin de garantir la durabilité, PostgreSQL effectue des écritures synchrones des journaux de transaction, donc une écriture physique des données sur le disque. Cela a un coût important sur les performances en écritures s’il y a de nombreuses transactions mais c’est le prix de la sécurité.

Le paramètre fsync permet de désactiver l’envoi de l’ordre de synchronisation au système d’exploitation. Ce paramètre doit rester à on en production. Dans le cas contraire, un arrêt brutal de la machine peut mener à la perte des journaux non encore enregistrés et à la corruption de l’instance. D’autres paramètres et techniques existent pour gagner en performance (et notamment si certaines données peuvent être perdues) sans pour autant risquer de corrompre l’instance.


Configuration des journaux de transactions 2/2

Niveaux de cache et fsync

Une écriture peut être soit synchrone soit asynchrone. Pour comprendre ce mécanisme, nous allons simplifier le cheminement de l’écriture d’un bloc :

  • Dans le cas d’une écriture asynchrone : Un processus qui modifie un fichier écrit en fait d’abord dans le cache du système de fichiers du système d’exploitation (OS), cache situé en RAM (mémoire volatile). L’OS confirme tout de suite au processus que l’écriture a été réalisée pour lui rendre la main au plus vite : il y a donc un gain en performance important. Cependant, le bloc ne sera écrit sur disque que plus tard afin notamment de grouper les demandes d’écritures des autres processus, et de réduire les déplacements des têtes de lecture/écriture des disques, qui sont des opérations coûteuses en temps. Entre la confirmation de l’écriture et l’écriture réelle sur les disques, il peut se passer un certain délai : si une panne survient durant celui-ci, les données soi-disant écrites seront perdues, car pas encore physiquement sur le disque.

  • Dans le cas d’une écriture synchrone : Un processus écrit dans le cache du système d’exploitation, puis demande explicitement à l’OS d’effectuer la synchronisation (écriture physique) sur disque. Les blocs sont donc écrits sur les disques immédiatement et le processus n’a la confirmation de l’écriture qu’une fois cela fait. Il attendra donc pendant la durée de cette opération, mais il aura la garantie que la donnée est bien présente physiquement sur les disques. Cette synchronisation est très coûteuse et lente (encore plus avec un disque dur classique et ses têtes de disques à déplacer).

Un phénomène équivalent peut se produire à nouveau au niveau matériel (hors du contrôle de l’OS) : pour gagner en performance, les constructeurs ont rajouté un système de cache au sein des cartes RAID. L’OS (et donc le processus qui écrit) a donc confirmation de l’écriture dès que la donnée est présente dans ce cache, alors qu’elle n’est pas encore écrite sur disque. Afin d’éviter la perte de donnée en cas de panne électrique, ce cache est secouru par une batterie qui laissera le temps d’écrire le contenu du cache. Vérifiez qu’elle est bien présente sur vos disques et vos cartes contrôleur RAID.


Configuration des traces

  • Selon système/distribution :
    • log_destination
    • logging_collector
    • emplacement et nom différent pour postgresql-????.log
  • log_line_prefix à compléter :
    • log_line_prefix = '%t [%p]: user=%u,db=%d,app=%a,client=%h '
  • lc_messages = C (anglais)

PostgreSQL dispose de plusieurs moyens pour enregistrer les traces : soit il les envoie sur la sortie des erreurs (stderr, csvlog et jsonlog), soit il les envoie à syslog (syslog, seulement sous Unix), soit il les envoie au journal des événements (eventlog, sous Windows uniquement). Dans le cas où les traces sont envoyées sur la sortie des erreurs, il peut récupérer les messages via un démon appelé logger process qui va enregistrer les messages dans des fichiers. Ce démon s’active en configurant le paramètre logging_collector à on.

Tout cela est configuré par défaut différemment selon le système et la distribution. Red Hat active logging_collector et PostgreSQL dépose ses traces dans des fichiers journaliers $PGDATA/log/postgresql-<jour de la semaine>.log. Debian utilise stderr sans autre paramétrage et c’est le système qui dépose les traces dans /var/log/postgresql/postgresql-VERSION-nominstance.log. Les deux variantes fonctionnent. En fonction des habitudes et contraintes locales, il est possible de préférer et d’activer l’une ou l’autre.

L’entête de chaque ligne des traces doit contenir au moins la date et l’heure exacte (%t ou %m suivant la précision désirée) : des traces sans date et heure ne servent à rien. Des entêtes complets sont suggérés par la documentation de l’analyseur de log pgBadger :

log_line_prefix = '%t [%p]: [%l-1] db=%d,user=%u,app=%a,client=%h '

Beaucoup d’utilisateurs français récupèrent les traces de PostgreSQL en français. Bien que cela semble une bonne idée au départ, cela se révèle être souvent un problème. Non pas à cause de la qualité de la traduction, mais plutôt parce que les outils de traitement des traces fonctionnent uniquement avec des traces en anglais. Même un outil comme pgBadger, pourtant écrit par un Français, ne sait pas interpréter des traces en français. De plus, la moindre recherche sur Internet ramènera plus de liens si le message est en anglais. Positionnez donc lc_messages à C.


Configuration des tâches de fond

Laisser ces deux paramètres à on :

  • autovacuum
  • track_counts

En dehors du logger process, PostgreSQL dispose d’autres tâches de fond.

Les processus autovacuum jouent un rôle important pour disposer de bonnes performances : ils empêchent une fragmentation excessive des tables et index, et mettent à jour les statistiques sur les données (statistiques servant à l’optimiseur de requêtes).

La récupération des statistiques sur l’activité permet le bon fonctionnement de l’autovacuum et donne de nombreuses informations importantes à l’administrateur de bases de données.

Ces deux tâches de fond devraient toujours être activés.


Se faciliter la vie

pgtune existe en plusieurs versions. La version en ligne de commande va détecter automatiquement le nombre de CPU et la quantité de RAM, alors que la version web nécessitera que ces informations soient saisies. Suivant le type d’utilisation, pgtune proposera une configuration adaptée. Cette configuration n’est évidemment pas forcément optimale par rapport à vos applications, tout simplement parce qu’il ne connaît que les ressources et le type d’utilisation, mais c’est généralement un bon point de départ.

pgconfigurator est un outil plus récent, un peu plus graphique, mais il remplit exactement le même but que pgtune.

Enfin, le site postgresql.co.nf est un peu particulier. C’est en quelque sorte une encyclopédie sur les paramètres de PostgreSQL, mais il est aussi possible de lui faire analyser une configuration. Après analyse, des informations supplémentaires seront affichées pour améliorer cette configuration, que ce soit pour la stabilité du serveur comme pour ses performances.


Mise à jour


Mise à jour

  • Recommandations
  • Mise à jour mineure
  • Mise à jour majeure
  • Mise à jour de l’OS

Recommandations

  • Les Release Notes
  • Intégrité des données
  • Bien redémarrer le serveur !

Chaque nouvelle version de PostgreSQL est accompagnée d’une note expliquant les améliorations, les corrections et les innovations apportées par cette version, qu’elle soit majeure ou mineure. Ces notes contiennent toujours une section dédiée aux mises à jour dans laquelle se trouvent des conseils essentiels.

Les Releases Notes sont présentes dans l’annexe E de la documentation officielle.

Les données de votre instance PostgreSQL sont toujours compatibles d’une version mineure à l’autre. Ainsi, les mises à jour vers une version mineure supérieure peuvent se faire sans migration de données, sauf cas exceptionnel qui serait alors précisé dans les notes de version. Par exemple, de la 15.3 à la 15.4, il a été recommandé de reconstruire les index de type BRIN pour prendre en compte une correction de bug les concernant. Autre exemple : à partir de la 10.3, pg_dump a imposé des noms d’objets qualifiés pour des raisons de sécurité, ce qui a posé problème pour certains réimports.

Pensez éventuellement à faire une sauvegarde préalable par sécurité.

À contrario, si la mise à jour consiste en un changement de version majeure (par exemple, de la 11 à la 17), il est nécessaire de s’assurer que les données seront transférées correctement sans incompatibilité. Là encore, il est important de lire les Releases Notes avant la mise à jour.

Le site why-upgrade, basé sur les release notes, permet de compiler les différences entre plusieurs versions de PostgreSQL.

Dans tous les cas, pensez à bien redémarrer le serveur. Mettre à jour les binaires ne suffit pas.


Mise à jour mineure

  • Méthode
    • arrêter PostgreSQL
    • mettre à jour les binaires
    • redémarrer PostgreSQL
  • Pas besoin de s’occuper des données, sauf cas exceptionnel
    • bien lire les Release Notes pour s’en assurer

Faire une mise à jour mineure est simple et rapide.

La première action est de lire les Release Notes pour s’assurer qu’il n’y a pas à se préoccuper des données. C’est généralement le cas mais il est préférable de s’en assurer avant qu’il ne soit trop tard.

La deuxième action est de faire la mise à jour. Tout dépend de la façon dont PostgreSQL a été installé :

  • par compilation, il suffit de remplacer les anciens binaires par les nouveaux ;
  • par paquets précompilés, il suffit d’utiliser le système de paquets (apt sur Debian et affiliés, yum ou dnf sur Red Hat et affiliés) ;
  • par l’installeur graphique, en le ré-exécutant.

Ceci fait, un redémarrage du serveur est nécessaire. Il est intéressant de noter que les paquets Debian s’occupent directement de cette opération. Il n’est donc pas nécessaire de le refaire.


Mise à jour majeure

  • Bien lire les Release Notes
  • Bien tester l’application avec la nouvelle version
    • rechercher les régressions en terme de fonctionnalités et de performances
    • penser aux extensions et aux outils
  • Pour mettre à jour
    • mise à jour des binaires
    • et mise à jour/traitement des fichiers de données
  • 3 méthodes
    • dump/restore
    • réplication logique
    • pg_upgrade

Faire une mise à jour majeure est une opération complexe à préparer prudemment.

La première action là-aussi est de lire les Release Notes pour bien prendre en compte les régressions potentielles en terme de fonctionnalités et/ou de performances. Cela n’arrive presque jamais mais c’est possible malgré toutes les précautions mises en place.

La deuxième action est de mettre en place un serveur de tests où se trouve la nouvelle version de PostgreSQL avec les données de production. Ce serveur sert à tester PostgreSQL mais aussi, et même surtout, l’application. Le but est de vérifier encore une fois les régressions possibles.

N’oubliez pas de tester les extensions non officielles, voire développées en interne, que vous avez installées. Elles sont souvent moins bien testées.

N’oubliez pas non plus de tester les outils d’administration, de monitoring, de modélisation. Ils nécessitent souvent une mise à jour pour être compatibles avec la nouvelle version installée.

Une fois que les tests sont concluants, arrive le moment de la mise en production. C’est une étape qui peut être longue car les fichiers de données doivent être traités. Il existe plusieurs méthodes que nous détaillerons après.


Mise à jour majeure par dump/restore

  • Méthode historique
  • Simple et sans risque
    • mais d’autant plus longue que le volume de données est important
  • Outils :
    • pg_dumpall -g puis pg_dump
    • psql puis pg_restore

Il s’agit de la méthode la plus ancienne et la plus sûre. L’idée est de sauvegarder l’ancienne version avec l’outil de sauvegarde de la nouvelle version. pg_dumpall peut suffire, mais pg_dump est malgré tout recommandé.

Le problème de lenteur vient surtout de la restauration. pg_restore est un outil assez lent pour des volumétries importantes. Il convient pour des volumes de données peu conséquents (au plus une centaine de Go), au cas où l’on est patient, ou si les autres méthodes ne sont pas possibles.

Il est cependant possible d’accélérer la restauration grâce à la parallélisation (option --jobs). Ce n’est possible que si la sauvegarde a été faite avec pg_dump -Fd ou -Fc. Il est à noter que cette sauvegarde peut elle aussi être parallélisée (option --jobs là encore).

Il existe un piège peu connu lié à toute migration logique d’une grande base : le gel massif des lignes. Pour des raisons techniques de recyclage des numéros de transaction, PostgreSQL doit « geler » les lignes anciennes et jamais modifiées, ce qui implique de réécrire le bloc. Or, toutes les lignes insérées par une migration ont le même « âge ». Si elles ne sont pas modifiées, ces lignes risquent d’être toutes gelées et réécrites en même temps : ce peut être très brutal en terme de saturation disque, de journaux générés, etc. si la base est grosse. Le délai avant le déclenchement du gel automatique dépend de la consommation des numéros de transaction sur l’instance migrée, et varie de quelques semaines à des années.

Des ordres VACUUM FREEZE sur les plus grosses tables à des moments calmes permettent d’étaler ces écritures. Si ces ordre sont interrompus, l’essentiel de qu’il a pu geler ne sera plus à re-geler plus tard.

Pour les détails, voir https://dali.bo/m4_html#le-wraparound-1 et https://dali.bo/m5_html#paramétrage-du-freeze-1.


Mise à jour majeure par réplication logique

  • Possible entre versions 10 et supérieures
  • Bascule très rapide
  • Et retour possible

La réplication logique rend possible une migration entre deux instances de version majeure différente avec une indisponibilité très courte. Le principe est de répliquer une base à l’identique vers une instance de version plus récente, alors que la production tourne.

La réplication logique n’est disponible en natif qu’à partir de PostgreSQL version 10, la base à migrer doit donc être en version 10 ou supérieure. (Autrefois, on utilisait des outils de réplication par triggers, plus complexes à manier.) Des clés primaires sur chaque table sont très fortement conseillées, mais pas forcément obligatoires.

L’idée est d’installer la nouvelle version de PostgreSQL normalement, sur le même serveur ou sur un autre serveur. On déclare la réplication de l’ancienne instance vers la nouvelle. Les utilisateurs peuvent continuer à travailler pendant le transfert initial des données. Ils verront au pire une baisse de performances, due à la lecture et à l’envoi des données vers le nouveau serveur. Une fois le transfert initial réalisé, les données modifiées entre-temps sont transférées vers le nouveau serveur. Toute cette opération peut s’étaler sur plusieurs jours.

Une fois les deux serveurs synchronisés, il ne reste plus qu’à déclencher un switchover (bascule) ; puis d’attendre que les dernières données soient répliquées, ce qui peut être très rapide ; et enfin de connecter les applications au nouveau serveur. La réplication peut alors être inversée pour garder l’ancienne production synchrone, permettant de rebasculer dessus en cas de problème sans perdre les données modifiées depuis la bascule. Une fois acté que le nouveau serveur donne pleine satisfaction, il suffit de débrancher la réplication logique des deux côtés.

Pour les grosses bases, il existe le même danger d’un gel brutal des lignes comme avec pg_restore.


Mise à jour majeure par pg_upgrade

  • pg_upgrade : fourni avec PostgreSQL
  • Prérequis : pas de changement de format des fichiers entre versions
  • Nécessite les deux versions sur le même serveur
  • Support des serveurs PostgreSQL à migrer :
    • version minimale 9.2 pour pg_upgrade v15
    • version minimale 8.4 sinon

pg_upgrade est certainement l’outil le plus rapide pour une mise à jour majeure.

Il profite du fait que les formats des fichiers de données n’évolue pas, ou de manière rétrocompatible, entre deux versions majeures. Il n’est donc pas nécessaire de tout réécrire.

Grossièrement, son fonctionnement est le suivant. Il récupère la déclaration des objets sur l’ancienne instance avec un pg_dump du schéma de chaque base et de chaque objet global. Il intègre la déclaration des objets dans la nouvelle instance. Il fait un ensemble de traitement sur les identifiants d’objets et de transactions. Puis, il copie les fichiers de données de l’ancienne instance vers la nouvelle instance. La copie est l’opération la plus longue, mais comme il n’est pas nécessaire de reconstruire les index et de vérifier les contraintes, cette opération est bien plus rapide que la restauration d’une sauvegarde style pg_dump. Pour aller encore plus rapidement, il est possible de créer des liens physiques à la place de la copie des fichiers. Ceci fait, la migration est terminée.

En 2010, Stefan Kaltenbrunner et Bruce Momjian avaient mesuré qu’une base de 150 Go mettait 5 heures à être mise à jour avec la méthode historique (sauvegarde/restauration). Elle mettait 44 minutes en mode copie et 42 secondes en mode lien lors de l’utilisation de pg_upgrade.

C’est une migration physique : le problème du gel ultérieur des lignes comme avec pg_restore ne se pose pas.

Vu ses performances, ce serait certainement l’outil à privilégier. Cependant, c’est un outil très complexe et quelques bugs particulièrement méchants ont terni sa réputation. Notre recommandation est de bien tester la mise à jour avant de le faire en production, et uniquement sur des bases suffisamment volumineuses dont le court délai de migration justifie l’utilisation de cet outil.

Lors du développement de la version 15, les développeurs ont supprimé certaines vieilles parties du code, ce qui le rend à présent incompatible avec des versions très anciennes (de la 8.4 à la 9.1, cette dernière remontant tout de même à 2012).


Mise à jour de l’OS

Si vous migrez aussi l’OS ou déplacez les fichiers d’une instance :

  • compatibilité architecture
  • compatibilité librairies
    • réindexation parfois nécessaire
    • ex : Debian 10 et glibc 2.28

Un projet de migration PostgreSQL est souvent l’occasion de mettre à jour le système d’exploitation. Vous pouvez également en profiter pour déplacer l’instance sur un autre serveur à l’OS plus récent en copiant (à froid) le PGDATA.

Il faut bien sûr que l’architecture physique (32/64 bits, big/little indian) reste la même. Cependant, même entre deux versions de la même distribution, certains composants du système d’exploitation peuvent avoir une influence, à commencer par la glibc. Cette librairie fondamentale de Linux définit l’ordre des caractères, ordre utilisé dans les index de champs textes. Une incompatibilité entre deux versions sur ce point oblige donc à reconstruire les index, sous peine d’incohérence avec les fonctions de comparaison sur le nouveau système et de corruption à l’écriture.

Daniel Vérité détaille sur son blog le problème pour les mises à jour entre Debian 9 et 10, à cause de la mise à jour de la glibc en version 2.28. (Voir aussi le wiki PostgreSQL.) L’utilisation des collations ICU dans les index contourne le problème, mais elles sont encore peu répandues. Dans beaucoup de cas, la collation C.UTF-8 propre à PostgreSQL (version 17 au moins) peut éliminer facilement tout souci de ce genre.

Ce problème ne touche bien sûr pas les migrations ou les restaurations avec pg_dump/pg_restore : les données sont alors transmises de manière logique, indépendamment des caractéristiques physiques des instances source et cible, et les index sont systématiquement reconstruits sur la machine cible.


Conclusion

  • L’installation est simple…
  • …mais elle doit être soigneusement préparée
  • Préférer les paquets officiels
  • Attention aux données lors d’une mise à jour !

Pour aller plus loin

  • Documentation officielle, chapitre Installation
  • Documentation Dalibo, pour l’installation sur Windows

Vous pouvez retrouver la documentation en ligne sur https://docs.postgresql.fr/current/installation.html.

La documentation de Dalibo pour l’installation de PostgreSQL sur Windows est disponible sur https://public.dalibo.com/archives/etudes/installer_postgresql_9.0_sous_windows.pdf.


Questions

N’hésitez pas, c’est le moment !


Quiz

Travaux pratiques

La version en ligne des solutions de ces TP est disponible sur https://dali.bo/b_solutions.

Installation depuis les paquets binaires du PGDG (Rocky Linux)

But : Installer PostgreSQL à partir des paquets communautaires.

Cette instance servira aux TP suivants.

Pré-installation

Quelle commande permet d’installer les paquets binaires de PostgreSQL ?

Quelle version est packagée ?

Quels paquets devront également être installés ?

Installation

Installer le dépôt.

Désactiver le module d’installation pour la version PostgreSQL de la distribution.

Installer les paquets de PostgreSQL16 : serveur, client, contribs.

Quel est le chemin des binaires ?

Création de la première instance

Créer une première instance avec les outils de la famille Red Hat en activant les sommes de contrôle (checksums).

Vérifier ce qui a été fait dans le journal initdb.log.

Démarrage

Démarrer l’instance.

Activer le démarrage de l’instance au démarrage de la machine.

Où sont les fichiers de données (PGDATA), et les traces de l’instance ?

Configuration

Vérifier la configuration par défaut de PostgreSQL. Est-ce que le serveur écoute sur le réseau ?

Quel est l’utilisateur sous lequel tourne l’instance ?

Connexion

En tant que root, tenter une connexion avec psql.

En tant que postgres, tenter une connexion avec psql. Quitter.

À quelle base se connecte-t-on par défaut ?

Créer une première base de données et y créer des tables.

Installation à partir des sources (optionnel)

But : Installer PostgreSQL à partir du code source

Note : Pour éviter tout problème lié au positionnement des variables d’environnement dans les exercices suivants, l’installation depuis les sources se fera avec un utilisateur dédié, différent de l’utilisateur utilisé par l’installation depuis les paquets de la distribution.

Outils de compilation

Installer les outils de compilation suivants, si ce n’est déjà fait.

NB : Ceci a été testé avec PostgreSQL 17 sur Rocky Linux 9 et Debian 12. Des dépendances un peu différentes pourraient apparaître pour d’autres versions.

Sous Rocky Linux, il faudra utiliser dnf :

sudo dnf -y group install "Development Tools"
sudo dnf -y install perl readline-devel openssl-devel wget bzip2

Sous Debian ou Ubuntu :

sudo apt install -y build-essential libreadline-dev zlib1g-dev flex bison \
  libxml2-dev libxslt-dev libssl-dev pkg-config

Créer l’utilisateur système srcpostgres avec /opt/pgsql pour répertoire HOME.

Se connecter en tant que l’utilisateur srcpostgres.

Téléchargement

  • Télécharger l’archive des fichiers sources de la dernière version stable depuis postgresql.org.
  • (Alternative : récupérer le dépôt git de la dernière branche stable.)
  • Les placer dans /opt/pgsql/src.

Compilation et installation

L’installation des binaires compilés se fera dans /opt/pgsql/17/.

  • Configurer en conséquence l’environnement de compilation (./configure).
  • Compiler PostgreSQL. Ne pas oublier les contribs.

Installer les fichiers obtenus.

Où se trouvent les binaires installés de PostgreSQL ?

Configurer le système

Ajouter les variables d’environnement PATH et LD_LIBRARY_PATH au ~srcpostgres/.bash_profile de l’utilisateur srcpostgres pour accéder facilement à ces binaires.

Création d’une instance

Avec initdb, initialiser une instance dans /opt/pgsql/17/data en spécifiant postgres comme nom de super-utilisateur, et en activant les sommes de contrôle.

Démarrer l’instance.

  • Tenter une première connexion avec psql.
  • Pourquoi cela échoue-t-il ?

Se connecter en tant qu’utilisateur postgres. Ressortir.

Dans .bash_profile, configurer la variable d’environnement PGUSER pour se connecter toujours en tant que postgres.

Première base

Créer une première base de donnée nommée test.

Se connecter à la base test et créer quelques tables.

Arrêt

Arrêter cette instance.


Travaux pratiques (solutions)

Installation depuis les paquets binaires du PGDG (Rocky Linux)

Pré-installation

Quelle commande permet d’installer les paquets binaires de PostgreSQL ?

Le présent TP utilise Rocky Linux en version 8 ou 9. C’est une distribution communautaire qui se veut succéder au projet CentOS, clone de Red Hat, interrompu en 2021.

(Une version plus complète, ainsi que l’utilisation de paquets Debian, sont traités dans l’annexe « Installation de PostgreSQL depuis les paquets communautaires ».)

Quelle version est packagée ?

La dernière version stable de PostgreSQL disponible au moment de la rédaction de ces lignes est la 17.0. Par contre, la dernière version disponible dans les dépôts dépend de votre distribution. C’est la raison pour laquelle les dépôts du PGDG sont à privilégier.

Quels paquets devront également être installés ?

Le paquet libpq doit également être installé. Il est aussi nécessaire d’installer les paquets llvmjit (pour la compilation à la volée), qui réclame elle-même la présence du dépôt EPEL, mais c’est une fonctionnalité optionnelle qui ne sera pas traitée ici.

Installation

Installer le dépôt en vous inspirant des consignes sur :
https://www.postgresql.org/download/linux/redhat
mais en ajoutant les contribs et les sommes de contrôle.

Préciser :

  • PostgreSQL 17
  • Red Hat Enterprise, Rocky or Oracle version 8 (ou 9 selon le cas)
  • x86_64

Nous allons reprendre ligne à ligne ce script et le compléter.

Se connecter avec l’utilisateur root sur la machine de formation, et recopier le script proposé par le guide. Dans la commande ci-dessous, les deux lignes doivent être copiées et collées ensemble.

# Rocky Linux 8
dnf install -y https://download.postgresql.org\
/pub/repos/yum/reporpms/EL-8-x86_64/pgdg-redhat-repo-latest.noarch.rpm
# Rocky Linux 9
dnf install -y https://download.postgresql.org\
/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm

Désactiver le module d’installation pour la version PostgreSQL de la distribution.

Cette opération est nécessaire pour Rocky Linux 8 ou 9.

dnf -qy module disable postgresql

Installer les paquets de PostgreSQL17 : serveur, client, contribs.

dnf install -y postgresql17-server postgresql17-contrib
# optionnel
dnf install -y postgresql17-llvmjit

Il s’agit respectivement 

  • des binaires du serveur ;
  • des « contribs » et extensions optionnelles (mais chaudement conseillées) ;
  • et du paquet nécessaire à la compilation à la volée (JIT).

Le paquet postgresql17 (outils client) fait partie des dépendances et est installé automatiquement.

Quel est le chemin des binaires ?

Ils se trouvent dans /usr/pgsql-17/bin/ (chemin propre à ces paquets) :

ls -1 /usr/pgsql-17/bin/
clusterdb
createdb
;
postgres
postgresql-17-check-db-dir
postgresql-17-setup
psql
reindexdb
vacuumdb
vacuumlo

Noter qu’il existe des liens dans /usr/bin pointant vers la version la plus récente des outils en cas d’installation de plusieurs versions :

which psql
/usr/bin/psql
file /usr/bin/psql
/usr/bin/psql: symbolic link to /etc/alternatives/pgsql-psql
file /etc/alternatives/pgsql-psql
/etc/alternatives/pgsql-psql: symbolic link to /usr/pgsql-17/bin/psql

Création de la première instance

Créer une première instance avec les outils de la famille Red Hat en activant les sommes de contrôle (checksums).

La création d’une instance passe par un outil spécifique à ces paquets.

Cet outil doit être appelé en tant que root (et non postgres).

Optionnellement, on peut ajouter des paramètres d’initialisation à cette étape. La mise en place des sommes de contrôle est généralement conseillée pour être averti de toute corruption des fichiers.

Toujours en temps que root :

export PGSETUP_INITDB_OPTIONS="--data-checksums"
/usr/pgsql-17/bin/postgresql-17-setup initdb
Initializing database ... OK

L’export est nécessaire pour activer les sommes de contrôle.

Vérifier ce qui a été fait dans le journal initdb.log.

La sortie de la commande précédente est redirigée vers le fichier initdb.log situé dans le répertoire qui contient celui de la base (PGDATA). Il est possible d’y vérifier l’ensemble des étapes réalisées, notamment l’activation des sommes de contrôle.

$ cat /var/lib/pgsql/17/initdb.log
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.

The database cluster will be initialized with locale "en_US.UTF-8".
The default database encoding has accordingly been set to "UTF8".
The default text search configuration will be set to "english".

Data page checksums are enabled.

fixing permissions on existing directory /var/lib/pgsql/17/data ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default "max_connections" ... 100
selecting default "shared_buffers" ... 128MB
selecting default time zone ... UTC
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok

Success. You can now start the database server using:

    /usr/pgsql-17/bin/pg_ctl -D /var/lib/pgsql/17/data/ -l logfile start

Ne pas tenir compte de la dernière ligne, qui est une suggestion qui ne tient pas compte des outils prévus pour cet OS.

Démarrage

Démarrer l’instance.

Attention, si vous avez créé une instance, par exemple à partir des sources, elle doit impérativement être arrêtée pour pouvoir démarrer la nouvelle instance ! Elles ne peuvent pas être démarrées en même temps, sauf à modifier le port dans la configuration de l’une d’entre elles.

En tant que root :

systemctl start postgresql-17

Si aucune erreur ne s’affiche, tout va bien à priori.

Pour connaître l’état de l’instance :

systemctl status postgresql-17
● postgresql-17.service - PostgreSQL 17 database server
     Loaded: loaded (/usr/lib/systemd/system/postgresql-17.service; disabled; preset: disabled)
     Active: active (running) since Tue 2024-10-15 18:44:08 UTC; 6s ago
       Docs: https://www.postgresql.org/docs/17/static/
    Process: 72901 ExecStartPre=/usr/pgsql-17/bin/postgresql-17-check-db-dir ${PGDATA} (code=exited, status=0/SUCCESS)
   Main PID: 72906 (postgres)
      Tasks: 7 (limit: 2676)
     Memory: 17.7M
        CPU: 37ms
     CGroup: /system.slice/postgresql-17.service
             ├─72906 /usr/pgsql-17/bin/postgres -D /var/lib/pgsql/17/data/
             ├─72907 "postgres: logger "
             ├─72908 "postgres: checkpointer "
             ├─72909 "postgres: background writer "
             ├─72911 "postgres: walwriter "
             ├─72912 "postgres: autovacuum launcher "
             └─72913 "postgres: logical replication launcher "

Oct 15 18:44:08 rocky9 systemd[1]: Starting PostgreSQL 17 database server...
Oct 15 18:44:08 rocky9 postgres[72906]: 2024-10-15 18:44:08.144 UTC [72906] LOG:  redirecting log output to logging collecto>
Oct 15 18:44:08 rocky9 postgres[72906]: 2024-10-15 18:44:08.144 UTC [72906] HINT:  Future log output will appear in director>
Oct 15 18:44:08 rocky9 systemd[1]: Started PostgreSQL 17 database server.

Activer le démarrage de l’instance au démarrage de la machine.

Le packaging Red Hat ne prévoie pas l’activation du service au boot, il faut le demander explicitement :

systemctl enable postgresql-17
Created symlink /etc/systemd/system/multi-user.target.wants/postgresql-17.service → /usr/lib/systemd/system/postgresql-17.service.

Où sont les fichiers de données (PGDATA), et les traces de l’instance ?

Les données et fichiers de configuration sont dans /var/lib/pgsql/17/data/.

ls -1 /var/lib/pgsql/17/data/
base
current_logfiles
global
log
pg_commit_ts
pg_dynshmem
pg_hba.conf
pg_ident.conf
pg_logical
pg_multixact
pg_notify
pg_replslot
pg_serial
pg_snapshots
pg_stat
pg_stat_tmp
pg_subtrans
pg_tblspc
pg_twophase
PG_VERSION
pg_wal
pg_xact
postgresql.auto.conf
postgresql.conf
postmaster.opts
postmaster.pid

Les traces sont par défaut dans le sous-répertoire log/ du PGDATA.

ls -l /var/lib/pgsql/17/data/log/
total 4
-rw-------. 1 postgres postgres 1092 Oct 15 18:49 postgresql-Tue.log

NB : Dans les paquets RPM, le nom exact du fichier dépend du jour de la semaine.

Configuration

Vérifier la configuration par défaut de PostgreSQL. Est-ce que le serveur écoute sur le réseau ?

Il est possible de vérifier dans le fichier postgresql.conf que par défaut, le serveur écoute uniquement l’interface réseau localhost (la valeur est commentée car c’est celle par défaut) :

grep listen_addresses /var/lib/pgsql/17/data/postgresql.conf
#listen_addresses = 'localhost'         # what IP address(es) to listen on;

Il faudra donc modifier ainsi et rédémarrer pour que des utilisateurs puissent se connecter depuis d’autres machines :

listen_addresses = '*'         # what IP address(es) to listen on;
systemctl restart postgresql-17

Il est aussi possible de vérifier au niveau système en utilisant la commande netstat (qui nécessite l’installation du paquet net-tools) :

netstat -anp|grep -E '(Active|Proto|postgres)'
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address   Foreign Address State   PID/Program name    
tcp        0      0 0.0.0.0:5432    0.0.0.0:*       LISTEN  73049/postgres      
tcp6       0      0 :::5432         :::*            LISTEN  73049/postgres      
Active UNIX domain sockets (servers and established)
Proto RefCnt Flags    Type    State      I-Node PID/Program name     Path
unix  2      [ ACC ]  STREAM  LISTENING  87790  73049/postgres       /run/postgresql/.s.PGSQL.5432
unix  2      [ ACC ]  STREAM  LISTENING  87791  73049/postgres       /tmp/.s.PGSQL.5432
unix  3      [ ]      STREAM  CONNECTED  88816  73050/postgres: log  

(La présence de lignes tcp6 dépend de la configuration de la machine.)

On notera que la socket écoute à deux endroits, dans /run/postgresql/ et dans /tmp. C’est un paramétrage par défaut lié aux paquets RPM.

Quel est l’utilisateur sous lequel tourne l’instance ?

C’est l’utilisateur nommé postgres :

ps -U postgres -f -o pid,user,cmd
    PID USER     CMD
    PID USER     CMD
  73049 postgres /usr/pgsql-17/bin/postgres -D /var/lib/pgsql/17/data/
  73050 postgres  \_ postgres: logger 
  73051 postgres  \_ postgres: checkpointer 
  73052 postgres  \_ postgres: background writer 
  73054 postgres  \_ postgres: walwriter 
  73055 postgres  \_ postgres: autovacuum launcher 
  73056 postgres  \_ postgres: logical replication launcher 

Il possède aussi le PGDATA :

ls -l /var/lib/pgsql/17/
total 8
drwx------.  2 postgres postgres    6 Sep 26 20:10 backups
drwx------. 20 postgres postgres 4096 Oct 15 19:29 data
-rw-------.  1 postgres postgres  914 Oct 15 18:41 initdb.log

postgres est le nom traditionnel sur la plupart des distributions, mais il n’est pas obligatoire (par exemple, le TP par compilation utilise un autre utilisateur).

Connexion

En tant que root, tenter une connexion avec psql.

 # psql
psql: error: connection to server on socket "/run/postgresql/.s.PGSQL.5432" failed: FATAL:  role "root" does not exist

Cela échoue car psql tente de se connecter avec l’utilisateur système en cours, soit root. Ça ne marchera pas mieux cependant en essayant de se connecter avec l’utilisateur postgres :

psql -U postgres
psql: error: connection to server on socket "/run/postgresql/.s.PGSQL.5432" failed: FATAL:  Peer authentication failed for user "postgres"

En effet, le pg_hba.conf est configuré de telle manière que l’utilisateur de PostgreSQL et celui du système doivent porter le même nom (connexion peer ici).

En tant que postgres, tenter une connexion avec psql. Quitter.

sudo -iu postgres psql
psql (17.0)
Type "help" for help.

postgres=# exit

La connexion fonctionne donc indirectement depuis tout utilisateur pouvant effectuer un sudo.

À quelle base se connecte-t-on par défaut ?

sudo -iu postgres psql
psql (17.0)
Type "help" for help.

postgres=# \conninfo 
You are connected to database "postgres" as user "postgres" via socket in "/run/postgresql" at port "5432".
postgres=# 

Là encore, la présence d’une base nommée postgres est une tradition et non une obligation.

Première base

Créer une première base de données et y créer des tables.

sudo -iu postgres psql
postgres=# CREATE DATABASE test ;
CREATE DATABASE

Alternativement :

sudo -iu postgres createdb test

Se connecter explicitement à la bonne base :

sudo -iu postgres psql -d test
test=# CREATE TABLE mapremieretable (x int);
CREATE TABLE

test=# \d+
                           Liste des relations
 Schéma |       Nom       | Type  | Propriétaire | Taille  | Description
--------+-----------------+-------+--------------+---------+-------------
 public | mapremieretable | table | postgres     | 0 bytes |

Installation à partir des sources (optionnel)

Outils de compilation

Installer les outils de compilation suivants, si ce n’est déjà fait.

Ces actions doivent être effectuées en tant qu’utilisateur privilégié (soit directement en tant que root, soit en utilisant la commande sudo).

NB : Ceci a été testé avec PostgreSQL 17 sur Rocky Linux 9 et Debian 12. Des dépendances un peu différentes pourraient apparaître pour d’autres versions.

Sous Rocky Linux, il faudra utiliser dnf :

sudo dnf -y group install "Development Tools"
sudo dnf -y install perl readline-devel openssl-devel wget bzip2

Sous Debian ou Ubuntu :

sudo apt install -y build-essential libreadline-dev zlib1g-dev flex bison \
  libxml2-dev libxslt-dev libssl-dev pkg-config

Une fois ces outils installés, tout ce qui suit devrait fonctionner sur toute version de Linux.

Créer l’utilisateur système srcpostgres avec /opt/pgsql pour répertoire HOME.

sudo useradd --home-dir /opt/pgsql --system --create-home srcpostgres
sudo usermod --shell /bin/bash srcpostgres

Se connecter en tant que l’utilisateur srcpostgres.

Se connecter en tant qu’utilisateur srcpostgres :

sudo su - srcpostgres

Téléchargement

  • Télécharger l’archive des fichiers sources de la dernière version stable depuis postgresql.org.
  • (Alternative : récupérer la dernière version stable dans le dépôt git.)
  • Les placer dans /opt/pgsql/src.

En tant qu’utilisateur srcpostgres, créer un répertoire dédié aux sources :

mkdir ~srcpostgres/src
cd ~/src

Aller sur https://postgresql.org, cliquer Download et récupérer le lien vers l’archive des fichiers sources de la dernière version stable (PostgreSQL 17.0 au moment où ceci est écrit). Il est possible de le faire en ligne de commande :

wget https://ftp.postgresql.org/pub/source/v17.0/postgresql-17.0.tar.bz2

Il faut décompresser l’archive :

tar xjvf postgresql-17.0.tar.bz2
cd postgresql-17.0

Alternativement, le dépôt git se récupère ainsi :

git clone --branch REL_17_0 --depth 1  \
https://git.postgresql.org/git/postgresql.git postgresql-17.0
cd postgresql-17.0

ou de manière moins économe :

git clone REL_17_0 https://git.postgresql.org/git/postgresql.git 
cd postgresql
git checkout REL_17_0
cd postgresql

(Au besoin git tag liste les versions disponibles.)

Compilation et installation

L’installation des binaires compilés se fera dans /opt/pgsql/17/.

  • Configurer en conséquence l’environnement de compilation (./configure).
  • Compiler PostgreSQL. Ne pas oublier les contribs.

Configuration :

./configure --prefix /opt/pgsql/17
checking build system type... x86_64-pc-linux-gnu
checking host system type... x86_64-pc-linux-gnu
checking which template to use... linux
checking whether NLS is wanted... no
checking for default port number... 5432


config.status: linking src/include/port/linux.h to src/include/pg_config_os.h
config.status: linking src/makefiles/Makefile.linux to src/Makefile.port

Des fichiers sont générés, notamment le Makefile.

La compilation se lance de manière classique. Elle peut prendre un certain temps sur les machines un peu anciennes :

make    # make -j3 selon le nombre de coeurs
make -C ./src/backend generated-headers
make[1]: Entering directory '/opt/pgsql/postgresql-17.0/src/backend'
make -C ../include/catalog generated-headers
make[2]: Entering directory '/opt/pgsql/postgresql-17.0/src/include/catalog'

make[1]: Leaving directory '/opt/pgsql/postgresql-17.0/src'
make -C config all
make[1]: Entering directory '/opt/pgsql/postgresql-17.0/config'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/opt/pgsql/postgresql-17.0/config'
cd contrib/
make
make -C ../src/backend generated-headers
make[1]: Entering directory '/opt/pgsql/postgresql-17.0/src/backend'
make -C ../include/catalog generated-headers
make[2]: Entering directory '/opt/pgsql/postgresql-17.0/src/include/catalog'

make[2]: Entering directory '/opt/pgsql/postgresql-17.0/src/backend/nodes'
make[2]: Nothing to be done for 'generated-header-symlinks'.
make[2]: Leaving directory '/opt/pgsql/postgresql-17.0/src/backend/nodes'

Installer les fichiers obtenus.

L’installation peut se faire en tant que srcpostgres (et non root) car nous avons défini comme cible le répertoire /opt/pgsql/17/ qui lui appartient :

cd ~srcpostgres/postgresql-17.0
make install
cd contrib
make install

Dans ce TP, nous nous sommes attachés à changer le moins possible d’utilisateur système. Il se peut que vous ayez à installer les fichiers obtenus en tant qu’utilisateur root dans d’autres environnements en fonction de la politique de sécurité adoptée.

Où se trouvent les binaires installés de PostgreSQL ?

Les binaires installés sont situés dans le répertoire /opt/pgsql/17/bin.

ls -1 /opt/pgsql/17/bin
clusterdb
createdb
createuser

pg_verifybackup
pg_waldump
pg_walsummary
pgbench
postgres
psql
reindexdb
vacuumdb
vacuumlo

Configurer le système

Ajouter les variables d’environnement PATH et LD_LIBRARY_PATH au ~srcpostgres/.bash_profile de l’utilisateur srcpostgres pour accéder facilement à ces binaires.

Ajouter les lignes suivantes à la fin du fichier ~srcpostgres/.bash_profile (ce fichier peut ne pas exister préalablement, et un autre fichier peut être nécessaire selon l’environnement utilisé) :

export PGDATA=/opt/pgsql/17/data
export PATH=/opt/pgsql/17/bin:$PATH
export LD_LIBRARY_PATH=/opt/pgsql/17/lib:$LD_LIBRARY_PATH

Il faut ensuite recharger le fichier à l’aide de la commande suivante (ne pas oublier le point et l’espace au début de la commande) ; ou se déconnecter et se reconnecter.

. ~srcpostgres/.bash_profile

Vérifier que les chemins sont bons :

which psql
~/17/bin/psql

Création d’une instance

Avec initdb, initialiser une instance dans /opt/pgsql/17/data en spécifiant postgres comme nom de super-utilisateur, et en activant les sommes de contrôle.

$ initdb -D $PGDATA -U postgres --data-checksums
The files belonging to this database system will be owned by user "srcpostgres".
This user must also own the server process.

The database cluster will be initialized with locale "en_US.UTF-8".
The default database encoding has accordingly been set to "UTF8".
The default text search configuration will be set to "english".

Data page checksums are enabled.

creating directory /opt/pgsql/17/data ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default "max_connections" ... 100
selecting default "shared_buffers" ... 128MB
selecting default time zone ... UTC
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok

initdb: warning: enabling "trust" authentication for local connections
initdb: hint: You can change this by editing pg_hba.conf or using the option -A, or --auth-local and --auth-host, the next time you run initdb.

Success. You can now start the database server using:

    pg_ctl -D /opt/pgsql/17/data -l logfile start

Démarrer l’instance.

Attention : s’il y a déjà une autre instance sur cette machine et qu’elle est démarrée, le port 5432 est occupé et votre nouvelle instance ne pourra pas fonctionner. Arrêter l’autre instance ou modifier le port 5432 d’une des instances.

pg_ctl -D $PGDATA -l $PGDATA/server.log start
waiting for server to start.... done
server started
cat $PGDATA/server.log
2024-10-15 17:56:13.354 UTC [69286] LOG:  starting PostgreSQL 17.0 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 11.4.1 20231218 (Red Hat 11.4.1-3), 64-bit
2024-10-15 17:56:13.354 UTC [69286] LOG:  listening on IPv4 address "127.0.0.1", port 5432
2024-10-15 17:56:13.354 UTC [69286] LOG:  could not bind IPv6 address "::1": Cannot assign requested address
2024-10-15 17:56:13.358 UTC [69286] LOG:  listening on Unix socket "/tmp/.s.PGSQL.5432"
2024-10-15 17:56:13.362 UTC [69289] LOG:  database system was shut down at 2024-10-15 17:54:39 UTC
2024-10-15 17:56:13.364 UTC [69286] LOG:  database system is ready to accept connections
  • Tenter une première connexion avec psql.
  • Pourquoi cela échoue-t-il ?
psql
psql: error: connection to server on socket "/tmp/.s.PGSQL.5432" failed: FATAL:  role "srcpostgres" does not exist

Par défaut, psql demande à se connecter avec un nom d’utilisateur (au sens PostgreSQL) identique à l’utilisateur système en cours, mais la base de données ne connaît pas d’utilisateur srcpostgres. Par défaut, elle ne connaît que postgres.

Se connecter en tant qu’utilisateur postgres. Ressortir.

psql -U postgres
psql (17.0)
Type "help" for help.

postgres=# exit

Noter que la connexion fonctionne parce que le pg_hba.conf livré avec les sources est par défaut très laxiste (méthode trust en local et via localhost !). (Il y a d’ailleurs eu un avertissement lors de la création de la base.)

Dans .bash_profile, configurer la variable d’environnement PGUSER pour se connecter toujours en tant que postgres. Retester la connextion directe avec psql.

Ajouter ceci à la fin du fichier ~srcpostgres/.bash_profile :

export PGUSER=postgres

Et recharger le fichier à l’aide de la commande suivante (ne pas oublier le point et l’espace au début de la commande) :

. ~/.bash_profile

La connexion doit fonctionner sur le champ :

psql
psql (17.0)
Type "help" for help.

postgres=# \conninfo
You are connected to database "postgres" as user "postgres" via socket in "/tmp" at port "5432"
postgres=# 
\q

Première base

Créer une première base de donnée nommée test.

En ligne de commande shell :

createdb --echo test
SELECT pg_catalog.set_config('search_path', '', false);
CREATE DATABASE test;

Alternativement, depuis psql :

postgres=# CREATE DATABASE test ;

CREATE DATABASE

Se connecter à la base test et créer quelques tables.

psql -d test
test=# CREATE TABLE premieretable (x int) ;
CREATE TABLE

Arrêt

Arrêter cette instance.

$ pg_ctl stop
waiting for server to shut down.... done
server stopped
tail $PGDATA/server.log
2024-10-15 18:00:33.830 UTC [69286] LOG:  received fast shutdown request
2024-10-15 18:00:33.835 UTC [69286] LOG:  aborting any active transactions
2024-10-15 18:00:33.836 UTC [69286] LOG:  background worker "logical replication launcher" (PID 69292) exited with exit code 1
2024-10-15 18:00:33.837 UTC [69287] LOG:  shutting down
2024-10-15 18:00:33.840 UTC [69287] LOG:  checkpoint starting: shutdown immediate
2024-10-15 18:00:33.918 UTC [69287] LOG:  checkpoint complete: wrote 975 buffers (6.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.036 s, sync=0.032 s, total=0.081 s; sync files=311, longest=0.015 s, average=0.001 s; distance=4585 kB, estimate=4585 kB; lsn=0/1BA9900, redo lsn=0/1BA9900
2024-10-15 18:00:33.924 UTC [69286] LOG:  database system is shut down

Installation de PostgreSQL depuis les paquets communautaires

L’installation est détaillée ici pour Rocky Linux 8 et 9 (similaire à Red Hat et à d’autres variantes comem Oracle Linux et Fedora), et Debian/Ubuntu.

Elle ne dure que quelques minutes.

Sur Rocky Linux 8 ou 9

ATTENTION : Red Hat, CentOS, Rocky Linux fournissent souvent par défaut des versions de PostgreSQL qui ne sont plus supportées. Ne jamais installer les packages postgresql, postgresql-client et postgresql-server ! L’utilisation des dépôts du PGDG est fortement conseillée.

Installation du dépôt communautaire  :

Les dépôts de la communauté sont sur https://yum.postgresql.org/. Les commandes qui suivent sont inspirées de celles générées par l’assistant sur https://www.postgresql.org/download/linux/redhat/, en précisant :

  • la version majeure de PostgreSQL (ici la 17) ;
  • la distribution (ici Rocky Linux 8) ;
  • l’architecture (ici x86_64, la plus courante).

Les commandes sont à lancer sous root :

# dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms\
/EL-8-x86_64/pgdg-redhat-repo-latest.noarch.rpm

# dnf -qy module disable postgresql

Installation de PostgreSQL 17 (client, serveur, librairies, extensions)

# dnf install -y postgresql17-server postgresql17-contrib

Les outils clients et les librairies nécessaires seront automatiquement installés.

Une fonctionnalité avancée optionnelle, le JIT (Just In Time compilation), nécessite un paquet séparé.

# dnf install postgresql17-llvmjit

Création d’une première instance :

Il est conseillé de déclarer PG_SETUP_INITDB_OPTIONS, notamment pour mettre en place les sommes de contrôle et forcer les traces en anglais :

# export PGSETUP_INITDB_OPTIONS='--data-checksums --lc-messages=C'
# /usr/pgsql-17/bin/postgresql-17-setup initdb
# cat /var/lib/pgsql/17/initdb.log

Ce dernier fichier permet de vérifier que tout s’est bien passé et doit finir par :

Success. You can now start the database server using:

    /usr/pgsql-17/bin/pg_ctl -D /var/lib/pgsql/17/data/ -l logfile start

Chemins :

Objet Chemin
Binaires /usr/pgsql-17/bin
Répertoire de l’utilisateur postgres /var/lib/pgsql
PGDATA par défaut /var/lib/pgsql/17/data
Fichiers de configuration dans PGDATA/
Traces dans PGDATA/log

Configuration :

Modifier postgresql.conf est facultatif pour un premier lancement.

Commandes d’administration habituelles :

Démarrage, arrêt, statut, rechargement à chaud de la configuration, redémarrage :

# systemctl start postgresql-17
# systemctl stop postgresql-17
# systemctl status postgresql-17
# systemctl reload postgresql-17
# systemctl restart postgresql-17

Test rapide de bon fonctionnement et connexion à psql :

# systemctl --all |grep postgres
# sudo -iu postgres psql

Démarrage de l’instance au lancement du système d’exploitation :

# systemctl enable postgresql-17

Ouverture du firewall pour le port 5432 :

Voir si le firewall est actif :

# systemctl status firewalld

Si c’est le cas, autoriser un accès extérieur :

# firewall-cmd --zone=public --add-port=5432/tcp --permanent
# firewall-cmd --reload
# firewall-cmd --list-all

(Rappelons que listen_addresses doit être également modifié dans postgresql.conf.)

Création d’autres instances :

Si des instances de versions majeures différentes doivent être installées, il faut d’abord installer les binaires pour chacune (adapter le numéro dans dnf install …) et appeler le script d’installation de chaque version. l’instance par défaut de chaque version vivra dans un sous-répertoire numéroté de /var/lib/pgsql automatiquement créé à l’installation. Il faudra juste modifier les ports dans les postgresql.conf pour que les instances puissent tourner simultanément.

Si plusieurs instances d’une même version majeure (forcément de la même version mineure) doivent cohabiter sur le même serveur, il faut les installer dans des PGDATA différents.

  • Ne pas utiliser de tiret dans le nom d’une instance (problèmes potentiels avec systemd).
  • Respecter les normes et conventions de l’OS : placer les instances dans un nouveau sous-répertoire de /var/lib/pgsqsl/17/ (ou l’équivalent pour d’autres versions majeures).

Pour créer une seconde instance, nommée par exemple infocentre :

  • Création du fichier service de la deuxième instance :
# cp /lib/systemd/system/postgresql-17.service \
        /etc/systemd/system/postgresql-17-infocentre.service
  • Modification de ce dernier fichier avec le nouveau chemin :
Environment=PGDATA=/var/lib/pgsql/17/infocentre
  • Option 1 : création d’une nouvelle instance vierge :
# export PGSETUP_INITDB_OPTIONS='--data-checksums --lc-messages=C'
# /usr/pgsql-17/bin/postgresql-17-setup initdb postgresql-17-infocentre
  • Option 2 : restauration d’une sauvegarde : la procédure dépend de votre outil.

  • Adaptation de /var/lib/pgsql/17/infocentre/postgresql.conf (port surtout).

  • Commandes de maintenance de cette instance :

# systemctl [start|stop|reload|status] postgresql-17-infocentre
# systemctl [enable|disable] postgresql-17-infocentre
  • Ouvrir le nouveau port dans le firewall au besoin.

Sur Debian / Ubuntu

Sauf précision, tout est à effectuer en tant qu’utilisateur root.

Référence : https://apt.postgresql.org/

Installation du dépôt communautaire :

L’installation des dépôts du PGDG est prévue dans le paquet Debian :

# apt update
# apt install -y  gnupg2  postgresql-common 
# /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh

Ce dernier ordre créera le fichier du dépôt /etc/apt/sources.list.d/pgdg.list adapté à la distribution en place.

Installation de PostgreSQL 17 :

La méthode la plus propre consiste à modifier la configuration par défaut avant l’installation :

Dans /etc/postgresql-common/createcluster.conf, paramétrer au moins les sommes de contrôle et les traces en anglais :

initdb_options = '--data-checksums --lc-messages=C'

Puis installer les paquets serveur et clients et leurs dépendances :

# apt install postgresql-17 postgresql-client-17

La première instance est automatiquement créée, démarrée et déclarée comme service à lancer au démarrage du système. Elle porte un nom (par défaut main).

Elle est immédiatement accessible par l’utilisateur système postgres.

Chemins :

Objet Chemin
Binaires /usr/lib/postgresql/17/bin/
Répertoire de l’utilisateur postgres /var/lib/postgresql
PGDATA de l’instance par défaut /var/lib/postgresql/17/main
Fichiers de configuration dans /etc/postgresql/17/main/
Traces dans /var/log/postgresql/

Configuration

Modifier postgresql.conf est facultatif pour un premier essai.

Démarrage/arrêt de l’instance, rechargement de configuration :

Debian fournit ses propres outils, qui demandent en paramètre la version et le nom de l’instance :

# pg_ctlcluster 17 main [start|stop|reload|status|restart]

Démarrage de l’instance avec le serveur :

C’est en place par défaut, et modifiable dans /etc/postgresql/17/main/start.conf.

Ouverture du firewall :

Debian et Ubuntu n’installent pas de firewall par défaut.

Statut des instances du serveur :

# pg_lsclusters

Test rapide de bon fonctionnement et connexion à psql :

# systemctl --all |grep postgres
# sudo -iu postgres psql

Destruction d’une instance :

# pg_dropcluster 17 main

Création d’autres instances :

Ce qui suit est valable pour remplacer l’instance par défaut par une autre, par exemple pour mettre les checksums en place :

  • optionnellement, /etc/postgresql-common/createcluster.conf permet de mettre en place tout d’entrée les checksums, les messages en anglais, le format des traces ou un emplacement séparé pour les journaux :
initdb_options = '--data-checksums --lc-messages=C'
log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h '
waldir = '/var/lib/postgresql/wal/%v/%c/pg_wal'
  • créer une instance :
# pg_createcluster 17 infocentre

Il est également possible de préciser certains paramètres du fichier postgresql.conf, voire les chemins des fichiers (il est conseillé de conserver les chemins par défaut) :

# pg_createcluster 17 infocentre \
  --port=12345 \
  --datadir=/PGDATA/17/infocentre \
  --pgoption shared_buffers='8GB' --pgoption work_mem='50MB' \
  --  --data-checksums --waldir=/ssd/postgresql/17/infocentre/journaux
  • adapter au besoin /etc/postgresql/17/infocentre/postgresql.conf ;

  • démarrage :

# pg_ctlcluster 17 infocentre start

Accès à l’instance depuis le serveur même (toutes distributions)

Par défaut, l’instance n’est accessible que par l’utilisateur système postgres, qui n’a pas de mot de passe. Un détour par sudo est nécessaire :

$ sudo -iu postgres psql
psql (17.0)
Type "help" for help.
postgres=#

Ce qui suit permet la connexion directement depuis un utilisateur du système :

Pour des tests (pas en production !), il suffit de passer à trust le type de la connexion en local dans le pg_hba.conf :

local   all             postgres                               trust

La connexion en tant qu’utilisateur postgres (ou tout autre) n’est alors plus sécurisée :

dalibo:~$ psql -U postgres
psql (17.0)
Type "help" for help.
postgres=#

Une authentification par mot de passe est plus sécurisée :

  • dans pg_hba.conf, paramétrer une authentification par mot de passe pour les accès depuis localhost (déjà en place sous Debian) :
# IPv4 local connections:
host    all             all             127.0.0.1/32            scram-sha-256
# IPv6 local connections:
host    all             all             ::1/128                 scram-sha-256

(Ne pas oublier de recharger la configuration en cas de modification.)

  • ajouter un mot de passe à l’utilisateur postgres de l’instance :
dalibo:~$ sudo -iu postgres psql
psql (17.0)
Type "help" for help.
postgres=# \password
Enter new password for user "postgres":
Enter it again:
postgres=# quit

dalibo:~$ psql -h localhost -U postgres
Password for user postgres:
psql (17.0)
Type "help" for help.
postgres=#
  • Pour se connecter sans taper le mot de passe à une instance, un fichier .pgpass dans le répertoire personnel doit contenir les informations sur cette connexion :
localhost:5432:*:postgres:motdepassetrèslong

Ce fichier doit être protégé des autres utilisateurs :

$ chmod 600 ~/.pgpass
  • Pour n’avoir à taper que psql, on peut définir ces variables d’environnement dans la session voire dans ~/.bashrc :
export PGUSER=postgres
export PGDATABASE=postgres
export PGHOST=localhost

Rappels :

  • en cas de problème, consulter les traces (dans /var/lib/pgsql/17/data/log ou /var/log/postgresql/) ;
  • toute modification de pg_hba.conf ou postgresql.conf impliquant de recharger la configuration peut être réalisée par une de ces trois méthodes en fonction du système :
root:~# systemctl reload postgresql-17
root:~# pg_ctlcluster 17 main reload
postgres:~$ psql -c 'SELECT pg_reload_conf()'

Outils graphiques et console

PostgreSQL

Préambule

Les outils graphiques et console :

  • les outils graphiques d’administration
  • la console
  • les outils de contrôle de l’activité
  • les outils DDL
  • les outils de maintenance

Ce module nous permet d’approcher le travail au quotidien du DBA et de l’utilisateur de la base de données. Nous verrons les différents outils disponibles, en premier lieu la console texte, psql.


Plan

  • Outils en ligne de commande de PostgreSQL
  • Réaliser des scripts
  • Outils graphiques

Outils console de PostgreSQL

  • Plusieurs outils PostgreSQL en ligne de commande existent
    • une console interactive
    • des outils de maintenance
    • des outils de sauvegardes/restauration
    • des outils de gestion des bases

Les outils console de PostgreSQL que nous allons voir sont fournis avec la distribution de PostgreSQL. Ils permettent de tout faire : exécuter des requêtes manuelles, maintenir l’instance, sauvegarder et restaurer les bases.


Outils : Gestion des bases

  • createdb : ajouter une nouvelle base de données
  • createuser : ajouter un nouveau compte utilisateur
  • dropdb : supprimer une base de données
  • dropuser : supprimer un compte utilisateur

Chacune de ces commandes est un « alias », un raccourci qui permet d’exécuter certaines commandes SQL directement depuis le shell sans se connecter explicitement au serveur. L’option --echo permet de voir les ordres SQL envoyés.

Par exemple, la commande système dropdb est équivalente à la commande SQL DROP DATABASE. L’outil dropdb se connecte à la base de données nommée postgres et exécute l’ordre SQL et enfin se déconnecte.

La création d’une base se fait en utilisant l’outil createdb et en lui indiquant le nom de la nouvelle base, de préférence avec un utilisateur dédié. createuser crée ce que l’on appelle un « rôle », et appelle CREATE ROLE en SQL. Nous verrons plus loin les droits de connexion, de superutilisateur, etc.

Une création de base depuis le shell peut donc ressembler à ceci :

$ createdb --echo --owner erpuser  erp_prod
SELECT pg_catalog.set_config('search_path', '', false);
CREATE DATABASE erp_prod OWNER erpuser;

Alors qu’une création de rôle peut ressembler à ceci :

$ createuser --echo --login --no-superuser erpuser
SELECT pg_catalog.set_config('search_path', '', false);
CREATE ROLE erpuser NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN;

Et si le pg_hba.conf le permet :

$ psql -U erpuser erp_prod  < script_installation.sql

Outils : Sauvegarde / Restauration

  • Sauvegarde logique, pour une instance
    • pg_dumpall : sauvegarder l’instance PostgreSQL
  • Sauvegarde logique, pour une base de données
    • pg_dump : sauvegarder une base de données
    • pg_restore : restaurer une base de données PostgreSQL
  • Sauvegarde physique :
    • pg_basebackup
    • avec : pg_verifybackup et pg_combinebackup (v17)

Ces commandes sont essentielles pour assurer la sécurité des données du serveur.

Comme son nom l’indique, pg_dumpall sauvegarde l’instance complète, autrement dit toutes les bases mais aussi les objets globaux. À partir de la version 12, il est cependant possible d’exclure une ou plusieurs bases de cette sauvegarde.

Pour ne sauvegarder qu’une seule base, il est préférable de passer par l’outil pg_dump, qui possède plus d’options. Il faut évidemment lui fournir le nom de la base à sauvegarder. Pour sauvegarder notre base b1, il suffit de lancer la commande suivante :

$ pg_dump -f b1.sql b1

Pour la restauration d’une sauvegarde, l’outil habituel est pg_restore. psql est utilisé pour la restauration d’une sauvegarde faite en mode texte (script SQL).

Ces deux outils réalisent des sauvegardes logiques, donc au niveau des objets logiques (tables, index, etc).

La sauvegarde physique (donc au niveau des fichiers) à chaud est possible avec pg_basebackup, qui copie un serveur en fonctionnement, journaux de transaction inclus. Son fonctionnement est nettement plus complexe qu’un simple pg_dump. pg_basebackup est utilisé par les outils de sauvegarde PITR, et pour créer des serveurs secondaires. pg_verifybackup permet de vérifier une sauvegarde réalisée avec pg_basebackup. À partir de PostgreSQL 17, pg_basebackup sait effectuer des sauvegardes incrémentales, à recombiner avec pg_combinebackup.


Outils : Maintenance

  • Maintenance des bases
    • vacuumdb : récupérer l’espace inutilisé, statistiques
    • clusterdb : réorganiser une table en fonction d’un index
    • reindexdb : réindexer

reindexdb, là encore, est un alias lançant des ordres REINDEX. Une réindexation périodique des index peut être utile. Par exemple, pour lancer une réindexation de la base b1 en affichant la commande exécutée :

$ reindexdb --echo --concurrently 11
SELECT pg_catalog.set_config('search_path', '', false);
REINDEX DATABASE CONCURRENTLY b1;
WARNING:  cannot reindex system catalogs concurrently, skipping all

vacuumdb permet d’exécuter les différentes variantes du VACUUM (FULL, ANALYZE, FREEZE…) depuis le shell, principalement le nettoyage des lignes mortes, la mise à jour des statistiques sur les données, et la reconstruction de tables. L’usage est ponctuel, le démon autovacuum s’occupant de cela en temps normal.

clusterdb lance un ordre CLUSTER, soit une reconstruction de la table avec tri selon un index. L’usage est très spécifique.

Rappelons que ces opérations posent des verrous qui peuvent être très gênants sur une base active.


Outils : Maintenance de l’instance

  • initdb : création d’instance
  • pg_ctl : lancer, arrêter, relancer, promouvoir l’instance
  • pg_upgrade : migrations majeures
  • pg_config, pg_controldata : configuration

Ces outils sont rarement utilisés directement, car on passe généralement par les outils du système d’exploitation et ceux fournis par les paquets, qui les utilisent. Ils peuvent toutefois servir et il faut les connaître.

initdb crée une instance, c’est-à-dire crée tous les fichiers nécessaires dans le répertoire indiqué (PGDATA). Les options permettent d’affecter certains paramètres par défaut. La plus importante (car on ne peut corriger plus tard qu’à condition que l’instance soit arrêtée, donc en arrêt de production) est l’option --data-checksums activant les sommes de contrôle, dont l’activation est généralement conseillée.

pg_ctl est généralement utilisé pour démarrer/arrêter une instance, pour recharger les fichiers de configuration après modification, ou pour promouvoir une instance secondaire en primaire. Toutes les actions possibles sont documentées ici.

pg_upgrade est utilisée pour convertir une instance existante lors d’une migration entre versions majeures. La version minimale supportée est la 8.4, sauf à partir de pg_upgrade 15 (version 9.2 dans ce cas).

pg_config fournit des informations techniques sur les binaires installés (chemins notamment).

pg_controldata fournit des informations techniques de base sur une instance.


Autres outils en ligne de commande

  • pgbench pour des tests
  • Outils liés à la réplication/sauvegarde physique, aux tests, analyse…

pgbench est l’outil de base pour tester la charge et l’influence de paramètres. Créez les tables de travail avec l’option -i, fixez la volumétrie avec -s, et lancez pgbench en précisant le nombre de clients, de transactions… L’outil vous calcule le nombre de transactions par secondes et diverses informations statistiques. Les requêtes utilisées sont basiques mais vous pouvez fournir les vôtres.

D’autres outils sont liés à l’archivage (pg_receivewal) et/ou à la réplication par log shipping (pg_archivecleanup) ou logique (pg_recvlogical), au sauvetage d’instances secondaires (pg_rewind), à la vérification de la disponibilité (pg_isready), à des tests de la configuration matérielle (pg_test_fsync, pg_test_timing), ou d’intégrité (pg_checksums), à l’analyse des journaux de transactions (pg_waldump).


Chaînes de connexion

Pour se connecter à une base :

  • paramètres propres aux outils
  • via la libpq
    • variables d’environnement
    • par chaînes clés/valeur
    • par chaînes URI
    • idem en Python,PHP,Perl
  • JDBC/.NET/ODBC ont des syntaxes spécifiques

Les types de connexion connus de PostgreSQL et de sa librairie cliente (libpq) sont, au choix, les paramètres explicites (assez variables suivants les outils), les variables d’environnement, les chaînes clés/valeur, et les URI (postgresql://…).

Les pilotes PHP…), Perl, ou Python utilisent la libpq, comme bien sûr les outils du projet PostgreSQL, et ce qui suit est directement utilisable.

Nous ne traiterons pas ici des syntaxes propres aux outils n’utilisant pas la libpq, comme les pilotes JDBC (dont les URI sont proches mais différente de celles décrites plus bas) ou .NET, ou encore les différents pilotes ODBC (comme psqlODBC, très limité).


Paramètres

Outils habituels, et très souvent :

$ psql -h serveur -d mabase -U nom -p 5432
Option Variable Valeur par défaut
-h HÔTE $PGHOST /tmp, /var/run/postgresql
-p PORT $PGPORT 5432
-U NOM $PGUSER nom de l’utilisateur OS
-d base $PGDATABASE nom de l’utilisateur PG
$PGOPTIONS options de connexions

Les options de connexion permettent d’indiquer comment trouver l’instance (serveur, port), puis d’indiquer l’utilisateur et la base de données concernés parmi les différentes de l’instance. Ces deux derniers champs doivent passer le filtre du pg_hba.conf du serveur pour que la connexion réussisse.

Lorsque l’une de ces options n’est pas précisée, la bibliothèque cliente PostgreSQL cherche une variable d’environnement correspondante et prend sa valeur. Si elle ne trouve pas de variable, elle se rabat sur une valeur par défaut.

Les paramètres et variables d’environnement qui suivent sont utilisés par les outils du projet, et de nombreux autres outils de la communauté.

La problématique du mot de passe est laissée de côté pour le moment.

Hôte :

Le paramètre -h <hôte> ou la variable d’environnement $PGHOST permettent de préciser le nom ou l’adresse IP de la machine qui héberge l’instance.

Sans précision, sur Unix, le client se connecte sur la socket Unix, généralement dans /var/run/postgresql (défaut sur Debian et Red Hat) ou /tmp (défaut de la version compilée). Le réseau n’est alors pas utilisé, et il y a donc une différence entre -h localhost (via ::1 ou 127.0.0.1 donc) et -h /var/run/postgresql (défaut), ce qui peut avoir un résultat différent selon la configuration du pg_hba.conf. Par défaut, l’accès par le réseau exige un mot de passe.

Sous Windows, le comportement par défaut est de se connecter à localhost.

Serveur :

-p <port> ou $PGPORT permettent de préciser le port sur lequel l’instance écoute les connexions entrantes. Sans indication, le port par défaut est le 5432.

Utilisateur :

-U <nom> ou $PGUSER permettent de préciser le nom de l’utilisateur, connu de PostgreSQL, qui doit avoir été créé préalablement sur l’instance.

Sans indication, le nom d’utilisateur PostgreSQL est le nom de l’utilisateur système connecté.

Base de données :

-d base ou $PGDATABASE permettent de préciser le nom de la base de données utilisée pour la connexion.

Sans précision, le nom de la base de données de connexion sera celui de l’utilisateur PostgreSQL (qui peut découler de l’utilisateur connecté au système).

Exemples :

  • Chaîne de connexion classique, implicitement au port 5432 en local sur le serveur :
$ psql -U jeanpierre -d comptabilite
  • Connexion à un serveur distant pour une sauvegarde :
$ pg_dump -h serveur3 -p 5435 -U postgres -d basecritique -f fichier.dump
  • Connexion sans paramètre via l’utilisateur système postgres, et donc implicitement en tant qu’utilisateur postgres de l’instance à la base postgres (qui existent généralement par défaut).
$ sudo -iu postgres  psql

Dans les configurations par défaut courantes, cette commande est généralement la seule qui fonctionne sur une instance fraîchement installée.

  • Utilisation des variables d’environnement pour alléger la syntaxe dans un petit script de maintenance :
#! /bin/bash
export PGHOST=/var/run/postgresql
export PGPORT=5435
export PGDATABASE=comptabilite
export PGUSER=superutilisateur
# liste des bases
psql -l
# nettoyage
vacuumdb
# une sauvegarde
pg_dump -f fichier.dump

Raccourcis

Généralement, préciser -d n’est pas nécessaire quand la base de données est le premier argument non nommé de la ligne de commande. Par exemple :

$ psql -U jeanpierre comptabilite
$ sudo -iu postgres  vacuumdb nomdelabase

Il faut faire attention à quelques différences entre les outils : pour pgbench, -d désigne le mode debug et le nom de la base est un argument non nommé ; alors que pour pg_restore, le -d est nécessaire pour restaurer vers une base.

Variable d’environnement PGOPTIONS

La variable d’environnement $PGOPTIONS permet de positionner la plupart des paramètres de sessions disponibles, qui surchargent les valeurs par défaut.

Par exemple, pour exécuter une requête avec un paramétrage différent de work_mem (mémoire de tri) :

$ PGOPTIONS="-c work_mem=100MB" psql -p 5433 -h serveur nombase  < grosse_requete.sql

ou pour importer une sauvegarde sans être freiné par un serveur secondaire synchrone :

$ PGOPTIONS="-c synchronous_commit=local" pg_restore -d nombase sauvegarde.dump

Autres variables d’environnement

  • $PGAPPNAME
  • $PGSSLMODE
  • $PGPASSWORD

Il existe aussi :

  • $PGSSLMODE pour définir le niveau de chiffrement SSL de la connexion avec les valeurs disable, prefer (le défaut), require…  (il existe d’autres variables d’environnement pour une gestion fine du SSL) ;
  • $PGAPPNAME permet de définir une chaîne de caractère arbitraire pour identifier la connexion dans les outils et vues d’administration (paramètre de session application_name) : mettez-y par exemple le nom du script ;
  • $PGPASSWORD peut stocker le mot de passe pour ne pas avoir à l’entrer à la connexion (voir plus loin).

Toutes ces variables d’environnement, ainsi que de nombreuses autres, et leurs diverses valeurs possibles, sont documentées.


Chaînes libpq clés/valeur

psql "host=serveur1  user=jeanpierre  dbname=comptabilite"
psql -d "host=serveur1  port=5432  user=jeanpierre  dbname=comptabilite
     sslmode=require  application_name='chargement'
     options='-c work_mem=30MB' "

En lieu du nom de la base, une chaîne peut inclure tous les paramètres nécessaires. Cette syntaxe permet de fournir plusieurs paramètres d’un coup.

Les paramètres disponibles sont listés dans la documentation. On retrouvera de nombreux paramètres équivalents aux variables d’environnement, ainsi que d’autres.

Dans cet exemple, on se connecte en exigeant le SSL, en positionnant application_name pour mieux repérer la session, et en modifiant la mémoire de tri, ainsi que le paramétrage de la synchronisation sur disque pour accélérer les choses :

$ psql "host=serveur1  port=5432  user=jeanpierre  dbname=comptabilite
       sslmode=require  application_name='chargement'
       options='-c work_mem=99MB' -c synchronous_commit=off' " \
       <  script_chargement.sql

Tous les outils de l’écosystème ne connaissent pas cette syntaxe (par exemple pgCluu).


Chaînes URI

psql -d "postgresql://jeanpierre@serveur1:5432/comptabilite"
psql \
"postgres://jeanpierre@serveur1/comptabilite?sslmode=require\
&options=-c%20synchronous_commit%3Doff"
psql -d postgresql://serveur1/comptabilite?user=jeanpierre\&port=5432

Une autre possibilité existe : des chaînes sous forme URI comme il en existe pour beaucoup de pilotes et d’outils. La syntaxe est de la forme :

postgresql://[user[:motdepasse]@][lieu][:port][/dbname][?param1=valeur21&param2=valeur2…]

Là encore cette chaîne remplace le nom de la base dans les outils habituels. postgres:// et postgresql:// sont tous deux acceptés.

Cette syntaxe est très souple. Une difficulté réside dans la gestion des caractères spéciaux, signes = et des espaces :

# Ces deux appels sont équivalents
$ psql -d postgresql:///var/lib/postgresql?dbname=pgbench\&user=pgbench\&port=5436
$ psql -h /var/lib/postgresql -d pgbench -U pgbench -p 5436
# Ces deux appels sont équivalents
$ psql "postgresql://bob@vm1/proddb?&options=-c%20synchronous_commit%3Doff"
$ psql -U bob -h vm1 -d proddb -c 'synchronous_commit=off'

Tous les outils de l’écosystème ne connaissent pas cette syntaxe (par exemple pgCluu).


Connexion avec choix automatique du serveur

psql "host=serveur1,serveur2,serveur3
      port=5432,5433,5434
      user=jeanpierre  dbname=comptabilite
      target_session_attrs=read-write
      load_balance_hosts=random"   # v16

Il est possible d’indiquer plusieurs hôtes et ports. Jusque PostgreSQL 15 inclus, l’hôte sélectionné est le premier qui répond avec les conditions demandées. À partir de PostgreSQL 16, on peut ajouter load_balance_hosts=random pour que la libpq choisisse un serveur au hasard, pour une répartition de charge basique, mais très simple à mettre en place.

Si l’authentification ne passe pas, la connexion tombera en erreur. Il est possible de préciser si la connexion doit se faire sur un serveur en lecture/écriture ou en lecture seule grâce au paramètre target_session_attrs.

Par exemple, on se connectera ainsi au premier serveur de la liste ouvert en écriture et disponible parmi les trois précisés :

psql postgresql://jeanpierre@serveur1:5432,serveur2:5433,serveur3:5434/\
comptabilite?target_session_attrs=read-write

qui équivaut à :

psql "host=serveur1,serveur2,serveur3 port=5432,5433,5434
      target_session_attrs=read-write  user=jeanpierre  dbname=comptabilite"

Depuis la version 14, dans le but de faciliter la connexion aux différents serveurs d’une grappe, target_session_attrs possède d’autres options que read-write, à savoir : any, read-only, primary, standby, prefer-standby. Par exemple, la commande suivante permet de se connecter à un secondaire au hasard parmi ceux qui répondent. serveur3 est doublé pour prendre une charge double des autres. S’il ne reste plus que le primaire, la connexion se fera à celui-ci.

psql "host=serveur1,serveur2,serveur3,serveur3 port=5432,5433,5434,5434
      target_session_attrs=prefer-standby
      load_balance_hosts=random
      user=jeanpierre  dbname=comptabilite"

Authentification d’un client (outils console)

  • En interactif (psql)
    • -W | --password
    • -w | --no-password
  • Variable $PGPASSWORD
  • À préférer : fichier .pgpass
    • chmod 600 .pgpass
    • nom_hote:port:database:nomutilisateur:motdepasse

Options -W et -w de psql

L’option -W oblige à saisir le mot de passe de l’utilisateur. C’est le comportement par défaut si le serveur demande un mot de passe. Si les accès aux serveurs sont configurés sans mot de passe et que cette option est utilisée, le mot de passe sera demandé et fourni à PostgreSQL lors de la demande de connexion. Mais PostgreSQL ne vérifiera pas s’il est bon si la méthode d’authentification ne le réclame pas.

L’option -w empêche la saisie d’un mot de passe. Si le serveur a une méthode d’authentification qui nécessite un mot de passe, ce dernier devra être fourni par le fichier .pgpass ou par la variable d’environnement $PGPASSWORD. Dans tous les autres cas, la connexion échoue.

Variable $PGPASSWORD

Si psql détecte une variable $PGPASSWORD initialisée, il se servira de son contenu comme mot de passe qui sera soumis pour l’authentification du client.

Fichier .pgpass

Le fichier .pgpass, situé dans le répertoire personnel de l’utilisateur ou celui référencé par $PGPASSFILE, est un fichier contenant les mots de passe à utiliser si la connexion requiert un mot de passe (et si aucun mot de passe n’a été spécifié). Sur Microsoft Windows, le fichier est nommé %APPDATA%\postgresql\pgpass.conf (où %APPDATA% fait référence au sous-répertoire Application Data du profil de l’utilisateur).

Ce fichier devra être composé de lignes du format :

nom_hote:port:nom_base:nom_utilisateur:mot_de_passe

Chacun des quatre premiers champs pourraient être une valeur littérale ou * (qui correspond à tout). La première ligne réalisant une correspondance pour les paramètres de connexion sera utilisée (du coup, placez les entrées plus spécifiques en premier lorsque vous utilisez des jokers). Si une entrée a besoin de contenir * ou \, échappez ce caractère avec \. Un nom d’hôte localhost correspond à la fois aux connexions host (TCP) et aux connexions local (socket de domaine Unix) provenant de la machine locale.

Les droits sur .pgpass doivent interdire l’accès aux autres et au groupe ; réalisez ceci avec la commande :

chmod 0600 ~/.pgpass

Attention : si les droits du fichier sont moins stricts, le fichier sera ignoré !

Les droits du fichier ne sont actuellement pas vérifiés sur Microsoft Windows.


La console psql

  • Un outil simple pour
    • les opérations courantes
    • les tâches de maintenance
    • l’exécution des scripts
    • les tests
postgres$ psql
  base=#

La console psql permet d’effectuer l’ensemble des tâches courantes d’un utilisateur de bases de données. Si ces tâches peuvent souvent être effectuées à l’aide d’un outil graphique, la console présente l’avantage de pouvoir être utilisée en l’absence d’environnement graphique ou de scripter les opérations à effectuer sur la base de données. Elle a la garantie d’être également toujours disponible.

Nous verrons également qu’il est possible d’administrer la base de données depuis cette console.

Enfin, elle permet de tester l’activité du serveur, l’existence d’une base, la présence d’un langage de programmation…


Obtenir de l’aide et quitter

  • Obtenir de l’aide sur les commandes internes psql
    • \?
  • Obtenir de l’aide sur les ordres SQL
    • \h <COMMANDE>
  • Quitter
    • \q ou ctrl-D
    • quit ou exit (v11)

\? liste les commandes propres à psql (par exemple \d ou \du pour voir les tables ou les utilisateurs), trop nombreuses pour pouvoir être mémorisées.

\h <COMMANDE> affiche l’aide en ligne des commandes SQL. Sans argument, la liste des commandes disponibles est affichée. La version 12 ajoute en plus l’URL vers la page web documentant cette commande.

Exemple :

postgres=# \h ALTER TA
Commande :    ALTER TABLE
Description : modifier la définition d'une table
Syntaxe :
ALTER TABLE [ IF EXISTS ] [ ONLY ] nom [ * ]
    action [, ... ]
ALTER TABLE [ IF EXISTS ] [ ONLY ] nom [ * ]

URL: https://www.postgresql.org/docs/15/sql-altertable.html

\q ou Ctrl-D permettent de quitter psql. Depuis la version 11, il est aussi possible d’utiliser quit ou exit.


Gestion de la connexion

  • Modifier le mot de passe d’un utilisateur
    • \password nomutilisateur
  • Quelle est la connexion courante ?
    • \conninfo
    • SELECT current_user,session_user,system_user;
  • Se connecter à une autre base :
    • \c ma base
    • \c mabase utilisateur serveur 5432

\c permet de changer d’utilisateur et/ou de base de données sans quitter le client. \conninfo permet de savoir où l’on est connecté.

Exemple :

CREATE DATABASE formation;
CREATE DATABASE prod;
CREATE USER stagiaire1;
CREATE USER stagiaire2;
CREATE USER admin;

postgres=# \c formation stagiaire1
You are now connected to database "formation" as user "stagiaire1".
formation=> \c - stagiaire2
You are now connected to database "formation" as user "stagiaire2".
formation=> \c prod admin
You are now connected to database "prod" as user "admin".
prod=> \conninfo
You are connected to database "prod" as user "admin"
                  on host "localhost" (address "::1") at port "5412".

Au niveau SQL, un équivalent partiel de \conninfo serait :

SELECT  current_user,
        current_catalog,                         -- base 
        inet_server_addr(), inet_server_port(),  -- serveur 
        system_user                              -- depuis PG16
        \gx
-[ RECORD 1 ]----+-----------------------
current_user     | dalibo
current_catalog  | pgbench
inet_server_addr | 192.168.74.5
inet_server_port | 5433
system_user      | scram-sha-256:postgres

Un superutilisateur pourra affecter un mot de passe à un autre utilisateur grâce à la commande \password, qui en fait encapsule un ALTER USER … PASSWORD …. Le gros intérêt de \password est d’envoyer le mot de passe chiffré au serveur. Ainsi, même si les traces contiennent toutes les requêtes SQL exécutées, il est impossible de retrouver les mots de passe via le fichier de traces. Ce n’est pas le cas avec un CREATE ROLE ou un ALTER ROLE manuel (à moins de chiffrer soi-même le mot de passe).


Catalogue système : objets utilisateurs

Lister :

  • les bases de données
    • \l , \l+
  • les tables
    • \d, \d+, \dt , \dt+
  • les index
    • \di, \di+
  • les schémas
    • \dn
  • les fonctions & procédures
    • \df[+]
  • etc…

Ces commandes permettent d’obtenir des informations sur les objets utilisateurs de tout type stockés dans la base de données. Pour les commandes qui acceptent un motif, celui-ci permet de restreindre les résultats retournés à ceux dont le nom d’opérateur correspond au motif précisé.

Le client psql en version 15 est compatible avec toutes les versions du serveur depuis la 9.2 incluse.

\l dresse la liste des bases de données sur le serveur. Avec \l+, les commentaires, les tablespaces par défaut et les tailles des bases sont également affichés, ce qui peut être très pratique.

\dt affiche les tables, \di les index, \dn les schémas, \ds les séquences, \dv les vues, etc. Là encore, on peut ajouter + pour d’autres informations comme la taille, et même S pour inclure les objets système normalement masqués.

Exemple :

Si l’on crée une simple base grâce à pgbench avec un utilisateur dédié :

$ psql
=# CREATE ROLE testeur LOGIN ;
=# \password testeur
Saisir le nouveau mot de passe :
Saisir le mot de passe à nouveau :

Utilisateurs en place :

=# \du
                              Liste des rôles
         Nom du rôle         |        Attributs        |       Membre de
-----------------------------+-------------------------+-------------------
 dalibo                      |                         | {}
 nagios                      | Superutilisateur        | {}
 postgres                    | Superutilisateur, (...) | {}
 testeur                     |                         | {}

Création de la base :

CREATE DATABASE pgbench OWNER testeur ;

Bases de données en place :

=# \l
                           Liste des bases de données
      Nom       | Propriétaire | Encodage | Collationnement | Type caract. | …
----------------+--------------+----------+-----------------+--------------+----
 pgbench        | testeur      | UTF8     | fr_FR.UTF-8     | fr_FR.UTF-8  |
 postgres       | postgres     | UTF8     | fr_FR.UTF-8     | fr_FR.UTF-8  |

Création des tables de cette nouvelle base :

$ pgbench --initialize --scale=10 -U testeur -h localhost pgbench

Connexion à la nouvelle base :

$ psql -h localhost -U testeur -d pgbench

Les tables :

=# \d
               Liste des relations
 Schéma |       Nom        | Type  | Propriétaire
--------+------------------+-------+--------------
 public | pgbench_accounts | table | testeur
 public | pgbench_branches | table | testeur
 public | pgbench_history  | table | testeur
 public | pgbench_tellers  | table | testeur
(4 lignes)
=# \dt+ pgbench_*s
                          Liste des relations
 Schéma |       Nom        | Type  | Propriétaire | Persistence |  M…  | Taille | 
--------+------------------+-------+--------------+-------------+------+--------+--
 public | pgbench_accounts | table | testeur      | permanent   | heap | 128 MB |
 public | pgbench_branches | table | testeur      | permanent   | heap | 40 kB  |
 public | pgbench_tellers  | table | testeur      | permanent   | heap | 40 kB  |
(3 lignes)

Une table (affichage réduit pour des raisons de mise en page) :

=# \d+ pgbench_accounts
                          Table « public.pgbench_accounts »
 Colonne  |     Type      | Col..nt | NULL-able | … | Stockage | Compr. | …
----------+---------------+---------+-----------+---+----------+--------+--
 aid      | integer       |         | not null  |   | plain    |        |
 bid      | integer       |         |           |   | plain    |        |
 abalance | integer       |         |           |   | plain    |        |
 filler   | character(84) |         |           |   | extended |        |
Index :
    "pgbench_accounts_pkey" PRIMARY KEY, btree (aid)
Méthode d'accès : heap
Options: fillfactor=100

Les index :

=> \di
                           Liste des relations
 Schéma |          Nom          | Type  | Propriétaire |      Table
--------+-----------------------+-------+--------------+------------------
 public | pgbench_accounts_pkey | index | testeur      | pgbench_accounts
 public | pgbench_branches_pkey | index | testeur      | pgbench_branches
 public | pgbench_tellers_pkey  | index | testeur      | pgbench_tellers
(3 lignes)

Les schémas, utilisateur ou système :

=> \dn+
                           Liste des schémas
  Nom   | Propriétaire |    Droits d´accès    |      Description
--------+--------------+----------------------+------------------------
 public | postgres     | postgres=UC/postgres+| standard public schema
        |              | =UC/postgres         |
(1 ligne)

=> \dnS
         Liste des schémas
        Nom         | Propriétaire
--------------------+--------------
 information_schema | postgres
 pg_catalog         | postgres
 pg_toast           | postgres
 public             | postgres
(4 lignes)

Les tablespaces (ici ceux par défaut) :

=> \db
          Liste des tablespaces
    Nom     | Propriétaire | Emplacement
------------+--------------+-------------
 pg_default | postgres     |
 pg_global  | postgres     |
(2 lignes)

Catalogue système : rôles et accès

  • Lister les rôles (utilisateurs et groupes)
    • \du[+]
  • Lister les droits d’accès
    • \dp
  • Lister les droits d’accès par défaut
    • \ddp
    • ALTER DEFAULT PRIVILEGES
  • Lister les configurations par rôle et par base
    • \drds

On a vu que \du (u pour user) affiche les rôles existants. Rappelons qu’un rôle peut être aussi bien un utilisateur (si le rôle a le droit de LOGIN) qu’un groupe d’utilisateurs, voire les deux à la fois.

Dans les versions antérieures à la 8.1, il n’y avait pas de rôles, et les groupes et les utilisateurs étaient deux notions distinctes. Certaines commandes ont conservé le terme de user, mais il s’agit bien de rôles dans tous les cas.

Les droits sont accessibles par les commandes \dp (p pour « permissions ») ou \z.

Dans cet exemple, le rôle admin devient membre du rôle système pg_signal_backend :

=# GRANT pg_signal_backend TO admin;
=# \du
                                List of roles
 Nom du rôle |                    Attributs         |      Membre de
-------------+--------------------------------------+---------------------
 admin       |                                      | {pg_signal_backend}
 postgres    | Superuser, Create role, Create DB,   |
             | Replication, Bypass RLS              | {}

Toujours dans la base pgbench :

=# GRANT SELECT ON TABLE pgbench_accounts TO dalibo ;
=# GRANT ALL ON TABLE pgbench_history TO dalibo ;
=# \z
=> \z
                                    Droits d'accès
 Schéma |       Nom        | Type  |     Droits d'accès      | Droits … | …
--------+------------------+-------+-------------------------+----------+---
 public | pgbench_accounts | table | testeur=arwdDxt/testeur+|          |
        |                  |       | dalibo=r/testeur        |          |
 public | pgbench_branches | table |                         |          |
 public | pgbench_history  | table | testeur=arwdDxt/testeur+|          |
        |                  |       | dalibo=arwdDxt/testeur  |          |
 public | pgbench_tellers  | table |                         |          |
(4 lignes)

La commande \ddp permet de connaître les droits accordés par défaut à un utilisateur sur les nouveaux objets avec l’ordre ALTER DEFAULT PRIVILEGES.

=# ALTER DEFAULT PRIVILEGES GRANT ALL ON TABLES TO dalibo ;
=# \ddp
                Droits d´accès par défaut
 Propriétaire | Schéma | Type  |     Droits d´accès
--------------+--------+-------+-------------------------
 testeur      |        | table | dalibo=arwdDxt/testeur +
              |        |       | testeur=arwdDxt/testeur
(1 ligne)

Enfin, la commande \drds permet d’obtenir la liste des paramètres appliqués spécifiquement à un utilisateur ou une base de données.

=# ALTER DATABASE pgbench SET work_mem TO '15MB';
=# ALTER ROLE testeur SET log_min_duration_statement TO '0';
=# \drds
              Liste des paramètres
  Rôle   | Base de données |         Réglages
---------+-----------------+--------------------------
 testeur |                 | log_min_duration_statement=0
         | pgbench         | work_mem=15MB

Visualiser le code des objets

  • Voir les vues ou les fonctions & procédures
    • \dv, \df
  • Code d’une vue
    • \sv
  • Code d’une procédure stockée
    • \sf

Ceci permet de visualiser le code de certains objets sans avoir besoin de l’éditer. Par exemple avec cette vue système :

=# \dv pg_tables
             Liste des relations
   Schéma   |    Nom    | Type | Propriétaire
------------+-----------+------+--------------
 pg_catalog | pg_tables | vue  | postgres
(1 ligne)


=# \sv+ pg_tables
1       CREATE OR REPLACE VIEW pg_catalog.pg_tables AS
2        SELECT n.nspname AS schemaname,
3           c.relname AS tablename,
4           pg_get_userbyid(c.relowner) AS tableowner,
5           t.spcname AS tablespace,
6           c.relhasindex AS hasindexes,
7           c.relhasrules AS hasrules,
8           c.relhastriggers AS hastriggers,
9           c.relrowsecurity AS rowsecurity
10         FROM pg_class c
11           LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
12           LEFT JOIN pg_tablespace t ON t.oid = c.reltablespace
13        WHERE c.relkind = ANY (ARRAY['r'::"char", 'p'::"char"])

Ou cette fonction :

=# CREATE FUNCTION nb_sessions_actives () RETURNS int
LANGUAGE sql
AS $$
  SELECT COUNT(*) FROM pg_stat_activity
  WHERE backend_type = 'client backend' AND state='active' ;
$$ ;
=# \df nb_sessions_actives
                                        Liste des fonctions
 Schéma |         Nom         | Type … du résultat | Type …des paramètres | Type
--------+---------------------+--------------------+----------------------+------
 public | nb_sessions_actives | integer            |                      | func
(1 ligne)
=# \sf nb_sessions_actives
CREATE OR REPLACE FUNCTION public.nb_sessions_actives()
 RETURNS integer
 LANGUAGE sql
AS $function$
  SELECT COUNT(*) FROM pg_stat_activity
  WHERE backend_type = 'client backend' AND state='active' ;
$function$

Il est même possible de lancer un éditeur depuis psql pour modifier directement la vue ou la fonction avec respectivement \ev ou \ef.


Configuration

  • Lister les paramètres correspondants au motif indiqué
    • \dconfig (v15+)

La méta-commande \dconfig permet de récupérer la configuration d’un paramètre. Par exemple :

b1=# \dconfig log_r*
  List of configuration parameters
          Parameter          | Value
-----------------------------+-------
 log_recovery_conflict_waits | off
 log_replication_commands    | off
 log_rotation_age            | 1d
 log_rotation_size           | 0
(4 rows)

En ajoutant le signe +, il est possible d’obtenir plus d’informations :

b1=# \dconfig+ log_r*
                       List of configuration parameters
          Parameter          | Value |  Type   |  Context  | Access privileges
-----------------------------+-------+---------+-----------+-------------------
 log_recovery_conflict_waits | off   | bool    | sighup    |
 log_replication_commands    | off   | bool    | superuser |
 log_rotation_age            | 1d    | integer | sighup    |
 log_rotation_size           | 0     | integer | sighup    |
(4 rows)

Exécuter des requêtes

  • Exécuter une requête :
SELECT * FROM pg_tables ;
SELECT * FROM pg_tables \g
SELECT * FROM pg_tables \gx   -- une ligne par champ
INSERT INTOVALUES (1) \; INSERT INTOVALUES (2) ; -- 1 transaction
  • Rappel des requêtes:
    • flèche vers le haut
    • \g
    • Ctrl-R suivi d’un extrait de texte représentatif

Les requêtes SQL doivent se terminer par ; ou, pour marquer la parenté de PostgreSQL avec Ingres, \g. Cette dernière commande permet de relancer un ordre.

Depuis version 10, il est aussi possible d’utiliser \gx pour bénéficier de l’affichage étendu sans avoir à jouer avec \x on et \x off.

La console psql, lorsqu’elle est compilée avec la bibliothèque libreadline ou la bibliothèque libedit (cas des distributions Linux courantes), dispose des mêmes possibilités de rappel de commande que le shell bash.

Exemple :

postgres=# SELECT * FROM pg_tablespace LIMIT 5;
  oid  |  spcname   | spcowner | spcacl | spcoptions
-------+------------+----------+--------+------------
  1663 | pg_default |       10 |        |
  1664 | pg_global  |       10 |        |
 16502 | ts1        |       10 |        |
postgres=# SELECT * FROM pg_tablespace LIMIT 5\g
  oid  |  spcname   | spcowner | spcacl | spcoptions
-------+------------+----------+--------+------------
  1663 | pg_default |       10 |        |
  1664 | pg_global  |       10 |        |
 16502 | ts1        |       10 |        |
postgres=# \g
  oid  |  spcname   | spcowner | spcacl | spcoptions
-------+------------+----------+--------+------------
  1663 | pg_default |       10 |        |
  1664 | pg_global  |       10 |        |
 16502 | ts1        |       10 |        |
postgres=# \gx
-[ RECORD 1 ]----------
oid        | 1663
spcname    | pg_default
spcowner   | 10
spcacl     |
spcoptions |
-[ RECORD 2 ]----------
oid        | 1664
spcname    | pg_global
spcowner   | 10
spcacl     |
spcoptions |
-[ RECORD 3 ]----------
oid        | 16502
spcname    | ts1
spcowner   | 10
spcacl     |
spcoptions |

Plusieurs ordres sur la même ligne séparés par des ; seront exécutés et affichés à la suite. Par contre, s’ils sont séparés par \;, ils seront envoyés ensemble et implicitement dans une même transaction (sauf utilisation explicite de BEGIN/COMMIT/ROLLBACK). Avant la version 15, ne sera affiché que le résultat du dernier ordre.

Dans cet exemple, la division par zéro fait tomber en erreur et annule l’insertion de la valeur 2 car les deux sont dans la même transaction :

=# CREATE TABLE demo_insertion (i float);
CREATE TABLE
=# INSERT INTO demo_insertion VALUES(1.0); INSERT INTO demo_insertion VALUES(1.0/0);
INSERT 0 1
ERROR:  division by zero
=# SELECT * FROM demo_insertion \; INSERT INTO demo_insertion VALUES (2.0) \;
   INSERT INTO demo_insertion VALUES (2.0/0) ;
 i
---
 1
(1 ligne)

INSERT 0 1
ERROR:  division by zero
=# SELECT * FROM demo_insertion ;
 i
---
 1
(1 ligne)

Afficher le résultat d’une requête

  • \x pour afficher un champ par ligne
  • Affichage par paginateur si l’écran ne suffit pas
  • Préférer less :
    • set PAGER='less -S'
  • Ou outil dédié :
    • \setenv PAGER 'pspg'

En mode interactif, psql cherche d’abord à afficher directement le résultat :

postgres=# SELECT relname,reltype, relchecks, oid,oid FROM pg_class LIMIT 3;
      relname      | reltype | relchecks |  oid  |  oid
-------------------+---------+-----------+-------+-------
 pg_statistic      |   11319 |         0 |  2619 |  2619
 t3                |   16421 |         0 | 16419 | 16419
 capitaines_id_seq |   16403 |         0 | 16402 | 16402
 t1                |   16415 |         0 | 16413 | 16413
 t2                |   16418 |         0 | 16416 | 16416

S’il y a trop de colonnes, on peut préférer n’avoir qu’un champ par ligne  grâce au commutateur \x :

postgres=# \x on
Expanded display is on.
postgres=# SELECT relname,reltype, relchecks, oid,oid FROM pg_class LIMIT 3;
-[ RECORD 1 ]----------------
relname   | pg_statistic
reltype   | 11319
relchecks | 0
oid       | 2619
oid       | 2619
-[ RECORD 2 ]----------------
relname   | t3
reltype   | 16421
relchecks | 0
oid       | 16419
oid       | 16419
-[ RECORD 3 ]----------------
relname   | capitaines_id_seq
reltype   | 16403
relchecks | 0
oid       | 16402
oid       | 16402

\x on et \x off sont alternativement appelés si l’on tape plusieurs fois \x. \x auto délègue à psql la décision du meilleur affichage, en général à bon escient. \gx à la place de ; bascule l’affichage pour une seule requête.

S’il n’y a pas la place à l’écran, psql appelle le paginateur par défaut du système. Il s’agit souvent de more, parfois de less. Ce dernier est bien plus puissant, permet de parcourir le résultat en avant et en arrière, avec la souris, de chercher une chaîne de caractères, de tronquer les lignes trop longues (avec l’option -S) pour naviguer latéralement.

Le paramétrage du paginateur s’effectue par des variables d’environnement :

export PAGER='less -S'
psql

ou :

PAGER='less -S'  psql

ou dans psql directement, ou .psqlrc :

\setenv PAGER 'less -S'

Mais less est généraliste. Un paginateur dédié à l’affichage de résultats de requêtes a été récemment développé par Pavel Stěhule et son paquet figure dans les principales distributions.

Pour les gens qui consultent souvent des données dans les tables depuis la console, pspg permet de naviguer dans les lignes avec la souris en figeant les entêtes ou quelques colonnes ; de poser des signets sur des lignes ; de sauvegarder les lignes. (Pour plus de détail, voir cette présentation et la page Github du projet). La mise en place est similaire :

\setenv PAGER 'pspg'

À l’inverse, la pagination se désactive complètement avec :

\pset pager

(et bien sûr en mode non interactif, par exemple quand la sortie de psql est redirigée vers un fichier).


Afficher les détails d’une requête

  • \gdesc
  • Afficher la liste des colonnes correspondant au résultat d’exécution d’une requête
    • noms
    • type de données

Après avoir exécuté une requête, ou même à la place de l’exécution, il est possible de connaître le nom des colonnes en résultat ainsi que leur type de données.

Requête :

SELECT nspname, relname
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname = 'public' AND c.relkind = 'r';

Description des colonnes :

postgres=# \gdesc
 Column  | Type
---------+------
 nspname | name
 relname | name

Ou sans exécution :

postgres=# SELECT * FROM generate_series (1, 1000) \gdesc
     Column      |  Type
-----------------+---------
 generate_series | integer

Exécuter le résultat d’une requête

  • Exécuter le résultat d’une requête
    • \gexec

Parfois, une requête permet de créer des requêtes sur certains objets. Par exemple, si nous souhaitons exécuter un VACUUM sur toutes les tables du schéma public, nous allons récupérer la liste des tables avec cette requête :

SELECT nspname, relname FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname = 'public' AND c.relkind = 'r';
 nspname |     relname
---------+------------------
 public  | pgbench_branches
 public  | pgbench_tellers
 public  | pgbench_accounts
 public  | pgbench_history
(4 lignes)

Plutôt que d’éditer manuellement cette liste de tables pour créer les ordres SQL nécessaires, autant modifier la requête pour qu’elle prépare elle-même les ordres SQL :

SELECT 'VACUUM ' || quote_ident(nspname) || '.' || quote_ident(relname)
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname = 'public' AND c.relkind = 'r';
           ?column?
--------------------------------
 VACUUM public.pgbench_branches
 VACUUM public.pgbench_tellers
 VACUUM public.pgbench_accounts
 VACUUM public.pgbench_history
(4 lignes)

Une fois que nous avons vérifié la validité des requêtes SQL, il ne reste plus qu’à les exécuter. C’est ce que permet la commande \gexec :

=# \gexec
VACUUM
VACUUM
VACUUM
VACUUM

Manipuler le tampon de requêtes

  • Éditer
    • dernière requête : \e
    • vue : \ev nom_vue
    • fonction PL/pgSQL : \ef nom_fonction
  • Historique :
    • \s

\e nomfichier.sql édite le tampon de requête courant ou un fichier existant indiqué à l’aide d’un éditeur externe.

L’éditeur désigné avec les variables d’environnement $EDITOR ou $PSQL_EDITOR notamment. Sur Unix, c’est généralement par défaut une variante de vi mais n’importe quel éditeur fait l’affaire :

PSQL_EDITOR=nano  psql

ou dans psql ou dans le .psqlrc :

\setenv PSQL_EDITOR 'gedit'

\p affiche le contenu du tampon de requête.

\r efface la requête qui est en cours d’écriture. La précédente requête reste accessible avec \p.

\w nomfichier.sql provoque l’écriture du tampon de requête dans le fichier indiqué (à modifier par la suite avec \e par exemple).

\ev v_mavue édite la vue indiquée. Sans argument, cette commande affiche le squelette de création d’une nouvelle vue.

\ef f_mafonction est l’équivalent pour une fonction.

\s affiche l’historique des commandes effectuées dans la session. \s historique.log sauvegarde cet historique dans le fichier indiqué.


Entrées/sorties

  • Charger et exécuter un script SQL
    • \i fichier.sql
  • Rediriger la sortie dans un fichier
    • \o resultat.out
  • Écrire un texte sur la sortie standard
    • \echo "Texte…"

\i fichier.sql lance l’exécution des commandes placées dans le fichier passé en argument. \ir fait la même chose sauf que le chemin est relatif au chemin courant.

\o resultat.out envoie les résultats de la requête vers le fichier indiqué (voire vers une commande UNIX via un pipe).

Exemple :

=# \o tables.txt
=# SELECT * FROM pg_tables ;
(Si l’on intercale \H, on peut avoir un formatage en HTML.)

Attention : cela va concerner toutes les commandes suivantes. Entrer \o pour revenir au mode normal.

\echo "Texte" affiche le texte passé en argument sur la sortie standard. Ce peut être utile entre les étapes d’un script.


Gestion de l’environnement système

  • Chronométrer les requêtes
    • \timing on
  • Exécuter une commande OS
    • \! ls -l (sur le client !)
  • Changer de répertoire courant
    • \cd /tmp
  • Affecter la valeur d’une variable d’environnement (v15+)
    • \getenv toto PATH

\timing on active le chronométrage de toutes les commandes. C’est très utile mais alourdit l’affichage. Sans argument, la valeur actuelle est basculée de on à off et vice-versa.

=# \timing on
Chronométrage activé.

=# VACUUM ;
VACUUM
Temps : 26,263 ms

\! <commande> ouvre un shell interactif en l’absence d’argument ou exécute la commande indiquée sur le client (pas le serveur !) :

\cd (et non \! cd !) permet de changer de répertoire courant, là encore sur le client. Cela peut servir pour définir le chemin d’un script à exécuter ou d’un futur export.

\getenv permet de récupérer la valeur d’une valeur d’environnement système et de l’affecter à une variable.

Exemple :

\! cat nomfichier.out
\! ls -l /tmp
\! mkdir /home/dalibo/travail
\cd /home/dalibo/travail
\! pwd
/home/dalibo/travail

Variables internes psql

  • Positionner des variables internes
    • \set NOMVAR nouvelle_valeur
  • Variables internes usuelles
    • ON_ERROR_STOP : on / off
    • ON_ERROR_ROLLBACK : on / off / interactive
    • ROW_COUNT : nombre de lignes renvoyées par la dernière requête (v11)
    • ERROR : true si dernière requête en erreur (v11)
  • Ne pas confondre avec SET (au niveau du serveur) !

Les variables déclarées avec \set sont positionnées au niveau de psql, outil client. Elles ne sont pas connues du serveur et n’existent pas dans les autres outils clients (pgAdmin, DBeaver…).

Il ne faut pas les confondre avec les paramètres définis sur le serveur au niveau de la session avec SET. Ceux-ci sont transmis directement au serveur quand ils sont entrés dans un outil client, quel qu’il soit.

\set affiche les variables internes et utilisateur.

\set NOMVAR nouvelle_valeur permet d’affecter une valeur.

La liste des variables prédéfinies est disponible dans la documentation de psql. Beaucoup modifient le comportement de psql.

Exemple :

postgres=# \set
AUTOCOMMIT = 'on'
COMP_KEYWORD_CASE = 'preserve-upper'
DBNAME = 'b1'
ECHO = 'none'
ECHO_HIDDEN = 'off'
ENCODING = 'UTF8'
FETCH_COUNT = '0'
HIDE_TABLEAM = 'off'
HIDE_TOAST_COMPRESSION = 'off'
HISTCONTROL = 'none'
HISTSIZE = '500'
HOST = '/var/run/postgresql'
IGNOREEOF = '0'
LAST_ERROR_MESSAGE = ''
LAST_ERROR_SQLSTATE = '00000'
ON_ERROR_ROLLBACK = 'off'
ON_ERROR_STOP = 'off'
PORT = '5432'
PROMPT1 = '%/%R%x%# '
PROMPT2 = '%/%R%x%# '
PROMPT3 = '>> '
QUIET = 'off'
SERVER_VERSION_NAME = '15.1'
SERVER_VERSION_NUM = '150001'
SHOW_ALL_RESULTS = 'on'
SHOW_CONTEXT = 'errors'
SINGLELINE = 'off'
SINGLESTEP = 'off'
USER = 'postgres'
VERBOSITY = 'default'
VERSION = 'PostgreSQL 15.1 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-15), 64-bit'
VERSION_NAME = '15.1'
VERSION_NUM = '150001'

Les variables ON_ERROR_ROLLBACK et ON_ERROR_STOP sont discutées dans la partie relative à la gestion des erreurs.

La version 11 ajoute quelques variables internes. ROW_COUNT indique le nombre de lignes de résultat de la dernière requête exécutée :

=# SELECT * FROM pg_namespace;
      nspname       | nspowner |               nspacl
--------------------+----------+-------------------------------------
 pg_toast           |       10 |
 pg_temp_1          |       10 |
 pg_toast_temp_1    |       10 |
 pg_catalog         |       10 | {postgres=UC/postgres,=U/postgres}
 public             |       10 | {postgres=UC/postgres,=UC/postgres}
 information_schema |       10 | {postgres=UC/postgres,=U/postgres}
=# \echo :ROW_COUNT
6

=# SELECT :ROW_COUNT ;
6

alors que ERROR est un booléen indiquant si la dernière requête était en erreur ou pas :

=# CREATE TABLE t1(id integer);
CREATE TABLE

=# CREATE TABLE t1(id integer);
ERROR:  relation "t1" already exists
postgres=# \echo :ERROR
true
postgres=# CREATE TABLE t2(id integer);
CREATE TABLE
postgres=# \echo :ERROR
false

Variables utilisateur psql

  • Définir une variable utilisateur
    • \set NOMVAR nouvelle_valeur
  • Invalider une variable
    • \unset NOMVAR
  • Stockage du résultat d’une requête :
    • si résultat est une valeur unique
    • Exemple :
    SELECT now() AS maintenant \gset
    SELECT :'maintenant' ;

En dehors des variables internes évoquées dans le chapitre précédent, il est possible à un utilisateur de psql de définir ses propres variables.

-- initialiser avec une constante
\set active 'y'
\echo :active
y
-- initialiser avec le résultat d'une commande système
\set uptime `uptime`
\echo :uptime
 09:38:58 up 53 min,  1 user,  load average: 0,12, 0,07, 0,07

Et pour supprimer la variable :

\unset uptime

Il est possible de stocker le résultat d’une requête dans une variable pour sa réutilisation dans un script avec la commande \gset. Le nom de la variable est celui de la colonne ou de son alias. La valeur retournée par la requête doit être unique sous peine d’erreur.

SELECT now() AS "curdate" \gset
\echo :curdate
2020-08-16 10:53:51.795582+02

Il est possible aussi de donner un préfixe au nom de la variable :

SELECT now() AS "curdate" \gset v_
\echo :v_curdate
2020-08-16 10:54:20.484344+02

Tests conditionnels

  • \if
  • \elif
  • \else
  • \endif

Ces quatre instructions permettent de tester la valeur de variables psql, ce qui permet d’aller bien plus loin dans l’écriture de scripts SQL. Le client doit être en version 10 au moins (pas forcément le serveur).

Par exemple, si on souhaite savoir si on se trouve sur un serveur standby ou sur un serveur primaire, il suffit de configurer la variable PROMPT1 à partir du résultat de l’interrogation de la fonction pg_is_in_recovery(). Pour cela, il faut enregistrer ce code dans le fichier .psqlrc :

SELECT pg_is_in_recovery() as est_standby \gset
\if :est_standby
    \set PROMPT1 'standby %x$ '
\else
    \set PROMPT1 'primaire %x$ '
\endif

Puis, en lançant psql sur un serveur primaire, on obtient :

psql (15.1)
Type "help" for help.

primaire $

alors qu’on obtient sur un serveur secondaire :

psql (15.1)
Type "help" for help.

standby $

Personnaliser psql

  • Fichier de configuration ~/.psqlrc
    • voire ~/.psqlrc-X.Y ou ~/.psqlrc-X
    • ignoré avec -X
  • Exemple de .psqlrc  :
\set ON_ERROR_ROLLBACK interactive  -- paramétrage de session
\timing on
\set PROMPT1 '%M:%> %n@%/%R%#%x'    -- invite
\set cfg 'SHOW ALL ;'               -- requête utilisable avec :cfg
\set cls '\\! clear;'               -- nettoyer l'écran avec :cls

psql est personnalisable par le biais de plusieurs variables internes. Il est possible de pérenniser ces personnalisations par le biais d’un fichier ~/.psqlrc (ou %APPDATA%\postgresql\psqlrc.conf sous Windows, ou dans un répertoire désigné par $PSQLRC). Il peut exister des fichiers par version (par exemple ~/.psqlrc-15 ou ~/.psqlrc-15.0), voire un fichier global.

Modifier l’invite est utile pour toujours savoir où et comment l’on est connecté. Tous les détails sont dans la documentation. Par exemple, ajouter %> dans l’invite affiche le port. On peut aussi afficher toutes les variables listées par \set (par exemple ainsi : %:PORT: ou %:USER:). En cas de reconnexion, psql en régénère certaines, mais pas toutes.

Exemple de fichier .psqlrc :

\set QUIET 1
\timing on
\pset pager on
\setenv pager
\pset null 'ø'
-- Mots clés autocomplétés en majuscule
\set COMP_KEYWORD_CASE upper
-- Affichage
\x auto
\pset linestyle 'unicode'
\pset border 2
\pset unicode_border_linestyle double
\pset unicode_column_linestyle double
\pset unicode_header_linestyle double
-- Prompt dynamique
-- %> indique le port, %m le serveur, %n l'utilisateur, %/ la base…
\set PROMPT1 '%m:%> %[%033[1;33;40m%]%n@%/%R%[%033[0m%]%#%x '
-- serveur secondaire ? (NB : non mis à jour lors d'une reconnexion !)
SELECT pg_is_in_recovery() as est_standby \gset
\if :est_standby
   \set PROMPT1 :PROMPT1 '(standby) '
\else
   \set PROMPT1 :PROMPT1
\endif
\set QUIET 0
$ psql -h serveur -p5435 -U jeanpierre -d mabase
[serveur]:5435 jeanpierre@postgres=# (standby) SELECT pi(), now(), null ;
╔═══════════════════╦═══════════════════════════════╦══════════╗
║        pi         ║              now              ║ ?column? ║
╠═══════════════════╬═══════════════════════════════╬══════════╣
║ 3.141592653589793 ║ 2022-01-07 16:08:39.925262+01 ║ ø        ║
╚═══════════════════╩═══════════════════════════════╩══════════╝
(1 ligne)

Il est aussi possible d’y rajouter du paramétrage de session avec SET pour adapter le fuseau horaire, par exemple.

Des requêtes très courantes peuvent être ajoutées dans le .psqlrc, par exemple celles-ci :

-- Paramètres en cours avec leur source
-- Ceci impérativement sur une seule ligne !
\set config 'SELECT name, current_setting(name), CASE source WHEN $$configuration file$$ THEN
regexp_replace(sourcefile, $$^/.*/$$, $$$$)||$$:$$||sourceline ELSE source END FROM pg_settings
WHERE source <> $$default$$ OR name LIKE $$%.%$$;'

(Requête inspirée de Christoph Berg).

\set extensions 'SELECT * FROM pg_available_extensions ORDER BY name ;'

…utilisables ainsi dans psql :

=# :config
=# :extensions

Attention : le .psqlrc n’est exécuté qu’au démarrage de psql, mais pas lors d’une reconnexion avec \c ! Les prompts dynamiques à base de variables utilisateur sont donc susceptibles d’être alors faux ! Pour relancer le script, utiliser :

\i ~/.psqlrc

Dans un script, il vaut mieux ignorer ce fichier de configuration grâce à --no-psqlrc (-X) pour revenir à l’environnement par défaut et éviter de polluer l’affichage :

$ psql -X -f script.sql
$ psql -X -At -c 'SELECT name, setting FROM pg_settings ;'

Écriture de scripts shell

  • Script SQL
  • Script Shell
  • Exemple sauvegarde

Exécuter un script SQL avec psql

  • Avec -c :
psql -c 'SELECT * FROM matable' -c 'SELECT fonction(123)' ;
  • Avec un script :
psql -f nom_fichier.sql
psql < nom_fichier.sql
  • Depuis psql :
    • \i nom_fichier.sql

L’option -c permet de spécifier du SQL en ligne de commande sans avoir besoin de faire un script. Plusieurs ordres seront enchaînés dans la même session.

Il est généralement préférable d’enregistrer les ordres dans des fichiers si on veut les exécuter plusieurs fois sans se tromper. L’option -f est très utile dans ce cas. La redirection avec < est une alternative répandue.


Gestion des transactions

  • psql est en mode auto-commit par défaut
    • variable AUTOCOMMIT
  • Ouvrir une transaction explicitement
    • BEGIN;
  • Terminer une transaction
    • COMMIT; ou ROLLBACK;
  • Ouvrir une transaction implicitement
    • option -1 (--single-transaction)

Par défaut, psql est en mode « autocommit », c’est-à-dire que tous les ordres SQL sont automatiquement validés après leur exécution.

Pour exécuter une suite d’ordres SQL dans une seule et même transaction, il faut soit ouvrir explicitement une transaction avec BEGIN; et la valider avec COMMIT; ou l’annuler avec ROLLBACK;. Les autres outils clients sont généralement dans ce même cas.

L’ordre

=# \set AUTOCOMMIT off

a pour effet d’insérer systématiquement un BEGIN avant chaque ordre s’il n’y a pas déjà une transaction ouverte ; il faudra valider ensuite avec COMMIT avant de déconnecter. Il est déconseillé de changer le comportement par défaut (on), même s’il peut désorienter au premier abord des personnes ayant connu une base de données concurrente.

Une autre possibilité est d’utiliser psql -1 ou psql --single-transation : les ordres sont automatiquement encadrés d’un BEGIN et d’un COMMIT. La présence d’ordres BEGIN, COMMIT ou ROLLBACK explicites modifiera ce comportement en conséquence.


Écrire un script SQL

  • Attention à l’encodage des caractères
    • \encoding
    • SET client_encoding
  • Écriture des requêtes

L’encodage a moins d’importance depuis qu’UTF-8 s’est généralisé, mais il y a encore parfois des problèmes dans de vieilles bases ou certains vieux outils.

Rappelons que les bases modernes devraient toutes utiliser l’encodage UTF-8 (c’est le défaut).

\encoding [ENCODAGE] permet, en l’absence d’argument, d’afficher l’encodage du client. En présence d’un argument, il permet de préciser l’encodage du client.

Exemple :

postgres=# \encoding
UTF8
postgres=# \encoding LATIN9
postgres=# \encoding
LATIN9

Cela a le même effet que d’utiliser l’ordre SQL SET client_encoding TO LATIN9;.

En terme de présentation, il est commun d’écrire les mots clés SQL en majuscules et d’écrire les noms des objets et fonctions manipulés dans les requêtes en minuscule. Le langage SQL est un langage au même titre que Java ou PHP, la présentation est importante pour la lisibilité des requêtes, même si les variations personnelles sont nombreuses.


Les blocs anonymes

  • Bloc procédural anonyme en PL/pgSQL :
DO $$
DECLARE r record;
BEGIN
    FOR r IN (SELECT schemaname, relname
              FROM pg_stat_user_tables
              WHERE coalesce(last_analyze, last_autoanalyze) IS NULL
              ) LOOP
        RAISE NOTICE 'Analyze %.%', r.schemaname, r.relname ;
        EXECUTE 'ANALYZE ' || quote_ident(r.schemaname)
                           || '.' || quote_ident(r.relname) ;
    END LOOP;
END$$;

Les blocs anonymes sont utiles pour des petits scripts ponctuels qui nécessitent des boucles ou du conditionnel, voire du transactionnel, sans avoir à créer une fonction ou une procédure. Ils ne renvoient rien. Ils sont habituellement en PL/pgSQL mais tout langage procédural installé est possible.

L’exemple ci-dessus lance un ANALYZE sur toutes les tables où les statistiques n’ont pas été calculées d’après la vue système, et donne aussi un exemple de SQL dynamique. Le résultat est par exemple :

NOTICE:  Analyze public.pgbench_history
NOTICE:  Analyze public.pgbench_tellers
NOTICE:  Analyze public.pgbench_accounts
NOTICE:  Analyze public.pgbench_branches
DO
Temps : 141,208 ms

(Pour ce genre de SQL dynamique, si l’on est sous psql , il est souvent plus pratique d’utiliser \gexec.)

Noter que les ordres constituent une transaction unique, à moins de rajouter des COMMIT ou ROLLBACK explicitement (ce n’est autorisé qu’à partir de la version 11).


Utiliser des variables

\set nom_table 'ma_table'
SELECT * FROM :"nom_table";

\set valeur_col1 'test'
SELECT * FROM :"nom_table" WHERE col1 = :'valeur_col1';
\prompt 'invite' nom_variable
\unset variable
psql -v VARIABLE=valeur

psql permet de manipuler des variables internes personnalisées dans les scripts. Ces variables peuvent être particulièrement utiles pour passer des noms d’objets ou des termes à utiliser dans une requête par le biais des options de ligne de commande (-v variable=valeur).

Noter la position des guillemets quand la variable est une chaîne de caractères !

Exemple :

Une fois connecté à la base pgbench, on déclare deux variables propres au client :

pgbench=# \set nomtable pgbench_accounts
pgbench=# \set taillemini 1000000

Elles apparaissent bien dans la liste des variables :

pgbench=# \set
AUTOCOMMIT = 'on'
COMP_KEYWORD_CASE = 'preserve-upper'
DBNAME = 'pgbench'

nomtable = 'pgbench_accounts'
taillemini = '1000000'

Elles s’utilisent ainsi :

SELECT pg_relation_size (:'nomtable') ;
 pg_relation_size
------------------
        134299648
SELECT relname, pg_size_pretty(pg_relation_size (oid))
FROM pg_class
WHERE relkind = 'r' AND pg_relation_size (oid) > :taillemini ;
     relname      | pg_size_pretty
------------------+----------------
 pgbench_accounts | 128 MB

La substitution s’effectue bien au niveau du client. Si l’on trace tout au niveau du serveur, ces requêtes apparaissent :

SELECT pg_relation_size ('pgbench_accounts') ;
SELECT relname, pg_size_pretty(pg_relation_size (oid))
FROM pg_class WHERE relkind = 'r'
AND pg_relation_size (oid) > 1000000 ;

Gestion des erreurs

  • Ignorer les erreurs dans une transaction
    • ON_ERROR_ROLLBACK
  • Gérer des erreurs SQL en shell
    • ON_ERROR_STOP

La variable interne ON_ERROR_ROLLBACK n’a de sens que si elle est utilisée dans une transaction. Elle peut prendre trois valeurs :

  • off (défaut) ;
  • on ;
  • interactive.

Lorsque ON_ERROR_ROLLBACK est à on, psql crée un SAVEPOINT systématiquement avant d’exécuter une requête SQL. Ainsi, si la requête SQL échoue, psql effectue un ROLLBACK TO SAVEPOINT pour annuler cette requête. Sinon il relâche le SAVEPOINT.

Lorsque ON_ERROR_ROLLBACK est à interactive, le comportement de psql est le même seulement si il est utilisé en interactif. Si psql exécute un script, ce comportement est désactivé. Cette valeur permet de se protéger d’éventuelles fautes de frappe.

Utiliser cette option n’est donc pas neutre, non seulement en terme de performances, mais également en terme d’intégrité des données. Il ne faut donc pas utiliser cette option à la légère.

Enfin, la variable interne ON_ERROR_STOP a deux objectifs : arrêter l’exécution d’un script lorsque psql rencontre une erreur et retourner un code retour shell différent de 0. Si cette variable reste à off, psql retournera toujours la valeur 0 même s’il a rencontré une erreur dans l’exécution d’une requête. Une fois activée, psql retournera un code d’erreur 3 pour signifier qu’il a rencontré une erreur dans l’exécution du script.

L’exécution d’un script qui comporte une erreur retourne le code 0, signifiant que psql a pu se connecter à la base de données et exécuté le script :

$ psql -f script_erreur.sql postgres
psql:script_erreur.sql:1: ERROR:  relation "vin" does not exist
LINE 1: SELECT * FROM vin;
                      ^
$ echo $?
0

Lorsque la variable ON_ERROR_STOP est activée, psql retourne un code erreur 3, signifiant qu’il a rencontré une erreur :

$ psql -v ON_ERROR_STOP=on -f script_erreur.sql postgres
psql:script_erreur.sql:1: ERROR:  relation "vin" does not exist
LINE 1: SELECT * FROM vin;
                      ^
$ echo $?
3

psql retourne les codes d’erreurs suivant au shell :

  • 0 au shell s’il se termine normalement ;
  • 1 s’il y a eu une erreur fatale de son fait (pas assez de mémoire, fichier introuvable) ;
  • 2 si la connexion au serveur s’est interrompue ou arrêtée ;
  • 3 si une erreur est survenue dans un script et si la variable ON_ERROR_STOP a été initialisée.

Formatage des résultats

  • Sortie simplifiée pour exploitation automatisée : -XAt
    • -t (--tuples-only)
    • -A (--no-align)
    • -X (--no-psqlrc)
    • séparateurs : -F (--field-separator) et -R (--record-separator)
  • Formats HTML ou CSV
    • -H | --html
    • --csv (à partir de la version 12)

psql peut servir à afficher des rapports basiques et possède quelques options de formatage.

L’option --csv suffit à répondre à ce besoin, à partir d’un client en version 12 au moins.

S’il faut définir plus finement le format, il existe des options. -A impose une sortie non alignée des données. En ajoutant -t, qui supprime l’entête, et -X, qui demande à ignorer le .psqlrc, la sortie peut être facilement exploitée par des outils classiques comme awk oused :

$ psql -XAt -c 'select datname, pg_database_size(datname) from pg_database'
postgres|87311139
powa|765977379
template1|9028387
template0|8593923
pgbench|166134563

Le séparateur | peut être remplacé par un autre avec -F, ou un octet nul avec -z, et le retour chariot de fin de ligne par une chaîne définie avec -R, ou un octet nul avec -0.

-H permet une sortie en HTML pour une meilleure lisibilité par un humain.


Résultats en pivot (tableau croisé)

  • \crosstabview [colV [colH [colD [colonnedetriH]]]]
  • Exécute la requête en tampon
    • au moins 3 colonnes

Par exemple, pour voir les différents types de clients connectés aux bases (clients système inclus), le résultat n’est pas très lisible :

=# \pset null ø

=# SELECT datname, backend_type, COUNT(*) as nb  FROM pg_stat_activity
   GROUP BY 1,2
   ORDER BY datname NULLS LAST, backend_type ;
 datname  |         backend_type         | nb
----------+------------------------------+----
 pgbench  | client backend               |  2
 postgres | client backend               |  1
 powa     | powa                         |  1
 ø        | archiver                     |  1
 ø        | autovacuum launcher          |  1
 ø        | background writer            |  1
 ø        | checkpointer                 |  1
 ø        | logical replication launcher |  1
 ø        | pg_wait_sampling collector   |  1
 ø        | walwriter                    |  1
(10 lignes)

On peut le reformater ainsi :

=# \crosstabview backend_type datname

         backend_type         | postgres | ø | powa | pgbench
------------------------------+----------+---+------+---------
 client backend               |        2 |   |      |       1
 walwriter                    |          | 1 |      |
 autovacuum launcher          |          | 1 |      |
 logical replication launcher |          | 1 |      |
 powa                         |          |   |    1 |
 background writer            |          | 1 |      |
 archiver                     |          | 1 |      |
 checkpointer                 |          | 1 |      |
(9 lignes)

Formatage dans les scripts SQL

  • Donner un titre au résultat de la requête
    • \pset title 'Résultat de la requête
  • Formater le résultat
    • \pset format html (ou csv…)
  • Diverses options peu utilisées

Il est possible de réaliser des modifications sur le format de sortie des résultats de requête directement dans le script SQL ou en mode interactif dans psql.

Afficher \pset permet de voir ces différentes options. La complétion automatique après \pset affiche les paramètres et valeurs possibles.

Par exemple, l’option format est par défaut à aligned mais possède d’autres valeurs :

=# \pset format <TAB>
aligned          csv              latex            troff-ms         wrapped
asciidoc         html             latex-longtable  unaligned

D’autres options existent, peu utilisées. La liste complète des options de formatage et leur description est disponible dans la documentation de la commande psql.


Scripts & Crontab

  • cron
    • Attention aux variables d’environnement !
  • Ou tout ordonnanceur

La planification d’un script périodique s’effectue de préférence avec les outils du système, donc sous Unix avec cron ou une de ses variantes, même si n’importe quel ordonnanceur peut convenir.

Avec cron, il faut se rappeler qu’à l’exécution d’un script, l’environnement de l’utilisateur n’est pas initialisé, ou plus simplement, les fichiers de personnalisation (par ex. .bashrc) de l’environnement ne sont pas lus. Seule la valeur $HOME est initialisée. Un script fonctionnant parfaitement dans une session peut échouer une fois planifié. Il faut donc prévoir ce cas, initialiser les variables d’environnement requises de façon adéquate, et bien tester.

Par exemple, pour charger l’environnement de l’utilisateur :

#!/bin/bash

. ${HOME}/.bashrc

Rappelons que chaque utilisateur du système peut avoir ses propres crontab. L’utilisateur peut les visualiser avec la commande crontab -l et les éditer avec la commande crontab -e.


Exemple de script de sauvegarde

  • Sauvegarder une base et classer l’archive (squelette) :
#!/bin/bash
# Paramètre : la base
t=$(mktemp)                    # fichier temporaire
pg_dump -Fc "$1" > $t          # sauvegarde
d=$(eval date +%d%m%y-%H%M%S)  # date
mv $t /backup/"${1}_${d}.dump" # déplacement
exit 0
  • …et ajouter la gestion des erreurs !
  • …et les surveiller

Il est délicat d’écrire un script fiable. Ce script d’exemple possède plusieurs problèmes potentiels si le paramètre (la base) manque, si la sauvegarde échoue, si l’espace disque manque dans /tmp, si le déplacement échoue, si la partition cible n’est pas montée…

Parmi les outils existants, nous évoquerons notamment pg_back lors des sauvegardes.

Par convention, un script doit renvoyer 0 s’il s’est déroulé correctement.


Outils graphiques

  • Outils graphiques d’administration
    • temBoard
    • pgAdminIII et pgAdmin 4
    • pgmodeler

Il existe de nombreux outils graphiques permettant d’administrer des bases de données PostgreSQL. Certains sont libres, d’autres propriétaires. Certains sont payants, d’autres gratuits. Ils ont généralement les mêmes fonctionnalités de base, mais vont se distinguer sur certaines fonctionnalités un peu plus avancées comme l’import et l’export de données.

Nous allons étudier ici plusieurs outils proposés par la communauté, temBoard, pgAdmin.


temBoard


temBoard - PostgreSQL Remote Control

  • Multi-instances
  • Surveillance OS / PostgreSQL
  • Suivi de l’activité
  • Gestion des performances de PostgreSQL
  • Configuration de chaque instance

temBoard est un outil permettant à un DBA de mener à bien la plupart de ses tâches courantes.

Le serveur web est installé de façon centralisée et un agent est déployé pour chaque instance.


temBoard - Vue parc

temBoard

temboard fournit un tableau de bord global.

temBoard peut administrer plusieurs centaines d’instances.


temBoard - Tableau de bord

temBoard

temboard présente un tableau de bord par instance.


temBoard - Activity

temBoard

La section Activity permet de lister toutes les requêtes courantes (Running), les requêtes bloquées (Waiting) ou bloquantes (Blocking). Il est possible à partir de cette vue de terminer une session.


temBoard - Supervision

temBoard

La section Monitoring permet de visualiser les graphiques historisés au niveau du système d’exploitation (CPU, mémoire, …) ainsi qu’au niveau de l’instance PostgreSQL.

temBoard inclut un système d’alerte par courriel et SMS lorsqu’une métrique dépasse un seuil.


temBoard - Configuration

temBoard

La section Configuration permet naviguer dans les paramètres de PostgreSQL de modifier ces paramètres

Suivant les cas, il sera proposé de recharger la configuration ou de redémarrer l’instance pour appliquer ces changements.


temBoard - Maintenance

temBoard

temBoard estime la fragmentation du stockage des tabl