Aller au contenu

5 Tâches courantes

Transcription française complète du chapitre « Tâches courantes » du manuel DBA1, révision 26.09, pages PDF 279 à 408.
DBA1

5/ Tâches courantes

Contenu extrait du manuel complet, pages PDF 279 à 408.

5.1 INTRODUCTION

  • Gestion des bases
  • Gestion des rôles
  • Gestion des droits
  • Tâches du DBA
  • Sécurité

5.2 BASES

  • Liste des bases
  • Modèle (Template)
  • Création
  • Suppression
  • Modification / configuration Pour gérer des bases, il faut savoir les créer, les configurer et les supprimer. Il faut surtout comprendre qui a le droit de faire quoi, et comment. Ce chapitre détaille chacune des opérations possibles concernant les bases sur une instance.

5.2.1 Liste des bases

  • Catalogue système : pg_database
  • Commande psql :
    • \l
    • \l+ La liste des bases de données est disponible grâce à un catalogue système appelé pg_database . Il suffit d’un SELECT pour récupérer les méta-données sur chaque base :
      Exemple 5-2 Exemple extrait du PDF, page 281.
      postgres=# \x
      
      Expanded display is on.
      Exemple 5-3 Exemple extrait du PDF, page 281.
      postgres=# SELECT * FROM pg_database \gx
      
      -[ RECORD 1 ]--+------------------------------------
      
      oid            | 5
      datname        | postgres
      datdba         | 10
      encoding       | 6
      datlocprovider | c
      datistemplate | f
      datallowconn   | t
      datconnlimit   | -1
      datfrozenxid   | 8885214
      datminmxid     | 1
      dattablespace | 1663
      datcollate     | fr_FR.UTF-8
      datctype       | fr_FR.UTF-8
      daticulocale   |
      daticurules    |
      datcollversion | 2.36
      datacl         |
      Tableau 5-1 Tableau ou résultat de commande extrait du PDF, page 281.

oid            | 18770
datname        | cave
datdba         | 18769
encoding       | 6
datlocprovider | c
datistemplate | f
datallowconn   | t
datconnlimit   | -1
datfrozenxid   | 722
datminmxid     | 1
dattablespace | 1663
datcollate     | fr_FR.UTF-8
datctype       | fr_FR.UTF-8
daticulocale   |
daticurules    |
datcollversion | 2.36
datacl         |
Tableau 5-2 Tableau ou résultat de commande extrait du PDF, page 282.
oid            | 1
datname        | template1
datdba         | 10
encoding       | 6
datlocprovider | c
datistemplate | t
datallowconn   | t
datconnlimit   | -1
datfrozenxid   | 722
datminmxid     | 1
dattablespace | 1663
datcollate     | fr_FR.UTF-8
datctype       | fr_FR.UTF-8
daticulocale   |
daticurules    |
datcollversion | 2.36
datacl         | {=c/postgres,postgres=CTc/postgres}
Tableau 5-3 Tableau ou résultat de commande extrait du PDF, page 282.
oid            | 4
datname        | template0
datdba         | 10
encoding       | 6
datlocprovider | c
datistemplate | t
datallowconn   | f
datconnlimit   | -1
datfrozenxid   | 722
datminmxid     | 1
dattablespace | 1663
datcollate     | fr_FR.UTF-8
datctype       | fr_FR.UTF-8
daticulocale   |
daticurules    |
datcollversion |
datacl         | {=c/postgres,postgres=CTc/postgres}
Tableau 5-4 Tableau ou résultat de commande extrait du PDF, page 282.
Exemple 5-4 Exemple extrait du PDF, page 282.
-[ RECORD 2 ]--+------------------------------------
-[ RECORD 3 ]--+------------------------------------
-[ RECORD 4 ]--+------------------------------------
… Voici la signification des principales colonnes :

  • oid : identifiant système de la base;
  • datname : nom de la base;
  • datdba : l’identifiant de l’utilisateur propriétaire de cette base (voir l’OID correspondant à cet identifiant dans le catalogue système pg_roles ) ;
  • encoding : avec la fonction pg_encoding_to_char() , on peut voir que les valeurs 6 correspondent à UTF8, ce qui est généralement recommandé;
  • datlocprovider : c indique que les collations sont fournies par la biliothèque libc du système, un i indiquerait l’utilisation de la bibliothèque ICU (indépendante du système), et dans ce dernier cas les champs daticulocale et daticurules sont remplies;
  • datistemplate : à true si cette base est utilisable comme modèle ;
  • datallowconn : à true s’il est autorisé de se connecter à cette base;
  • datconnlimit : limite du nombre de connexions simultanées pour les utilisateurs standards sur cette base ( 0 interdit toute connexion, et -1 ne pose pas de limite, jusque max_connections du moins) ;
  • datfrozenxid : plus ancien identifiant de transaction géré par cette base (cela a une importance dans le recyclage des numéros de transaction);
  • dattablespace : identifiant du tablespace par défaut de cette base (1663 indique généralement le tablespace pg_default, de fait le répertoire PGDATA; les emplacements des tablespaces peuvent se trouver dans la table pg_tablespace ou par la fonction pg_tablespace_location()).
  • datcollate et datctype : laissés de côté car bientôt obsolètes (voir https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=b0f6c437160db640d4ea3e49398ebc3ba39d).
  • datacl : droits pour cette base (un champ vide indique qu’il n’y a pas de droits spécifiques). Pour avoir une vue plus simple, il est préférable d’utiliser la métacommande \l dans psql (vue raccourcie pour la mise en page) :
        Name    |   Owner   | Enc. | Locale P. |   Collate   |…|   Access privileges
    ------------+-----------+------+-----------+-------------+-+-----------------------
      cave      | caviste   | UTF8 | libc      | fr_FR.UTF-8 | |
      pgbench   | pgbench   | UTF8 | libc      | fr_FR.UTF-8 | |
      postgres  | postgres | UTF8 | libc       | fr_FR.UTF-8 | |
      template0 | postgres | UTF8 | libc       | fr_FR.UTF-8 | | =c/postgres           +
                |           |      |           |             | | postgres=CTc/postgres
      template1 | postgres | UTF8 | libc       | fr_FR.UTF-8 | | =c/postgres           +
                |           |      |           |             | | postgres=CTc/postgres
      tpc       | tpc_owner | UTF8 | libc      | fr_FR.UTF-8 | |
    Tableau 5-5 Tableau ou résultat de commande extrait du PDF, page 283.
    Exemple 5-5 Exemple extrait du PDF, page 283.
    postgres=# \l
                                              List of databases
    
    … Avec \l+ , il est possible d’avoir plus d’informations (notamment la taille de la base ou le commentaire). Noter que si l’on veut savoir où psql va chercher ces informations, il est possible de lui demander d’afficher la requête qu’il envoie au serveur :

Exemple 5-6 Exemple extrait du PDF, page 284.
$ psql --echo-hidden -c '\l+'
Exemple 5-7 Exemple extrait du PDF, page 284.
********* QUERY **********
SELECT
   d.datname as "Name",
   pg_catalog.pg_get_userbyid(d.datdba) as "Owner",
   pg_catalog.pg_encoding_to_char(d.encoding) as "Encoding",
   CASE d.datlocprovider WHEN 'c' THEN 'libc' WHEN 'i' THEN 'icu' END AS "Locale Provider",
   d.datcollate as "Collate",
   d.datctype as "Ctype",
   d.daticulocale as "ICU Locale",
   d.daticurules as "ICU Rules",
   pg_catalog.array_to_string(d.datacl, E'\n') AS "Access privileges",
   CASE WHEN pg_catalog.has_database_privilege(d.datname, 'CONNECT')
        THEN pg_catalog.pg_size_pretty(pg_catalog.pg_database_size(d.datname))
        ELSE 'No Access'
   END as "Size",
   t.spcname as "Tablespace",
   pg_catalog.shobj_description(d.oid, 'pg_database') as "Description"
FROM pg_catalog.pg_database d
   JOIN pg_catalog.pg_tablespace t on d.dattablespace = t.oid
ORDER BY 1;
**************************
… (suivent les résultats.) La requête affichée montre bien que psql accède au catalogue pg_database , ainsi qu’à des fonctions système permettant d’éviter d’avoir à faire soi-même les jointures.

5.2.2 Modèle (template)

  • Toute création de base se fait à partir d’un modèle
    • template1
  • Permet de personnaliser sa création de base
  • Mais il est aussi possible d’utiliser une autre base Toute création de base se fait à partir d’un modèle. Par défaut, PostgreSQL utilise le modèle template1 . Tout objet ajouté dans le modèle est copié dans la nouvelle base. Cela concerne le schéma (la structure) comme les données. Il est donc intéressant d’ajouter des objets directement dans template1 pour que ces derniers soient copiés dans les prochaines bases qui seront créées. Pour éviter malgré tout que cette base soit trop modifiée, il est possible de créer des bases qui seront ensuite utilisées comme modèle.

5.2.3 Création d’une base

  • SQL : CREATE DATABASE
    • droit nécessaire : SUPERUSER ou CREATEDB
    • prérequis : base inexistante
  • Outil système : createdb L’ordre CREATE DATABASE est le seul moyen avec PostgreSQL de créer une base de données. Il suffit d’y ajouter le nom de la base à créer pour que la création se fasse. Il est néanmoins possible d’y ajouter un certain nombre d’options :
  • OWNER , pour préciser le propriétaire de la base de données (si cette option n’est pas utilisée, le propriétaire est celui qui exécute la commande);
  • TEMPLATE , pour indiquer le modèle à copier (par défaut template1 ) ;
  • ENCODING , pour forcer un autre encodage que celui du serveur (à noter qu’il faudra utiliser le modèle template0 dans ce cas);
  • LC_COLLATE et LC_CTYPE , pour préciser respectivement l’ordre de tri des données textes et le jeu de caractères (par défaut, il s’agit de la locale utilisée lors de l’initialisation de l’instance);
  • STRATEGY (depuis la version 15), pour indiquer la stratégie employée pour créer la base de donnée. Deux choix sont disponibles :
  • FILE_COPY : C’est la méthode historique. Le contenu des répertoires d’une base de données est intégralement copié pour initialiser la nouvelle base, avec juste une trace dans les journaux de transaction. Elle implique deux checkpoints parfois gênants, mais peut être intéressante pour copier de grosses bases en générant moins de journaux ;
  • WAL_LOG : C’est la méthode par défaut à partir de la version 15. L’opération est entièrement journalisée, et la liste des objets à copier et générée via le catalogue de PostgreSQL. Cette méthode évite les checkpoints et sécurise la copie en garantissant que toutes les opérations sont tracées, tout en évitant la copie accidentelle de fichier orphelins de la base modèle vers la base cible. Cette opération peut écrire beaucoup de journaux dans le cas où la base modèle est grosse, mais elle est idéale pour les créations de nouvelles bases presque vides;
  • TABLESPACE , pour stocker la base dans un autre tablespace que le répertoire des données ;
  • ALLOW_CONNECTIONS , pour autoriser ou non les connexions à la base;
  • CONNECTION LIMIT , pour limiter le nombre de connexions d’utilisateurs standards simultanées sur cette base (illimité par défaut, tout en respectant le paramètre max_connections ) ;
  • IS_TEMPLATE , pour configurer ou non le mode template. La copie se fait par clonage de la base de données modèle sélectionnée. Tous les objets et toutes les données faisant partie du modèle seront copiés sans exception. Par exemple, avant la 9.0, on ajoutait le langage PL/pgSQL dans la base de données template1 pour que toutes les bases créées à partir de template1 disposent directement de ce langage. Ce n’est plus nécessaire à partir de la 9.0 car le langage PL/pgSQL est activé dès la création de l’instance. Mais il est possible d’envisager d’autres usages de ce comportement (par exemple installer une extension ou une surcouche comme PostGIS

dans chaque base). À noter qu’il peut être nécessaire de sélectionner le modèle template0 en cas de sélection d’un autre encodage que celui par défaut (comme la connexion est interdite sur template0 , il y a peu de chances que des données textes avec un certain encodage aient été enregistrées dans cette base). Voici l’exemple le plus simple de création d’une base :

Exemple 5-8 Exemple extrait du PDF, page 286.
CREATE DATABASE b1 ;
Cet ordre crée la base de données b1. Elle aura toutes les options par défaut. Autre exemple :
Exemple 5-9 Exemple extrait du PDF, page 286.
CREATE DATABASE b2 OWNER u1;
Cette commande SQL crée la base b2 et s’assure que le propriétaire de cette base soit l’utilisateur u1 (il faut que ce dernier existe au préalable). Tous les utilisateurs n’ont pas le droit de créer une base de données. L’utilisateur qui exécute la commande SQL doit avoir soit l’attribut SUPERUSER soit l’attribut CREATEDB . S’il utilise un autre modèle que celui par défaut, il doit être propriétaire de ce modèle ou le modèle doit être marqué comme étant un modèle officiel (autrement dit la colonne datistemplate doit être à true ). Voici un exemple complet :
Exemple 5-10 Exemple extrait du PDF, page 286.
postgres=# CREATE DATABASE b1;
CREATE DATABASE
Exemple 5-11 Exemple extrait du PDF, page 286.
postgres=# CREATE USER u1;
CREATE ROLE
Exemple 5-12 Exemple extrait du PDF, page 286.
postgres=# CREATE DATABASE b2 OWNER u1;
CREATE DATABASE
Exemple 5-13 Exemple extrait du PDF, page 286.
postgres=# CREATE USER u2 CREATEDB;
CREATE ROLE NB : pour que la connexion qui suit fonctionne, et sans mot de passe, il faut paramétrer pg_hba.conf pour autoriser la connexion de cet utilisateur. Ce sera traité plus bas.
Exemple 5-14 Exemple extrait du PDF, page 286.
postgres=# \c postgres u2
You are now connected to database “postgres” as user “u2”.
Exemple 5-15 Exemple extrait du PDF, page 286.
postgres=> CREATE DATABASE b3;
CREATE DATABASE
Exemple 5-16 Exemple extrait du PDF, page 286.
postgres=> CREATE DATABASE b4 TEMPLATE b2;
ERROR: permission denied to copy database "b2"
postgres=> CREATE DATABASE b4 TEMPLATE b3;
CREATE DATABASE
Exemple 5-17 Exemple extrait du PDF, page 286.
postgres=> \c postgres postgres

You are now connected to database “postgres” as user “postgres”.

Exemple 5-18 Exemple extrait du PDF, page 287.
postgres=# ALTER DATABASE b2 IS_TEMPLATE=true;
ALTER DATABASE
Exemple 5-19 Exemple extrait du PDF, page 287.
postgres=# \c postgres u2
You are now connected to database “postgres” as user “u2”.
Exemple 5-20 Exemple extrait du PDF, page 287.
postgres=> CREATE DATABASE b5 TEMPLATE b2;
CREATE DATABASE
Exemple 5-21 Exemple extrait du PDF, page 287.
postgres=> \c postgres postgres
postgres=# \l

postgres=# \l
                                                 List of databases
   Name   | Owner    | Enc… |   Collate   |    Ctype    | … |   Access privileges
----------+----------+------+-------------+-------------+---+---------------------
b1        | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 |   |
b2        | u1       | UTF8 | en_US.UTF-8 | en_US.UTF-8 |   |
b3        | u2       | UTF8 | en_US.UTF-8 | en_US.UTF-8 |   |
b4        | u2       | UTF8 | en_US.UTF-8 | en_US.UTF-8 |   |
b5        | u2       | UTF8 | en_US.UTF-8 | en_US.UTF-8 |   |
postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 |    |
template0 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 |   | =c/postgres           +
          |          |      |             |             |   | postgres=CTc/postgres
template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 |   | =c/postgres           +
          |          |      |             |             |   | postgres=CTc/postgres
Tableau 5-6 Tableau ou résultat de commande extrait du PDF, page 287.
L’outil système createdb se connecte à la base de données postgres et exécute la commande
Exemple 5-22 Exemple extrait du PDF, page 287.
 CREATE DATABASE , exactement comme ci-dessus. Appelée sans aucun argument, createdb crée
une base de donnée portant le nom de l’utilisateur connecté (si cette dernière n’existe pas). L’option –echo de cette commande permet de voir exactement ce que createdb exécute :
Exemple 5-23 Exemple extrait du PDF, page 287.
$ createdb --echo --owner u1 b6

SELECT pg_catalog.set_config('search_path', '', false)
CREATE DATABASE b6 OWNER u1;
À titre d’exemple de trace de l’activité d’un client, voici ce qui peut être tracé : [unknown] - LOG: connection received: host=[local] [unknown] - LOG: connection authorized: user=postgres database=postgres
Exemple 5-24 Exemple extrait du PDF, page 287.
createdb - LOG: duration: 1.018 ms statement: SELECT
   pg_catalog.set_config('search_path', ''Z, false)
createdb - CREATE DATABASE b6 OWNER u1;
createdb - LOG: disconnection: session time: 0:00:00.277 user=postgres
   database=postgres host=[local]
Cela réclame ce paramétrage des traces :
Exemple 5-25 Exemple extrait du PDF, page 287.
log_min_duration_statement = 0
log_connections = on
log_disconnections = on

5.2.4 Suppression d’une base

  • SQL : DROP DATABASE
    • droit nécessaire : SUPERUSER ou propriétaire de la base
    • prérequis : aucun utilisateur connecté sur la base
    • ou déconnexion forcée
  • Outil système : dropdb Supprimer une base de données supprime tous les objets et toutes les données contenues dans la base. La destruction d’une base de données ne peut pas être annulée. La suppression se fait uniquement avec l’ordre DROP DATABASE . Seuls les superutilisateurs et le propriétaire d’une base peuvent supprimer cette base. Cependant, pour que cela fonctionne, il faut qu’aucun utilisateur ne soit connecté à cette base. Si quelqu’un est connecté, un message d’erreur apparaîtra :
    Exemple 5-26 Exemple extrait du PDF, page 288.
    postgres=# DROP DATABASE b6;
    
    ERROR: database "b6" is being accessed by other users
    DETAIL: There are 1 other session(s) using the database.
    
    Il faut donc attendre que les utilisateurs se déconnectent, ou leur demander de le faire, voire les déconnecter autoritairement :
    Exemple 5-27 Exemple extrait du PDF, page 288.
    postgres=# SELECT pg_terminate_backend(pid)
           FROM pg_stat_activity
           WHERE datname='b6';
    
    pg_terminate_backend
    --------------------
     t
    Tableau 5-7 Tableau ou résultat de commande extrait du PDF, page 288.
    Exemple 5-28 Exemple extrait du PDF, page 288.
    postgres=# DROP DATABASE b6;
    
    DROP DATABASE Là-aussi, PostgreSQL propose un outil système appelé dropdb pour faciliter la suppression des bases. Cet outil se comporte comme createdb . Il se connecte à la base postgres et exécute l’ordre SQL correspondant à la suppression de la base :
    Exemple 5-29 Exemple extrait du PDF, page 288.
    $ dropdb --echo b5
    
    SELECT pg_catalog.set_config('search_path', '', false)
    DROP DATABASE b5;
    
    Contrairement à createdb , sans nom de base, dropdb ne fait rien. Pour forcer la déconnexion des utilisateurs, il est possible d’utiliser la clause WITH (FORCE) de l’ordre DROP DATABASE ou l’option en ligne de commande –force de l’outil dropdb .

5.2.5 Modification / configuration

  • ALTER DATABASE
  • pour modifier quelques méta-données
  • pour ajouter, modifier ou supprimer une configuration
  • Catalogue système pg_db_role_setting Avec la commande ALTER DATABASE , il est possible de modifier quelques méta-données :
  • le nom de la base;
  • son propriétaire;
  • la limite de connexions;
  • le tablespace de la base. Dans le cas d’un changement de nom ou de tablespace, aucun utilisateur ne doit être connecté à la base pendant l’opération. Il est aussi possible d’ajouter, de modifier ou de supprimer une configuration spécifique pour une base de données en utilisant la syntaxe suivante :
    Exemple 5-30 Exemple extrait du PDF, page 289.
    ALTER DATABASE base SET paramètre TO valeur;
    La configuration spécifique de chaque base de données surcharge toute configuration reçue sur la ligne de commande du processus postgres père ou du fichier de configuration postgresql.conf . L’ajout d’une configuration avec ALTER DATABASE sauvegarde le paramétrage mais ne l’applique pas immédiatement. Il n’est appliqué que pour les prochaines connexions. Notez que les utilisateurs peuvent cependant modifier ce réglage pendant la session ; il s’agit seulement d’un réglage par défaut, pas d’un réglage forcé. Voici un exemple complet :
    Exemple 5-31 Exemple extrait du PDF, page 289.
    b1=# SHOW work_mem;
    work_mem
    ----------
     4MB
    Tableau 5-8 Tableau ou résultat de commande extrait du PDF, page 289.
    Exemple 5-32 Exemple extrait du PDF, page 289.
    b1=# ALTER DATABASE b1 SET work_mem TO '2MB';
    ALTER DATABASE
    Exemple 5-33 Exemple extrait du PDF, page 289.
    b1=# SHOW work_mem;
    work_mem
    ----------
     4MB
    Tableau 5-9 Tableau ou résultat de commande extrait du PDF, page 289.
    Exemple 5-34 Exemple extrait du PDF, page 289.
    b1=# \c b1
    You are now connected to database “b1” as user “postgres”.
    Exemple 5-35 Exemple extrait du PDF, page 289.
    b1=# SHOW work_mem;

work_mem

----------
 2MB
Tableau 5-10 Tableau ou résultat de commande extrait du PDF, page 290.
Cette configuration est présente même après un redémarrage du serveur. Elle n’est pas enregistrée dans le fichier de configuration postgresql.conf , mais dans un catalogue système appelé pg_db_role_setting :
Exemple 5-36 Exemple extrait du PDF, page 290.
b1=# ALTER DATABASE b2 SET work_mem TO '32MB';
ALTER DATABASE
Exemple 5-37 Exemple extrait du PDF, page 290.
b1=# ALTER USER u1 SET maintenance_work_mem TO '256MB';
ALTER ROLE
Exemple 5-38 Exemple extrait du PDF, page 290.
b1=# SELECT * FROM pg_db_role_setting;
 setdatabase | setrole |          setconfig
-------------+---------+------------------------------
       16384 |       0 | {work_mem=2MB}
       16386 |       0 | {work_mem=32MB}
           0 |   16385 | {maintenance_work_mem=256MB}
Tableau 5-11 Tableau ou résultat de commande extrait du PDF, page 290.
Exemple 5-39 Exemple extrait du PDF, page 290.
b1=# SELECT d.datname AS "Base", r.rolname AS "Utilisateur",
     setconfig AS "Configuration"
     FROM pg_db_role_setting
     LEFT JOIN pg_database d ON d.oid=setdatabase
     LEFT JOIN pg_roles r ON r.oid=setrole
     ORDER BY 1, 2;
 Base | Utilisateur |        Configuration
------+-------------+------------------------------
 b1   |             | {work_mem=2MB}
 b2   |             | {work_mem=32MB}
      | u1          | {maintenance_work_mem=256MB}
Tableau 5-12 Tableau ou résultat de commande extrait du PDF, page 290.
Exemple 5-40 Exemple extrait du PDF, page 290.
b1=# ALTER DATABASE b3 SET work_mem to '10MB';
ALTER DATABASE
Exemple 5-41 Exemple extrait du PDF, page 290.
b1=# ALTER DATABASE b3 SET maintenance_work_mem to '128MB';
ALTER DATABASE
Exemple 5-42 Exemple extrait du PDF, page 290.
b1=# ALTER DATABASE b3 SET random_page_cost to 3;
ALTER DATABASE
Exemple 5-43 Exemple extrait du PDF, page 290.
b1=# SELECT d.datname AS "Base", r.rolname AS "Utilisateur",
     setconfig AS "Configuration"
     FROM pg_db_role_setting
     LEFT JOIN pg_database d ON d.oid=setdatabase
     LEFT JOIN pg_roles r ON r.oid=setrole
     ORDER BY 1, 2;
 Base | Utilisateur |                         Configuration
------+-------------+---------------------------------------------------------------
 b1   |             | {work_mem=2MB}
Tableau 5-13 Tableau ou résultat de commande extrait du PDF, page 290.

 b2    |                | {work_mem=32MB}
 b3    |                | {work_mem=10MB,maintenance_work_mem=128MB,random_page_cost=3}
       | u1             | {maintenance_work_mem=256MB}
Tableau 5-14 Tableau ou résultat de commande extrait du PDF, page 291.
Pour annuler la configuration d’un paramètre, utilisez :
Exemple 5-44 Exemple extrait du PDF, page 291.
ALTER DATABASE base RESET paramètre;
Par exemple :
Exemple 5-45 Exemple extrait du PDF, page 291.
b1=# ALTER DATABASE b3 RESET random_page_cost;
ALTER DATABASE
Exemple 5-46 Exemple extrait du PDF, page 291.
b1=# SELECT d.datname AS "Base", r.rolname AS "Utilisateur",
     setconfig AS "Configuration"
     FROM pg_db_role_setting
     LEFT JOIN pg_database d ON d.oid=setdatabase
     LEFT JOIN pg_roles r ON r.oid=setrole
     ORDER BY 1, 2;
 Base | Utilisateur |                Configuration
------+-------------+--------------------------------------------
 b1   |             | {work_mem=2MB}
 b2   |             | {work_mem=32MB}
 b3   |             | {work_mem=10MB,maintenance_work_mem=128MB}
      | u1          | {maintenance_work_mem=256MB}
Tableau 5-15 Tableau ou résultat de commande extrait du PDF, page 291.
Si vous copiez avec CREATE DATABASE … TEMPLATE une base dont certains paramètres ont été configurés spécifiquement pour elle, ces paramètres ne sont pas appliqués à la nouvelle base de données.

5.3 RÔLES

  • Utilisateur/groupe
  • Liste des rôles
  • Création
  • Suppression
  • Modification
  • Gestion des mots de passe Un rôle peut être vu soit comme un utilisateur de la base de données, soit comme un groupe d’utilisateurs de la base de données, suivant la façon dont le rôle est conçu et configuré. Les rôles peuvent être propriétaires d’objets de la base de données (par exemple des tables) et peuvent affecter des droits sur ces objets à d’autres rôles pour contrôler l’accès à ces objets. De plus, il est possible de donner l’appartenance d’un rôle à un autre rôle, l’autorisant ainsi à utiliser les droits affectés au rôle dont il est membre. Nous allons voir dans cette partie comment gérer les rôles, en allant de leur création à leur suppression, en passant par leur configuration.

5.3.1 Utilisateurs et groupes

  • « Rôle » = utilisateurs et groupes
  • CREATE/DROP/ALTER ROLE
  • Historique
    • CREATE/DROP/ALTER USER
    • CREATE/DROP/ALTER GROUP Avant PostgreSQL 8.1, les notions d’utilisateur et de groupe étaient séparées. Pour conserver la compatibilité avec les anciennes applications, les ordres SQL pour les utilisateurs et les groupes ont été conservés. Il est donc toujours possible de les utiliser mais il est actuellement conseillé de passer par les ordres SQL pour les rôles. En fait, un utilisateur est juste un rôle qui a par défaut le droit de se connecter.

5.3.2 Liste des rôles

  • Catalogue système : pg_roles
  • Dans psql :
  • \du
  • \drg (v15+) La liste des rôles est disponible grâce à un catalogue système appelé pg_roles . Il suffit d’un SELECT pour récupérer les méta-données sur chaque rôle :
    Exemple 5-47 Exemple extrait du PDF, page 293.
    postgres=# \x
    
    Expanded display is on.
    rolname        | postgres
    rolsuper       | t
    rolinherit     | t
    rolcreaterole | t
    rolcreatedb    | t
    rolcanlogin    | t
    rolreplication | t
    rolconnlimit   | -1
    rolpassword    | ********
    rolvaliduntil |
    rolbypassrls   | t
    rolconfig      |
    oid            | 10
    Tableau 5-16 Tableau ou résultat de commande extrait du PDF, page 293.
    rolname        | pg_monitor
    rolsuper       | f
    rolinherit     | t
    rolcreaterole | f
    rolcreatedb    | f
    rolcanlogin    | f
    rolreplication | f
    rolconnlimit   | -1
    rolpassword    | ********
    rolvaliduntil |
    rolbypassrls   | f
    rolconfig      |
    oid            | 3373
    Tableau 5-17 Tableau ou résultat de commande extrait du PDF, page 293.
    Exemple 5-48 Exemple extrait du PDF, page 293.
    postgres=# SELECT * FROM pg_roles LIMIT 3;
    
    -[ RECORD 1 ]--+---------------------
    -[ RECORD 2 ]--+---------------------
    -[ RECORD 3 ]--+---------------------
    
    rolname        | pg_read_all_settings
    rolsuper       | f
    rolinherit     | t
    rolcreaterole | f
    rolcreatedb    | f
    rolcanlogin    | f
    rolreplication | f
    Tableau 5-18 Tableau ou résultat de commande extrait du PDF, page 293.

rolconnlimit        | -1
rolpassword         | ********
rolvaliduntil       |
rolbypassrls        | f
rolconfig           |
oid                 | 3374
Tableau 5-19 Tableau ou résultat de commande extrait du PDF, page 294.
Voici la signification des différentes colonnes :

  • rolname , le nom du rôle;
  • rolsuper , le rôle a-t-il l’attribut SUPERUSER ?;
  • rolinherit , le rôle hérite-t-il automatiquement des droits des rôles dont il est membre? ;
  • rolcreaterole , le rôle a-t-il le droit de créer des rôles? ;
  • rolcreatedb , le rôle a-t-il le droit de créer des bases? ;
  • rolcanlogin , le rôle a-t-il le droit de se connecter? ;
  • rolreplication , le rôle peut-il être utilisé dans une connexion de réplication? ;
  • rolconnlimit , limite du nombre de connexions simultanées pour ce rôle ( 0 indiquant « pas de connexions possibles », -1 permet d’indiquer qu’il n’y a pas de limite en dehors de la valeur du paramètre max_connections );
  • rolpassword , mot de passe du rôle (non affiché) ;
  • rolvaliduntil , date limite de validité du mot de passe;
  • rolbypassrls , le rôle court-circuite-t-il les droits sur les lignes ? ;
  • rolconfig , configuration spécifique du rôle;
  • oid , identifiant système du rôle. Pour avoir une vue plus simple, il est préférable d’utiliser la métacommande \du dans psql :
    Exemple 5-49 Exemple extrait du PDF, page 294.
    postgres=# \du
    
    List of roles
    Role name | postgres
    Attributes | Superuser, Create role, Create DB, Replication, Bypass RLS
    Member of | {}
    Tableau 5-20 Tableau ou résultat de commande extrait du PDF, page 294.
    Role name | u1
    Attributes |
    Member of | {}
    Tableau 5-21 Tableau ou résultat de commande extrait du PDF, page 294.
    Exemple 5-50 Exemple extrait du PDF, page 294.
    -[ RECORD 1 ]----------------------------------------------------------
    -[ RECORD 2 ]----------------------------------------------------------
    -[ RECORD 3 ]----------------------------------------------------------
    Role name | u2
    Attributes | Create DB
    Member of | {}
    Tableau 5-22 Tableau ou résultat de commande extrait du PDF, page 294.
    Il est à noter que les rôles systèmes ne sont pas affichés. Les rôles systèmes sont tous ceux commençant par pg_ . La métacommande \du ne fait qu’accéder aux tables systèmes. Par exemple :
    Exemple 5-51 Exemple extrait du PDF, page 294.
    $ psql postgres
    

Exemple 5-52 Exemple extrait du PDF, page 295.
psql (17.0)
Type “help” for help.
Exemple 5-53 Exemple extrait du PDF, page 295.
postgres=# \set ECHO_HIDDEN true
postgres=# \x on
postgres=# \du
Exemple 5-54 Exemple extrait du PDF, page 295.
******** REQUÊTE *********
SELECT r.rolname, r.rolsuper, r.rolinherit,
  r.rolcreaterole, r.rolcreatedb, r.rolcanlogin,
  r.rolconnlimit, r.rolvaliduntil
, r.rolreplication
, r.rolbypassrls
FROM pg_catalog.pg_roles r
WHERE r.rolname !~ '^pg_'
ORDER BY 1;
Exemple 5-55 Exemple extrait du PDF, page 295.
**************************
List of roles
Nom du rôle | postgres
Attributs   | Superutilisateur, Créer un rôle, Créer
            | une base, Réplication, Contournement RLS
Tableau 5-23 Tableau ou résultat de commande extrait du PDF, page 295.
Exemple 5-56 Exemple extrait du PDF, page 295.
-[ RECORD 7 ]------------------------------------------
[…] \du affiche aussi les groupes jusqu’à la version 14 de PostgreSQL. Pour les voir avec PostgreSQL 15 ou postérieur, utiliser \drg :
Exemple 5-57 Exemple extrait du PDF, page 295.
CREATE ROLE devs ;
CREATE ROLE alphonse LOGIN ;
CREATE ROLE alfred LOGIN ;
GRANT devs TO alphonse ;
GRANT devs TO alfred ;
Liste des droits de rôles
 Nom du rôle | Membre de |   Options    | Donneur de droits
-------------+-----------+--------------+-------------------
 alfred      | devs      | INHERIT, SET | postgres
 alphonse    | devs      | INHERIT, SET | postgres
Tableau 5-24 Tableau ou résultat de commande extrait du PDF, page 295.

5.3.3 Création d’un rôle

  • SQL : CREATE ROLE

  • droit nécessaire : SUPERUSER ou CREATEROLE

  • prérequis : utilisateur inexistant

  • Outil système : createuser

  • attribut LOGIN par défaut L’ordre CREATE ROLE est le seul moyen avec PostgreSQL de créer un rôle. Il suffit d’y ajouter le nom du rôle à créer pour que la création se fasse. Il est néanmoins possible d’y ajouter un certain nombre d’options :

  • SUPERUSER , pour que le nouveau rôle soit superutilisateur (autrement dit, ce rôle a le droit de tout faire une fois connecté à une base de données) ;

  • CREATEDB , pour que le nouveau rôle ait le droit de créer des bases de données ;

  • CREATEROLE , pour que le nouveau rôle ait le droit de créer un rôle;

  • INHERIT , pour que le nouveau rôle hérite automatiquement des droits des rôles dont il est membre;

  • LOGIN , pour que le nouveau rôle ait le droit de se connecter (c’est automatique avec

    Exemple 5-58 Exemple extrait du PDF, page 296.
             CREATE USER ou createuser , pas avec CREATE GROUP ou CREATE ROLE ) ;

  • REPLICATION , pour que le nouveau rôle puisse se connecter en mode réplication;

  • BYPASSRLS , pour que le nouveau rôle puisse ne pas être vérifié pour les sécurités au niveau ligne;

  • CONNECTION LIMIT , pour limiter le nombre de connexions simultanées pour ce rôle;

  • PASSWORD , pour préciser le mot de passe de ce rôle;

  • VALID UNTIL , pour indiquer la date limite de validité du mot de passe;

  • IN ROLE , pour indiquer à quel rôle ce rôle appartient;

  • IN GROUP , pour indiquer à quel groupe ce rôle appartient;

  • ROLE , pour indiquer les membres de ce rôle;

  • ADMIN , pour indiquer les membres de ce rôles (les nouveaux membres ayant en plus la possibilité d’ajouter d’autres membres à ce rôle);

  • USER , pour indiquer les membres de ce rôle;

  • SYSID , pour préciser l’identifiant système, mais est ignoré. Par défaut, un rôle n’a aucun attribut (ni superutilisateur, ni le droit de créer des rôles ou des bases, ni la possibilité de se connecter en mode réplication, ni la possibilité de se connecter). Voici quelques exemples simples :

    Exemple 5-59 Exemple extrait du PDF, page 296.
    postgres=# CREATE ROLE u3;
    
    CREATE ROLE
    Exemple 5-60 Exemple extrait du PDF, page 296.
    postgres=# CREATE ROLE u4 CREATEROLE;
    
    CREATE ROLE
    Exemple 5-61 Exemple extrait du PDF, page 296.
    postgres=# CREATE ROLE u5 LOGIN IN ROLE u2;
    
    CREATE ROLE
    Exemple 5-62 Exemple extrait du PDF, page 296.
    postgres=# CREATE ROLE u6 ROLE u5;
    
    CREATE ROLE
    Exemple 5-63 Exemple extrait du PDF, page 296.
    postgres=# \du
    
    List of roles
    Role name | postgres
    Attributes | Superuser, Create role, Create DB, Replication, Bypass RLS
    Member of | {}
    Tableau 5-25 Tableau ou résultat de commande extrait du PDF, page 296.
    Exemple 5-64 Exemple extrait du PDF, page 296.
    -[ RECORD 1 ]----------------------------------------------------------
    -[ RECORD 2 ]----------------------------------------------------------

Role name | u1
Attributes |
Member of | {}
Tableau 5-26 Tableau ou résultat de commande extrait du PDF, page 297.
Role name | u2
Attributes | Create DB
Member of | {}
Tableau 5-27 Tableau ou résultat de commande extrait du PDF, page 297.
Role name | u3
Attributes | Cannot login
Member of | {}
Tableau 5-28 Tableau ou résultat de commande extrait du PDF, page 297.
Role name | u4
Attributes | Create role, Cannot login
Member of | {}
Tableau 5-29 Tableau ou résultat de commande extrait du PDF, page 297.
Role name | u5
Attributes |
Member of | {u2,u6}
Tableau 5-30 Tableau ou résultat de commande extrait du PDF, page 297.
Exemple 5-65 Exemple extrait du PDF, page 297.
-[ RECORD 3 ]----------------------------------------------------------
-[ RECORD 4 ]----------------------------------------------------------
-[ RECORD 5 ]----------------------------------------------------------
-[ RECORD 6 ]----------------------------------------------------------
-[ RECORD 7 ]----------------------------------------------------------
Role name | u6
Attributes | Cannot login
Member of | {}
Tableau 5-31 Tableau ou résultat de commande extrait du PDF, page 297.
Tous les rôles n’ont pas le droit de créer un rôle. Le rôle qui exécute la commande SQL doit avoir soit l’attribut SUPERUSER soit l’attribut CREATEROLE . Un utilisateur qui a l’attribut CREATEROLE pourra créer tout type de rôles sauf des superutilisateurs. Voici un exemple complet :
Exemple 5-66 Exemple extrait du PDF, page 297.
postgres=# CREATE ROLE u7 LOGIN CREATEROLE;
CREATE ROLE
Exemple 5-67 Exemple extrait du PDF, page 297.
postgres=# \c postgres u7
You are now connected to database “postgres” as user “u7”.
Exemple 5-68 Exemple extrait du PDF, page 297.
postgres=> CREATE ROLE u8 LOGIN;
CREATE ROLE
Exemple 5-69 Exemple extrait du PDF, page 297.
postgres=> CREATE ROLE u9 LOGIN CREATEDB;
CREATE ROLE
Exemple 5-70 Exemple extrait du PDF, page 297.
postgres=> CREATE ROLE u10 LOGIN SUPERUSER;

ERROR:   must be superuser to create superusers

postgres=> \du
List of roles
 Role name |                         Attributes                         | Member of
-----------+------------------------------------------------------------+-----------
 postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
 u1        |                                                            | {}
 u2        | Create DB                                                  | {}
 u3        | Cannot login                                               | {}
Tableau 5-32 Tableau ou résultat de commande extrait du PDF, page 297.

 u4          | Create role, Cannot login                                               | {}
 u5          |                                                                         | {u2,u6}
 u6          | Cannot login                                                            | {}
 u7          | Create role                                                             | {}
 u8          |                                                                         | {}
 u9          | Create DB                                                               | {}
Tableau 5-33 Tableau ou résultat de commande extrait du PDF, page 298.
Il est toujours possible d’utiliser les ordres SQL CREATE USER et CREATE GROUP . PostgreSQL les comprend comme étant l’ordre CREATE ROLE . Dans le premier cas ( CREATE USER ), il ajoute automatiquement l’option LOGIN . Un outil shell existe aussi pour créer un utilisateur, c’est-à-dire là encore un rôle avec l’attribut LOGIN :
Exemple 5-71 Exemple extrait du PDF, page 298.
$ createuser --echo u10 --superuser

SELECT pg_catalog.set_config('search_path', '', false)
CREATE ROLE u10 SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN;

5.3.4 Suppression d’un rôle

  • SQL : DROP ROLE
  • droit nécessaire : SUPERUSER ou CREATEROLE
  • prérequis : rôle existant, rôle ne possédant pas d’objet
  • penser à REASSIGN OWNED et DROP OWNED
  • Outil système : dropuser La suppression d’un rôle se fait uniquement avec l’ordre DROP ROLE . Seuls les utilisateurs disposant des attributs SUPERUSER et CREATEROLE peuvent supprimer des rôles. Cependant, pour que cela fonctionne, il faut que le rôle à supprimer ne soit pas propriétaire d’objets dans l’instance. S’il est propriétaire, un message d’erreur apparaîtra :
    Exemple 5-72 Exemple extrait du PDF, page 298.
    postgres=> DROP ROLE u1;
    
    ERROR: role "u1" cannot be dropped because some objects depend on it
    DETAIL: owner of database b2
    Il faut donc changer le propriétaire des objets en question ou supprimer les objets. Vous pouvez utiliser respectivement les ordres REASSIGN OWNED et DROP OWNED pour cela. Un rôle qui n’a pas l’attribut SUPERUSER ne peut pas supprimer un rôle qui a cet attribut :
    Exemple 5-73 Exemple extrait du PDF, page 298.
    postgres=> DROP ROLE u10;
    
    ERROR:   must be superuser to drop superusers

Par contre, il est possible de supprimer un rôle qui est connecté. Le rôle connecté gardera des possibilités limitées après sa suppression. Par exemple, il peut toujours lire quelques tables systèmes mais il ne peut plus créer d’objets. Il existe aussi un outil système appelé dropuser pour faciliter la suppression d’un rôle :

Exemple 5-74 Exemple extrait du PDF, page 299.
$ dropuser --echo u10

SELECT pg_catalog.set_config('search_path', '', false)
DROP ROLE u10;

5.3.5 Modification d’un rôle

  • ALTER ROLE
  • pour modifier quelques méta-données
  • pour ajouter, modifier ou supprimer une configuration
  • Catalogue système : pg_db_role_setting Avec la commande ALTER ROLE , il est possible de modifier quelques méta-données du rôle :
  • son nom ;
  • son mot de passe;
  • sa limite de validité;
  • ses attributs :
  • SUPERUSER ;
  • CREATEDB ;
  • CREATEROLE ;
  • CREATEUSER ;
  • INHERIT ;
  • LOGIN ;
  • REPLICATION ;
  • BYPASSRLS . Toutes ces opérations peuvent s’effectuer alors que le rôle est connecté à la base. Il est aussi possible d’ajouter, de modifier ou de supprimer une configuration spécifique pour un rôle en utilisant la syntaxe suivante :
    Exemple 5-75 Exemple extrait du PDF, page 299.
    ALTER ROLE rôle SET paramètre TO valeur;
    La configuration spécifique de chaque rôle surcharge toute configuration reçue sur la ligne de commande du processus postgres père ou du fichier de configuration postgresql.conf , mais aussi la configuration spécifique de la base de données où le rôle est connecté. L’ajout d’une configuration avec ALTER ROLE sauvegarde le paramétrage mais ne l’applique pas immédiatement. Il n’est appliqué que pour les prochaines connexions. Notez que les rôles peuvent cependant modifier ce réglage pendant la session; il s’agit seulement d’un réglage par défaut, pas d’un réglage forcé.

Voici un exemple complet :

Exemple 5-76 Exemple extrait du PDF, page 300.
$ psql -U u2 postgres

psql (13.0)
Type “help” for help.
Exemple 5-77 Exemple extrait du PDF, page 300.
postgres=> SHOW work_mem;
work_mem
----------
 4MB
Tableau 5-34 Tableau ou résultat de commande extrait du PDF, page 300.
Exemple 5-78 Exemple extrait du PDF, page 300.
postgres=> ALTER ROLE u2 SET work_mem TO '20MB';
ALTER ROLE
Exemple 5-79 Exemple extrait du PDF, page 300.
postgres=> SHOW work_mem;
work_mem
----------
 4MB
Tableau 5-35 Tableau ou résultat de commande extrait du PDF, page 300.
Exemple 5-80 Exemple extrait du PDF, page 300.
postgres=> \c - u2
You are now connected to database “postgres” as user “u2”.
Exemple 5-81 Exemple extrait du PDF, page 300.
postgres=> SHOW work_mem;
work_mem
----------
 20MB
Tableau 5-36 Tableau ou résultat de commande extrait du PDF, page 300.
Cette configuration est présente même après un redémarrage du serveur. Elle n’est pas enregistrée dans le fichier de configuration postgresql.conf mais dans un catalogue système appelé pg_db_role_setting :
Exemple 5-82 Exemple extrait du PDF, page 300.
b1=# SELECT d.datname AS "Base", r.rolname AS "Utilisateur",
     setconfig AS "Configuration"
     FROM pg_db_role_setting
     LEFT JOIN pg_database d ON d.oid=setdatabase
     LEFT JOIN pg_roles r ON r.oid=setrole
     ORDER BY 1, 2;
 Base | Utilisateur |                Configuration
------+-------------+--------------------------------------------
 b1   |             | {work_mem=2MB}
 b2   |             | {work_mem=32MB}
 b3   |             | {work_mem=10MB,maintenance_work_mem=128MB}
      | u1          | {maintenance_work_mem=256MB}
      | u2          | {work_mem=20MB}
Tableau 5-37 Tableau ou résultat de commande extrait du PDF, page 300.
Il est aussi possible de configurer un paramétrage spécifique pour un utilisateur et une base donnés :
Exemple 5-83 Exemple extrait du PDF, page 300.
postgres=# ALTER ROLE u2 IN DATABASE b1 SET work_mem to '10MB';
ALTER ROLE

Exemple 5-84 Exemple extrait du PDF, page 301.
postgres=# \c postgres u2
You are now connected to database “postgres” as user “u2”.
Exemple 5-85 Exemple extrait du PDF, page 301.
postgres=> SHOW work_mem;
work_mem
----------
 20MB
Tableau 5-38 Tableau ou résultat de commande extrait du PDF, page 301.
Exemple 5-86 Exemple extrait du PDF, page 301.
postgres=> \c b1 u2
You are now connected to database “b1” as user “u2”.
Exemple 5-87 Exemple extrait du PDF, page 301.
b1=> SHOW work_mem;
work_mem
----------
 10MB
Tableau 5-39 Tableau ou résultat de commande extrait du PDF, page 301.
Exemple 5-88 Exemple extrait du PDF, page 301.
b1=> \c b1 u1
You are now connected to database “b1” as user “u1”.
Exemple 5-89 Exemple extrait du PDF, page 301.
b1=> SHOW work_mem;
work_mem
----------
 2MB
Tableau 5-40 Tableau ou résultat de commande extrait du PDF, page 301.
Exemple 5-90 Exemple extrait du PDF, page 301.
b1=> \c postgres u1
You are now connected to database “postgres” as user “u1”.
Exemple 5-91 Exemple extrait du PDF, page 301.
postgres=> SHOW work_mem;
work_mem
----------
 4MB
Tableau 5-41 Tableau ou résultat de commande extrait du PDF, page 301.
Exemple 5-92 Exemple extrait du PDF, page 301.
b1=# SELECT d.datname AS "Base", r.rolname AS "Utilisateur",
     setconfig AS "Configuration"
     FROM pg_db_role_setting
     LEFT JOIN pg_database d ON d.oid=setdatabase
     LEFT JOIN pg_roles r ON r.oid=setrole
     ORDER BY 1, 2;
 Base | Utilisateur |                Configuration
------+-------------+--------------------------------------------
 b1   | u2          | {work_mem=10MB}
 b1   |             | {work_mem=2MB}
 b2   |             | {work_mem=32MB}
 b3   |             | {work_mem=10MB,maintenance_work_mem=128MB}
      | u1          | {maintenance_work_mem=256MB}
      | u2          | {work_mem=20MB}
Tableau 5-42 Tableau ou résultat de commande extrait du PDF, page 301.
Pour annuler la configuration d’un paramètre pour un rôle, utilisez :
Exemple 5-93 Exemple extrait du PDF, page 301.
ALTER ROLE rôle RESET paramètre;

Attention : la prise en compte de ces options dans les sauvegardes est un point délicat. Il est détaillé dans notre module de formation sur les sauvegardes logiques. Après sa création, il est toujours possible d’ajouter et de supprimer un rôle dans un autre rôle. Pour cela, il est possible d’utiliser les ordres GRANT et REVOKE :

Exemple 5-94 Exemple extrait du PDF, page 302.
GRANT role_groupe TO role_utilisateur;
Il est aussi possible de passer par la commande ALTER GROUP de cette façon :
Exemple 5-95 Exemple extrait du PDF, page 302.
ALTER GROUP role_groupe ADD USER role_utilisateur;

5.3.6 Mot de passe

Le mot de passe ne concerne pas toutes les méthodes d’authentification

  • Par défaut : l’utilisateur n’a pas de mot de passe
    • donc pas de connexion possible
  • Modification : ALTER ROLE u1 PASSWORD ‘supersecret’; – dangereux
  • Attention à ne pas l’afficher dans les traces
    • fournir un mot de passe déjà chiffré
    • utiliser \password ou createuser createuser –login –echo –pwprompt u1 Certaines méthodes d’authentification n’ont pas besoin de mot de passe (comme peer pour les connexions depuis le serveur même), ou délèguent l’authentification à un système extérieur (comme ldap ). Par défaut, les utilisateurs n’ont pas de mot de passe. Si la méthode en exige un, ils ne pourront pas se connecter. Il est très fortement conseillé d’utiliser une méthode d’authentification avec saisie du mot de passe. On peut le créer ainsi :
      Exemple 5-96 Exemple extrait du PDF, page 302.
      ALTER ROLE u1 PASSWORD 'supersecret';
      À partir de là, avec une méthode d’authentification bien configurée, le mot de passe sera demandé. Il faudra, dans cet exemple, saisir « supersecret » pour que la connexion se fasse.

ATTENTION! Le mot de passe peut apparaître en clair dans les traces! Notamment si log_min_duration_statement vaut 0.

Exemple 5-97 Exemple extrait du PDF, page 303.
             $ grep PASSWORD $PGDATA/log/traces.log

             psql - LOG: duration: 1.865 ms
                    statement: ALTER ROLE u1 PASSWORD 'supersecret';
La vue système pg_stat_activity ou l’extension pg_stat_statements , voire d’autres outils, sont aussi susceptibles d’afficher la commande et donc le mot de passe en clair. Il est donc essentiel de s’arranger pour que seules des personnes de confiance aient accès aux traces et vues systèmes. Il est certes possible de demander temporairement la désactivation des traces pendant le changement de mot de passe (si l’on est superutilisateur) :
Exemple 5-98 Exemple extrait du PDF, page 303.
SET log_min_duration_statement TO -1;
SET log_statement TO none;
ALTER ROLE u1 PASSWORD 'supersecret';

$ grep PASSWORD $PGDATA/log/postgresql-Mon.log
[rien] Cependant, cela ne règle pas le cas de pg_stat_statements et pg_stat_activity . De manière générale, il est donc chaudement conseillé de ne renseigner que des mots de passe chiffrés. C’est très simple en mode interactif avec psql , la métacommande \password opère le chiffrement :
Exemple 5-99 Exemple extrait du PDF, page 303.
\password u1
Saisissez le nouveau mot de passe : Saisissez-le à nouveau : L’ordre effectivement envoyé au serveur et éventuellement visible dans les traces sera :
Exemple 5-100 Exemple extrait du PDF, page 303.
ALTER USER u1 PASSWORD 'SCRAM-SHA-256$4096:KcHoLSZE…Hj81r3w='
De même si on crée le rôle depuis le shell avec createuser :
Exemple 5-101 Exemple extrait du PDF, page 303.
createuser --login --echo --pwprompt u1
Saisir le mot de passe pour le nouveau rôle : Le saisir de nouveau :
Exemple 5-102 Exemple extrait du PDF, page 303.
SELECT pg_catalog.set_config('search_path', '', false);
CREATE ROLE u1 PASSWORD ‘SCRAM-SHA-256$4096:/LVaGESxmDwyF92urT…hS0kOxIpwWAbTpW1i9peGIfg=’ NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS; Lors des changements de mots de passe, ne pas oublier qu’il reste un risque de fuite aussi au niveau des outils système, par exemple l’historique du shell, l’affichage de la liste des processus ou les traces système!

5.3.7 Sécurité des mots de passe

Exemple 5-103 Exemple extrait du PDF, page 304.
             password_encryption = "scram-sha-256"
  • SCRAM-SHA-256 : défaut
  • MD5 : déprécié
  • Sécurité :
  • date limite sur le mot de passe (pas le rôle)
  • pas de vérification de la force du mot de passe
  • pas de limite de tentatives échouées (tester fail2ban )
  • itérations SCRAM (v16) Le mot de passe est chiffré en interne, et visible dans les sauvegardes avec pg_dumpall -g , ou dans la vue système pg_authid . La méthode de chiffrement à utiliser est SCRAM-SHA-256. Une méthode historique, MD5, est obsolète, dépréciée depuis PostgreSQL 10 et ne doit plus être utilisée. Elle fonctionne encore, et peut encore se rencontrer sur des instances migrées de versions plus anciennes. Pour faciliter la migration, les deux méthodes sont utilisables simultanément pour des utilisateurs différents. L’exemple suivant montre que la méthode de chiffrement peut différer selon les rôles, en fonction de la valeur du paramètre password_encryption au moment de la mise en place du mot de passe :
    rolname        | u1
    rolsuper       | f
    rolinherit     | t
    rolcreaterole | f
    rolcreatedb    | f
    rolcanlogin    | t
    rolreplication | f
    rolbypassrls   | f
    rolconnlimit   | -1
    rolpassword    | md5fb75f17111cea61e62b54ab950dd1268
    rolvaliduntil |
    Tableau 5-43 Tableau ou résultat de commande extrait du PDF, page 304.
    Exemple 5-104 Exemple extrait du PDF, page 304.
    SET password_encryption TO "scram-sha-256";
    
    CREATE ROLE u12 LOGIN PASSWORD 'supersecret';
    
    SELECT * FROM pg_authid       WHERE rolname IN ('u1', 'u12') ORDER BY 1;
    
    -[ RECORD 1 ]--+-----------------------------------------------------------
    -[ RECORD 2 ]--+-----------------------------------------------------------
    rolname        | u12
    rolsuper       | f
    rolinherit     | t
    rolcreaterole | f
    rolcreatedb    | f
    rolcanlogin    | t
    Tableau 5-44 Tableau ou résultat de commande extrait du PDF, page 304.

rolreplication | f
rolbypassrls   | f
rolconnlimit   | -1
rolpassword    | SCRAM-SHA-256$4096:0/uC6oDNuQWO8H9pMaVg8g==$nDUpGSefHOZMd
                 TcbWR13NPELJubGg7PduiJjX/Hyt/M=:PSUzE+rP5g4f6mb5sFDRq/Hds
                 OrLvfYew9ZIdzO/GDw=
rolvaliduntil |
Tableau 5-45 Tableau ou résultat de commande extrait du PDF, page 305.
Chiffrement SCRAM-SHA-256 : SCRAM-SHA-256 n’est la méthode par défaut que depuis PostgreSQL 14, bien que disponible depuis PostgreSQL 10. Elle est plus complexe et plus sûre que l’ancienne méthode MD5. Notamment, le même mot de passe entré plusieurs fois pour un même utilisateur, même sur la même instance, donnera des versions chiffrées à chaque fois différentes, mais interchangeables. Un chiffrement SCRAM-SHA-256 est de la forme : SCRAM-SHA-256$<sel>:<nombre d'itérations>$<hash> Pour quelques détails d’implémentation et une comparaison avec MD5, voir par exemple cette présentation de Jonathan Katz 1 . Générer soi-même des mots de passe chiffrés en SCRAM-SHA-256 en-dehors de psql est plus compliqué qu’avec MD5, et les outils comme \password s’appuient souvent sur les fonctions fournies par la bibliothèque libpq . Cette dernière sert aussi de base à la bibliothèque python psycopg3 2 et sa fonction PGconn.encrypt_password() 3 . Indépendamment de la libpq , il existe aussi un script python de Jonathan Katz 4 (version 3.6 minimum). Depuis la version 16, il est possible d’ajuster le nombre d’itérations avec le paramètre scram_iterations . Le défaut de 4096 est un compromis. Monter plus haut permet de lutter contre les attaques par force brute. Réduire ce paramètre permet de parer aux problèmes de performances depuis certains terminaux peu puissants, ou en cas de connexions très fréquentes (le début de la discussion entre les développeurs 5 illustre bien ces deux contraintes). Chiffrement MD5 : md5 était la méthode de chiffrement par défaut jusque PostgreSQL 13 inclus. On peut la rencontrer sur des instances de versions supérieures qui ont été migrées. La méthode md5 est officiellement dépréciée. Migrez les mots de passe de vos utilisateurs. Cette méthode calcule la somme MD5 du mot de passe concaténé au nom du rôle (pour que deux utilisateurs de même mot de passe n’ait pas la même version chiffrée); puis « md5 » est ajouté devant. 1 https://fr.slideshare.net/jkatz05/safely-protect-postgresql-passwords-tell-others-to-scram

2 https://github.com/psycopg/psycopg

3 https://www.psycopg.org/psycopg3/docs/api/pq.html#psycopg.pq.PGconn.encrypt_password

4 https://gist.github.com/jkatz/e0a1f52f66fa03b732945f6eb94d9c21#file-encrypt_password-py-L20

5 https://www.postgresql.org/message-id/F72E7BC7-189F-4B17-BF47-9735EB72C364@yesql.se

De manière plus générale, l’algorithme MD5 est considéré comme trop faible de nos jours. Un autre problème de sécurité est que la version chiffrée d’un mot de passe est identique sur deux instances différentes pour un même nom d’utilisateur, ce qui ouvre la possibilité d’attaques par rainbow tables 6 . Un inconvénient plus mineur du chiffrement MD5 est qu’il utilise le nom de l’utilisateur : en cas de changement de ce nom, il faudra de nouveau rentrer le mot de passe pour que son stockage chiffré soit correct. Pour éviter de fournir un mot de passe en clair à PostgreSQL, il était facile de le chiffrer en MD5 avant de le fournir à PostgreSQL :

$ echo -n "supersecretu1" | md5sum
Tableau 5-46 Tableau ou résultat de commande extrait du PDF, page 306.
fb75f17111cea61e62b54ab950dd1268 -
Exemple 5-105 Exemple extrait du PDF, page 306.
ALTER ROLE u1 PASSWORD 'md5fb75f17111cea61e62b54ab950dd1268';
Dans les traces on ne trouvera que la version chiffrée :
Exemple 5-106 Exemple extrait du PDF, page 306.
psql - LOG:          duration: 2.100 ms           statement: ALTER ROLE u1
                                                  PASSWORD 'md5fb75f17111cea61e62b54ab950dd1268';
Cohabitation de mots de passe SCRAM-SHA-256 et MD5 : Lors de la mise à jour d’une ancienne instance, il n’est pas forcément possible de ré-entrer immédiatement tous les mots de passe existants chiffrés en MD5. De plus, certains outils clients anciens pourraient ne pas supporter SCRAM-SHA-256 (voir le wiki 7 ). Changer la méthode d’authentification selon l’utilisateur est donc utile. La méthode d’authentification SCRAM-SHA-256 doit rester le défaut, et tout mot de passe réentré sera chiffré en SCRAM-SHA-256 :
Exemple 5-107 Exemple extrait du PDF, page 306.
password_encryption = "scram-sha-256"
Si le mot de passe est stocké au format SCRAM-SHA-256, une authentification paramétrée sur md5 ou password dans le fichier pg_hba.conf fonctionne aussi. Cela facilite la migration progressive des utilisateurs de md5 à scram-sha-256 , qui peuvent réentrer leur mot de passe quand ils veulent. Par contre, indiquer la méthode scram-sha-256 dans pg_hba.conf impose un chiffrement en SCRAM-SHA-256 et interdit d’utiliser MD5. On peut mixer les deux méthodes dans pg_hba.conf si le besoin se fait sentir, par exemple pour n’utiliser md5 que pour une seule application (avec un compte dédié) avec un ancien client :
Exemple 5-109 Exemple extrait du PDF, page 306.
# vieille application (avant le cas général)
host compta mathusalem 192.168.66.66/32 md5
Exemple 5-110 Exemple extrait du PDF, page 306.
# authentification habituelle
host all all 192.168.66.0/24 scram-sha-256 Dates de fin de validité (expiration) des mots de passe : On peut définir une date de fin de validité pour les mots de passe mais pas pour les rôles eux-mêmes. Il reste donc possible de se connecter en local par la méthode peer même si le mot de passe a expiré. 6 https://fr.wikipedia.org/wiki/Rainbow_table

7 https://wiki.postgresql.org/wiki/List_of_drivers

Sécurité des mots de passe : Enfin, il est à noter que PostgreSQL ne vérifie pas la faiblesse d’un mot de passe. Il est certes possible d’installer une extension appelée passwordcheck (voir sa documentation 8 ).

Exemple 5-111 Exemple extrait du PDF, page 307.
postgres=# ALTER ROLE u1 PASSWORD 'supersecret';

ERROR:       password must contain both letters and nonletters
Il est possible de modifier le code source de cette extension pour y ajouter les règles convenant à votre cas, ou d’utiliser la bibliothèque Cracklib . Des extensions de ce genre, extérieures au projet, existent. Cependant, ces outils exigent que le mot de passe soit fourni en clair, et donc sujet à une fuite (dans les traces par exemple), ce qui, répétons-le, est fortement déconseillé! Un rôle peut tenter de se connecter autant de fois qu’il le souhaite, ce qui expose à des attaques de type force brute. Il est possible d’interdire toute connexion à partir d’un certain nombre de connexions échouées si vous utilisez une méthode d’authentification externe qui le gère (comme PAM, LDAP ou Active Directory). Vous pouvez aussi obtenir cette fonctionnalité en utilisant un outil comme fail2ban . Sa configuration est détaillée dans notre base de connaissances 9 . 8 https://docs.postgresql.fr/current/passwordcheck.html

9 https://dali.bo/kb_fail2ban

5.4 DROITS SUR LES OBJETS

  • Droits sur les objets
  • Droits sur les méta-données
  • Héritage des droits
  • Changement de rôle Pour bien comprendre l’intérêt des utilisateurs, il faut bien comprendre la gestion des droits. Les droits sur les objets vont permettre aux utilisateurs de créer des objets ou de les utiliser. Les commandes GRANT et REVOKE sont essentielles pour cela. Modifier la définition d’un objet demande un autre type de droit, que les commandes précédentes ne permettent pas d’obtenir. Donner des droits à chaque utilisateur peut paraître long et difficile. C’est pour cela qu’il est généralement préférable de donner des droits à une entité spécifique dont certains utilisateurs hériteront.

5.4.1 Droits sur les objets

  • Donner un droit :
    Exemple 5-112 Exemple extrait du PDF, page 308.
                       GRANT USAGE ON SCHEMA unschema TO utilisateur ;
                       GRANT SELECT,DELETE,INSERT ON TABLE matable TO utilisateur ;
  • Retirer un droit :
    Exemple 5-113 Exemple extrait du PDF, page 308.
                       REVOKE UPDATE ON TABLE matable FROM utilisateur ;
  • Droits spécifiques pour chaque type d’objets : ALTER DEFAULT PRIVILEGES IN SCHEMA … ALTER DEFAULT PRIVILEGES FOR ROLE …
  • Avoir le droit de donner le droit : WITH GRANT OPTION
  • Groupe implicite : public
  • Schéma par défaut : public lisible par tous (≤ 14) !
    Exemple 5-114 Exemple extrait du PDF, page 308.
                      REVOKE ALL ON SCHEMA public FROM public ;
    Par défaut, seul le propriétaire a des droits sur son objet. Les superutilisateurs n’ont pas de droit spécifique sur les objets mais étant donné leur statut de superutilisateur, ils peuvent tout faire sur tous les objets.

Le propriétaire d’un objet peut décider de donner certains droits sur cet objet à certains rôles. Il le fera avec la commande GRANT :

Exemple 5-115 Exemple extrait du PDF, page 309.
GRANT droits ON type_objet nom_objet TO role
Les droits disponibles dépendent du type d’objet visé. Par exemple, il est possible de donner le droit SELECT sur une table mais pas sur une fonction. Une fonction ne se lit pas, elle s’exécute. Il est donc possible de donner le droit EXECUTE sur une fonction. La liste complète des droits figure dans la documentation officielle 10 . Il faut donner les droits aux différents objets séparément. De plus, donner le droit ALL sur une base de données donne tous les droits sur la base de données, autrement dit l’objet base de donnée, pas sur les objets à l’intérieur de la base de données. GRANT n’est pas une commande récursive. Prenons un exemple :
Exemple 5-116 Exemple extrait du PDF, page 309.
b1=# CREATE ROLE u20 LOGIN;
CREATE ROLE
Exemple 5-117 Exemple extrait du PDF, page 309.
b1=# CREATE ROLE u21 LOGIN;
CREATE ROLE
Exemple 5-118 Exemple extrait du PDF, page 309.
b1=# \c b1 u20
You are now connected to database “b1” as user “u20”.
Exemple 5-119 Exemple extrait du PDF, page 309.
b1=> CREATE SCHEMA s1;

ERROR:        permission denied for database b1

b1=> \c b1 postgres
You are now connected to database “b1” as user “postgres”.
Exemple 5-120 Exemple extrait du PDF, page 309.
b1=# GRANT CREATE ON DATABASE b1 TO u20;
GRANT
Exemple 5-121 Exemple extrait du PDF, page 309.
b1=# \c b1 u20
You are now connected to database “b1” as user “u20”.
Exemple 5-122 Exemple extrait du PDF, page 309.
b1=> CREATE SCHEMA s1;
CREATE SCHEMA
Exemple 5-123 Exemple extrait du PDF, page 309.
b1=> CREATE TABLE s1.t1 (c1 integer);

CREATE TABLE

b1=> INSERT INTO s1.t1 VALUES (1), (2);
INSERT 0 2
Exemple 5-124 Exemple extrait du PDF, page 309.
b1=> SELECT * FROM s1.t1;
10 https://docs.postgresql.fr/current/sql-grant.html

c1

Exemple 5-125 Exemple extrait du PDF, page 310.
----
  1
  2

b1=> \c b1 u21
You are now connected to database “b1” as user “u21”.
Exemple 5-126 Exemple extrait du PDF, page 310.
b1=> SELECT * FROM s1.t1;

ERROR: permission denied for schema s1
LINE 1: SELECT * FROM s1.t1; ^
Exemple 5-127 Exemple extrait du PDF, page 310.
b1=> \c b1 u20
You are now connected to database “b1” as user “u20”.
Exemple 5-128 Exemple extrait du PDF, page 310.
b1=> GRANT SELECT ON TABLE s1.t1 TO u21;
GRANT
Exemple 5-129 Exemple extrait du PDF, page 310.
b1=> \c b1 u21
You are now connected to database “b1” as user “u21”.
Exemple 5-130 Exemple extrait du PDF, page 310.
b1=> SELECT * FROM s1.t1;

ERROR: permission denied for schema s1
LINE 1: SELECT * FROM s1.t1; ^
Exemple 5-131 Exemple extrait du PDF, page 310.
b1=> \c b1 u20
You are now connected to database “b1” as user “u20”.
Exemple 5-132 Exemple extrait du PDF, page 310.
b1=> GRANT USAGE ON SCHEMA s1 TO u21;
GRANT
Exemple 5-133 Exemple extrait du PDF, page 310.
b1=> \c b1 u21
You are now connected to database “b1” as user “u21”.
Exemple 5-134 Exemple extrait du PDF, page 310.
b1=> SELECT * FROM s1.t1;
c1
Exemple 5-135 Exemple extrait du PDF, page 310.
----
  1
  2

b1=> INSERT INTO s1.t1 VALUES (3);

ERROR:   permission denied for relation t1
Le problème de ce fonctionnement est qu’il faut indiquer les droits pour chaque utilisateur, ce qui peut devenir difficile et long. Imaginez avoir à donner le droit SELECT sur les 400 tables d’un schéma… Il est néanmoins possible de donner les droits sur tous les objets d’un certain type dans un schéma. Voici un exemple :

Exemple 5-136 Exemple extrait du PDF, page 311.
GRANT SELECT ON ALL TABLES IN SCHEMA s1 to u21;
Notez aussi que, lors de la création d’une base, PostgreSQL ajoute automatiquement un schéma nommé public . Avant la version 15, tous les droits sont donnés sur ce schéma à un pseudo-rôle, lui aussi appelé public , et dont tous les rôles existants et à venir sont membres d’office. À partir de la version 15, le schéma public appartient au propriétaire de la base et aucun droit par défaut n’est donné aux autres utilisateurs. Avec une version antérieure à la version 15, n’importe quel utilisateur peut donc, par défaut, créer des tables dans le schéma public de toute base où il peut se connecter (mais ne peut lire les tables créées là par d’autres, sans droit supplémentaire)! Dans une logique de sécurisation, avant la version 15, il faut donc penser à enlever les droits à public . Une fausse bonne idée est de tout simplement supprimer le schéma public , ou de le récréer (par défaut, sans droits pour le groupe public ). Cependant, une sauvegarde logique serait restaurée dans une base qui, par défaut, aurait à nouveau un schéma public ouvert à tous. Une révocation explicite des droits se retrouvera par contre dans une sauvegarde :
Exemple 5-137 Exemple extrait du PDF, page 311.
REVOKE ALL ON SCHEMA public FROM public ;
(Noter la subtilité de syntaxe : GRANT… TO… et REVOKE… FROM… ) Nombre de scripts et outils peuvent tomber en erreur sans ces droits. Il faudra remonter cela aux auteurs en tant que bugs. Cette modification peut être faite aussi dans la base template1 (qui sert de modèle à toute nouvelle base), sur toute nouvelle instance. Enfin il est possible d’ajouter des droits pour des objets qui n’ont pas encore été créés. En fait, la commande ALTER DEFAULT PRIVILEGES permet de donner des droits par défaut à certains rôles. De cette façon, sur un schéma qui a tendance à changer fréquemment, il n’est plus nécessaire de se préoccuper des droits sur les objets.
Exemple 5-138 Exemple extrait du PDF, page 311.
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO public ;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT INSERT ON TABLES TO utilisateur ;
Lorsqu’un droit est donné à un rôle, par défaut, ce rôle ne peut pas le donner à un autre. Pour lui donner en plus le droit de donner ce droit à un autre rôle, il faut utiliser la clause WITH GRANT OPTION comme le montre cet exemple :
Exemple 5-139 Exemple extrait du PDF, page 311.
b1=# CREATE TABLE t2 (id integer);

CREATE TABLE

b1=# INSERT INTO t2 VALUES (1);
INSERT 0 1

Exemple 5-140 Exemple extrait du PDF, page 312.
b1=# SELECT * FROM t2;
id
Exemple 5-141 Exemple extrait du PDF, page 312.
----
  1

b1=# \c b1 u1
You are now connected to database “b1” as user “u1”.
Exemple 5-142 Exemple extrait du PDF, page 312.
b1=> SELECT * FROM t2;

ERROR:   permission denied for relation t2

b1=> \c b1 postgres
You are now connected to database “b1” as user “postgres”.
Exemple 5-143 Exemple extrait du PDF, page 312.
b1=# GRANT SELECT ON TABLE t2 TO u1;
GRANT
Exemple 5-144 Exemple extrait du PDF, page 312.
b1=# \c b1 u1
You are now connected to database “b1” as user “u1”.
Exemple 5-145 Exemple extrait du PDF, page 312.
b1=> SELECT * FROM t2;
id
Exemple 5-146 Exemple extrait du PDF, page 312.
----
  1

b1=> \c b1 u2
You are now connected to database “b1” as user “u2”.
Exemple 5-147 Exemple extrait du PDF, page 312.
b1=> SELECT * FROM t2;

ERROR:   permission denied for relation t2

b1=> \c b1 u1
You are now connected to database “b1” as user “u1”.
Exemple 5-148 Exemple extrait du PDF, page 312.
b1=> GRANT SELECT ON TABLE t2 TO u2;

WARNING:   no privileges were granted for "t2"
GRANT
Exemple 5-149 Exemple extrait du PDF, page 312.
b1=> \c b1 postgres
You are now connected to database “b1” as user “postgres”.
Exemple 5-150 Exemple extrait du PDF, page 312.
b1=# GRANT SELECT ON TABLE t2 TO u1 WITH GRANT OPTION;
GRANT
Exemple 5-151 Exemple extrait du PDF, page 312.
b1=# \c b1 u1
You are now connected to database “b1” as user “u1”.
Exemple 5-152 Exemple extrait du PDF, page 312.
b1=> GRANT SELECT ON TABLE t2 TO u2;

GRANT

Exemple 5-153 Exemple extrait du PDF, page 313.
b1=> \c b1 u2
You are now connected to database “b1” as user “u2”.
Exemple 5-154 Exemple extrait du PDF, page 313.
b1=> SELECT * FROM t2;
id
Exemple 5-155 Exemple extrait du PDF, page 313.
----
  1

5.4.2 Afficher les droits

  • Colonne *acl sur les tables systèmes

    • datacl pour pg_database
    • relacl pour pg_class
  • Codage role1=xxxx/role2 (format aclitem )

    • role1 : rôle concerné par les droits
    • xxxx : droits parmi xxxx
    • role2 : rôle qui a donné les droits
  • Sous psql :

    • \dp et \z Les colonnes *acl des catalogues systèmes indiquent les droits sur un objet. Leur contenu est un codage au format aclitem indiquant le rôle concerné, ses droits, et qui lui a fourni ces droits (ou le propriétaire de l’objet, si celui qui a fourni les droits est un superutilisateur). Les droits sont codés avec des lettres. Les voici avec leur signification :
  • r pour la lecture ( SELECT ) ;

  • w pour les modifications ( UPDATE );

  • a pour les insertions ( INSERT );

  • d pour les suppressions ( DELETE );

  • D pour la troncation ( TRUNCATE );

  • x pour l’ajout de clés étrangères;

  • t pour l’ajout de triggers;

  • X pour l’exécution de routines ;

  • U pour l’utilisation (d’un schéma par exemple);

  • C pour la création d’objets permanents (tables ou vues par exemple);

  • c pour la connexion (spécifique aux bases de données) ;

  • T pour la création d’objets temporaires (tables ou index temporaires);

  • m pour le droit de « maintenir », c’est-à-dire lancer des VACUUM , ANALYZE , rafraîchir, réindexer… (à partir de PostgreSQL 17) ;

  • s pour la modification d’un paramètre superutilisateur avec SET ;

  • A pour la modification d’un paramètre avec ALTER SYSTEM .

    Exemple 5-156 Exemple extrait du PDF, page 314.
    CREATE TABLE public.demo1 (
        champ1 text,
        champ2 text);
    ALTER TABLE public.demo1 OWNER TO postgres;
    CREATE TABLE public.demo2 (
        champ3 text);
    ALTER TABLE public.demo2 OWNER TO postgres;
    GRANT SELECT,INSERT ON TABLE public.demo1 TO jack;
    GRANT SELECT ON TABLE public.demo1 TO paul;
    GRANT DELETE,TRUNCATE ON TABLE public.demo1 TO pierre;
    GRANT UPDATE(champ2) ON TABLE public.demo1 TO pierre;
    GRANT MAINTAIN ON demo1 TO jack,pierre; /* v17 */
    GRANT SELECT ON TABLE public.demo2 TO pierre;
    GRANT SELECT(champ3) ON TABLE public.demo2 TO paul;
    
    # \z
                                              Droits d'accès
     Schéma | Nom | Type |          Droits d'accès        | Droits … la colonne | …
    --------+-------+-------+----------------------------+---------------------+--
     public | demo1 | table | postgres=arwdDxtm/postgres+| champ2:             +|
            |       |       | jack=armi/postgres         +|   pierre=w/postgres |
            |       |       | paul=r/postgres            +|                     |
            |       |       | pierre=dDm/postgres         |                     |
     public | demo2 | table | postgres=arwdDxtm/postgres+| champ3:             +|
            |       |       | pierre=r/postgres           |   paul=r/postgres   |
    Tableau 5-47 Tableau ou résultat de commande extrait du PDF, page 314.

5.4.3 Droits sur les métadonnées

  • Seul le propriétaire peut changer la structure d’un objet
    • le renommer
    • le changer de schéma ou de tablespace
    • lui ajouter/retirer des colonnes
  • Un seul propriétaire
    • peut être un utilisateur ou un groupe
  • REASSIGN OWNED / DROP OWNED Les droits sur les objets ne concernent pas le changement des méta-données et de la structure de l’objet. Seul le propriétaire (et les superutilisateurs) peut le faire. S’il est nécessaire que plusieurs personnes puissent utiliser la commande ALTER sur l’objet, il faut que ces différentes personnes aient un rôle qui soit membre du rôle propriétaire de l’objet. Prenons un exemple :
    Exemple 5-157 Exemple extrait du PDF, page 314.
    b1=# \c b1 u21
    You are now connected to database “b1” as user “u21”.

Exemple 5-158 Exemple extrait du PDF, page 315.
b1=> ALTER TABLE s1.t1 ADD COLUMN c2 text;

ERROR:       must be owner of relation t1

b1=> \c b1 u20
You are now connected to database “b1” as user “u20”.
Exemple 5-159 Exemple extrait du PDF, page 315.
b1=> GRANT u20 TO u21;
GRANT ROLE
Exemple 5-160 Exemple extrait du PDF, page 315.
b1=> \du
List of roles
 Role name |                         Attributes                         | Member of
-----------+------------------------------------------------------------+-----------
 postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
 u1        |                                                            | {}
 u11       | Create DB                                                  | {}
 u12       |                                                            | {}
 u2        | Create DB                                                  | {}
 u20       |                                                            | {}
 u21       |                                                            | {u20}
 u3        | Cannot login                                               | {}
 u4        | Create role, Cannot login                                  | {}
 u5        |                                                            | {u2,u6}
 u6        | Cannot login                                               | {}
 u7        | Create role                                                | {}
 u8        |                                                            | {}
 u9        | Create DB                                                  | {}
Tableau 5-48 Tableau ou résultat de commande extrait du PDF, page 315.
Exemple 5-161 Exemple extrait du PDF, page 315.
b1=> \c b1 u21
You are now connected to database “b1” as user “u21”.
Exemple 5-162 Exemple extrait du PDF, page 315.
b1=> ALTER TABLE s1.t1 ADD COLUMN c2 text;

ALTER TABLE
Pour assigner un propriétaire différent aux objets ayant un certain propriétaire, il est possible de faire appel à l’ordre REASSIGN OWNED 11 . De même, il est possible de supprimer tous les objets appartenant à un utilisateur avec l’ordre DROP OWNED 12 . Voici un exemple de ces deux commandes :
Exemple 5-163 Exemple extrait du PDF, page 315.
b1=# \d
List of relations
 Schema | Name | Type | Owner
--------+------+-------+-------
 public | t1   | table | u2
 public | t2   | table | u21
Tableau 5-49 Tableau ou résultat de commande extrait du PDF, page 315.
Exemple 5-164 Exemple extrait du PDF, page 315.
b1=# REASSIGN OWNED BY u21 TO u1;
REASSIGN OWNED 11 https://docs.postgresql.fr/current/sql-reassign-owned.html

12 https://docs.postgresql.fr/17/sql-drop-owned.html

Exemple 5-165 Exemple extrait du PDF, page 316.
b1=# \d
List of relations
 Schema | Name | Type | Owner
--------+------+-------+-------
 public | t1   | table | u2
 public | t2   | table | u1
Tableau 5-50 Tableau ou résultat de commande extrait du PDF, page 316.
Exemple 5-166 Exemple extrait du PDF, page 316.
b1=# DROP OWNED BY u1;
DROP OWNED
Exemple 5-167 Exemple extrait du PDF, page 316.
b1=# \d
List of relations
 Schema | Name | Type | Owner
--------+------+-------+-------
 public | t1   | table | u2
Tableau 5-51 Tableau ou résultat de commande extrait du PDF, page 316.

5.4.4 Droits plus globaux 1/2

  • Rôles systèmes d’administration

  • pg_maintain (17+, évite GRANT MAINTAIN )

  • pg_signal_backend

  • pg_checkpoint (15+)

  • pg_reserved_connections (16+)

  • Rôles systèmes de supervision

  • pg_read_all_stats

  • pg_read_all_settings

  • pg_stat_scan_tables

  • pg_monitor (reprend les 3 précédents) Certaines fonctionnalités nécessitent l’attribut SUPERUSER alors qu’il serait bon de pouvoir les effectuer sans avoir ce droit suprême. Cela concerne principalement l’administration, la sauvegarde et la supervision. Ce besoin concerne aussi des opérations normalement réservées au propriétaire de la table. Après beaucoup de discussions, les développeurs de PostgreSQL ont décidé de créer des rôles systèmes permettant d’avoir plus de droits. Plusieurs ont été créés au fil des versions. Rôles d’administration : Un des plus récents (PostgreSQL 17) et importants est pg_maintain : ce rôle autorise son détenteur à procéder à des opérations de maintenance sans être propriétaire des tables. Les opérations de maintenance couvertes sont :

  • VACUUM , ANALYZE , REINDEX

  • REFRESH MATERIALIZED VIEW

  • CLUSTER , LOCK TABLE Une alternative plus granulaire est de donner le droit MAINTAIN sur chaque table :

    Exemple 5-168 Exemple extrait du PDF, page 317.
    GRANT MAINTAIN ON public.matable TO pierre ;
    (Avant la version 17, il est toujours possible d’avoir un groupe propriétaire de l’objet et d’ajouter un rôle de maintenance comme membre de ce groupe. Mais si jamais un objet est créé par quelqu’un sans transférer la propriété au groupe, le rôle de maintenance ne peut faire d’opération de maintenance sur cet objet.) Exemple avec un VACUUM sur trois tables :
    Exemple 5-169 Exemple extrait du PDF, page 317.
    $ export PGDATABASE=b3
    $ createdb b3
    $ createuser maintenance
    $ psql --username postgres --command 'CREATE TABLE t1 (id integer);' \
    
    –command ‘CREATE TABLE t2 (id integer);’ \ –command ‘CREATE TABLE t3 (id integer);’
    Exemple 5-170 Exemple extrait du PDF, page 317.
    CREATE TABLE
    CREATE TABLE
    CREATE TABLE
    Le VACUUM ne fonctionne pas pour l’utilisateur de maintenance non propriétaire de la table :
    Exemple 5-171 Exemple extrait du PDF, page 317.
    $ vacuumdb --username postgres --table t1
    
    vacuumdb: vacuuming database “b3”
    Exemple 5-172 Exemple extrait du PDF, page 317.
    $ vacuumdb --username maintenance --table t1
    
    vacuumdb: vacuuming database “b3”
    Exemple 5-173 Exemple extrait du PDF, page 317.
    WARNING: permission denied to vacuum "t1", skipping it
    Et encore moins pour les tables système :
    Exemple 5-174 Exemple extrait du PDF, page 317.
    $ vacuumdb --username maintenance
    
    vacuumdb: vacuuming database “b3”
    Exemple 5-175 Exemple extrait du PDF, page 317.
    WARNING: permission denied to vacuum "pg_proc", skipping it
    WARNING: permission denied to vacuum "pg_attribute", skipping it
    […]
    Exemple 5-176 Exemple extrait du PDF, page 317.
    WARNING: permission denied to vacuum "pg_publication", skipping it
    WARNING: permission denied to vacuum "t3", skipping it
    WARNING: permission denied to vacuum "pg_foreign_table", skipping it
    […]
    Exemple 5-177 Exemple extrait du PDF, page 317.
    WARNING: permission denied to vacuum "pg_sequence", skipping it
    WARNING: permission denied to vacuum "t2", skipping it
    WARNING: permission denied to vacuum "pg_publication_namespace", skipping it
    […] Si maintenance devient membre du rôle pg_maintain , tout fonctionne :
    Exemple 5-178 Exemple extrait du PDF, page 317.
    $ psql --username postgres --command 'GRANT pg_maintain TO maintenance'
    
    GRANT ROLE
    Exemple 5-179 Exemple extrait du PDF, page 317.
    $ vacuumdb --username maintenance
    
    vacuumdb: vacuuming database “b3” Pour autant, cet utilisateur ne peut pas lire les données de la table :

Exemple 5-180 Exemple extrait du PDF, page 318.
$ psql --username maintenance --command 'SELECT * FROM t1'
ERROR: permission denied for table t1
Le rôle pg_signal_backend donne le droit à un simple utilisateur d’exécuter les fonctions pg_cancel_backend() (annulation de requête) et pg_terminate_backend() (fin de session), sur des requêtes autres que les siennes, à l’exception des connexions des superutilisateurs.
Exemple 5-181 Exemple extrait du PDF, page 318.
postgres=# \c - u1
You are now connected to database “postgres” as user “u1”.
Exemple 5-182 Exemple extrait du PDF, page 318.
postgres=> SELECT usename, pid FROM pg_stat_activity WHERE usename IS NOT NULL;
 usename | pid
---------+-------
 u2      | 23194
 u1      | 23195
Tableau 5-52 Tableau ou résultat de commande extrait du PDF, page 318.
Exemple 5-183 Exemple extrait du PDF, page 318.
postgres=> SELECT pg_terminate_backend(23194);

ERROR:   must be a member of the role whose process is being terminated
         or member of pg_signal_backend

postgres=> \c - postgres
You are now connected to database “postgres” as user “postgres”.
Exemple 5-184 Exemple extrait du PDF, page 318.
postgres=# GRANT pg_signal_backend TO u1;
GRANT ROLE
Exemple 5-185 Exemple extrait du PDF, page 318.
postgres=# \c - u1
You are now connected to database “postgres” as user “u1”.
Exemple 5-186 Exemple extrait du PDF, page 318.
postgres=> SELECT pg_terminate_backend(23194);
pg_terminate_backend
----------------------
 t
Tableau 5-53 Tableau ou résultat de commande extrait du PDF, page 318.
Exemple 5-187 Exemple extrait du PDF, page 318.
postgres=> SELECT usename, pid FROM pg_stat_activity WHERE usename IS NOT NULL;
 usename | pid
---------+-------
 u1      | 23212
Tableau 5-54 Tableau ou résultat de commande extrait du PDF, page 318.
Le rôle pg_checkpoint permet à un rôle non SUPERUSER de lancer un CHECKPOINT . Le rôle pg_use_reserved_connections (à partir de PostgreSQL 16) donne accès à un pool de connexions réservées défini par le paramètre reserved connections (0 par défaut). Cela peut servir à garantir un accès à la supervision ou à divers utilitaires sans en faire des superutilisateurs. Rôles de supervision : Parmi ces rôles, pg_read_all_stats permet de lire les tables de statistiques d’activité (vues pg_stat_* ).

pg_read_all_settings permet de lire la configuration de tous les paramètres. pg_stat_scan_tables permet notamment d’exécuter les procédures stockées de lecture des statistiques. pg_monitor est le rôle idéal à donner à un utilisateur de supervision : il combine les trois rôles précédents.

5.4.5 Droits plus globaux 2/2

  • Rôles d’accès aux fichiers
  • pg_database_owner (14+)
  • pg_read_server_files
  • pg_write_server_files
  • pg_execute_server_program
  • Rôles d’accès aux données (14+)
  • pg_read_all_data
  • pg_write_all_data
  • Autres
  • pg_create_subscription (16+) pg_read_server_files permet d’autoriser la lecture de fichiers auxquels le serveur peut accéder avec la commande SQL COPY et toutes autres fonctions d’accès de fichiers. pg_write_server_files permet la même chose en écriture. Cela sous-entend aussi les sauvegardes de fichiers côté serveur, par exemple avec pg_basebackup (à partir de la version 15) ou d’autres outils. pg_execute_server_program autorise les utilisateurs membres à exécuter des programmes en tant que l’utilisateur qui exécute le serveur PostgreSQL au travers de la commande SQL COPY et de toute fonction permettant l’exécution d’un programme sur le serveur. Ces rôles ne sont pas à octroyer à la légère : c’est une potentielle faille de sécurité. Notamment, COPY … FROM PROGRAM… permet d’exécuter des ordres shell sur le serveur, avec l’utilisateur système postgres en général. pg_read_all_data permet de lire toutes les données des tables, vues et séquences. pg_write_all_data permet d’y écrire. pg_database_owner permet de se comporter comme le propriétaire des bases de données.

En version 16 apparaît pg_create_subscription pour gérer des souscriptions en réplication logique.

5.4.6 Héritage des droits

  • Créer un rôle sans droit de connexion
  • Donner les droits à ce rôle
  • Placer les utilisateurs concernés comme membre de ce rôle Plutôt que d’avoir à donner les droits sur chaque objet à chaque ajout d’un rôle, il est beaucoup plus simple d’utiliser le système d’héritage des droits. Supposons qu’une nouvelle personne arrive dans le service de facturation. Elle doit avoir accès à toutes les tables concernant ce service. Sans utiliser l’héritage, il faudra récupérer les droits d’une autre personne du service pour retrouver la liste des droits à donner à cette nouvelle personne. De plus, si un nouvel objet est créé et que chaque personne du service doit pouvoir y accéder, il faudra ajouter l’objet et ajouter les droits pour chaque personne du service sur cet objet. C’est long et sujet à erreur. Il est préférable de créer un rôle facturation, de donner les droits sur ce rôle, puis d’ajouter chaque rôle du service facturation comme membre du rôle facturation. L’ajout et la suppression d’un objet est très simple : il suffit d’ajouter ou de retirer le droit sur le rôle facturation, et cela impactera tous les rôles membres. Voici un exemple complet :
    Exemple 5-188 Exemple extrait du PDF, page 320.
    b1=# CREATE ROLE facturation;
    CREATE ROLE
    Exemple 5-189 Exemple extrait du PDF, page 320.
    b1=# CREATE TABLE factures(id integer, dcreation date, libelle text,
    montant numeric);
    Exemple 5-190 Exemple extrait du PDF, page 320.
    CREATE TABLE
    
    b1=# GRANT ALL ON TABLE factures TO facturation;
    GRANT
    Exemple 5-191 Exemple extrait du PDF, page 320.
    b1=# CREATE TABLE clients (id integer, nom text);
    
    CREATE TABLE
    
    b1=# GRANT ALL ON TABLE clients TO facturation;
    GRANT
    Exemple 5-192 Exemple extrait du PDF, page 320.
    b1=# CREATE ROLE r1 LOGIN;
    CREATE ROLE
    Exemple 5-193 Exemple extrait du PDF, page 320.
    b1=# GRANT facturation TO r1;

GRANT ROLE

Exemple 5-194 Exemple extrait du PDF, page 321.
b1=# \c b1 r1
You are now connected to database “b1” as user “r1”.
 id | dcreation | libelle | montant
----+-----------+---------+---------
Tableau 5-55 Tableau ou résultat de commande extrait du PDF, page 321.
Exemple 5-195 Exemple extrait du PDF, page 321.
b1=> SELECT * FROM factures;
(0 rows)
Exemple 5-196 Exemple extrait du PDF, page 321.
b1=# CREATE ROLE r2 LOGIN;
CREATE ROLE
Exemple 5-197 Exemple extrait du PDF, page 321.
b1=# \c b1 r2
You are now connected to database “b1” as user “r2”.
Exemple 5-198 Exemple extrait du PDF, page 321.
b1=> SELECT * FROM factures;

ERROR:    permission denied for relation factures

5.4.7 Changement de rôle

  • Rôle par défaut
    • celui de la connexion
  • Rôle emprunté :
    • après un SET ROLE
    • pour tout rôle dont il est membre Par défaut, un utilisateur se connecte avec un rôle de connexion. Il obtient les droits et la configuration spécifique de ce rôle. Dans le cas où il hérite automatiquement des droits des rôles dont il est membre, il obtient aussi ces droits qui s’ajoutent aux siens. Dans le cas où il n’hérite pas automatiquement de ces droits, il peut temporairement les obtenir en utilisant la commande SET ROLE . Il ne peut le faire qu’avec les rôles dont il est membre.
      Exemple 5-199 Exemple extrait du PDF, page 321.
      b1=# CREATE ROLE r31 LOGIN;
      CREATE ROLE
      Exemple 5-200 Exemple extrait du PDF, page 321.
      b1=# CREATE ROLE r32 LOGIN NOINHERIT IN ROLE r31;
      CREATE ROLE
      Exemple 5-201 Exemple extrait du PDF, page 321.
      b1=# \c b1 r31
      You are now connected to database “b1” as user “r31”.
      Exemple 5-202 Exemple extrait du PDF, page 321.
      b1=> CREATE TABLE t1(id integer);
      
      CREATE TABLE

Exemple 5-203 Exemple extrait du PDF, page 322.
b1=> INSERT INTO t1 VALUES (1), (2);
INSERT 0 2
Exemple 5-204 Exemple extrait du PDF, page 322.
b1=> \c b1 r32
You are now connected to database “b1” as user “r32”.
Exemple 5-205 Exemple extrait du PDF, page 322.
b1=> SELECT * FROM t1;

ERROR:    permission denied for relation t1

b1=> SET ROLE TO r31;
SET
Exemple 5-206 Exemple extrait du PDF, page 322.
b1=> SELECT * FROM t1;
id
Exemple 5-207 Exemple extrait du PDF, page 322.
----
  1
  2

b1=> \c b1 postgres
You are now connected to database “b1” as user “postgres”.
Exemple 5-208 Exemple extrait du PDF, page 322.
b1=# ALTER ROLE r32 INHERIT;
ALTER ROLE
Exemple 5-209 Exemple extrait du PDF, page 322.
b1=# \c b1 r32
You are now connected to database “b1” as user “r32”.
Exemple 5-210 Exemple extrait du PDF, page 322.
b1=> SELECT * FROM t1;
id
Exemple 5-211 Exemple extrait du PDF, page 322.
----
  1
  2

b1=> \c b1 postgres
You are now connected to database “b1” as user “postgres”.
Exemple 5-212 Exemple extrait du PDF, page 322.
b1=# REVOKE r31 FROM r32;
REVOKE ROLE
Exemple 5-213 Exemple extrait du PDF, page 322.
b1=# \c b1 r32
You are now connected to database “b1” as user “r32”.
Exemple 5-214 Exemple extrait du PDF, page 322.
b1=> SELECT * FROM t1;

ERROR:    permission denied for relation t1

b1=> SET ROLE TO r31;

ERROR:    permission denied to set role "r31"
Le changement de rôle peut se faire uniquement au niveau de la transaction. Pour cela, il faut utiliser la clause LOCAL . Il peut se faire aussi sur la session, auquel cas il faut passer par la clause SESSION .

5.5 DROITS DE CONNEXION

  • Lors d’une connexion, indication :
  • de l’hôte (socket Unix ou alias/adresse IP)
  • du nom de la base de données
  • du nom du rôle
  • du mot de passe (parfois optionnel)
  • Selon les 3 premières informations
  • impose une méthode d’authentification Lors d’une connexion, l’utilisateur fournit, explicitement ou non, plusieurs informations. PostgreSQL va choisir une méthode d’authentification en se basant sur les informations fournies et sur la configuration d’un fichier appelé pg_hba.conf .

5.5.1 Informations de connexion

Pour se connecter à une base, il faut :

  • 4 informations

    • socket Unix ou adresse/alias IP
    • n° de port
    • nom de la base
    • nom du rôle
  • Fournies explicitement

    • paramètres
    • environnement
  • ou implicitement

    • environnement
    • défauts Tous les outils fournis avec la distribution PostgreSQL (par exemple createuser ) acceptent des options en ligne de commande pour fournir les informations en question :
  • -h pour la socket Unix ou l’adresse/alias IP ;

  • -p pour le numéro de port ;

  • -d pour le nom de la base;

  • -U pour le nom du rôle. Si l’utilisateur ne passe pas ces informations, plusieurs variables d’environnement sont vérifiées :

  • PGHOST pour la socket Unix ou l’adresse/alias IP;

  • PGPORT pour le numéro de port ;

  • PGDATABASE pour le nom de la base;

  • PGUSER pour le nom du rôle. Au cas où ces variables ne seraient pas configurées, des valeurs par défaut sont utilisées :

  • la socket Unix ( /var/run/postgresql , parfois /tmp ) en lieu d’un nom de machine ;

  • le port 5432 ;

  • la base postgres ou le nom de l’utilisateur PostgreSQL demandé, (suivant l’outil);

  • le nom de l’utilisateur au niveau du système d’exploitation pour le nom du rôle. Autrement dit, quelle que soit la situation, PostgreSQL remplacera les informations non fournies explicitement par des informations provenant des variables d’environnement, voire par des informations par défaut.

5.5.2 Configuration de l’authentification : pg_hba.conf

PostgreSQL utilise les informations de connexion pour choisir la méthode de connexion

  • Fichier de configuration : pg_hba.conf
  • Exemple : local DATABASE USER METHOD [OPTIONS] host DATABASE USER ADDRESS METHOD [OPTIONS] hostssl DATABASE USER ADDRESS METHOD [OPTIONS] hostnossl DATABASE USER ADDRESS METHOD [OPTIONS]
  • Clauses include , include_if_exists , include_dir (v16)
  • Contrôle avec la vue pg_hba_file_rules Lorsque le serveur PostgreSQL récupère une demande de connexion, il connaît le type de connexion utilisé par le client (socket Unix, connexion TCP SSL, connexion TCP simple, etc.). Il connaît aussi l’adresse IP du client (dans le cas d’une connexion via une socket TCP), le nom de la base et celui de l’utilisateur. PostgreSQL va donc chercher les lignes correspondantes dans le tableau enregistré dans le fichier pg_hba.conf (HBA est l’acronyme de Host Based Authentication). Ce fichier est présent à côté de postgresql.conf , donc par exemple dans /var/lib/pgsql/16/data (défaut du packaging Red Hat) ou /etc/postgresql/16/main (défaut du packaging Debian). L’accès doit bien sûr en être le plus restreint possible. Le contenu du fichier se présente en principe ainsi :
  • 4 colonnes d’informations;
  • 1 colonne indiquant la méthode à appliquer ;
  • 1 colonne optionnelle d’options. Depuis PostgreSQL 16, il est possible d’indiquer ou plusieurs fichiers externes :

include pg_hba.ansible.conf include “/chemin/fichier 2.conf” (Existent aussi include_if_exists , qui ne tombe pas en erreur si le fichier n’existe pas, et include_dir , pour inclure tous les fichiers .conf d’un répertoire dans l’ordre de leurs noms.) pg_hba.conf ne peut pas être modifié depuis PostgreSQL même, uniquement depuis le système d’exploitation. Après une modification, il faut dire explicitement à PostgreSQL de le recharger. Selon l’installation et l’OS, cet ordre peut être :

Exemple 5-215 Exemple extrait du PDF, page 325.
pg_ctl reload -D /mnt/base_pg/
systemctl reload postgresql-16
pg_ctlcluster 16 main reload ou depuis PostgreSQL même :
Exemple 5-216 Exemple extrait du PDF, page 325.
SELECT pg_reload_conf() ;
Il y a une exception : sous Windows, le fichier est relu dès modification. Si le fichier contient une erreur de syntaxe, il sera ignoré (et la ligne fautive apparaîtra dans les traces), sauf au (re)démarrage, où un fichier fautif bloque le démarrage!· Exemple de trace problématique : …user=,db=,app=,client= LOG: invalid connection type “hsot” …user=,db=,app=,client= CONTEXT: line 121 of configuration file “/etc/postgresql/16/main/pg_hba.conf” …user=,db=,app=,client= FATAL: could not load /etc/postgresql/16/main/pg_hba.conf Depuis une session utilisateur, il est possible de consulter le fichier via la vue pg_hba_file_rules , tel qu’il est sur le disque, et tel qu’il serait compris par PostgreSQL après un rechargement. La colonne error est très pratique pour repérer immédiatement les erreurs de syntaxe ou de frappe. La colonne file_name indique le fichier source (à partir de PostgreSQL 16).

5.5.3 Exemple de pg_hba.conf

Exemple 5-217 Exemple extrait du PDF, page 326.
             # TYPE DATABASE          USER      ADDRESS           METHOD
              # accès direct par la socket
              local    all             all                         peer
              # accès en local via réseau (localhost IPv6 et IPv4)
              host      all            all      ::1/128            scram-sha-256
              host      all            all      127.0.0.0/24       scram-sha-256
              # accès distants
              hostssl   erp            all      192.168.0.0/16     scram-sha-256
              hostssl   logistique     all      192.168.30.0/24    scram-sha-256
              hostnossl test           demo     192.168.74.150/32 scram-sha-256
              # vieux client
              hostssl   compta         durand   192.168.10.99/32   md5
              # Connexions de réplication
              local   replication      all                         peer
              host    replication      all      192.168.74.5/32    scram-sha-256
              host    replication      all      127.0.0.1/32       scram-sha-256
              host    replication      all      ::1/128            scram-sha-256
PostgreSQL lit le fichier dans l’ordre. La première ligne correspondant à la connexion demandée lui précise la méthode d’authentification à utiliser. Nous verrons que certaines méthodes comme scram-sha-256 ou md5 (les plus courantes) exigent un mot de passe, d’autres non, comme ldap ou cert . Il ne reste plus qu’à appliquer cette méthode. Si elle fonctionne, la connexion est autorisée et se poursuit. Si elle ne fonctionne pas, quelle qu’en soit la raison, la connexion est refusée. Aucune autre ligne du fichier ne sera lue ! La raison du refus est tracée sur le serveur de manière beaucoup plus complète que pour le client : 2023-11-03 10:41:24 CET [1805700]: [2-1] user=attaquant,db=erp,
Exemple 5-218 Exemple extrait du PDF, page 326.
app=[unknown],client=::1 DETAIL: Role "mechant_hacker" does not exist.
Connection matched file “/etc/postgresql/16/defo/pg_hba.conf” line 110: “host all all ::1/128 scram-sha-256” Il est donc essentiel de bien configurer ce fichier pour avoir une protection maximale, et de faire très attention à l’ordre des entrées.

5.5.4 pg_hba.conf : colonne type

  • 6 valeurs possibles
  • local
  • host
  • hostssl ( ssl=on prérequis)
  • hostnossl
  • hostgssenc
  • hostnogssenc La colonne type peut contenir six valeurs différentes. La valeur local concerne les connexions via la socket Unix, donc depuis un compte sur le serveur même. Elle est généralement couplée à la méthode peer . En pratique, on s’en sert surtout pour les connexions administratives (de l’utiliseur système postgres au rôle postgres), pour les sauvegardes depuis le serveur même, ou pour une connexion depuis une application qui tourne sur la même machine. Toutes les autres valeurs concernent les connexions via le réseau (socket TCP). La différence réside dans le chiffrement exigé, SSL ou GSSAPI :
  • host : chiffrement au choix du client ;
  • hostssl : impérativement avec chiffrement SSL ;
  • hostnossl : impérativement sans SSL;
  • hostgssenc : impérativement avec chiffrement GSSAPI;
  • hostnogssenc : impérativement sans GSSAPI. Il est à noter que l’option hostssl n’est utilisable que si le paramètre ssl du fichier postgresql.conf est à on (c’est le cas par défaut sous Debian, pas forcément ailleurs). hostgssenc et hostnogssenc sont les équivalents pour le chiffrement GSSAPI, disponible quand l’authentification passe par Kerberos (méthode gss , voir plus bas). Noter qu’une connexion chiffrée avec SSL correspond aussi à hostnogssenc .

5.5.5 pg_hba.conf : colonne database

À quelle(s) base(s) autoriser la connexion?

  • mabase
  • base1,base2,base3
  • sameuser / samerole
  • all
  • @fichier.txt (fichier avec plusieurs bases)
  • /db_[1-6] , /erp.* (regex, v16+)
  • replication (pseudo-base pour réplication physique) La seconde colonne de pg_hba.conf peut recueillir le nom d’une base, le nom de plusieurs bases en les séparant par des virgules, le nom d’un fichier contenant la liste des bases ou quelques valeurs en dur. La valeur all indique toutes les bases. Enfin, la valeur sameuser spécifie une base de données de même nom que le rôle demandé (l’utilisateur durand peut se connecter à la base durand), alors que la valeur samerole spécifie que le rôle demandé doit être membre du rôle portant le même nom que la base de données demandée (par exemple, si l’utilisateur durand est membre du groupe compta, il peut se connecter à la base compta). Il est possible d’indiquer un fichier (préfixé de @ ) qui contiendra une liste de rôles. Il peut être à côté de pg_hba.conf mais on peut fournir un chemin complet. Depuis PostgreSQL 16, il est possible d’utiliser des expressions régulières, qui doivent être précédées de / (voir exemple plus bas). La valeur replication est utilisée pour définir les connexions de réplication physique (mais pas logique). Il n’est pas nécessaire d’avoir une base nommée replication, toutes les bases sont concernées par une réplication physique.

5.5.6 pg_hba.conf : colonne user

À quel(s) utilisateur(s) permettre la connexion?

  • unrole
  • role1,role2,role3
  • all
  • +groupe (membres d’un rôle-groupe)
  • @fichier.txt (fichier avec plusieurs utilisateurs)
  • /user_.* (regex, v16+)

La troisième colonne peut recueillir :

  • le nom d’un rôle, ou plusieurs rôles séparés par des virgules ;
  • le nom d’un groupe (en le précédant d’un signe + ) : tous les membres de ce groupe pourront se connecter avec leur rôle propre;
  • le nom d’un fichier (précédé de @ ) contenant une liste de rôles;
  • la valeur all qui indique tous les rôles;
  • une regex (précédée de / ) pour indiquer plusieurs rôles (nouveauté de la version 16). Exemple :
    Exemple 5-219 Exemple extrait du PDF, page 329.
    # TYPE      DATABASE      USER              ADDRESS       METHOD
    hostssl erp_hr u_virginie,u_sharon 10.1.17.67/32 scram-sha-256 hostssl all +grp_admin 10.0.0.0/8 scram-sha-256 hostssl /erp_dev[1-5] @prestataires.txt 10.1.0.0/16 scram-sha-256 hostssl /erp.* /u_.* 10.0.0.0/8 scram-sha-256 Ici, ne peuvent se connecter à la base erp_hr que deux utilisatrices nommées, et uniquement depuis une IP précise. Les membres du groupe grp_admin peuvent se connecter à toutes les bases depuis tout le réseau privé. Aux bases erp_dev1 à erp_dev5 peuvent se connecter les prestataires dont les rôles sont dans le fichier prestataires.txt , depuis un certain sous-réseau. Et tous les utilisateurs dont le rôle commence par u_ peuvent se connecter aux bases dont le nom commence par erp_.

5.5.7 pg_hba.conf : colonne adresse IP

  • Uniquement pour host / hostssl / hostnossl
  • Adresse IPv4 ou IPv6 : format CIDR ou avec masque
    • 192.168.1.0/24
    • 192.168.1.1/32
    • 192.168.1.0 255.255.255.0
  • Nom d’hôte possible
    • mais coût recherche DNS La colonne de l’adresse IP permet d’indiquer une adresse IP ou un sous-réseau IP. Il est donc possible de filtrer les connexions par rapport aux adresses IP, ce qui est une excellente protection. Voici deux exemples d’adresses IP au format adresse et masque de sous-réseau :

192.168.168.1 255.255.255.255

192.168.168.0 255.255.255.0

Et voici quelques exemples d’adresses IP (ou plages d’adresses) au format CIDR : 192.168.0.0/8 # réseau 192.168.x.y 192.168.66.0/24 # réseau 192.168.66.x 192.168.168.1/32 # 1 IP 0.0.0.0/0 # toutes les IPv4 ::0/0 # toutes les IPv6

::1/128 # localhost en IPv6 fe80::7a31:c1ff:0000:0000/96 # un réseau en IPv6 Dans le doute, la vue pg_hba_file_rules indique le masque calculé. Les alias samehost et samenet désignent le serveur (avec toutes ses adresses) et tous les réseaux auquels il appartient. Il est possible d’utiliser un nom d’hôte ou un domaine DNS au prix d’une recherche DNS pour chaque hostname présent, pour chaque nouvelle connexion. Rappelons que le paramètre listen_addresses dans postgresql.conf restreint les IP sur lesquelles que PostgreSQL écoute. En général on le modifie dès l’installation.

5.5.8 pg_hba.conf : colonne méthode

Quelle méthode d’authentification utiliser ?

  • Interne / externe
    • et options dans la dernière colonne
  • Le client peut en exiger une (v16) La dernière colonne est généralement la méthode d’authentification, mais certaines acceptent des options dans une colonne supplémentaire. Nous verrons plus bas les différentes méthodes d’authentification, internes ou externes. Le serveur a toujours le dernier mot pour imposer une méthode d’authentification, mais pour des raisons de sécurité le client peut aussi exiger celle qu’il attend (depuis PostgreSQL 16). Dans l’exemple suivant, le client exige la méthode scram-sha-256 , mais le serveur est encore configuré en md5 et la connexion échoue.
    Exemple 5-220 Exemple extrait du PDF, page 330.
    psql 'host=s1 dbname=postgres user=postgres require_auth=scram-sha-256'
    psql: error: connection to server at "s1" (192.168.74.65), port 54325 failed:
     authentication method requirement "scram-sha-256" failed:
     server requested a hashed password
    D’autres variantes pour require_auth sont :
  • require_auth=scram-sha-256,md5
  • require_auth=!password
  • require_auth=cert

5.5.9 pg_hba.conf : colonne options

  • Dépend de la méthode d’authentification
  • Identité externe : option map Les options disponibles dépendent de la méthode d’authentification sélectionnée. Les méthodes qui fournissent un nom d’utilisateur extérieur à l’instance ( peer , ident , gss , sspi , cert , et oauth en version 18) acceptent toutes l’option map . Cette option a notamment pour but d’indiquer la carte de correspondance entre utilisateur authentifié et utilisateur connu de PostgreSQL, carte à définir dans le fichier pg_ident.conf (voir plus bas).

5.5.10 pg_hba.conf : méthodes internes

                 -
                  -
                      trust : dangereux!
                      reject : pour interdire
Tableau 5-56 Tableau ou résultat de commande extrait du PDF, page 331.
  • password : en clair, à éviter!
  • md5 : déconseillée
  • scram-sha-256 La méthode trust est certainement la pire. À partir du moment où le rôle est reconnu, aucun mot de passe n’est demandé. Si le mot de passe est fourni malgré tout, il n’est pas vérifié. De plus, avant PostgreSQL 17, l’utilisation de la méthode trust n’est pas directement visible dans les traces. N’utilisez pas la méthode trust . La méthode password force la saisie d’un mot de passe. Cependant, ce dernier est envoyé en clair sur le réseau. Il n’est donc pas conseillé d’utiliser cette méthode, surtout sur un réseau non sécurisé. (Noter que PostgreSQL chiffrera toujours le mot de passe chiffré avec une des méthodes ci-dessous.) La méthode md5 est encore très utilisée mais obsolète. La saisie du mot de passe est forcée. Le mot de passe transite chiffré en md5 . Cette méthode souffre néanmoins de certaines faiblesses décrites dans la section Mot de passe, on préférera la suivante. La méthode scram-sha-256 est la plus sécurisée, elle offre moins d’angles d’attaque que md5 . Elle est à privilégier.

La méthode reject sert pour interdire définitivement un accès à un utilisateur, un réseau ou une base quelles que soient les lignes suivantes de pg_hba.conf . Par exemple, on veut que le rôle u1 puisse se connecter à la base de données b1 mais pas aux autres. Voici un moyen de le faire (pour une connexion via les sockets Unix) : local b1 u1 scram-sha-256 local all u1 reject

5.5.11 pg_hba.conf : méthodes externes

  • Système externe : ldap , radius , oauth
  • Kerberos : gss , sspi
  • SSL : cert
  • OS : peer , pam , ident , bsd Certaines méthodes permettent d’utiliser des annuaires d’entreprise comme RADIUS, LDAP ou ActiveDirectory. Certaines méthodes sont spécifiques à un système d’exploitation. La méthode ldap utilise un serveur LDAP pour authentifier l’utilisateur. À la différence de gss (cidessous), le mot de passe transite jusqu’au serveur, qui le vérifie auprès de l’annuaire : il n’y a donc pas de Single Sign-On. Si l’annuaire supporte Kerberos, comme Active Directory, gss est préférable. La méthode radius permet d’utiliser un serveur RADIUS pour authentifier l’utilisateur. La méthode gss (GSSAPI) correspond au protocole du standard de l’industrie pour l’authentification sécurisée définie dans RFC 2743. PostgreSQL supporte GSSAPI avec l’authentification Kerberos suivant la RFC 1964 ce qui permet de faire du Single Sign-On. C’est la méthode à utiliser avec Active Directory. sspi (uniquement dans le monde Windows) permet d’utiliser NTLM faute d’Active Directory. La méthode cert utilise un certificat SSL sur le client 13 , sans mot de passe. Comme gss , elle n’échange donc aucun secret, mais l’identité est prouvée par un certificat signé par l’autorité déclarée dans ssl_ca_file , et non par un annuaire : elle n’est utilisable que sur une connexion chiffrée SSL, et exige que le CN du certificat corresponde au rôle demandé (au besoin via pg_ident.conf ). La méthode ident permet d’associer les noms des utilisateurs du système d’exploitation aux noms des utilisateurs du système de gestion de bases de données. Un démon fournissant le service ident est nécessaire. (Les paquets RPM la mettaient en place jusque PostgreSQL 12 inclus pour une connexion depuis localhost , mais peer est maintenant recommandé.) La méthode peer permet d’associer les noms des utilisateurs du système d’exploitation aux noms des utilisateurs du système de gestion de bases de données. Ceci n’est possible qu’avec une connexion 13 https://docs.postgresql.fr/current/auth-cert.html

locale. Par défaut, les paquets d’installation l’utilisent pour autoriser l’utilisateur système postgres à se connecter à l’instance locale, sans passer par le réseau, en tant que postgres sans mot de passe. La méthode pam authentifie l’utilisateur en passant par les Pluggable Authentication Modules (PAM) fournis par Linux et d’autres Unix. De manière similaire, la méthode bsd utilise le système d’authentification BSD sur OpenBSD. La méthode oauth implémente OAuth 2.0 14 , et est arrivée en version 18. Elle se base sur un module d’authentification, à installer en prérequis. La société Percona en a écrit un pour OpenID Connect 15 .

5.5.12 Un (mauvais) exemple de pg_hba.conf

Un exemple : TYPE DATABASE USER ADDRESS METHOD local all postgres peer local web web md5 local sameuser all peer host all postgres 127.0.0.1/32 peer host all all 127.0.0.1/32 md5 host all all 89.192.0.3/8 md5 hostssl recherche recherche 89.192.0.4/32 scram-sha-256 à ne pas suivre… Ce fichier comporte plusieurs erreurs. Tout d’abord, host all all 127.0.0.1/32 md5 autorise tous les utilisateurs, en IP, en local (127.0.0.1) à se connecter à toutes les bases, ce qui est en contradiction avec : local sameuser all peer qui, sera appliqué d’abord et restreint à la base de même nom. Dans cette règle : host all all 89.192.0.3/8 md5 le masque CIDR /8 est incorrect, et donc, au lieu d’autoriser seulement 89.192.0.3 à se connecter, on autorise tout le réseau 89.*. L’entrée : hostssl recherche recherche 89.192.0.4/32 scram-sha-256 14 https://docs.postgresql.fr/18/auth-oauth.html

15 https://github.com/percona/pg_oidc_validator

est bonne, mais inutile, car masquée par la ligne précédente : toute ligne correspondant à cette entrée correspondra aussi à la ligne précédente. Le fichier étant lu séquentiellement, cette dernière entrée ne sert à rien. Enfin, il aurait mieux valu que tous les mots de passe utilisent le chiffrement scram-sha-256 et non md5 .

5.5.13 Mapping : pg_ident.conf

  • Correspondance entre :
    • utilisateurs authentifiés (serveur, Kerberos, certificat)
    • rôle de la base
  • Fichier pg_ident.conf :
    Exemple 5-221 Exemple extrait du PDF, page 334.
                  # MAPNAME           SYSTEM-USERNAME               PG-USERNAME
                  pgadmins            joe                           postgres
                  pgadmins            zoe                           postgres
  • Prise en compte des modifications : rechargement
  • Includes possibles
  • Contrôle : pg_ident_file_mapping (v15+) En utilisant une méthode d’authentification qui fournit un nom d’utilisateur extérieur à l’instance ( peer , ident , gss , sspi , cert ), le nom de l’utilisateur authentifié ne correspond pas forcément au nom du rôle avec lequel on souhaite se connecter. On peut le préciser avec -U mais il y a plus simple et sécurisé. Par exemple, deux DBA ont les comptes joe et zoe sur le système d’exploitation. Ils souhaitent pouvoir se connecter en tant que rôle postgres sans saisie de mot de passe depuis leur compte, sans devoir faire un sudo -iu postgres pour lesquels on ne va pas forcément leur donner les droits. Ceci n’est pas possible avec la configuration par défaut : joe$ psql -U postgres
    Exemple 5-222 Exemple extrait du PDF, page 334.
    psql: error: FATAL: Peer authentication failed for user "postgres"
    Mais cela devient possible en configurant correctement les fichiers pg_hba.conf et pg_ident.conf . Il est tout d’abord nécessaire d’indiquer trois informations dans le fichier pg_ident.conf , une « map », les utilisateurs authentifiés (ici joe et zoe) et le rôle de connexion (ici postgres) :
    Exemple 5-223 Exemple extrait du PDF, page 334.
    # MAPNAME          SYSTEM-USERNAME               PG-USERNAME
    pgadmins joe postgres pgadmins zoe postgres À partir de la version 15, il est possible de vérifier que la configuration de ce fichier est bonne. Pour cela, il faut lire la vue pg_ident_file_mappings (et vérifier que le champ error est vide).

Exemple 5-224 Exemple extrait du PDF, page 335.
SELECT * FROM pg_ident_file_mappings;
 line_number | map_name | sys_name | pg_username | error
-------------+----------+----------+-------------+-------
          43 | pgadmins | joe      | postgres    |
          44 | pgadmins | zoe      | postgres    |
Tableau 5-57 Tableau ou résultat de commande extrait du PDF, page 335.
Il faut ensuite ajouter cette map à la ligne de configuration d’authentification en haut du fichier pg_hba.conf : local all all peer map=pgadmins Après rechargement de la conf, la connexion de joe ou zoe à postgres en local se fera. Noter que la sécurité de l’accès à PostgreSQL repose à présent sur la sécurisation des utilisateurs système joe et zoe! Cet exemple n’est valable que pour les connexions depuis le système d’exploitation même. En pratique, pg_ident.conf est utilisé pour se connecter directement en tant que postgres depuis un compte utilisateur sur le serveur, avec une authentification Kerberos ( gss ) ou bien par certificat ( cert ). L’option map n’est d’ailleurs acceptée que par ces méthodes : ldap ou les méthodes par mot de passe reçoivent directement le nom du rôle, il n’y a rien à faire correspondre. Depuis PostgreSQL 16, pg_ident.conf supporte les mêmes directives d’inclusion que pg_hba.conf ( include , include_if_exists , include_dir ), et le fichier d’origine d’une ligne est visible dans pg_ident_file_mappings .

5.6 SYNCHRONISATION DES RÔLES

  • PostgreSQL externalise l’authentification uniquement
    • Le rôle, ses paramètres, ses héritages sont toujours dans PostgreSQL
  • ldap2pg synchronise les rôles depuis un annuaire LDAP
    • un rôle PostgreSQL absent de l’annuaire est supprimé
    • sait synchroniser les privilèges
    • configuration avancée en YAML
    • https://labs.dalibo.com/ldap2pg PostgreSQL sait externaliser la vérification du mot de passe ou authentifier depuis une autre méthode qu’un mot de passe. Néanmoins, PostgreSQL n’externalise jamais la configuration d’un rôle. Au delà de son nom et de son mot de passe, un rôle possède des options ( SUPERUSER , LOGIN , etc.), des héritages, des privilèges, des privilèges par défaut pour chaque base voire pour chaque schéma, des paramètres globaux ou par base. Tout cela reste dans PostgreSQL, même si l’authentification est externe. L’outil ldap2pg permet de centraliser les rôles dans l’annuaire d’entreprise. ldap2pg est un binaire (programmé en Go) à installer à côté de PostgreSQL ou ailleurs. ldap2pg traite une configuration avancée au format YAML décrivant toutes les recherches LDAP énumérant les rôles à provisionner dans l’annuaire. Par exemple : la liste des DBA, la liste des développeurs d’une application, etc. À chaque recherche LDAP est associé un modèle de rôle où sont substitués des attributs LDAP fournit par l’annuaire. ldap2pg sait également configurer les paramètres propres à chaque rôle, les privilèges par défaut, et les privilèges sur les objets. La recherche LDAP des rôles d’une instance est indépendante de l’authentification. On peut très bien rechercher dans l’annuaire la liste des DBA avec le protocole LDAP et authentifier les connexions avec Kerberos. Page du projet : https://labs.dalibo.com/ldap2pg

5.7 TÂCHES DE MAINTENANCE

  • Trois opérations essentielles
    • VACUUM
    • ANALYZE
    • REINDEX
  • En arrière-plan : démon autovacuum (pour les deux premiers)
  • Optionnellement : automatisable par cron
  • Manuellement : VACUUM ANALYZE table (batchs, gros imports…)
  • Par : propriétaire, superuser, GRANT MAINTAIN ou pg_maintain PostgreSQL demande peu de maintenance par rapport à d’autres SGBD. Néanmoins, un suivi vigilant de ces tâches participera beaucoup à conserver un système performant et agréable à utiliser. La maintenance d’un serveur PostgreSQL revient à s’occuper de trois opérations :
  • le VACUUM , pour éviter une fragmentation trop importante des tables;
  • l’ ANALYZE , pour mettre à jour les statistiques sur les données contenues dans les tables;
  • le REINDEX , pour reconstruire les index. Il s’agit donc de maintenir, voire d’améliorer, les performances du système. Il ne s’agit en aucun cas de s’assurer de la stabilité du système. Généralement on se reposera sur le processus d’arrière-plan autovacuum, qui s’occupe des VACUUM et ANALYZE (mais pas REINDEX ), en fonction de l’activité, et prend soin de ne pas la gêner. Un appel explicite est parfois nécessaire, notamment au sein de batchs ou de gros imports, car l’autovacuum n’a pas forcément eu le temps de passer entre deux étapes, et les statistiques ne sont alors pas à jour : le planificateur pense que les tables sont encore vides et peut choisir un plan désastreux. On lancera donc systématiquement au moins un ANALYZE sur les tables modifiées après les modifications lourdes. Un VACUUM ANALYZE est parfois encore plus intéressant, notamment si des données ont été modifiées ou effacées, ou si les requêtes suivantes peuvent profiter d’un parcours d’index seul (Index Only Scan). Il est parfaitement possible et très courant de planifier des exécutions régulières, avec cron, pg_cron , ou tout autre ordonnanceur, de VACUUM et/ou ANALYZE sur tout ou partie des tables, voire de REINDEX ou CLUSTER dans certains cas. Ces opérations de maintenance peuvent être effectuées par le propriétaire de la table, ou par un superutilisateur. À partir de PostgreSQL 17, il suffit d’un utilisateur possédant le rôle pg_maintain , ou un utilisateur s’ayant vu octroyer un GRANT MAINTAIN sur la table. Cet utilisateur pourra lancer les commandes de maintenance sans pouvoir lire les données.

5.7.1 Maintenance : VACUUM

  • VACUUM nomtable ;
    • nettoie les espaces morts pour réutilisation (& autre maintenance)
    • ne gêne pas l’activité
  • Version démon :
    • autovacuum
  • Outil shell :
    • vacuumdb –echo
  • Suivi :
    • vue pg_stat_progress_vacuum PostgreSQL ne supprime pas des tables les versions périmées des lignes après un UPDATE ou un DELETE , elles deviennent juste invisibles. La commande VACUUM permet de récupérer l’espace utilisé par ces lignes afin d’éviter un accroissement continuel du volume occupé sur le disque. Une table qui subit beaucoup de mises à jour et suppressions nécessitera des nettoyages plus fréquents que les tables rarement modifiées. Le VACUUM « simple » ( VACUUM nomdematable ; ) marque les données expirées dans les tables et les index pour une utilisation future. Il ne tente pas de rendre au système de fichiers l’espace utilisé par les données obsolètes, sauf si l’espace est à la fin de la table et qu’un verrou exclusif de table peut être facilement obtenu. L’espace inutilisé au début ou au milieu du fichier ne provoque pas un raccourcissement du fichier et ne redonne pas d’espace mémoire au système d’exploitation. De même, l’espace d’une colonne supprimée n’est pas rendu. Cet espace libéré n’est pas perdu : il sera disponible pour les prochaines lignes insérées et mises à jour, et la table n’aura pas besoin de grandir. (Nous verrons plus bas un ordre différent, VACUUM FULL , pour récupérer l’espace au besoin.) VACUUM est paramétré pour être lancé sans gêne pour les utilisateurs. Il va surtout générer des lectures et écritures supplémentaires. On verra plus loin que l’autovacuum s’occupe de tout cela en tâche de fond et de manière encore moins intrusive, mais il arrive encore que l’on lance un VACUUM manuellement. Noter qu’un VACUUM s’occupe également de quelques autres opérations de maintenance qui ne seront pas détaillées ici. L’option VERBOSE permet de suivre ce qui a été fait. Dans l’exemple suivant, 100 000 lignes sont nettoyées dans 541 blocs, mais 300 316 lignes ne peuvent être supprimées car une autre transaction reste susceptible de les voir.
      Exemple 5-226 Exemple extrait du PDF, page 338.
      VACUUM VERBOSE livraisons ;
      
      INFO:  vacuuming "public.livraisons"
      INFO:  "livraisons": removed 100000 row versions in 541 pages
      INFO:  "livraisons": found 100000 removable, 300316 nonremovable
                                               row versions in 2165 out of 5406 pages
      DÉTAIL : 0 dead row versions cannot be removed yet, oldest xmin: 6249883
      There were 174 unused item pointers.

Skipped 0 pages due to buffer pins, 540 frozen pages.

Exemple 5-227 Exemple extrait du PDF, page 339.
0 pages are entirely empty.
CPU: user: 0.04 s, system: 0.00 s, elapsed: 0.08 s. VACUUM Temps : 88,990 ms L’ordre VACUUM possède de nombreux paramètres que nous ne décrirons pas ici, car ils servent peu au quotidien (voir le module M5 16 , notamment pour accélérer les opérations du VACUUM ). Supervision : La vue pg_stat_progress_vacuum permet de suivre un VACUUM simple pendant son déroulement : TABLE pg_stat_progress_vacuum \gx
Exemple 5-228 Exemple extrait du PDF, page 339.
-[ RECORD 1 ]------+--------------
pid                  | 2603780
datid                | 1308955
datname              | pgbench_100
relid                | 1308962
phase                | scanning heap
heap_blks_total      | 163935
heap_blks_scanned    | 3631
heap_blks_vacuumed   | 0
index_vacuum_count   | 0
max_dead_tuple_bytes | 67108864
dead_tuple_bytes     | 0
num_dead_item_ids    | 0
indexes_total        | 0
indexes_processed    | 0
Tableau 5-58 Tableau ou résultat de commande extrait du PDF, page 339.
La vue pg_stat_user_tables contient pour chaque table la date du dernier passage d’un VACUUM simple (champ last_vacuum ) celle du dernier passage automatique ( last_autovacuum ).
Exemple 5-229 Exemple extrait du PDF, page 339.
SELECT * FROM pg_stat_user_tables where relname = 'pgbench_accounts' \gx

-[ RECORD 1 ]-------+------------------------------
relid               | 1308962
schemaname          | public
relname             | pgbench_accounts
seq_scan            | 6
last_seq_scan       | 2024-10-18 11:03:08.493099+02
seq_tup_read        | 50000000
idx_scan            | 9954460
last_idx_scan       | 2024-10-18 19:14:30.946211+02
idx_tup_fetch       | 89954076
n_tup_ins           | 10000000
n_tup_upd           | 3318032
n_tup_del           | 0
n_tup_hot_upd       | 3127413
n_tup_newpage_upd   | 190619
n_live_tup          | 9992800
n_dead_tup          | 345265
n_mod_since_analyze | 1894832
Tableau 5-59 Tableau ou résultat de commande extrait du PDF, page 339.
16 https://dali.bo/m5_html

n_ins_since_vacuum      | 0
last_vacuum             | 2024-10-18 17:58:53.305703+02
last_autovacuum         |
last_analyze            | 2024-10-18 11:01:20.131883+02
last_autoanalyze        | 2024-10-18 19:12:36.344106+02
vacuum_count            | 7
autovacuum_count        | 0
analyze_count           | 1
autoanalyze_count       | 1
Tableau 5-60 Tableau ou résultat de commande extrait du PDF, page 340.
Pour les passages précédents, il faudra se rabattre sur les traces (on conseille de positionner log_autovacuum_min_duration suffisamment bas, ou à 0). Il est important de vérifier que les tables actives sont régulièrement nettoyées. Outil en ligne de commande : L’outil vacuumdb permet d’exécuter depuis le shell un VACUUM sur une ou toutes les bases. C’est l’outil idéal lorsque l’on planifie un nettoyage quotidien ou hebdomadaire. Il sait exécuter des VACUUM sur plusieurs tables en parallèle, ou profiter des optimisations liées aux nettoyages en masse apparues en v16.
Exemple 5-230 Exemple extrait du PDF, page 340.
$ vacuumdb --echo --all

SELECT pg_catalog.set_config('search_path', '', false);
vacuumdb : exécution de VACUUM sur la base de données « pgbench »
Exemple 5-231 Exemple extrait du PDF, page 340.
RESET search_path;
SELECT c.relname, ns.nspname FROM pg_catalog.pg_class c
  JOIN pg_catalog.pg_namespace ns ON c.relnamespace OPERATOR(pg_catalog.=) ns.oid
  LEFT JOIN pg_catalog.pg_class t ON c.reltoastrelid OPERATOR(pg_catalog.=) t.oid
  WHERE c.relkind OPERATOR(pg_catalog.=) ANY (array['r', 'm'])
  ORDER BY c.relpages DESC;
SELECT pg_catalog.set_config('search_path', '', false);
VACUUM (SKIP_DATABASE_STATS) public.pgbench_accounts;
VACUUM (SKIP_DATABASE_STATS) pg_catalog.pg_proc;
VACUUM (SKIP_DATABASE_STATS) pg_catalog.pg_attribute;
VACUUM (SKIP_DATABASE_STATS) pg_catalog.pg_description;
VACUUM (SKIP_DATABASE_STATS) pg_catalog.pg_statistic;
… …
Exemple 5-232 Exemple extrait du PDF, page 340.
VACUUM (ONLY_DATABASE_STATS);
SELECT pg_catalog.set_config('search_path', '', false);
vacuumdb : exécution de VACUUM sur la base de données « postgres » … …
Exemple 5-233 Exemple extrait du PDF, page 340.
VACUUM (ONLY_DATABASE_STATS);
L’ordre VACUUM connaît beaucoup de variantes. Surtout, ne confondez pas la version simple ci-dessus avec sa variante VACUUM FULL ci-dessous.

5.7.2 Maintenance : VACUUM FULL

  • VACUUM FULL nomtable ;
    • défragmente la table
    • réécrit (place nécessaire!)
    • verrou exclusif (ni lecture ni écriture!)
    • réindexe
    • utilisation exceptionnelle
    • vue pg_stat_progress_cluster Un VACUUM simple fait rarement gagner de l’espace disque. Il faut utiliser l’option FULL pour ça : la commande VACUUM FULL nomtable ; réécrit la table en ne gardant que les données actives, et au final libère donc l’espace consommé par les lignes périmées ou les colonnes supprimées, et le rend au système d’exploitation. Les index sont réécrits au passage. Inconvénient principal : VACUUM FULL acquiert un verrou exclusif sur chaque table concernée : personne ne peut plus y écrire ni même lire avant la fin de l’opération, et les sessions accédant aux tables sont mises en attente. Cela peut être long pour de grosses tables. D’autre part, le VACUUM FULL peut lui-même attendre la libération d’un verrou, tout en bloquant les transactions suivantes (phénomène d’empilement des verrous). Il est conseillé d’opérer par exemple ainsi :
      Exemple 5-234 Exemple extrait du PDF, page 341.
      SET lock_timout TO '3s';
      VACUUM (FULL, VERBOSE) nomtable ;
      Ainsi l’ordre VACUUM FULL sera annulé s’il n’obtient pas son verrou assez vite. Autre inconvénient : VACUUM FULL écrit la nouvelle version de la table à côté de l’ancienne version avant d’effacer cette dernière : l’espace occupé peut donc temporairement doubler. Si vos disques sont presque pleins, vous ne pourrez donc pas faire un VACUUM FULL d’une grosse table pour récupérer de l’espace! L’autovacuum ne procédera jamais à un VACUUM FULL , vous devrez toujours le demander explicitement. On le réservera aux périodes de maintenance, dans les cas où il est vraiment nécessaire. En effet, il ne sert à rien de chercher à réduire au strict minimum la taille des tables par des VACUUM FULL répétés. Dans une base active, les espaces libres sont vite réutilisés par de nouvelles données. Le bloat (l’espace inutilisé d’une table) se stabilise généralement dans une proportion dépendant des débits d’insertions, suppressions et modifications dans la table. Supervision : La vue pg_stat_progress_cluster permet de suivre un VACUUM FULL (à partir de PostgreSQL 12). Son nom provient de la proximité avec la commande CLUSTER (voir plus bas). Les VACUUM FULL ne sont pas tracés dans la vue pg_stat_user_tables . Outil en ligne de commande :

L’outil vacuumdb possède une option –full .

5.7.3 VACUUM vs VACUUM FULL

  • VACUUM
    • maintenance quotidienne
    • entre étapes d’un batch
    • l’autovacuum suffit généralement
  • VACUUM FULL
    • après de grosses modifications
    • exceptionnel Quand faut-il utiliser VACUUM sur une table?
  • pour des nettoyages réguliers;
  • entre les étapes d’un batch;
  • si vous constatez que l’autovacuum ne passe pas assez souvent et qu’un changement de paramétrage ne suffit pas;
  • et ce, pendant que votre base tourne. Quand faut-il utiliser VACUUM FULL sur une table?
  • après des suppressions massives de données ;
  • si le verrou exclusif ne gêne pas la production;
  • dans le cadre d’une maintenance exceptionnelle. Des VACUUM standards et une fréquence modérée sont une meilleure approche que des
    Exemple 5-236 Exemple extrait du PDF, page 342.
     VACUUM FULL , même non fréquents, pour maintenir des tables mises à jour fréquemment :
    faites confiance à l’autovacuum jusque preuve du contraire. VACUUM FULL est recommandé dans les cas où vous savez que vous avez supprimé ou modifié une grande partie des lignes d’une table, et que les espaces libres ne seront pas à nouveau remplis assez vite, de façon à ce que la taille de la table soit réduite de façon conséquente. Les deux outils peuvent se lancer à la suite. Après un VACUUM FULL (bloquant) sur une table, on lance souvent immédiatement un VACUUM ANALYZE . Cela semble inutile du point de vue des données, mais les autres opérations de maintenance impliquées peuvent améliorer les performances.

5.7.4 Maintenance : ANALYZE

  • Met à jour les statistiques sur les données pour l’optimiseur de requêtes
  • Géré par l’autovacuum
  • Parfois manuel : batch, ALTER TABLE , tables temporaires…
  • Échantillonnage :
  • default_statistics_target (défaut 100)
  • ALTER TABLE ma_table ALTER ma_colonne SET STATISTICS 500;
  • Attention au temps de planification!
  • Progression avec pg_stat_progress_analyze L’optimiseur de requêtes de PostgreSQL s’appuie sur des informations statistiques calculées à partir des données des tables. Ces statistiques sont récupérées par la commande ANALYZE , qui peut être invoquée seule ou comme une option de VACUUM . Il est important d’avoir des statistiques relativement à jour sans quoi des mauvais choix dans les plans d’exécution pourraient pénaliser les performances de la base. L’autovacuum de PostgreSQL appelle au besoin ANALYZE si l’activité de la table le nécessite. C’est généralement suffisant, même s’il est fréquent de modifier le paramétrage sur de grosses tables. Il est possible de programmer ANALYZE périodiquement (le dimanche, la nuit par exemple, à l’aide d’une commande cron par exemple), éventuellement couplé à un VACUUM :
    Exemple 5-237 Exemple extrait du PDF, page 343.
    VACUUM ANALYZE nomdematable ;
    Il existe des cas où lancer un ANALYZE manuellement est nécessaire :
  • en mode « batch » : l’autovacuum n’a pas forcément le temps de passer entre deux étapes, on peut être amené à intercaler un VACUUM ANALYZE sur des tables modifiées ;
  • quand certains plans de requêtes affichent des statistiques aberrantes : la mise à jour des statistiques peut suffire (et l’on regardera ensuite dans pg_stat_user_tables.last_autoanalyze si l’autovacuum a tardé et s’il y a un ajustement à faire ce côté);
  • après un ALTER TABLE […] ALTER COLUMN , car les statistiques de la colonne peuvent disparaître, ou bien sûr lors de l’ajout d’une colonne pré-remplie;
  • lors de l’ajout d’un index fonctionnel : l’ ANALYZE mène à la création d’une nouvelle entrée dans pg_statistics ;
  • lors de l’utilisation des tables temporaires : l’autovacuum ne les voit pas. Le paramètre default_statistics_target définit l’échantillonnage par défaut des statistiques pour les colonnes de chacune des tables. La valeur par défaut est de 100. Ainsi, pour chaque colonne, 30 000 lignes sont choisies au hasard, et les 100 valeurs les plus fréquentes et un histogramme à 100 bornes sont stockés dans pg_statistics en guise d’échantillon représentatif des données. Des valeurs supérieures provoquent un ralentissement important d’ANALYZE, un accroissement de la table pg_statistics, et un temps de calcul des plans d’exécution plus long. On conserve généralement la valeur 100 par défaut (sauf peut-être sur certaines grosses bases aux requêtes complexes et longues, comme des entrepôts de données). Voici la commande à utiliser si l’on veut modifier cette valeur pour une colonne précise, la valeur ainsi spécifiée prévalant sur la valeur de default_statistics_target :
    Exemple 5-239 Exemple extrait du PDF, page 344.
    ALTER TABLE ma_table ALTER ma_colonne SET STATISTICS 200 ;
    
    ANALYZE ma_table ;
    Sans l’ ANALYZE explicite, la mise à jour attendrait le prochain passage de l’autovacuum. La vue pg_stat_user_tables contient aussi les dates du dernier passage d’un ANALYZE manuel (champ last_analyze ) ou automatique ( last_autoanalyze ). Là encore, vérifier que les tables actives sont régulièrement analysées. La vue pg_stat_progress_analyze permet de suivre l’avancement des ANALYZE en cours (sans historisation) : TABLE pg_stat_progress_analyze ;
    Exemple 5-240 Exemple extrait du PDF, page 344.
    -[ RECORD 1 ]-------------+----------------------
    pid                       | 518842
    datid                     | 207023
    datname                   | pgbench
    relid                     | 207030
    phase                     | acquiring sample rows
    sample_blks_total         | 30000
    sample_blks_scanned       | 26862
    ext_stats_total           | 0
    ext_stats_computed        | 0
    child_tables_total        | 0
    child_tables_done         | 0
    current_child_table_relid | 0
    Tableau 5-61 Tableau ou résultat de commande extrait du PDF, page 344.

5.7.5 Maintenance : REINDEX

  • Lancer REINDEX régulièrement permet
    • de gagner de l’espace disque
    • d’améliorer les performances
    • de réparer un index corrompu/invalide
  • VACUUM ne provoque pas de réindexation
  • VACUUM FULL réindexe
  • Clause CONCURRENTLY
  • Clause TABLESPACE
  • REINDEX SYSTEM
  • reindexdb pour des réindexations en masse

Principe : REINDEX reconstruit un index en utilisant les données stockées dans la table, remplaçant l’ancienne copie de l’index. La même commande peut réindexer tous les index d’une table :

Exemple 5-242 Exemple extrait du PDF, page 345.
REINDEX INDEX nomindex ;
REINDEX (VERBOSE) TABLE nomtable ;
Les pages d’index qui sont devenues complètement vides sont récupérées pour être réutilisées. Il existe toujours la possibilité d’une utilisation inefficace de l’espace : même s’il ne reste qu’une clé d’index dans une page, la page reste allouée. La possibilité d’inflation n’est pas indéfinie, mais il est souvent utile de planifier une réindexation périodique pour les index fréquemment modifiés. De plus, pour les index B-tree, un index tout juste construit est plus rapide qu’un index qui a été mis à jour plusieurs fois. En effet, dans un index nouvellement créé, les pages logiquement adjacentes sont aussi physiquement adjacentes. La réindexation est aussi utile dans le cas d’un index corrompu. Ce cas est heureusement très rare, et souvent lié à des problèmes matériels. Comme avec CREATE INDEX , il est généralement souhaitable d’ajouter la clause CONCURRENTLY pour réindexer sans bloquer les écritures dans la table (les lectures restent possibles) :
Exemple 5-243 Exemple extrait du PDF, page 345.
REINDEX (VERBOSE) INDEX nomindex CONCURRENTLY ;
Pour réindexer les tables systèmes :
Exemple 5-244 Exemple extrait du PDF, page 345.
REINDEX (VERBOSE) SYSTEM nom_de_base_courante ;
Ce peut être ponctuellement utile. Il est possible de réindexer un index tout en le changeant de tablespace. Pour cela, il faut utiliser la clause TABLESPACE avec en argument le nom du tablespace de destination. L’opération VACUUM (sans FULL ) nettoie les index, mais ne provoque pas de réindexab tion complète. Une certaine fragmentation peut alors subsister. Par contre, une réindexation est effectuée lors d’un VACUUM FULL . Index invalide : Les index « invalides » sont inutilisables et ignorés, et doivent être reconstruits. Ce statut apparaît en bas de la description de la table associée :
Exemple 5-245 Exemple extrait du PDF, page 345.
# \d+ pgbench_accounts
… Index : “pgbench_accounts_pkey” PRIMARY KEY, btree (aid) INVALID Un index peut devenir invalide si l’on utilise l’option CONCURRENTLY des ordres CREATE INDEX et
Exemple 5-246 Exemple extrait du PDF, page 345.
 REINDEX . Cette clause permet de créer/réindexer un index sans bloquer les écritures dans la table.
Cependant, si, au bout de deux passes, l’index n’est toujours pas complet, il est considéré comme invalide, et doit être soit détruit, soit reconstruit avec REINDEX .

reindexdb : La commande système reindexdb peut être utilisée pour réindexer une table, une base ou une instance entière. Elle génère des ordres REINDEX (que l’on peut voir avec l’option –echo ), sur des tables (option –table , à répéter au besoin), des schémas ( –schema ), des index ( –index ), les tables système ( –system )… De manière similaire à vacuumdb , l’intêrêt de reindexdb , par rapport à des exécutions d’un

Exemple 5-247 Exemple extrait du PDF, page 346.
 REINDEX global ou sur une table ou un schéma, réside dans ses possibilités d’exécution en masse
et de parallélisation. L’option –jobs=N utilise plusieurs connexions pour autant de réindexations en parallèle. Une table est réindexée par un seul job pour des raisons techniques. Cela marche bien avec –table ou –schema , mais il faut PostgreSQL 17 pour qu’une liste d’index à reconstruire ( –index ) soit parallélisée. S’il y a trop de jobs, il y a un risque de saturer la RAM ou le CPU (sachant que chaque indexation est déjà susceptible d’utiliser plusieurs processeurs). Autre point fort, l’option –all permet de réindexer toutes les bases. À partir de PostgreSQL 17, cette dernière option peut se conjuguer avec –table , –index , etc. pour réindexer uniquement des objets de même nom dans toutes les bases. Exemples (les sorties ont été allégées de quelques ordres techniques) :
Exemple 5-248 Exemple extrait du PDF, page 346.
$ reindexdb -d pgbench --table=pgbench_accounts --echo --verbose


SELECT pg_catalog.set_config('search_path', '', false);
RESET search_path;
[…]
Exemple 5-249 Exemple extrait du PDF, page 346.
REINDEX (VERBOSE) TABLE public.pgbench_accounts;
INFO: index "pgbench_accounts_pkey" was reindexed
DÉTAIL : CPU: user: 0.08 s, system: 0.01 s, elapsed: 0.10 s


$ reindexdb -d pgbench_10 --echo \
  --index=pgbench_accounts_pkey --index=pgbench_branches_pkey


SELECT pg_catalog.set_config('search_path', '', false);
RESET search_path;
[…]
Exemple 5-250 Exemple extrait du PDF, page 346.
REINDEX INDEX public.pgbench_accounts_pkey;
RESET search_path;
[…]
Exemple 5-251 Exemple extrait du PDF, page 346.
REINDEX INDEX public.pgbench_branches_pkey;


$ reindexdb -d pgbench --system --echo


SELECT pg_catalog.set_config('search_path', '', false);
REINDEX SYSTEM pgbench;

5.7.6 Maintenance : CLUSTER

  • CLUSTER
    • alternative à VACUUM FULL
    • tri des données de la table suivant un index
  • Attention, CLUSTER nécessite près du double de l’espace disque utilisé pour stocker la table et ses index
  • Progression avec pg_stat_progress_cluster La commande CLUSTER provoque une réorganisation des données de la table en triant les lignes suivant l’ordre indiqué par l’index. Du fait de la réorganisation, le résultat obtenu est équivalent à un
    Exemple 5-252 Exemple extrait du PDF, page 347.
     VACUUM FULL en terme de fragmentation. Elle verrouille tout aussi complètement la table et nécessite autant de place.
    Les valeurs des données sont alors corrélées avec leur emplacement physique. L’intérêt d’une telle réorganisation peut être l’amélioration de l’effet de cache, ou l’utilisation d’un index de type spécifique (BRIN). Attention, cette réorganisation est ponctuelle, et les données modifiées ou insérées par la suite n’en tiennent généralement pas compte. L’opération peut donc être à refaire après un certain temps. Comme après un VACUUM FULL , lancer un VACUUM ANALYZE manuellement peut être bénéfique pour les performances. En ligne de commande, l’outil associé clusterdb permet de lancer la réorganisation de tables ayant déjà fait l’objet d’une « clusterisation ». La vue pg_stat_progress_cluster permet de suivre le déroulement du CLUSTER .

5.7.7 Maintenance : automatisation

  • Automatisation des tâches de maintenance
  • Cron sous Unix
  • Tâches planifiées sous Windows L’exécution des commandes VACUUM , ANALYZE et REINDEX peut se faire manuellement dans certains cas. Il est cependant préférable de mettre en place une exécution automatique de ces commandes. La plupart des administrateurs utilise cron sous Unix et les tâches planifiées sous Windows. pgAgent peut aussi être d’une aide précieuse pour la mise en place de ces opérations automatiques.

Peu importe l’outil. L’essentiel est que ces opérations soient réalisées et que le statut de leur exécution soit vérifié périodiquement. La fréquence d’exécution dépend principalement de la fréquence des modifications et suppressions pour le VACUUM et de la fréquence des insertions, modifications et suppressions pour l’ ANALYZE .

5.7.8 Maintenance : autovacuum

  • Automatisation par cron
    • simple, voire simpliste
  • Processus autovacuum
    • VACUUM / ANALYZE si nécessaire
    • Nombreux paramètres
    • Nécessite la récupération des statistiques d’activité L’automatisation du vacuum par cron est simple à mettre en place. Cependant, elle s’exécute pour toutes les tables, sans distinction. Que la table ait été modifiée plusieurs millions de fois ou pas du tout, elle sera traitée par le script. À l’inverse, l’autovacuum est un outil qui vérifie l’état des tables et, suivant le dépassement d’une limite, déclenche ou non l’exécution d’un VACUUM ou d’un ANALYZE , voire des deux. L’autovacuum est activé par défaut, et il est conseillé de le laisser ainsi. Son paramétrage permet d’aller assez loin si nécessaire selon la taille et l’activité des tables.

5.7.9 Maintenance : Script de REINDEX

  • Automatisation par cron
  • Recherche des index fragmentés
    • extension pgstattuple , fonction pgstatindex()
  • Si clé primaire ou contrainte unique :
    • REINDEX
  • Sinon :
    • CREATE INDEX CONCURRENTLY
  • Exemple Voici un script créé pour un client dans le but d’automatiser la réindexation uniquement pour les index le méritant. Pour cela, il vérifie les index fragmentés avec la fonction pgstatindex() de l’extension pgstattuple (installable avec un simple CREATE EXTENSION pgstattuple ; dans chaque base).

Au-delà de 30 % de fragmentation (par défaut), l’index est réindexé. Pour minimiser le risque de blocage, le script utilise CREATE INDEX CONCURRENTLY en priorité, et REINDEX dans les autres cas (clés primaires et contraintes uniques). REINDEX possède aussi l’option CONCURRENTLY . Ce script pourrait l’utiliser après avoir détecté qu’il se trouve sur une version compatible.

Exemple 5-253 Exemple extrait du PDF, page 349.
#!/bin/bash
# Script de réindexation d'une base
# ce script va récupérer la liste des index disponibles sur la base
# et réindexer l'index s'il est trop fragmenté ou invalide

# Mode debug
#set -x
  round(100-(pgstatindex(n.nspname||'.'||ic.relname)).avg_leaf_density)
    as \"Fragmentation\",
  pg_get_indexdef(i.indexrelid) as \"IndexDef\"
Tableau 5-62 Tableau ou résultat de commande extrait du PDF, page 349.
Exemple 5-254 Exemple extrait du PDF, page 349.
# Récupération de la base maintenance
if test -z "$PGDATABASE"; then
   export PGDATABASE=postgres
fi

# quelques constantes personnalisables
TAUX_FRAGMENTATION_MAX=30
NOM_INDEX_TEMPORAIRE=index_traitement_en_cours
NB_TESTS=3
BASES=""

# Quelques requêtes
REQ_LISTEBASES="SELECT array_to_string(array(
  SELECT datname
  FROM pg_database
  WHERE datallowconn AND datname NOT IN ('postgres', 'template1')), ' ')"
REQ_LISTEINDEX="
SELECT n.nspname as \"Schéma\", tc.relname as \"Table\", ic.relname as \"Index\",
  i.indexrelid as \"IndexOid\",
  i.indisprimary OR i.indisunique as \"Contrainte\", i.indisvalid as \"Valide?\",
FROM pg_index i
JOIN pg_class ic ON i.indexrelid=ic.oid
JOIN pg_class tc ON i.indrelid=tc.oid
JOIN pg_namespace n ON tc.relnamespace=n.oid
WHERE n.nspname <> 'pg_catalog'
  AND n.nspname !~ '^pg_toast'
ORDER BY ic.relname;"
Exemple 5-255 Exemple extrait du PDF, page 349.
# vérification de la liste des bases
if test $# -gt 1; then
  echo "Usage: $0 [nom_base]"
  exit 1
elif test $# -eq 1; then
  BASE_PRESENTE=$(psql -XAtqc \
  "SELECT count(*) FROM pg_database WHERE datname='$1'" 2>/dev/null)
  if test $BASE_PRESENTE -ne 1; then
    echo "La base de donnees $BASE n'existe pas."
    exit 2
  fi

Exemple 5-256 Exemple extrait du PDF, page 350.
   BASES=$1
else
   BASES=$(psql -XAtqc "$REQ_LISTEBASES" 2>/dev/null)
fi

# Pour chaque base
for BASE in $BASES
do
    # Afficher la base de données
    echo "########## $BASE ##########"

  # Vérification de la présence de la fonction pgstatindex
  FONCTION_PRESENTE=$(psql -XAtqc \
  "SELECT count(*) FROM pg_proc WHERE proname='pgstatindex'" $BASE 2>/dev/null)
  if test $FONCTION_PRESENTE -eq 0; then
    echo "La fonction pgstatindex n'existe pas."
    echo "Veuillez installer le module pgstattuple."
    exit 3
  fi

 # pour chaque index
 echo "Récupération de la liste des index (ratio cible $TAUX_FRAGMENTATION_MAX)..."
 psql -XAtF " " -c "$REQ_LISTEINDEX" $BASE | \
 while read schema table index indexoid contrainte validite fragmentation definition
 do
 # NaN (not a number) est possible si la table est vide
 # dans ce cas, une réindexation est rapide
   if test "$fragmentation" = "NaN"; then
      fragmentation=0
   fi

  #    afficher index, validité et fragmentation
        if test "$validite" = "t"; then
           chaine_validite="valide"
        else
           chaine_validite="invalide"
        fi
      echo "Index $index, $chaine_validite, ratio libre ${fragmentation}%"

  #  si index fragmenté ou non valide
      if test "$validite" = "f" -o $fragmentation -gt $TAUX_FRAGMENTATION_MAX; then
  # verifier les verrous sur l'index, attendre un peu si nécessaire
         verrous=1
         tests=0
         while test $verrous -gt 0 -a $tests -le $NB_TESTS
         do
      if test $tests -gt 0; then
         echo \
    " objet verrouillé, attente de $tests secondes avant nouvelle tentative..."
         sleep $tests
      fi
      verrous=$(psql -XAtqc \
      "SELECT count(*) FROM pg_locks WHERE relation=$indexoid" 2>/dev/null)
      tests=$(($tests + 1))
         done
      if test $verrous -gt 0; then
Exemple 5-257 Exemple extrait du PDF, page 351.
           echo " objet toujours verrouillé, pas de reindexation pour $schema.$index"
           continue
      fi

  #    si contrainte, reindexation simple
         if test "$contrainte" = "t"; then
           echo -n " reindexation de la contrainte... "
         psql -Xqc "REINDEX INDEX $schema.$index;" $BASE
         if test $? -eq 0; then
           echo "OK"
         else
           echo "PROBLEME!!"
           continue
         fi
  #    sinon
         else
  #    renommer <ancien nom> en <index_traitement_en_cours>
              echo -n " renommage... "
         psql -Xqc \
         "ALTER INDEX $schema.$index RENAME TO $NOM_INDEX_TEMPORAIRE;" $BASE
         if test $? -eq 0; then
           echo "OK"
         else
           echo "PROBLEME!!"
           continue
         fi
  #    create index <ancien nom>
              echo -n " création nouvel index..."
         psql -Xqc "$definition;" $BASE
  #    si create OK, drop index <index_traitement_en_cours>
         if test $? -eq 0; then
           echo "OK"
                echo -n " suppression ancien index..."
           psql -Xqc "DROP INDEX $schema.$NOM_INDEX_TEMPORAIRE;" $BASE
           if test $? -eq 0; then
              echo "OK"
           else
              echo "PROBLEME!!"
              continue
           fi
  #    sinon, renommer <index_traitement_en_cours> en <ancien nom>
         else
           echo "PROBLEME!!"
                echo -n " renommage inverse..."
           psql -Xqc \
           "ALTER INDEX $schema.$NOM_INDEX_TEMPORAIRE RENAME TO $index;" $BASE
           if test $? -eq 0; then
              echo "OK"
           else
              echo "PROBLEME!!"
              continue
           fi
         fi
      fi
    fi
  done
done

5.8 SÉCURITÉ

  • Ce qu’un utilisateur standard peut faire
    • et ne peut pas faire
  • Restreindre les droits
  • Chiffrement
  • Corruption de données À l’installation de PostgreSQL, il est essentiel de s’assurer de la sécurité du serveur : sécurité au niveau des accès, au niveau des objets, ainsi qu’au niveau des données. Ce chapitre va faire le point sur ce qu’un utilisateur peut faire par défaut et sur ce qu’il ne peut pas faire. Nous verrons ensuite comment restreindre les droits. Enfin, nous verrons les possibilités de chiffrement et de non-corruption de PostgreSQL.

5.8.1 Droits par défaut

Un utilisateur standard peut :

  • CONNECT : accéder à toutes les bases de données
  • CREATE :
    • créer des objets dans le schéma public de toute base de données
    • révocation fréquente
    • … mais plus en v15+ !
  • SELECT : voir les données de ses tables
  • INSERT , UPDATE , DELETE , TRUNCATE : les modifier
  • TEMP : créer des objets temporaires
  • CREATE , USAGE ON LANGUAGE : créer des fonctions
  • EXECUTE : exécuter des fonctions définies par d’autres dans le schéma public Par défaut, un utilisateur a beaucoup de droits. Il peut accéder à toutes les bases de données. Il faut modifier le fichier pg_hba.conf pour éviter cela. Il est aussi possible de supprimer ce droit avec l’ordre suivant :
    Exemple 5-258 Exemple extrait du PDF, page 352.
    REVOKE CONNECT ON DATABASE nom_base FROM nom_utilisateur ;
    Avant la version 15, l’utilisateur peut créer des objets dans le schéma disponible par défaut (nommé public ) sur chacune des bases de données où il peut se connecter, même si la base ne lui appartient pas. Il est assez courant de supprimer ce droit :
    Exemple 5-259 Exemple extrait du PDF, page 352.
    REVOKE CREATE ON SCHEMA public FROM nom_utilisateur ;

ou de manière globale :

Exemple 5-260 Exemple extrait du PDF, page 353.
REVOKE CREATE ON SCHEMA public FROM public ;
Certains DBA suppriment même le schéma de la base. Dans tous les cas, il faut faire attention à ce que le schéma ne réapparaisse pas lors d’une restauration de sauvegarde logique, car sa création est implicite. L’utilisateur peut créer des objets temporaires sur chacune des bases de données où il peut se connecter. Il est possible de supprimer ce droit avec l’ordre suivant :
Exemple 5-261 Exemple extrait du PDF, page 353.
REVOKE TEMP ON DATABASE nom_base FROM nom_utilisateur ;
L’utilisateur peut créer des fonctions, uniquement avec les langages de confiance, uniquement dans les schémas où il a le droit de créer des objets (donc dans le schéma public de toute base avant la V15). Il existe deux solutions :

  • supprimer le droit d’utiliser un langage :
    Exemple 5-262 Exemple extrait du PDF, page 353.
    REVOKE USAGE ON LANGUAGE nom_langage FROM nom_utilisateur ;
  • supprimer le droit de créer des objets dans un schéma :
    Exemple 5-263 Exemple extrait du PDF, page 353.
    REVOKE CREATE ON SCHEMA nom_schema FROM nom_utilisateur ;
    L’utilisateur peut exécuter n’importe quelle fonction, y compris définie par quelqu’un d’autre, à condition que la fonction soit dans un schéma où il a accès (dont le schéma public , par défaut) (et donc public , par défaut) Il est possible d’empêcher cela en supprimant le droit d’exécution d’une fonction :
    Exemple 5-264 Exemple extrait du PDF, page 353.
    REVOKE EXECUTE ON FUNCTION nom_fonction FROM nom_utilisateur ;

5.8.2 Droits par défaut (suite)

Un utilisateur standard peut aussi :

  • récupérer des informations sur l’instance
  • visualiser les sources des vues et des fonctions
  • Modifier des paramètres de la session :
    Exemple 5-265 Exemple extrait du PDF, page 353.
                        SET parametre TO valeur ;
                        SET LOCAL parametre TO valeur ;
                        SHOW parametre ;
    
                        PGOPTIONS='-c param=valeur' psql
  • Vue : pg_settings
  • Dans psql : \dconfig Il peut récupérer des informations sur l’instance car il a le droit de lire tous les catalogues systèmes. Par exemple, en lisant pg_class , il peut connaitre la liste des tables, vues, séquences, etc. En parcourant

pg_proc , il dispose de la liste des fonctions. Il n’y a pas de contournement à cela : un utilisateur doit pouvoir accéder aux catalogues systèmes pour travailler normalement. Il peut visualiser les sources des vues et des fonctions. Il existe des modules propriétaires de chiffrement (ou plutôt d’obfuscation) du code mais rien de natif. Le plus simple est certainement de coder les fonctions sensibles en C. Un utilisateur peut agir sur de nombreux paramètres au sein de sa session pour modifier les valeurs par défaut du postgresql.conf ou ceux imposés à son rôle ou à sa base. Un cas courant consiste à modifier la liste des schémas par défaut où chercher les tables :

Exemple 5-266 Exemple extrait du PDF, page 354.
SET search_path TO rh,admin,ventes,public ;
L’utilisateur peut aussi décider de s’octroyer plus de mémoire de tri :
Exemple 5-267 Exemple extrait du PDF, page 354.
SET work_mem TO '500MB' ;
Il est impossible d’interdire cela. Toutefois, cela permet de conserver un paramétrage par défaut prudent, tout en autorisant l’utilisation de plus de ressources quand cela s’avère nécessaire. Les exemples suivants modifient le fuseau horaire du client, désactivent la parallélisation le temps de la session, et changent le nom de l’applicatif visible dans les outils de supervision :
Exemple 5-268 Exemple extrait du PDF, page 354.
SET timezone TO GMT ;
SET max_parallel_workers_per_gather TO 0 ;
SET application_name TO 'batch_comptabilite' ;
Pour une session lancée en ligne de commande, pour les outils qui utilisent la libpq, on peut fixer les paramètres à l’appel grâce à la variable d’environnement PGOPTIONS :
Exemple 5-269 Exemple extrait du PDF, page 354.
PGOPTIONS="-c max_parallel_workers_per_gather=0 -c work_mem=4MB" psql < requete.sql
La valeur en cours est visible avec :
Exemple 5-270 Exemple extrait du PDF, page 354.
SHOW parametre ;
ou :
Exemple 5-271 Exemple extrait du PDF, page 354.
SELECT current_setting('parametre') ;
ou encore :
Exemple 5-272 Exemple extrait du PDF, page 354.
SELECT * FROM pg_settings WHERE name = 'parametre' ;
Sous psql , à partir de la version 15 du client, la métacommande \dconfig affiche les paramètres qui n’ont pas leur valeur par défaut. Ajouter un + pour afficher le type et le « contexte », ce dernier permettant de savoir si le paramètre peut être changé dans la session ( user ), s’il faut recharger la configuration ( sighup ), redémarrer ( postmaster ), etc.
Exemple 5-273 Exemple extrait du PDF, page 354.
# \dconfig+
                                         Liste des paramètres de configuration à valeur
      personnalisée
             Paramètre               |                        Valeur                             |   Type
      |     Contexte         | D…
Tableau 5-63 Tableau ou résultat de commande extrait du PDF, page 354.

-----------------------------+-------------------------------------------------+----
    -----+-------------------+----
 application_name              | psql                                          | string
    | user               |
 client_encoding               | UTF8                                          | string
    | user               |
 cluster_name                  | 17/defo                                       | string
    | postmaster         |
 config_file                    | /etc/postgresql/17/defo/postgresql.conf         |
    string | postmaster           |
 data_directory                 | /var/lib/postgresql/17/defo                     |
    string | postmaster           |
 DateStyle                    | ISO, DMY                                       | string
    | user               |
 default_text_search_config | pg_catalog.french                                   |
    string | user                 |
 external_pid_file              | /var/run/postgresql/17-defo.pid                 |
    string | postmaster           |
 hba_file                       | /etc/postgresql/17/defo/pg_hba.conf             |
    string | postmaster           |
 ident_file                     | /etc/postgresql/17/defo/pg_ident.conf           |
    string | postmaster           |
 lc_messages                   | C                                             | string
    | superuser          |
 lc_monetary                  | fr_FR.UTF-8                                    | string
    | user               |
 lc_numeric                   | fr_FR.UTF-8                                    | string
    | user               |
 lc_time                      | fr_FR.UTF-8                                    | string
    | user               |
 log_autovacuum_min_duration | 0                                                  |
    integer | sighup              |
 log_connections                | on                                             | bool
    | superuser-backend |
 log_line_prefix                | %t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h |
    string | sighup               |
 log_replication_commands       | on                                             | bool
    | superuser          |
 log_timezone                 | Europe/Paris                                   | string
    | sighup             |
 max_prepared_transactions      | 10                                              |
    integer | postmaster          |
 port                         | 17001                                         | integer
    | postmaster         |
 shared_preload_libraries       | pg_stat_statements                              |
    string | postmaster           |
 ssl                            | on                                             | bool
    | sighup             |
 ssl_cert_file                  | /etc/ssl/certs/ssl-cert-snakeoil.pem            |
    string | sighup               |
 ssl_key_file                   | /etc/ssl/private/ssl-cert-snakeoil.key          |
    string | sighup               |
 TimeZone                     | Europe/Paris                                   | string
    | user               |
 work_mem                     | 100MB                                         | integer
    | user               |
Tableau 5-64 Tableau ou résultat de commande extrait du PDF, page 355.

L’effet d’un ordre SET est limité à la session en cours, et disparaît avec elle à la déconnexion, ou si l’on demande un retour à la valeur par défaut :

Exemple 5-274 Exemple extrait du PDF, page 356.
RESET parametre ;
Enfin, on peut n’appliquer des paramètres que le temps d’une transaction, c’est-à-dire jusqu’au prochain COMMIT ou ROLLBACK :
Exemple 5-275 Exemple extrait du PDF, page 356.
SET LOCAL work_mem TO '100MB' ;
De nombreux paramètres sont cependant non modifiables, ou réservés aux superutilisateurs.

5.8.3 Droits par défaut (suite)

  • Un utilisateur standard ne peut pas :
    • créer une base
    • créer un rôle
    • accéder au contenu des objets créés par d’autres
    • modifier le contenu d’objets créés par d’autres Un utilisateur standard ne peut pas créer de bases et de rôles. Il a besoin pour cela d’attributs particuliers (respectivement CREATEDB et CREATEROLE ). Il ne peut pas accéder au contenu (aux données) d’objets créés par d’autres utilisateurs. Ces derniers doivent lui donner ce droit explicitement : GRANT USAGE ON SCHEMA secret TO utilisateur ; pour lire un schéma, ou GRANT SELECT ON TABLE matable TO utilisateur ; pour lire une table. De même, il ne peut pas modifier le contenu et la définition d’objets créés par d’autres utilisateurs. Làaussi, ce droit doit être donné explicitement : GRANT INSERT,DELETE,UPDATE,TRUNCATE ON TABLE matable TO util Il existe d’autres droits plus rares, dont :
  • GRANT TRIGGER ON TABLE … autorise la création de trigger;
  • GRANT REFERENCES ON TABLE … autorise la création d’une clé étrangère pointant vers cette table (ce qui est interdit par défaut car cela interdit au propriétaire de supprimer ou modifier des lignes, entre autres);
  • GRANT USAGE ON SEQUENCE… autorise l’utilisation d’une séquence. Par facilité, on peut octroyer des droits en masse :
  • GRANT ALL PRIVILEGES ON TABLE matable TO utilisateur ;
  • GRANT SELECT ON TABLE matable TO public ;
  • GRANT SELECT ON ALL TABLES IN SCHEMA monschema ;

5.8.4 Restreindre les droits

  • Sur les connexions
  • pg_hba.conf
  • Sur les objets
  • GRANT / REVOKE
  • SECURITY LABEL
  • Sur les fonctions
  • SECURITY DEFINER
  • LEAKPROOF
  • Sur les vues
  • security_barrier
  • WITH CHECK OPTION Pour sécuriser plus fortement une instance, il est nécessaire de restreindre les droits des utilisateurs. Connexions : Cela commence par la gestion des connexions. Les droits de connexion sont généralement gérés via le fichier de configuration pg_hba.conf . Cette configuration a déjà été abordée dans le chapitre Droits de connexion de ce module de formation. GRANT & REVOKE : Cela passe ensuite par les droits sur les objets. On dispose pour cela des instructions GRANT et REVOKE , qui ont été expliquées dans le chapitre Droits sur les objets de ce module de formation. SECURITY LABEL : Il est possible d’aller plus loin avec l’instruction SECURITY LABEL . Un label de sécurité est un commentaire supplémentaire pris en compte par un module de sécurité qui disposera de la politique de sécurité. Le seul module de sécurité actuellement disponible est un module contrib pour l’intégration à SELinux, appelé sepgsql 17 . SECURITY DEFINER & LEAKPROOF : Certains objets disposent de droits particuliers. Par exemple, les fonctions disposent des clauses SECURITY DEFINER et LEAKPROOF . SECURITY DEFINER indique au système que la fonction doit s’exécuter avec les droits de son propriétaire (et non pas avec ceux de l’utilisateur l’exécutant). Cela permet d’éviter de donner des droits d’accès à certains objets. LEAKPROOF permet de dire à PostgreSQL que cette fonction ne peut pas occasionner de fuites d’informations. Ainsi, le planificateur de PostgreSQL sait qu’il peut optimiser l’exécution des fonctions. 17 https://docs.postgresql.fr/current/sepgsql.html

Vues avec security_barrier : Quant aux vues, elles disposent d’une option appelée security_barrier . Certains utilisateurs créent des vues pour filtrer des lignes, afin de restreindre la visibilité sur certaines données. Or, cela peut se révéler dangereux si un utilisateur malintentionné a la possibilité de créer une fonction car il peut facilement contourner cette sécurité si cette option n’est pas utilisée. Voici un exemple complet :

Exemple 5-276 Exemple extrait du PDF, page 358.
CREATE TABLE elements (id int, contenu text, prive boolean);

CREATE TABLE

INSERT INTO elements (id, contenu, prive)
VALUES (1, 'a', false), (2, 'b', false), (3, 'c super prive', true),
       (4, 'd', false), (5, 'e prive aussi', true) ;
INSERT 0 5
Exemple 5-277 Exemple extrait du PDF, page 358.
SELECT * FROM elements ;
 id |    contenu    | prive
----+---------------+-------
  1 | a             | f
  2 | b             | f
  3 | c super prive | t
  4 | d             | f
  5 | e prive aussi | t
Tableau 5-65 Tableau ou résultat de commande extrait du PDF, page 358.
La table elements contient cinq lignes, dont deux considérées comme privées. Nous allons donc créer une vue ne permettant de voir que les lignes publiques, sauf pour un utilisateur particulier :
Exemple 5-278 Exemple extrait du PDF, page 358.
CREATE OR REPLACE VIEW elements_public AS
  SELECT * FROM elements
  WHERE CASE WHEN current_user = 'guillaume' THEN TRUE
            ELSE NOT prive
        END ;
CREATE VIEW Ce nouvel utilisateur ne verra donc pas les lignes privées masquées par la vue :
Exemple 5-279 Exemple extrait du PDF, page 358.
CREATE USER u1;
CREATE ROLE
Exemple 5-280 Exemple extrait du PDF, page 358.
GRANT SELECT ON elements_public TO u1;
GRANT
Exemple 5-281 Exemple extrait du PDF, page 358.
\c - u1
You are now connected to database “demo” as user “u1”.
Exemple 5-282 Exemple extrait du PDF, page 358.
SELECT * FROM elements ;

ERROR:    permission denied for relation elements

SELECT * FROM elements_public ;

 id | contenu | prive
----+---------+-------
  1 | a       | f
  2 | b       | f
  4 | d       | f
Tableau 5-66 Tableau ou résultat de commande extrait du PDF, page 359.
L’utilisateur u1 n’a pas le droit de lire directement la table elements mais a le droit d’y accéder via la vue elements_public , uniquement pour les lignes dont le champ prive est à false . Avec une simple fonction, u1 peut contourner le problème. (Il devra toutefois avoir le droit d’écrire dans un schéma de la base, ce qui est par défaut possible dans public avant la version 15.)
Exemple 5-283 Exemple extrait du PDF, page 359.
CREATE SCHEMA schemau1;
CREATE OR REPLACE FUNCTION schemau1.abracadabra (integer, text, boolean)
RETURNS bool AS $$
BEGIN
  RAISE NOTICE '% - % - %', $1, $2, $3 ;
  RETURN true;
END$$ LANGUAGE plpgsql
COST 0.0000000000000000000001 ;
Exemple 5-284 Exemple extrait du PDF, page 359.
CREATE SCHEMA
CREATE FUNCTION
Exemple 5-285 Exemple extrait du PDF, page 359.
SELECT * FROM elements_public
WHERE schemau1.abracadabra(id, contenu, prive) ;

NOTICE: 1 - a - f
NOTICE: 2 - b - f
NOTICE: 3 - c super prive - t
NOTICE: 4 - d - f
NOTICE: 5 - e prive aussi - t
 id | contenu | prive
----+---------+-------
  1 | a       | f
  2 | b       | f
  4 | d       | f
Tableau 5-67 Tableau ou résultat de commande extrait du PDF, page 359.
Les lignes secrètes ne font toujours pas partie du résultat, mais la fonction est capable d’y accéder, et de les afficher. Que s’est-il passé? Pour comprendre, il suffit de regarder le plan d’exécution de cette requête : EXPLAIN SELECT * FROM elements_public
Exemple 5-286 Exemple extrait du PDF, page 359.
WHERE schemau1.abracadabra(id, contenu, prive) ;
QUERY PLAN
------------------------------------------------------------
 Seq Scan on elements (cost=0.00..28.75 rows=208 width=37)
   Filter: (schemau1.abracadabra(id, contenu, prive)
           AND CASE WHEN (CURRENT_USER = 'guillaume'::name) THEN true ELSE (NOT
   prive) END)
Tableau 5-68 Tableau ou résultat de commande extrait du PDF, page 359.
La fonction abracadrabra a un coût délibérément si faible que PostgreSQL l’exécute avant le filtre de la vue : la fonction voit toutes les lignes de la table. L’option security_barrier permet d’interdire cette optimisation au planificateur :

Exemple 5-287 Exemple extrait du PDF, page 360.
\c - postgres
You are now connected to database “demo” as user “postgres”.
Exemple 5-288 Exemple extrait du PDF, page 360.
ALTER VIEW elements_public SET (security_barrier);
ALTER VIEW
Exemple 5-289 Exemple extrait du PDF, page 360.
\c - u1
You are now connected to database “demo” as user “u1”.
Exemple 5-290 Exemple extrait du PDF, page 360.
SELECT * FROM elements_public WHEREschemau1.abracadabra(id, contenu, prive);

NOTICE: 1 - a - f
NOTICE: 2 - b - f
NOTICE: 4 - d - f
 id | contenu | prive
----+---------+-------
  1 | a       | f
  2 | b       | f
  4 | d       | f
Tableau 5-69 Tableau ou résultat de commande extrait du PDF, page 360.
La fonction n’affiche plus les données privées. Son plan montre qu’elle filtre par la vue, puis appelle la fonction : EXPLAIN SELECT * FROM elements_public
Exemple 5-291 Exemple extrait du PDF, page 360.
WHERE schemau1.abracadabra(id, contenu, prive);
QUERY PLAN
----------------------------------------------------------------------------------
 Subquery Scan on elements_public (cost=0.00..35.00 rows=208 width=37)
   Filter: schemau1.abracadabra(elements_public.id, elements_public.contenu,
   elements_public.prive)
   -> Seq Scan on elements (cost=0.00..28.75 rows=625 width=37)
         Filter: CASE WHEN (CURRENT_USER = 'guillaume'::name) THEN true ELSE (NOT
   prive) END
Tableau 5-70 Tableau ou résultat de commande extrait du PDF, page 360.
Noter que security_barrier peut avoir un effet négatif sur les performances puisqu’il interdit de « pousser » des critères de recherche dans la vue. Voir aussi cet article, par Robert Haas 18 . Vue WITH CHECK OPTION : Écrire à travers une vue permet de modifier les lignes même si la nouvelle valeur les lui rend inaccessibles : UPDATE elements_public
Exemple 5-292 Exemple extrait du PDF, page 360.
SET prive = true
WHERE id = 1 ;
UPDATE 1
Exemple 5-293 Exemple extrait du PDF, page 360.
SELECT * FROM elements_public ;
 id | contenu | prive
----+---------+-------
  2 | b       | f
  4 | d       | f
Tableau 5-71 Tableau ou résultat de commande extrait du PDF, page 360.
18 https://rhaas.blogspot.com/2012/03/security-barrier-views.html

Pour bloquer ce comportement, la vue peut porter l’option WITH CHECK OPTION : CREATE OR REPLACE VIEW elements_public

Exemple 5-294 Exemple extrait du PDF, page 361.
WITH (security_barrier)
AS
Exemple 5-295 Exemple extrait du PDF, page 361.
   SELECT * FROM elements
   WHERE CASE WHEN current_user = 'guillaume'
   THEN true ELSE NOT prive END
WITH CHECK OPTION ;

\c - u1
You are now connected to database “demo” as user “u1”. UPDATE elements_public
Exemple 5-296 Exemple extrait du PDF, page 361.
SET prive = true
WHERE id = 2 ;

ERROR: new row violates check option for view "elements_public"
DETAIL: Failing row contains (prive) = (t).

5.8.5 Arrêter une requête ou une session

  • Annuler une requête
    • pg_cancel_backend (pid int)
  • Fermer une connexion
    • pg_terminate_backend(pid int, timeout bigint)
    • kill -SIGTERM pid , kill -15 pid (éviter)
  • Jamais kill -9 !! Les fonctions pg_cancel_backend et pg_terminate_backend sont le plus souvent utilisées. Le paramètre est le numéro du processus auprès de l’OS. pg_terminate_backend comprend un deuxième argument, dont la valeur par défaut est 0. Si cet argument n’est pas indiqué ou vaut 0, la fonction renvoie le booléen true si elle a réussi à envoyer le signal. Ce résultat n’indique donc pas la bonne terminaison du processus serveur visé. À une valeur supérieure à 0, la fonction attend que le processus visé soit arrêté. S’il ne s’est pas arrêté dans le temps indiqué par cette valeur (en millisecondes), la valeur false est renvoyée avec un message de niveau WARNING . La première permet d’annuler une requête en cours d’exécution. Elle requiert un argument, à savoir le numéro du PID du processus postgres exécutant cette requête. Généralement, l’annulation est immédiate. Voici un exemple de son utilisation. L’utilisateur, connecté au processus de PID 10901 comme l’indique la fonction pg_backend_pid , exécute une très grosse insertion :
    Exemple 5-297 Exemple extrait du PDF, page 361.
    b1=# SELECT pg_backend_pid();

pg_backend_pid

----------------
          10901
Tableau 5-72 Tableau ou résultat de commande extrait du PDF, page 362.
b1=# INSERT INTO t4 SELECT i, 'Ligne '||i
Tableau 5-73 Tableau ou résultat de commande extrait du PDF, page 362.
Exemple 5-298 Exemple extrait du PDF, page 362.
FROM generate_series(2000001, 3000000) AS i;
Supposons qu’on veuille annuler l’exécution de cette requête. Voici comment faire à partir d’une autre connexion :
Exemple 5-299 Exemple extrait du PDF, page 362.
b1=# SELECT pg_cancel_backend(10901);
pg_cancel_backend
-------------------
 t
Tableau 5-74 Tableau ou résultat de commande extrait du PDF, page 362.
L’utilisateur qui a lancé la requête d’insertion verra ce message apparaître :
Exemple 5-300 Exemple extrait du PDF, page 362.
ERROR:    canceling statement due to user request
Si la requête du INSERT faisait partie d’une transaction, la transaction elle-même devra se conclure par un ROLLBACK à cause de l’erreur. À noter cependant qu’il n’est pas possible d’annuler une transaction qui n’exécute rien à ce moment. En conséquence, pg_cancel_backend ne suffit pas pour parer à une session en statut idle in transaction . Il est possible d’aller plus loin en supprimant la connexion d’un utilisateur. Cela se fait avec la fonction pg_terminate_backend qui se manie de la même manière :
Exemple 5-301 Exemple extrait du PDF, page 362.
b1=# SELECT pid, datname, usename, application_name, state
     FROM pg_stat_activity WHERE backend_type = 'client backend';
 procpid | datname | usename | application_name | state
---------+---------+-----------+------------------+--------
   13267 | b1      | u1        | psql             | idle
   10901 | b1      | guillaume | psql             | active
Tableau 5-75 Tableau ou résultat de commande extrait du PDF, page 362.
Exemple 5-302 Exemple extrait du PDF, page 362.
b1=# SELECT pg_terminate_backend(13267);
pg_terminate_backend
----------------------
 t
Tableau 5-76 Tableau ou résultat de commande extrait du PDF, page 362.
Exemple 5-303 Exemple extrait du PDF, page 362.
b1=# SELECT pid, datname, usename, application_name, state
     FROM pg_stat_activity WHERE backend_type = 'client backend';
 procpid | datname | usename | application_name | state
---------+---------+-----------+------------------+--------
   10901 | b1      | guillaume | psql             | active
Tableau 5-77 Tableau ou résultat de commande extrait du PDF, page 362.
L’utilisateur de la session supprimée verra un message d’erreur au prochain ordre qu’il enverra. psql se reconnecte automatiquement mais cela n’est pas forcément le cas d’autres outils client.
Exemple 5-304 Exemple extrait du PDF, page 362.
b1=# select 1 ;

FATAL: terminating connection due to administrator command la connexion au serveur a été coupée de façon inattendue Le serveur s‘est peut-être arrêté anormalement avant ou durant le traitement de la requête. La connexion au serveur a été perdue. Tentative de réinitialisation : Succès. Temps : 7,309 ms Depuis la ligne de commande du serveur, un kill <pid> (c’est-à-dire kill -SIGTERM ou kill -15 ) a le même effet qu’un SELECT pg_terminate_backend (<pid>) . Cette méthode n’est pas recommandée car il n’y a pas de vérification que vous tuez bien un processus postgres. N’utilisez jamais kill -9 <pid> (ou kill -SIGKILL ), ou (sous Windows) taskkill /f /pid <pid> pour tuer une connexion : l’arrêt est alors brutal, et le processus principal n’a aucun moyen de savoir pourquoi. Pour éviter une corruption de la mémoire partagée, il va arrêter et redémarrer immédiatement tous les processus, déconnectant tous les utilisateurs au passage! L’utilisation de pg_terminate_backend et pg_cancel_backend n’est disponible que pour les utilisateurs appartenant au même rôle que l’utilisateur à déconnecter, les utilisateurs membres du rôle pg_signal_backend (à partir de la 9.6) et bien sûr les superutilisateurs.

5.8.6 Chiffrements

  • Connexions
    • SSL
    • avec ou sans certificats serveur et/ou client
  • Données disques
    • pas en natif
    • par colonne : pgcrypto Par défaut, les sessions ne sont pas chiffrées. Les requêtes et les données passent donc en clair sur le réseau. Il est possible de les chiffrer avec SSL, ou avec GSSAPI si l’authentification passe par Kerberos, ce qui aura une conséquence négative sur les performances. Il est aussi possible d’utiliser les certificats (au niveau serveur et/ou client) pour augmenter encore la sécurité des connexions. PostgreSQL ne chiffre pas les données sur disque. Si l’instance complète doit être chiffrée, il est conseillé d’utiliser un système de fichiers qui propose cette fonctionnalité. Attention au fait que cela ne vous protège que contre la récupération des données sur un disque non monté. Quand le disque est monté, les données sont lisibles suivant les règles d’accès d’Unix. Néanmoins, il existe un module contrib appelé pgcrypto, permettant d’accéder à des fonctions de chiffrement et de hachage. Cela permet de protéger les informations provenant de colonnes spécifiques. Le chiffrement se fait du côté serveur, ce qui sous-entend que l’information est envoyée en clair sur le réseau. Le chiffrement SSL est donc obligatoire dans ce cas.

5.8.7 En cas de crash

  • Ça arrive
  • Redémarrage sans souci, en général
  • Quelques statistiques d’activité sont perdues
    • ANALYZE (voire VACUUM )
  • Et analyser la cause Même si PostgreSQL est conçu pour être le plus robuste possible, un arrêt brutal est toujours possible, pour des causes externes (perte de courant, plantage du système d’exploitation, corruption de fichier sur le disque…), liées à l’utilisation de PostgreSQL (requêtes menant à une saturation mémoire sur un système mal paramétré…) ou exceptionnellement internes (extension buggée, bug de PostgreSQL). Dans l’écrasante majorité des cas, le système de journalisation de PostgreSQL vous permettra de redémarrer sans perte de données (sinon ce qui n’a pas été committé). Le rejeu des journaux est souvent si rapide qu’un redémarrage peut passer inaperçu. Malheureusement, les statistiques d’activités sont perdues lors d’un arrêt brutal (ou d’un arrêt en mode immediate, ou d’une restauration physique). Cela peut provoquer par la suite un retard dans le déclenchement de l’autovacuum et la mise à jour des statistiques. Pour éviter cela, le DBA doit penser à relancer un ordre ANALYZE , ainsi que, dans l’idéal, un ordre VACUUM . Surtout, il faut comprendre quelle est la cause pour que le problème ne se reproduise pas.

5.8.8 Corruption de données

  • initdb –data-checksums
    • option par défaut à partir de la v18
  • Détecte les corruptions silencieuses
  • Impact faible sur les performances
  • Fortement conseillé! PostgreSQL ne verrouille pas tous les fichiers dès son ouverture. Sans mécanisme de sécurité, il est donc possible de modifier un fichier sans que PostgreSQL s’en rende compte, ce qui aboutit à une corruption silencieuse. L’apparition des sommes de contrôles (checksums) permet de se prémunir contre des corruptions silencieuses de données. Leur mise en place est fortement recommandée sur une nouvelle instance. À titre d’exemple, créons une instance sans utiliser les checksums, et une autre qui les utilisera :
    Exemple 5-305 Exemple extrait du PDF, page 364.
    $ initdb --pgdata /tmp/sans_checksums/ --no-data-checksums
    $ initdb --pgdata /tmp/avec_checksums/ --data-checksums
    

Insérons une valeur de test, sur chacune des deux instances :

Exemple 5-306 Exemple extrait du PDF, page 365.
postgres=# CREATE TABLE test (name text);

CREATE TABLE

postgres=# INSERT INTO test (name) VALUES ('toto');
INSERT 0 1
Exemple 5-307 Exemple extrait du PDF, page 365.
On récupère le chemin du fichier de la table pour aller le corrompre à la main (seul celui sans checksums est montré en exemple).
postgres=# SELECT pg_relation_filepath('test');
pg_relation_filepath
----------------------
 base/14415/16384
Tableau 5-78 Tableau ou résultat de commande extrait du PDF, page 365.
Instance arrêtée (pour ne pas être gêné par le cache), on va s’attacher à corrompre ce fichier, en remplaçant la valeur « toto » par « goto » avec un éditeur hexadécimal :
Exemple 5-308 Exemple extrait du PDF, page 365.
$ hexedit /tmp/sans_checksums/base/base/14415/16384
Enfin, on peut ensuite exécuter des requêtes sur ces deux instances. Sans checksums :
Exemple 5-309 Exemple extrait du PDF, page 365.
postgres=# TABLE test;
name
------
 goto
Tableau 5-79 Tableau ou résultat de commande extrait du PDF, page 365.
Avec checksums :
Exemple 5-310 Exemple extrait du PDF, page 365.
postgres=# TABLE test;

WARNING:   page verification failed, calculated checksum 29393
           but expected 24228
ERROR:   invalid page in block 0 of relation base/14415/16384
L’outil pg_checksums permet de vérifier une instance complète :
Exemple 5-311 Exemple extrait du PDF, page 365.
$ pg_checksums -D /tmp/avec_checksums
pg_checksums: error: checksum verification failed in file “/tmp/avec_checksums/base/14415/16384”, block 0: calculated checksum 72D1 but block contains 5EA4 Checksum operation completed Files scanned: 919 Blocks scanned: 3089 Bad checksums: 1 Data checksum version: 1 En pratique, depuis PostgreSQL 9.5 et si votre processeur supporte les instructions SSE 4.2 (ce qui est le cas de tous les processeurs Intel/AMD récents, voir dans /proc/cpuinfo ), il n’y aura pas d’impact notable en performances. Par contre vous générerez un peu plus de journaux.

L’outil pg_checksums permet aussi d’activer (ou désactiver) la gestion des sommes de contrôle, instance arrêtée. Les sommes de contrôle sont activées par défaut depuis PostgreSQL 18, mais il reste de nombreuses instances où l’utilisateur n’a pas pensé à les activer, et qui seront migrées en PostgreSQL 18 et plus. Pensez toujours à vérifier la présence des sommes de contrôle sur une instance.

5.9 CONCLUSION

  • PostgreSQL demande peu de travail au quotidien
  • À l’installation :
    • veiller aux accès et aux droits
    • mettre la maintenance en place
  • Pour le reste, surveiller :
    • les scripts automatisés
    • le contenu des journaux applicatifs
  • Supervisez le serveur! Pour aller plus loin :
  • La documentation officielle est toujours une bonne lecture :
  • Planifier les tâches de maintenance 19
  • Catalogues système 20
  • Managing rights in PostgreSQL, from the basics to SE PostgreSQL 21 , Nicolas Thauvin, 2011

5.9.1 Questions

N’hésitez pas, c’est le moment ! 19 https://docs.postgresql.fr/current/maintenance.html

20 https://docs.postgresql.fr/current/catalogs.html

21 https://wiki.postgresql.org/images/d/d1/Managing_rights_in_postgresql.pdf

5.10 QUIZ

https://dali.bo/f_quiz

5.11 TRAVAUX PRATIQUES

La version en ligne des solutions de ces TP est disponible sur https://dali.bo/f_solutions.

5.11.1 Traces maximales

But : suivre toutes les requêtes dans les traces À titre pédagogique et pour alimenter un rapport pgBadger plus tard, toutes les requêtes vont être tracées. Dans postgresql.conf , positionner ceci :

Exemple 5-312 Exemple extrait du PDF, page 369.
  log_min_duration_statement = 0
  log_temp_files = 0
  log_autovacuum_min_duration = 0
  lc_messages = 'C'
  log_line_prefix = '%t [%p]: db=%d,user=%u,app=%a,client=%h '
Puis passer à on les paramètres suivants s’ils ne le sont pas déjà : log_checkpoints log_connections log_disconnections log_lock_waits Recharger la configuration. Laisser une fenêtre ouverte pour voir défiler le contenu des traces.

5.11.2 Méthode d’authentification

But : Gérer les rôles et les permissions Activer la méthode d’authentification scram-sha-256 dans postgresql.conf si elle n’est pas déjà en place.

Consulter les droits définis dans pg_hba.conf au travers de la vue système pg_hba_file_rules . Dans pg_hba.conf , supprimer les accès avec la méthode trust pouvant rester après les précédents exercices. Vérifier dans la vue avant de recharger la configuration.

5.11.3 Création des bases

But : Créer des bases appartenant à un utilisateur non privilégié Créer un utilisateur nommé testperf avec attribut LOGIN . Créer une base pgbench lui appartenant. Vérifier que la connexion ne marche pas encore depuis votre compte habituel, y compris avec -h localhost . Créer un rôle patron avec attribut LOGIN , et une base entreprise lui appartenant.

5.11.4 Mots de passe

But : Mise en place de l’authentification par mot de passe Créer des mots de passe pour les rôles patron et testperf. Consulter la table pg_authid pour voir la version chiffrée. Pour ouvrir les accès :

  • ajuster pg_hba.conf pour permettre l’accès à la base pgbench uniquement à l’utilisateur testperf, permettre l’accès à la base entreprise à tout utilisateur, en local avec son mot de passe, en authentification scram-sha-256 ;

  • vérifier avec la vue pg_hba_file_rules ;

  • recharger la configuration;

  • tester la connexion. Créer un fichier .pgpass dans votre répertoire utilisateur ( /home/dalibo ) ou dans celui de l’utilisateur postgres ( /var/lib/pgsql ) pour qu’il puisse se connecter aux bases entreprise et

    Exemple 5-313 Exemple extrait du PDF, page 371.
      pgbench sans entrer le mot de passe.
    Le superutilisateur PostgreSQL postgres peut-il se connecter aux bases entreprise et pgbench? Si non, comment lui permettre? Remplir la base pgbench et générer un peu d’activité :
    Exemple 5-314 Exemple extrait du PDF, page 371.
      /usr/pgsql-17/bin/pgbench -i -s1 --foreign-keys pgbench -U testperf
      /usr/pgsql-17/bin/pgbench -P1 -T3 pgbench -U testperf
    
    Aucun mot de passe ne doit être demandé, avec ou sans -h localhost . Compléter pg_ident.conf pour pouvoir se connecter au rôle postgres depuis le compte système habituel (dalibo), en local . Tester la connexion depuis postgres au rôle postgres, de dalibo à postgres.

5.11.5 Rôles et permissions

But : Gérer les rôles et les permissions sur les tables Sous les utilisateurs dalibo comme postgres, créer un nouveau fichier ~/.psqlrc contenant \set PROMPT1 ‘%n@%/%R%# ’ pour que l’invite indique quels sont les utilisateur et base en cours. Ajouter à la base de données entreprise la table facture (id int, objet text, creationts timestamp) . Elle appartiendra à patron, administrateur de la base. Créer un rôle secretariat sans droit de connexion, mais qui peut visualiser, ajouter, mettre à jour et supprimer des éléments de la table facture .

Créer un rôle boulier qui peut se connecter et appartient au rôle secretariat, avec un mot de passe (à ajouter au .pgpass ). Vérifier la création des deux rôles. En tant que boulier, créer une table brouillon identique à facture . Dans quel schéma cela se passe-t-il? Avec PostgreSQL 15 ou supérieur, il y a une étape supplémentaire. Vérifier les tables présentes et les droits \dp . Comment les lire? À l’aide du rôle boulier : insérer 2 factures ayant pour objet « Vin de Bordeaux » et « Vin de Bourgogne » avec la date et l’heure courante. Afficher le contenu de la table facture . Mettre à jour la deuxième facture avec la date et l’heure courante. Supprimer la première facture. Retirer les droits DELETE sur la table facture au rôle secretariat. Vérifier qu’il n’est plus possible de supprimer la deuxième facture avec le rôle boulier. En tant que patron, créer une table produit contenant une colonne texte nommée appellation et la remplir avec des noms de boissons. Afficher les droits sur cette table avec \dt et \dp . Vérifier que le rôle boulier appartenant au rôle secretariat ne peut pas sélectionner les produits contenus dans la table produit . Retirer tous les droits pour le groupe secretariat sur la table produit . Que deviennent les droits affichés ? boulier peut-il lire la table?

Autoriser l’utilisateur boulier à accéder à la table produit en lecture. Vérifier que boulier peut désormais accéder à la table produit . Créer un rôle tina appartenant au rôle secretariat, avec l’attribut LOGIN , mais n’héritant pas des droits à la connexion. Vérifier les droits avec \du . Lui donner un mot de passe. Vérifier que tina ne peut pas accéder à la table facture . En tant que tina, activer le rôle secretariat ( SET ROLE ). Vérifier que tina possède maintenant les droits du rôle secretariat. Sélectionner les données de la table facture .

5.11.6 Autorisation d’accès distant

But : Mettre en place les accès dans pg_hba.conf . Autoriser tous les membres du réseau local à se connecter avec un mot de passe (autorisation en IP sans SSL) avec les utilisateurs boulier ou tina. Tester avec l’IP du serveur avant de demander aux voisins de tester.

5.11.7 VACUUM, VACUUM FULL, DELETE, TRUNCATE

But : Effacer des données, distinguer VACUUM et VACUUM FULL . Désactiver le démon autovacuum de l’instance. Se connecter à la base pgbench en tant que testperf.

Grâce aux fonctions pg_relation_size et pg_size_pretty , afficher la taille de la table pgbench_accounts . Copier le contenu de la table dans une nouvelle table ( pba_copie ). Supprimer le contenu de la table pba_copie , à l’exception de la dernière ligne ( aid=100000 ), avec un ordre DELETE . Quel est alors l’espace disque utilisé par cette table? Insérer le contenu de la table pgbench_accounts dans la table pba_copie . Quel est alors l’espace disque utilisé par la table? Effectuer un VACUUM simple sur pba_copie . Vérifier la taille de la base. Vider à nouveau la table pba_copie des lignes d’ aid inférieur à 100 000. Insérer à nouveau le contenu de la table pgbench_accounts . L’espace mis à disposition a-t-il été utilisé ? Voir la taille de la base. Supprimer la table pba_copie . Voir l’impact sur la taille de la base. Tout d’abord, repérer les tailles des différentes tables et le nombre de lignes de chacune. Pour amplifier le phénomène à observer, on peut créer une session de très longue durée, laissée ouverte sans COMMIT ni ROLLBACK . Il faut qu’elle ait consulté une des tables pour que l’effet soit visible :

Exemple 5-315 Exemple extrait du PDF, page 374.
  testperf@pgbench=> BEGIN ;
BEGIN Temps : 0,608 ms
Exemple 5-316 Exemple extrait du PDF, page 374.
  testperf@pgbench=> SELECT count(*) FROM pgbench_accounts ;
count
  --------
   100000
  (1 ligne)
Tableau 5-80 Tableau ou résultat de commande extrait du PDF, page 374.
Temps : 26,059 ms
Exemple 5-317 Exemple extrait du PDF, page 374.
  testperf@pgbench=> SELECT pg_sleep (10000) ;

Depuis un autre terminal, générer de l’activité sur la table, ici avec 10 000 transactions sur 20 clients :

Exemple 5-318 Exemple extrait du PDF, page 375.
  PGOPTIONS='-c synchronous_commit=off' \
  /usr/pgsql-17/bin/pgbench -U testperf -d pgbench \
  --client=20 --jobs=2 -t 10000 --no-vacuum
(NB : La variable d’environnement PGOPTIONS restreint l’utilisation des journaux de transaction pour accélérer les écritures (données NON critiques ici). Le –no-vacuum est destiné à éviter que l’outil demande lui-même un VACUUM . Le test dure quelques minutes. Le relancer au besoin.) (Optionnel) C’est l’occasion d’installer l’outil pg_activity depuis les dépôts du PGDG (il peut y avoir besoin du dépôt EPEL) et de le lancer en tant que postgres pour voir ce qui se passe dans la base. Comparer les nouvelles tailles des tables (taille sur le disque et nombre de lignes). La table pg_stat_user_tables contient l’activité sur chaque table. Comment s’expliquent les évolutions? Exécuter un VACUUM FULL VERBOSE sur pgbench_tellers . Exécuter un VACUUM FULL VERBOSE sur pgbench_accounts . Effectuer un VACUUM FULL VERBOSE . Quel est l’impact sur la taille de la base? Créer copie1 et copie2 comme des copies de pgbench_accounts , données incluses. Effacer le contenu de copie1 avec DELETE . Effacer le contenu de copie2 avec TRUNCATE . Quelles sont les tailles de ces deux tables après ces opérations? Réactiver l’autovacuum de l’instance.

Attendre quelques secondes et voir si copie1 change de taille.

5.11.8 Statistiques

But : Savoir trouver les statistiques des données et les mettre à jour Créer une table copie3 , copie de pgbench_accounts . Dans la vue système pg_stats , afficher les statistiques collectées pour la table copie3 . Lancer la collecte des statistiques pour cette table uniquement. Afficher de nouveau les statistiques.

5.11.9 Réindexation

But : Réindexer Recréer les index de la table pgbench_accounts . Comment recréer tous les index de la base pgbench? Comment recréer uniquement les index des tables systèmes? Quelle est la différence entre la commande REINDEX et la séquence DROP INDEX + CREATE INDEX ?

5.11.10 Traces

But : Gérer les fichiers de traces

Quelle est la méthode de gestion des traces utilisée par défaut? Paramétrer le programme interne de rotation des journaux :

  • modifier le fichier postgresql.conf pour utiliser le logging collector ;
  • les traces doivent désormais être sauvegardés dans le répertoire /var/lib/pgsql/traces ;
  • la rotation des journaux doit être automatisée pour générer un nouveau fichier de logs toutes les 30 minutes, quelle que soit la quantité de logs archivés; le nom du fichier devra donc comporter les minutes.
  • Tester en forçant des rotations avec la fonction pg_rotate_logfile .
  • Augmenter la trace (niveau info ). Comment éviter l’accumulation des fichiers?

5.12 TRAVAUX PRATIQUES (SOLUTIONS)

Tout ce qui suit suppose : - une installation sous Rocky Linux avec les paquets RPM du PDGD (yum.postgresql.org) ;

  • une instance installée avec les options par défaut;
  • un administrateur dont le compte habituel sur la machine, non privilégié, est nommé dalibo.

5.12.1 Traces maximales

But : suivre toutes les requêtes dans les traces À titre pédagogique et pour alimenter un rapport pgBadger plus tard, toutes les requêtes vont être tracées. Dans postgresql.conf , positionner ceci :

Exemple 5-319 Exemple extrait du PDF, page 378.
  log_min_duration_statement = 0
  log_temp_files = 0
  log_autovacuum_min_duration = 0
  lc_messages = 'C'
  log_line_prefix = '%t [%p]: db=%d,user=%u,app=%a,client=%h '
Éviter de faire cela en production, surtout log_min_duration_statement = 0 ! Sur une base très active, les traces peuvent rapidement monter à plusieurs dizaines de gigaoctets! Dans le présent TP, il faut surveiller l’espace disque pour cette raison. Puis passer à on les paramètres suivants s’ils ne le sont pas déjà : log_checkpoints log_connections log_disconnections log_lock_waits Recharger la configuration. Laisser une fenêtre ouverte pour voir défiler le contenu des traces. Dans postgresql.conf :
Exemple 5-320 Exemple extrait du PDF, page 378.
log_min_duration_statement = 0

Exemple 5-321 Exemple extrait du PDF, page 379.
log_temp_files = 0
log_autovacuum_min_duration = 0
lc_messages='C'
log_line_prefix = '%t [%p]: db=%d,user=%u,app=%a,client=%h '
log_checkpoints = on
log_connections = on
log_disconnections = on
log_lock_waits = on

SELECT pg_reload_conf() ;
pg_reload_conf
----------------
 t
Tableau 5-81 Tableau ou résultat de commande extrait du PDF, page 379.
Exemple 5-322 Exemple extrait du PDF, page 379.
SHOW log_min_duration_statement ;
log_min_duration_statement
----------------------------
 0
Tableau 5-82 Tableau ou résultat de commande extrait du PDF, page 379.
Laisser une fenêtre ouverte avec le fichier, par exemple avec : tail -f /var/lib/pgsql/15/data/log/postgresql-Wed.log La moindre requête doit y apparaître, y compris quand l’utilisateur effectue un simple \d .

5.12.2 Méthode d’authentification

But : Gérer les rôles et les permissions Activer la méthode d’authentification scram-sha-256 dans postgresql.conf si elle n’est pas déjà en place. Cette méthode existe depuis PostgreSQL 10 mais n’est le défaut que depuis PostgreSQL 14. Dans postgresql.conf :

Exemple 5-323 Exemple extrait du PDF, page 379.
password_encryption = scram-sha-256
Ne pas oublier de recharger la configuration. Depuis psql en tant que postgres :
Exemple 5-324 Exemple extrait du PDF, page 379.
SELECT pg_reload_conf() ;
pg_reload_conf
----------------
 t
Tableau 5-83 Tableau ou résultat de commande extrait du PDF, page 379.
Alternative depuis le shell :
Exemple 5-325 Exemple extrait du PDF, page 379.
 # systemctl reload postgresql-15

Exemple 5-326 Exemple extrait du PDF, page 380.
SHOW password_encryption ;
password_encryption
---------------------
 scram-sha-256
Tableau 5-84 Tableau ou résultat de commande extrait du PDF, page 380.
Consulter les droits définis dans pg_hba.conf au travers de la vue système pg_hba_file_rules . La vue permet de voir le contenu de pg_hba.conf avant de le recharger.
Exemple 5-327 Exemple extrait du PDF, page 380.
SELECT * FROM pg_hba_file_rules ;
ln|type | database|user_name| address |       netmask        | auth_method | …
--+-----+---------+---------+---------+----------------------+---------------+--
80|local|{pgbench}|{all}    |         |                      | trust         |
81|local|{all}    |{all}    |         |                      | peer          |
84|host |{all}    |{all}    |127.0.0.1|255.255.255.255       | scram-sha-256 |
86|host |{all}    |{all}    |::1      |ffff:ffff:ffff:ffff:… | scram-sha-256 |
89|local|{replica…|{all}    |         |                      | peer          |
90|host |{replica…|{all}    |127.0.0.1|255.255.255.255       | scram-sha-256 |
91|host |{replica…|{all}    |::1      |ffff:ffff:ffff:ffff:… | scram-sha-256 |
Tableau 5-85 Tableau ou résultat de commande extrait du PDF, page 380.
Dans pg_hba.conf , supprimer les accès avec la méthode trust pouvant rester après les précédents exercices. Vérifier dans la vue avant de recharger la configuration. Dans le fichier pg_hba.conf , supprimer les lignes avec la méthode trust . La vue se met à jour immédiatement. En cas d’erreur de syntaxe dans le fichier, elle doit aussi indiquer une erreur. Puis on recharge :
Exemple 5-328 Exemple extrait du PDF, page 380.
SELECT pg_reload_conf() ;

5.12.3 Création des bases

But : Créer des bases appartenant à un utilisateur non privilégié Créer un utilisateur nommé testperf avec attribut LOGIN . Au choix, on peut utiliser les commandes shell ou les commandes SQL qu’elles affichent :

Exemple 5-329 Exemple extrait du PDF, page 380.
$ sudo -iu postgres

$ createuser --login --echo testperf
SELECT pg_catalog.set_config('search_path', '', false);
CREATE ROLE testperf NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN;

Créer une base pgbench lui appartenant.

Exemple 5-330 Exemple extrait du PDF, page 381.
$ createdb --echo pgbench --owner testperf
SELECT pg_catalog.set_config('search_path', '', false);
CREATE DATABASE pgbench OWNER testperf;
Vérifier que la connexion ne marche pas encore depuis votre compte habituel, y compris avec -h localhost . La connexion ne peut se faire car dans pg_hba.conf ne figure, en local , que l’accès peer pour un utilisateur nommé postgres (nom système comme nom du rôle.) Elle ne fonctionnera pas pour des utilisateurs nommés différemment. Corriger cela impliquerait de modifier pg_ident.conf .
Exemple 5-331 Exemple extrait du PDF, page 381.
$ psql -U testperf pgbench
psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432"
   failed: FATAL: Peer authentication failed for user "testperf"
Si l’on ajoute -h localhost , l’accès est contrôlé via une ligne host , et exige un mot de passe qui n’existe pas encore :
Exemple 5-332 Exemple extrait du PDF, page 381.
$ psql -U testperf pgbench -h localhost
Password for user testperf: Nous créerons ces mots de passe plus bas. Créer un rôle patron avec attribut LOGIN , et une base entreprise lui appartenant.
Exemple 5-333 Exemple extrait du PDF, page 381.
$ createuser --login patron
$ createdb --owner patron entreprise
Ce qui est équivalent à :
Exemple 5-334 Exemple extrait du PDF, page 381.
CREATE ROLE patron LOGIN;
CREATE DATABASE entreprise OWNER patron;
Noter que, là encore, c’est le superutilisateur postgres qui crée la base et affecte les droits à patron. Celui-ci n’a pas le droit de créer des bases.

5.12.4 Mots de passe

But : Mise en place de l’authentification par mot de passe

Créer des mots de passe pour les rôles patron et testperf. Déclarer le mot de passe se fait facilement depuis psql (en tant que superutilisateur postgres) :

Exemple 5-335 Exemple extrait du PDF, page 382.
postgres=# \password testperf
Saisissez le nouveau mot de passe : Saisissez-le à nouveau :
Exemple 5-336 Exemple extrait du PDF, page 382.
postgres=# \password patron
Saisissez le nouveau mot de passe : Saisissez-le à nouveau : Un outil courant pour générer des mots de passe aléatoires longs est pwgen :
Exemple 5-337 Exemple extrait du PDF, page 382.
$ pwgen 20 1
jahT0eeRov2aiQuae1iM Si pwgen n’est pas présent sur le système, un bon mot de passe peut être généré ainsi :
$ echo "faitespasserunchatsurleclavier" | md5sum
Tableau 5-86 Tableau ou résultat de commande extrait du PDF, page 382.
b0cdc36ff6c986b3930bfc269f37f3f2 Pour l’exercice, il est possible de donner le même mot de passe à tous les utilisateurs (ce que personne ne fait en production, bien sûr). Consulter la table pg_authid pour voir la version chiffrée. Noter que, même identiques, les mots de passe n’ont pas la même signature.
oid            | 25097
rolname        | patron
rolsuper       | f
rolinherit     | t
rolcreaterole | f
rolcreatedb    | f
rolcanlogin    | t
rolreplication | f
rolbypassrls   | f
rolconnlimit   | -1
rolpassword    | SCRAM-SHA-256$4096:a0IE9MKlZRTYd9FlXxDX0g==$wT0rQtaolI2gpP...
rolvaliduntil |
Tableau 5-87 Tableau ou résultat de commande extrait du PDF, page 382.
Exemple 5-338 Exemple extrait du PDF, page 382.
SELECT * FROM pg_authid WHERE rolname IN ('testperf','patron') \gx

-[ RECORD 1 ]--+--------------------------------------------------------------
-[ RECORD 2 ]--+--------------------------------------------------------------
oid            | 25096
rolname        | testperf
rolsuper       | f
rolinherit     | t
rolcreaterole | f
rolcreatedb    | f
rolcanlogin    | t
rolreplication | f
Tableau 5-88 Tableau ou résultat de commande extrait du PDF, page 382.

rolbypassrls      | f
rolconnlimit      | -1
rolpassword       | SCRAM-SHA-256$4096:XNd9Ndrb6ljGAVyTek3sig==$ofeTaBumh2p6WA...
rolvaliduntil     |
Tableau 5-89 Tableau ou résultat de commande extrait du PDF, page 383.
Pour ouvrir les accès :

  • ajuster pg_hba.conf pour permettre l’accès à la base pgbench uniquement à l’utilisateur testperf, permettre l’accès à la base entreprise à tout utilisateur, en local avec son mot de passe, en authentification scram-sha-256 ;
  • vérifier avec la vue pg_hba_file_rules ;
  • recharger la configuration;
  • tester la connexion. Ajouter ceci dans pg_hba.conf , en tête (nous verrons que c’est une erreur) :
    Exemple 5-339 Exemple extrait du PDF, page 383.
    # TYPE    DATABASE           USER                ADDRESS                       METHOD
    local pgbench testperf scram-sha-256 local entreprise all scram-sha-256 Recharger la configuration et tenter une connexion depuis un compte utilisateur normal : cela doit fonctionner en entrant le mot de passe.
    Exemple 5-340 Exemple extrait du PDF, page 383.
    $ sudo -iu postgres psql -c 'SELECT pg_reload_conf()';
    $ psql -U testperf -d pgbench
    
    Mot de passe pour l’utilisateur testperf :
    Exemple 5-341 Exemple extrait du PDF, page 383.
    psql (15.1)
    Type “help” for help.
    Exemple 5-342 Exemple extrait du PDF, page 383.
    pgbench=>
    patron doit aussi pouvoir se connecter :
    Exemple 5-343 Exemple extrait du PDF, page 383.
    $ psql -U patron -d entreprise
    
    Créer un fichier .pgpass dans votre répertoire utilisateur ( /home/dalibo ) ou dans celui de l’utilisateur postgres ( /var/lib/pgsql ) pour qu’il puisse se connecter aux bases entreprise et
    Exemple 5-344 Exemple extrait du PDF, page 383.
       pgbench sans entrer le mot de passe.
    Le fichier doit contenir le mot de passe en clair sous cette forme : localhost:5432:pgbench:testperf:b0cdc36ff6c986b3930bfc269f37f3f2 localhost:5432:entreprise:patron:b0cdc36ff6c986b3930bfc269f37f3f2 NB : la mention localhost dans ce fichier couvre aussi bien les accès via :::1 ou 127.0.0.1 (lignes host de pg_hba.conf ) que les accès via la socket unix (lignes local ). Si le mot de passe est le même pour tous les utilisateurs créés par la suite, le nom d’utilisateur patron peut même être remplacé par * . Si la connexion échoue, vérifier que le fichier est en mode 600 :

Exemple 5-345 Exemple extrait du PDF, page 384.
WARNING: password file "/home/dalibo/.pgpass" has group or world access; permissions
   should be u=rw (0600) or less

$ chmod u=rw,go= ~/.pgpass
La connexion doit à présent se faire sans mot de passe. Le superutilisateur PostgreSQL postgres peut-il se connecter aux bases entreprise et pgbench? Si non, comment lui permettre? Même depuis l’utilisatur système postgres, la connexion aux deux bases que nous venons de créer échoue :
Exemple 5-346 Exemple extrait du PDF, page 384.
$ sudo -iu postgres psql -d entreprise -U postgres
Mot de passe pour l’utilisateur postgres :
Exemple 5-347 Exemple extrait du PDF, page 384.
psql: fe_sendauth: no password supplied
La cause est dans les traces : FATAL: password authentication failed for user “postgres”
Exemple 5-348 Exemple extrait du PDF, page 384.
DETAIL: User "postgres" has no password assigned.
        Connection matched pg_hba.conf line 81:
        "local   entreprise      all            scram-sha-256"
L’échec est donc normal : la ligne de pg_hba.conf qui permet un accès inconditionnel à toute base depuis le compte système postgres est à présent la troisième. Pour corriger cela sans créer de mot de passe, la remplacer par cette toute première ligne de pg_hba.conf :
Exemple 5-349 Exemple extrait du PDF, page 384.
# TYPE    DATABASE               USER             ADDRESS                   METHOD
local all postgres peer Et recharger la configuration. Depuis l’utilisateur système postgres , il ne doit plus y avoir de mot de passe demandé à la connexion :
Exemple 5-350 Exemple extrait du PDF, page 384.
$ psql -d entreprise
Remplir la base pgbench et générer un peu d’activité :
Exemple 5-351 Exemple extrait du PDF, page 384.
   /usr/pgsql-17/bin/pgbench -i -s1 --foreign-keys pgbench -U testperf
   /usr/pgsql-17/bin/pgbench -P1 -T3 pgbench -U testperf
Aucun mot de passe ne doit être demandé, avec ou sans -h localhost .
Exemple 5-352 Exemple extrait du PDF, page 384.
$ sudo -iu postgres

$ createuser --login --echo testperf
SELECT pg_catalog.set_config('search_path', '', false);
CREATE ROLE testperf NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN;

$ createdb --echo pgbench
SELECT pg_catalog.set_config('search_path', '', false);
CREATE DATABASE pgbench OWNER testperf;

Initialisation de la base (23 Mo) :

Exemple 5-353 Exemple extrait du PDF, page 385.
$ sudo -iu postgres
$ /usr/pgsql-17/bin/pgbench -i --foreign-keys -d pgbench -U testperf -h localhost
Génération de données :
Exemple 5-354 Exemple extrait du PDF, page 385.
/usr/pgsql-17/bin/pgbench -P1 -T3 pgbench -U testperf -h localhost
Le pg_hba.conf doit ressembler à celui-ci :
Exemple 5-355 Exemple extrait du PDF, page 385.
# TYPE    DATABASE             USER             ADDRESS                     METHOD
local all postgres peer local pgbench testperf scram-sha-256 local entreprise all scram-sha-256 host all all 127.0.0.1/32 scram-sha-256 host all all ::1/128 scram-sha-256 local replication all peer host replication all 127.0.0.1/32 scram-sha-256 host replication all ::1/128 scram-sha-256 L’accès sans -h localhost fonctionne grâce aux lignes local . L’accès via -h localhost fonctionne grâce à l’une des lignes host . On avait vu qu’il ne manquait que le mot de passe. Évidemment cela ne marche pas depuis une adresse extérieure. Les 3 dernières lignes sont réservées à des connexions de réplication. Compléter pg_ident.conf pour pouvoir se connecter au rôle postgres depuis le compte système habituel (dalibo), en local . Remplacer la première ligne de pg_hba.conf par : local all postgres peer map=admins Dans pg_ident.conf , ajouter :
Exemple 5-356 Exemple extrait du PDF, page 385.
# MAPNAME            SYSTEM-USERNAME            PG-USERNAME
admins postgres postgres admins dalibo postgres On recharge :
Exemple 5-357 Exemple extrait du PDF, page 385.
$ sudo systemctl reload postgresql-15
Toute erreur peut empêcher la connexion au rôle postgres!

Tester la connexion depuis postgres au rôle postgres, de dalibo à postgres. Ces connexions doivent fonctionner sans mot de passe : postgres$ psql postgres$ psql -d entreprise dalibo$ psql -U postgres dalibo$ psql -U postgres -d entreprise

5.12.5 Rôles et permissions

But : Gérer les rôles et les permissions sur les tables Sous les utilisateurs dalibo comme postgres, créer un nouveau fichier ~/.psqlrc contenant \set PROMPT1 ‘%n@%/%R%# ’ pour que l’invite indique quels sont les utilisateur et base en cours.

Exemple 5-358 Exemple extrait du PDF, page 386.
$ psql -U testperf -d pgbench
psql (15.1)
Type “help” for help.
Exemple 5-359 Exemple extrait du PDF, page 386.
testperf@pgbench=>
Noter que l’affichage de l’invite est légèrement différente selon que le type d’utilisateur : superutilisateur ou un utilisateur « normal ». Ajouter à la base de données entreprise la table facture (id int, objet text, creationts timestamp) . Elle appartiendra à patron, administrateur de la base. Se connecter avec l’utilisateur patron (administrateur de la base entreprise) :
Exemple 5-360 Exemple extrait du PDF, page 386.
$ psql -U patron entreprise
Créer la table facture : patron@entreprise=> CREATE TABLE facture (id int, objet text, creationts timestamp); Noter que la table appartient à celui qui la crée : patron@entreprise=> \d Liste des relations
 Schéma |   Nom   | Type | Propriétaire
--------+---------+-------+--------------
 public | facture | table | patron
Tableau 5-90 Tableau ou résultat de commande extrait du PDF, page 386.
Création d’un utilisateur et d’un groupe

Créer un rôle secretariat sans droit de connexion, mais qui peut visualiser, ajouter, mettre à jour et supprimer des éléments de la table facture . Il faut le faire avec le rôle postgres, car patron n’a pas les droits : patron@entreprise=# \c - postgres You are now connected to database “entreprise” as user “postgres”.

Exemple 5-361 Exemple extrait du PDF, page 387.
postgres@entreprise=# CREATE ROLE secretariat;
postgres@entreprise=# GRANT SELECT, INSERT, UPDATE, DELETE ON facture TO secretariat;
Créer un rôle boulier qui peut se connecter et appartient au rôle secretariat, avec un mot de passe (à ajouter au .pgpass ).
Exemple 5-362 Exemple extrait du PDF, page 387.
postgres@entreprise=# CREATE ROLE boulier LOGIN IN ROLE SECRETARIAT;
postgres@entreprise=# \password boulier
Saisissez le nouveau mot de passe : Saisissez-le à nouveau : Vérifier la création des deux rôles.
Exemple 5-363 Exemple extrait du PDF, page 387.
postgres@entreprise=# \du
                              List of roles
  Role name |                          Attributes
-------------+------------------------------------------------------------
 boulier     |
 dupont      |
 patron      |
 postgres    | Superuser, Create role, Create DB, Replication, Bypass RLS
 secretariat | Cannot login
 testperf    |
Tableau 5-91 Tableau ou résultat de commande extrait du PDF, page 387.
 Role name | Member of |     Options    | Grantor
-----------+-------------+--------------+----------
 boulier   | secretariat | INHERIT, SET | postgres
Tableau 5-92 Tableau ou résultat de commande extrait du PDF, page 387.
Exemple 5-364 Exemple extrait du PDF, page 387.
postgres=# \drg
                List of role grants
(1 row) En tant que boulier, créer une table brouillon identique à facture . Dans quel schéma cela se passe-t-il? Avec PostgreSQL 15 ou supérieur, il y a une étape supplémentaire. La connexion doit se faire sans problème avec le mot de passe.
Exemple 5-365 Exemple extrait du PDF, page 387.
$ psql -U boulier -d entreprise
Une astuce à connaître pour créer une table vide de même structure qu’une autre est : boulier@entreprise=> CREATE TABLE brouillon (LIKE facture INCLUDING ALL) ;

Cet ordre fonctionnera directement jusque PostgreSQL 14 compris, car la table est créée implicitement dans le schéma public. À partir de PostgreSQL 15, par défaut, seul le propriétaire de la base (patron) peut écrire dans le schéma public :

Exemple 5-366 Exemple extrait du PDF, page 388.
ERROR: permission denied for schema public
LINE 1: CREATE table brouillon (like facture); Il doit donc donner ce droit à boulier : patron@entreprise=> GRANT CREATE ON schema public TO boulier ; patron@entreprise=> \dn+ public List of schemas
 Name |       Owner      |           Access privileges            | Descript…
-------+-----------------+----------------------------------------+----------
public |pg_database_owner| pg_database_owner=UC/pg_database_owner+| standard
       |                 | =U/pg_database_owner                  +| public
       |                 | boulier=C/pg_database_owner            |   schema
Tableau 5-93 Tableau ou résultat de commande extrait du PDF, page 388.
boulier@entreprise=> CREATE TABLE brouillon (LIKE facture INCLUDING ALL) ; Vérifier les tables présentes et les droits \dp . Comment les lire? La nouvelle table appartient bien à boulier : boulier@entreprise=> \dt List of relations
 Schema |   Name    | Type | Owner
--------+-----------+-------+---------
 public | brouillon | table | boulier
 public | facture   | table | patron
Tableau 5-94 Tableau ou résultat de commande extrait du PDF, page 388.
(2 rows) boulier@entreprise=> \dp Access privileges
 Schema |   Name    | Type |    Access privileges     | …
--------+-----------+-------+-------------------------+---
 public | brouillon | table |                         | …
 public | facture   | table | patron=arwdDxt/patron +| …
        |           |       | secretariat=arwd/patron | …
Tableau 5-95 Tableau ou résultat de commande extrait du PDF, page 388.
Sans affectation explicite de droits, les droits par défauts ne figurent pas : par exemple, brouillon pourra être lu et modifié par son propriétaire, boulier. patron a tous les droits sur la table facture (il possède la table). On retrouve les droits donnés plus haut au rôle secretariat : insertion (a pour append), lecture (r pour read), modification (w pour write) et suppression (d pour delete). On ne voit pas explicitement les droits de boulier (membre de secretariat) sur facture.

À l’aide du rôle boulier : insérer 2 factures ayant pour objet « Vin de Bordeaux » et « Vin de Bourgogne » avec la date et l’heure courante. boulier@entreprise=> INSERT INTO facture VALUES (1, ‘Vin de Bordeaux’, now()), (2, ‘Vin de Bourgogne’, now()); Afficher le contenu de la table facture . boulier@entreprise=> SELECT * FROM facture;

 id |      objet       |         creationts
----+------------------+----------------------------
  1 | Vin de Bordeaux | 2019-07-16 17:50:28.942862
  2 | Vin de Bourgogne | 2019-07-16 17:50:28.942862
Tableau 5-96 Tableau ou résultat de commande extrait du PDF, page 389.
Mettre à jour la deuxième facture avec la date et l’heure courante. boulier@entreprise=> UPDATE facture SET creationts = now() WHERE id = 2 ; UPDATE 1 Supprimer la première facture. boulier@entreprise=> DELETE FROM facture WHERE id = 1 ; DELETE 1 Modification des permissions Retirer les droits DELETE sur la table facture au rôle secretariat. boulier@entreprise=> \c - patron Vous êtes maintenant connecté à la base de données « entreprise » en tant qu’utilisateur « patron ». patron@entreprise=> REVOKE DELETE ON facture FROM secretariat; REVOKE Vérifier qu’il n’est plus possible de supprimer la deuxième facture avec le rôle boulier. patron@entreprise=> \c - boulier Vous êtes maintenant connecté à la base de données « entreprise » en tant qu’utilisateur « boulier ». boulier@entreprise=> DELETE FROM facture WHERE id = 2;
Exemple 5-369 Exemple extrait du PDF, page 389.
ERROR:   permission denied for table facture

En tant que patron, créer une table produit contenant une colonne texte nommée appellation et la remplir avec des noms de boissons. boulier@entreprise=> \c - patron Vous êtes maintenant connecté à la base de données « entreprise » en tant qu’utilisateur « patron ». patron@entreprise=> CREATE TABLE produit (appellation text) ;

Exemple 5-370 Exemple extrait du PDF, page 390.
CREATE TABLE
patron@entreprise=> INSERT INTO produit VALUES (‘Gewurtzraminer vendanges tardives’), (‘Cognac’), (‘Eau plate’), (‘Eau gazeuse’), (‘Jus de groseille’) ; INSERT 0 5 Afficher les droits sur cette table avec \dt et \dp . Vérifier que le rôle boulier appartenant au rôle secretariat ne peut pas sélectionner les produits contenus dans la table produit . On voit bien que produit appartient à patron et que secretariat n’a à priori aucun droit dessus. patron@entreprise=> \dt List of relations
 Schema |   Name    | Type | Owner
--------+-----------+-------+--------------
 public | brouillon | table | boulier
 public | facture   | table | patron
 public | produit   | table | patron
Tableau 5-97 Tableau ou résultat de commande extrait du PDF, page 390.
patron@entreprise=> \dp Access privileges
 Schema |   Name    | Type |    Access privileges    | …
--------+-----------+-------+------------------------+----
 public | brouillon | table |                        |
 public | facture   | table | patron=arwdDxt/patron +|
        |           |       | secretariat=arw/patron |
 public | produit   | table |                        |
Tableau 5-98 Tableau ou résultat de commande extrait du PDF, page 390.
En conséquence, boulier ne peut lire la table : patron@entreprise=> \c - boulier Vous êtes maintenant connecté à la base de données « entreprise » en tant qu’utilisateur « boulier ». boulier@entreprise=> SELECT * FROM produit;
Exemple 5-371 Exemple extrait du PDF, page 390.
ERROR: permission denied for table produit
Retirer tous les droits pour le groupe secretariat sur la table produit . patron@entreprise=> REVOKE ALL ON produit from secretariat;

Que deviennent les droits affichés ? boulier peut-il lire la table? secretariat n’avait pourtant aucun droit, mais l’affichage a changé et énumère à présent explicitement les droits présents : patron@entreprise=> \dp Access privileges

 Schema |   Name    | Type |    Access privileges    | …
--------+-----------+-------+------------------------+----
 public | brouillon | table |                        |
 public | facture   | table | patron=arwdDxt/patron +|
        |           |       | secretariat=arw/patron |
 public | produit   | table | patron=arwdDxt/patron |
Tableau 5-99 Tableau ou résultat de commande extrait du PDF, page 391.
Autoriser l’utilisateur boulier à accéder à la table produit en lecture. patron@entreprise=> GRANT SELECT ON produit TO boulier ; GRANT Vérifier que boulier peut désormais accéder à la table produit . boulier@entreprise=> SELECT * FROM produit ; appellation
-----------------------------------
 Gewurtzraminer vendanges tardives
 Cognac
 Eau plate
 Eau gazeuse
 Jus de groseille
Tableau 5-100 Tableau ou résultat de commande extrait du PDF, page 391.
(5 rows) Héritage des droits au login Créer un rôle tina appartenant au rôle secretariat, avec l’attribut LOGIN , mais n’héritant pas des droits à la connexion. Vérifier les droits avec \du et \drg . Lui donner un mot de passe. La clause NOINHERIT évite qu’un rôle hérite immédiatement des droits des autres rôles :
Exemple 5-372 Exemple extrait du PDF, page 391.
postgres@entreprise=> CREATE ROLE tina LOGIN NOINHERIT ;
CREATE ROLE
Exemple 5-373 Exemple extrait du PDF, page 391.
postgres@entreprise=> GRANT secretariat TO tina;

postgres@entreprise=# \du
List of roles
   Role name |                         Attributes
-------------+------------------------------------------------------------
 …
Tableau 5-101 Tableau ou résultat de commande extrait du PDF, page 391.

 tina        | No inheritance
 …
Tableau 5-102 Tableau ou résultat de commande extrait du PDF, page 392.
 Role name | Member of |      Options   | Grantor
-----------+-------------+--------------+----------
 …
 tina      | secretariat | SET          | postgres
Tableau 5-103 Tableau ou résultat de commande extrait du PDF, page 392.
Exemple 5-374 Exemple extrait du PDF, page 392.
postgres=# \drg
                List of role grants
(2 rows)
Exemple 5-375 Exemple extrait du PDF, page 392.
postgres@entreprise=# \password tina
Tester la connexion en tant que tina. Vérifier que tina ne peut pas accéder à la table facture . tina@entreprise=> SELECT * FROM facture;
Exemple 5-376 Exemple extrait du PDF, page 392.
ERROR:   permission denied for table facture
En tant que tina, activer le rôle secretariat ( SET ROLE ). tina@entreprise=> SET ROLE secretariat; Vérifier que tina possède maintenant les droits du rôle secretariat. Sélectionner les données de la table facture . L’utilisateur tina possède maintenant les droits du rôle secretariat : tina@entreprise=> SELECT * FROM facture;
 id |      objet       |         creationts
----+------------------+----------------------------
  2 | Vin de Bourgogne | 2019-07-16 17:50:53.725971
Tableau 5-104 Tableau ou résultat de commande extrait du PDF, page 392.

5.12.6 Autorisation d’accès distant

But : Mettre en place les accès dans pg_hba.conf . Autoriser tous les membres du réseau local à se connecter avec un mot de passe (autorisation en IP sans SSL) avec les utilisateurs boulier ou tina. Tester avec l’IP du serveur avant de demander aux voisins de tester. Pour tester, repérer l’adresse IP du serveur avec ip a , par exemple 192.168.28.1 , avec un réseau local en 192.168.28.* . Ensuite, lors des appels à psql , utiliser -h 192.168.28.1 au lieu de la connexion locale ou de localhost :

Exemple 5-377 Exemple extrait du PDF, page 393.
$ psql -h 192.168.123.180 -d entreprise -U tina
Ajouter les lignes suivantes dans le fichier pg_hba.conf : host entreprise tina,boulier 192.168.28.0/24 scram-sha-256 Il ne faut pas oublier d’ouvrir PostgreSQL aux connexions extérieures dans postgresql.conf :
Exemple 5-378 Exemple extrait du PDF, page 393.
listen_addresses = '*'
Cette modification nécessite un redémarrage Plus prudemment, on peut juste ajouter l’adresse publique de l’instance PostgreSQL :
Exemple 5-379 Exemple extrait du PDF, page 393.
listen_addresses = 'localhost,192.168.28.1'
Il y a peut-être un firewall à désactiver :
Exemple 5-380 Exemple extrait du PDF, page 393.
$ sudo systemctl status firewalld
$ sudo systemctl stop firewalld

5.12.7 VACUUM, VACUUM FULL, DELETE, TRUNCATE

But : Effacer des données, distinguer VACUUM et VACUUM FULL . Pré-requis Désactiver le démon autovacuum de l’instance. Dans le fichier postgresql.conf , désactiver le démon autovacuum en modifiant le paramètre suivant :

Exemple 5-381 Exemple extrait du PDF, page 393.
autovacuum = off
Ne jamais faire cela en production! On recharge la configuration :
Exemple 5-382 Exemple extrait du PDF, page 393.
$ psql -c 'SELECT pg_reload_conf()'
On vérifie que le paramètre a bien été modifié :
Exemple 5-383 Exemple extrait du PDF, page 393.
postgres@postgres=# show autovacuum ;
autovacuum
------------
 off
Tableau 5-105 Tableau ou résultat de commande extrait du PDF, page 393.
Nettoyage avec VACUUM

Se connecter à la base pgbench en tant que testperf.

Exemple 5-384 Exemple extrait du PDF, page 394.
$ psql -U testperf -d pgbench
Grâce aux fonctions pg_relation_size et pg_size_pretty , afficher la taille de la table pgbench_accounts . \d+ affiche les tailles des tables, mais il existe des fonctions plus ciblées. Pour visualiser la taille de la table, il suffit d’utiliser la fonction pg_relation_size . Comme l’affichage a parfois trop de chiffres pour être facilement lisible, on utilise pg_size_pretty . Il est facile de retrouver facilement une fonction en effectuant une recherche par mot clé dans psql , notamment pour retrouver ses paramètres. Exemple :
Exemple 5-385 Exemple extrait du PDF, page 394.
              postgres=# \df *pretty*
Liste des fonctions
Exemple 5-386 Exemple extrait du PDF, page 394.
              -[ RECORD 1 ]------------------+---------------
              Schéma                         | pg_catalog
              Nom                            | pg_size_pretty
              Type de données du résultat    | text
              Type de données des paramètres | bigint
              Type                           | normal
Tableau 5-106 Tableau ou résultat de commande extrait du PDF, page 394.
Cela donne au final :
Exemple 5-387 Exemple extrait du PDF, page 394.
testperf@pgbench=> SELECT pg_relation_size('pgbench_accounts');
pg_relation_size
 ------------------
         13434880
Tableau 5-107 Tableau ou résultat de commande extrait du PDF, page 394.
Exemple 5-388 Exemple extrait du PDF, page 394.
testperf@pgbench=> SELECT pg_size_pretty(pg_relation_size('pgbench_accounts'));
pg_size_pretty
 ----------------
  13 MB
Tableau 5-108 Tableau ou résultat de commande extrait du PDF, page 394.
Copier le contenu de la table dans une nouvelle table ( pba_copie ).
Exemple 5-389 Exemple extrait du PDF, page 394.
testperf@pgbench=> CREATE table pba_copie AS SELECT * FROM pgbench_accounts;
SELECT 100000 Supprimer le contenu de la table pba_copie , à l’exception de la dernière ligne ( aid=100000 ), avec un ordre DELETE . Quel est alors l’espace disque utilisé par cette table?
Exemple 5-390 Exemple extrait du PDF, page 394.
testperf@pgbench=> DELETE FROM pba_copie WHERE aid <100000;

DELETE 99999 Il ne reste qu’une ligne, mais l’espace disque est toujours utilisé :

Exemple 5-391 Exemple extrait du PDF, page 395.
testperf@pgbench=> SELECT pg_size_pretty(pg_relation_size('pba_copie'));
pg_size_pretty
----------------
 13 MB
Tableau 5-109 Tableau ou résultat de commande extrait du PDF, page 395.
Noter que même si l’autovacuum n’était pas désactivé, il n’aurait pas réduit l’espace occupé par la table car il reste la ligne à la fin de celle-ci. De plus, il n’aurait pas eu forcément le temps de passer sur la table entre les deux ordres précédents. Insérer le contenu de la table pgbench_accounts dans la table pba_copie . Quel est alors l’espace disque utilisé par la table?
Exemple 5-392 Exemple extrait du PDF, page 395.
testperf@pgbench=> INSERT into pba_copie SELECT * FROM pgbench_accounts;
INSERT 0 100000 L’espace disque utilisé a doublé :
Exemple 5-393 Exemple extrait du PDF, page 395.
testperf@pgbench=> SELECT pg_size_pretty(pg_relation_size('pba_copie'));
pg_size_pretty
----------------
 26 MB
Tableau 5-110 Tableau ou résultat de commande extrait du PDF, page 395.
Les nouvelles données se sont ajoutées à la fin de la table. Elles n’ont pas pris la place des données effacées précédemment. Effectuer un VACUUM simple sur pba_copie . Vérifier la taille de la base. La commande vacuum « nettoie » mais ne libère pas d’espace disque :
Exemple 5-394 Exemple extrait du PDF, page 395.
testperf@pgbench=> VACUUM pba_copie;
VACUUM
Exemple 5-395 Exemple extrait du PDF, page 395.
testperf@pgbench=> SELECT pg_size_pretty(pg_relation_size('pba_copie'));
pg_size_pretty
----------------
 26 MB
Tableau 5-111 Tableau ou résultat de commande extrait du PDF, page 395.
Vider à nouveau la table pba_copie des lignes d’ aid inférieur à 100 000. Insérer à nouveau le contenu de la table pgbench_accounts . L’espace mis à disposition a-t-il été utilisé ?
Exemple 5-396 Exemple extrait du PDF, page 395.
testperf@pgbench=> DELETE FROM pba_copie WHERE aid <100000;
DELETE 99999
Exemple 5-397 Exemple extrait du PDF, page 395.
testperf@pgbench=> INSERT into pba_copie SELECT * FROM pgbench_accounts;

INSERT 0 100000

Exemple 5-398 Exemple extrait du PDF, page 396.
testperf@pgbench=> SELECT pg_size_pretty(pg_relation_size('pba_copie'));
pg_size_pretty
----------------
 26 MB
Tableau 5-112 Tableau ou résultat de commande extrait du PDF, page 396.
Cette fois, la table n’a pas augmenté de taille. PostgreSQL a pu réutiliser la place des lignes effacées que VACUUM a marqué comme disponibles. Voir la taille de la base. Supprimer la table pba_copie . Voir l’impact sur la taille de la base. Nous verrons plus tard comment récupérer de l’espace. Pour le moment, on se contente de supprimer la table.
Exemple 5-399 Exemple extrait du PDF, page 396.
postgres@pgbench=# SELECT pg_size_pretty(pg_database_size ('pgbench')) ;
pg_size_pretty
----------------
 48 MB
Tableau 5-113 Tableau ou résultat de commande extrait du PDF, page 396.
Exemple 5-400 Exemple extrait du PDF, page 396.
postgres@pgbench=# DROP TABLE pba_copie ;

DROP TABLE

postgres@pgbench=# SELECT pg_size_pretty(pg_database_size ('pgbench')) ;
pg_size_pretty
----------------
 23 MB
Tableau 5-114 Tableau ou résultat de commande extrait du PDF, page 396.
Supprimer une table rend immédiatement l’espace disque au système. VACUUM avec les requêtes de pgbench Tout d’abord, repérer les tailles des différentes tables et le nombre de lignes de chacune.
Exemple 5-401 Exemple extrait du PDF, page 396.
postgres@pgbench=#\d+
Liste des relations
 Schéma |         Nom          | Type | Propriétaire |    Taille   | Description
--------+----------------------+-------+--------------+------------+-------------
 public | pgbench_accounts     | table | testperf     | 13 MB      |
 public | pgbench_branches     | table | testperf     | 40 kB      |
 public | pgbench_history      | table | testperf     | 0 bytes    |
 public | pgbench_tellers      | table | testperf     | 40 kB      |
Tableau 5-115 Tableau ou résultat de commande extrait du PDF, page 396.
Exemple 5-402 Exemple extrait du PDF, page 396.
testperf@pgbench=> SELECT count(*) FROM pgbench_accounts;
count
--------
 100000
Tableau 5-116 Tableau ou résultat de commande extrait du PDF, page 396.
Exemple 5-403 Exemple extrait du PDF, page 396.
testperf@pgbench=> SELECT count(*) FROM pgbench_tellers;

count

-------
    10
Tableau 5-117 Tableau ou résultat de commande extrait du PDF, page 397.
Exemple 5-404 Exemple extrait du PDF, page 397.
testperf@pgbench=> SELECT count(*) FROM pgbench_branches;
count
-------
     1
Tableau 5-118 Tableau ou résultat de commande extrait du PDF, page 397.
Exemple 5-405 Exemple extrait du PDF, page 397.
testperf@pgbench=> SELECT count(*) FROM pgbench_history;
count
--------
     0
Tableau 5-119 Tableau ou résultat de commande extrait du PDF, page 397.
(Le contenu de cette dernière table dépend de l’historique de la base.) Pour amplifier le phénomène à observer, on peut créer une session de très longue durée, laissée ouverte sans COMMIT ni ROLLBACK . Il faut qu’elle ait consulté une des tables pour que l’effet soit visible :
Exemple 5-406 Exemple extrait du PDF, page 397.
  testperf@pgbench=> BEGIN ;
BEGIN Temps : 0,608 ms
Exemple 5-407 Exemple extrait du PDF, page 397.
  testperf@pgbench=> SELECT count(*) FROM pgbench_accounts ;
count
  --------
   100000
  (1 ligne)
Tableau 5-120 Tableau ou résultat de commande extrait du PDF, page 397.
Temps : 26,059 ms
Exemple 5-408 Exemple extrait du PDF, page 397.
  testperf@pgbench=> SELECT pg_sleep (10000) ;
Depuis un autre terminal, générer de l’activité sur la table, ici avec 10 000 transactions sur 20 clients :
Exemple 5-409 Exemple extrait du PDF, page 397.
  PGOPTIONS='-c synchronous_commit=off' \
  /usr/pgsql-17/bin/pgbench -U testperf -d pgbench \
  --client=20 --jobs=2 -t 10000 --no-vacuum
(NB : La variable d’environnement PGOPTIONS restreint l’utilisation des journaux de transaction pour accélérer les écritures (données NON critiques ici). Le –no-vacuum est destiné à éviter que l’outil demande lui-même un VACUUM . Le test dure quelques minutes. Le relancer au besoin.) Après quelques minutes, pgbench affichera le nombre de transactions par seconde, bien sûr très dépendant de la machine :

transaction type: <builtin: TPC-B (sort of)> scaling factor: 1 query mode: simple number of clients: 20 number of threads: 2 number of transactions per client: 10000 number of transactions actually processed: 200000/200000 latency average = 58.882 ms

Exemple 5-410 Exemple extrait du PDF, page 398.
tps = 339.663189 (including connections establishing)
tps = 339.664978 (excluding connections establishing)
(Optionnel) C’est l’occasion d’installer l’outil pg_activity depuis les dépôts du PGDG (il peut y avoir besoin du dépôt EPEL) et de le lancer en tant que postgres pour voir ce qui se passe dans la base. Pour pg_activity :
Exemple 5-411 Exemple extrait du PDF, page 398.
$ sudo yum install epel-release
$ sudo yum install pg_activity
Il se lance ainsi :
Exemple 5-412 Exemple extrait du PDF, page 398.
$ sudo -iu postgres pg_activity
Le premier écran affiche les sessions en cours, le deuxième celles en attente de libération d’un verrou, le troisième celles qui en bloquent d’autres. Noter que la session restée délibérément ouverte n’est pas bloquante. Comparer les nouvelles tailles des tables (taille sur le disque et nombre de lignes). La table pg_stat_user_tables contient l’activité sur chaque table. Comment s’expliquent les évolutions? La volumétrie des tables a explosé :
Exemple 5-413 Exemple extrait du PDF, page 398.
testperf@pgbench=> \d+
 Schéma |       Nom        | Type | Propriétaire | Taille | Description
--------+------------------+-------+--------------+---------+-------------
 public | pgbench_accounts | table | testperf     | 39 MB   |
 public | pgbench_branches | table | testperf     | 7112 kB |
 public | pgbench_history | table | testperf      | 10 MB   |
 public | pgbench_tellers | table | testperf      | 8728 kB |
Tableau 5-121 Tableau ou résultat de commande extrait du PDF, page 398.
Exemple 5-414 Exemple extrait du PDF, page 398.
On constate que le nombre de lignes reste le même malgré l’activité, sauf pour la table d’historique :
testperf@pgbench=> SELECT count(*) FROM pgbench_accounts;
count
--------
 100000
Tableau 5-122 Tableau ou résultat de commande extrait du PDF, page 398.
Exemple 5-415 Exemple extrait du PDF, page 398.
testperf@pgbench=> SELECT count(*) FROM pgbench_tellers;

count

-------
    10
Tableau 5-123 Tableau ou résultat de commande extrait du PDF, page 399.
Exemple 5-416 Exemple extrait du PDF, page 399.
testperf@pgbench=> SELECT count(*) FROM pgbench_branches;
count
-------
     1
Tableau 5-124 Tableau ou résultat de commande extrait du PDF, page 399.
Exemple 5-417 Exemple extrait du PDF, page 399.
testperf@pgbench=> SELECT count(*) FROM pgbench_history;
count
--------
 200000
Tableau 5-125 Tableau ou résultat de commande extrait du PDF, page 399.
Ce dernier chiffre dépend de l’activité réelle et du nombre de requêtes. Les statistiques d’activité de la table sont dans pg_stat_user_tables . Pour pgbench_accounts , la plus grosse table, on y trouve ceci :
Exemple 5-418 Exemple extrait du PDF, page 399.
testperf@pgbench=> SELECT * FROM pg_stat_user_tables ORDER BY relname \gx

-[ RECORD 1 ]-------+------------------------------
relid               | 17487
schemaname          | public
relname             | pgbench_accounts
seq_scan            | 6
seq_tup_read        | 300000
idx_scan            | 600000
idx_tup_fetch       | 600000
n_tup_ins           | 100000
n_tup_upd           | 200000
n_tup_del           | 0
n_tup_hot_upd       | 1120
n_live_tup          | 100000
n_dead_tup          | 200000
n_mod_since_analyze | 300000
last_vacuum         | 2021-09-04 18:51:31.889825+02
last_autovacuum     | NULL
last_analyze        | 2021-09-04 18:51:31.927611+02
last_autoanalyze    | NULL
vacuum_count        | 1
autovacuum_count    | 0
analyze_count       | 1
autoanalyze_count   | 0
Tableau 5-126 Tableau ou résultat de commande extrait du PDF, page 399.
Le champ n_tup_upd montre qu’il y a eu 200 000 mises à jour après l’insertion initiale de 100 000 lignes (champ n_tup_ins ). Il y a toujours 100 000 lignes visibles ( n_live_tup ). Le VACUUM a été demandé explicitement à la création ( last_vacuum ) mais n’est pas passé depuis. La VACUUM étant inhibé, il est normal que les lignes mortes se soient accumulées ( n_dead_tup ) : il y en a 200 000, ce sont les anciennes versions des lignes modifiées. Pour la table pgbench_history :

relid               | 17481
schemaname          | public
relname             | pgbench_history
seq_scan            | 4
seq_tup_read        | 200000
idx_scan            | NULL
idx_tup_fetch       | NULL
n_tup_ins           | 200000
n_tup_upd           | 0
n_tup_del           | 0
n_tup_hot_upd       | 0
n_live_tup          | 200000
n_dead_tup          | 0
Tableau 5-127 Tableau ou résultat de commande extrait du PDF, page 400.
Exemple 5-419 Exemple extrait du PDF, page 400.
-[ RECORD 3 ]-------+------------------------------
...
La table pgbench_history a subi 200 000 insertions et contient à présent 200 000 lignes : il est normal qu’elle ait grossi de 0 à 10 Mo. Pour la table pgbench_tellers :
relname             | pgbench_tellers
seq_scan            | 20383
seq_tup_read        | 117437
idx_scan            | 379620
idx_tup_fetch       | 379620
n_tup_ins           | 10
n_tup_upd           | 200000
Tableau 5-128 Tableau ou résultat de commande extrait du PDF, page 400.
n_live_tup          | 10
n_dead_tup          | 199979
n_mod_since_analyze | 200010
Tableau 5-129 Tableau ou résultat de commande extrait du PDF, page 400.
Exemple 5-420 Exemple extrait du PDF, page 400.
-[ RECORD 4 ]-------+------------------------------
...
...
...
Elle ne contient toujours que 10 lignes visibles ( n_live_up ), mais 199 979 lignes « mortes » ( n_dead_tup ). Même s’il n’a gêné aucune opération du point de vue de l’utilisateur, le verrou posé par la session en attente est visible dans la table des verrous pg_locks :
Exemple 5-421 Exemple extrait du PDF, page 400.
postgres@pgbench=# SELECT * FROM pg_locks
  WHERE relation = (SELECT relid FROM pg_stat_user_tables
  WHERE relname = 'pgbench_accounts' ) ;

-[ RECORD 1 ]------+----------------
locktype           | relation
database           | 16729
relation           | 17487
page               | NULL
tuple              | NULL
virtualxid         | NULL
transactionid      | NULL
classid            | NULL
objid              | NULL
Tableau 5-130 Tableau ou résultat de commande extrait du PDF, page 400.

objsubid           | NULL
virtualtransaction | 1/37748
pid                | 22581
mode               | AccessShareLock
granted            | t
fastpath           | t
waitstart          | 2021-09-04 19:01:27.824567+02
Tableau 5-131 Tableau ou résultat de commande extrait du PDF, page 401.
Nettoyage avec VACUUM FULL Exécuter un VACUUM FULL VERBOSE sur pgbench_tellers .
Exemple 5-422 Exemple extrait du PDF, page 401.
postgres@pgbench=# VACUUM FULL VERBOSE pgbench_tellers ;

INFO: vacuuming "public.pgbench_tellers"
INFO: "pgbench_tellers": found 200000 removable, 10 nonremovable row versions in
   1082 pages
DÉTAIL : 0 dead row versions cannot be removed yet.
CPU: user: 0.03 s, system: 0.00 s, elapsed: 0.03 s. VACUUM Un \d+ indique que la taille de la table est bien retombée à 8 ko (1 bloc), ce qui suffit pour 10 lignes. Exécuter un VACUUM FULL VERBOSE sur pgbench_accounts . Si celui-ci reste bloqué, il faudra sans doute arrêter la transaction restée ouverte plus haut.
Exemple 5-423 Exemple extrait du PDF, page 401.
postgres@pgbench=# VACUUM FULL VERBOSE pgbench_accounts ;

INFO:  vacuuming "public.pgbench_accounts"
INFO:  "pgbench_accounts": found 200000 removable,100000 nonremovable row versions
       in 4925 pages
DÉTAIL : 0 dead row versions cannot be removed yet.
CPU: user: 0.09 s, system: 0.06 s, elapsed: 0.17 s. VACUUM Durée : 16411,719 ms (00:16,412) Soit : 100 000 lignes conservées, 200 000 mortes supprimées dans 4925 blocs (39 Mo). Effectuer un VACUUM FULL VERBOSE . Quel est l’impact sur la taille de la base? Même les tables système seront nettoyées :
Exemple 5-424 Exemple extrait du PDF, page 401.
postgres@pgbench=> VACUUM FULL VERBOSE ;

INFO: vacuuming "pg_catalog.pg_statistic"
INFO: "pg_statistic": found 11 removable, 433 nonremovable row versions in 20 pages
DÉTAIL : 0 dead row versions cannot be removed yet.
...
INFO: vacuuming "public.pgbench_branches"
INFO: "pgbench_branches": found 200000 removable, 1 nonremovable row versions in
   885 pages
DÉTAIL : 0 dead row versions cannot be removed yet.
CPU: user: 0.03 s, system: 0.00 s, elapsed: 0.03 s.

Exemple 5-425 Exemple extrait du PDF, page 402.
INFO: vacuuming "public.pgbench_history"
INFO: "pgbench_history": found 0 removable, 200000 nonremovable row versions in
   1281 pages
DÉTAIL : 0 dead row versions cannot be removed yet.
CPU: user: 0.11 s, system: 0.02 s, elapsed: 0.13 s.
Exemple 5-426 Exemple extrait du PDF, page 402.
INFO: vacuuming "public.pgbench_tellers"
INFO: "pgbench_tellers": found 0 removable, 10 nonremovable row versions in 1 pages
DÉTAIL : 0 dead row versions cannot be removed yet.
CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s.
Exemple 5-427 Exemple extrait du PDF, page 402.
INFO: vacuuming "public.pgbench_accounts"
INFO: "pgbench_accounts": found 0 removable, 100000 nonremovable row versions in
   1640 pages
DÉTAIL : 0 dead row versions cannot be removed yet.
CPU: user: 0.03 s, system: 0.01 s, elapsed: 0.05 s. VACUUM Seule pgbench_branches était encore à nettoyer (1 ligne conservée). La taille de la base retombe à 32 Mo selon \l+ . Elle faisait au départ 22 Mo, et 10 Mo ont été ajoutés dans pgbench_history . Truncate ou Delete? Créer copie1 et copie2 comme des copies de pgbench_accounts , données incluses.
Exemple 5-428 Exemple extrait du PDF, page 402.
postgres@pgbench=# CREATE TABLE copie1 AS SELECT * FROM pgbench_accounts ;
SELECT 100000
Exemple 5-429 Exemple extrait du PDF, page 402.
postgres@pgbench=# CREATE TABLE copie2 AS SELECT * FROM pgbench_accounts ;
SELECT 100000 Effacer le contenu de copie1 avec DELETE .
Exemple 5-430 Exemple extrait du PDF, page 402.
postgres@pgbench=# DELETE FROM copie1 ;
DELETE 100000 Effacer le contenu de copie2 avec TRUNCATE .
Exemple 5-431 Exemple extrait du PDF, page 402.
postgres@pgbench=# TRUNCATE copie2 ;
TRUNCATE TABLE Quelles sont les tailles de ces deux tables après ces opérations?
Exemple 5-432 Exemple extrait du PDF, page 402.
postgres@pgbench=# \d+
Liste des relations
 Schéma |       Nom        | Type | Propriétaire | Taille | Description
--------+------------------+-------+--------------+---------+-------------
 public | copie1           | table | postgres     | 13 MB   |
 public | copie2           | table | postgres     | 0 bytes |
Tableau 5-132 Tableau ou résultat de commande extrait du PDF, page 402.
Exemple 5-433 Exemple extrait du PDF, page 402.
...

Pour une purge complète, TRUNCATE est à préférer : il vide la table et rend l’espace au système. DELETE efface les lignes mais l’espace n’est pas encore rendu.

5.12.8 Réactivation de l’autovacuum

Réactiver l’autovacuum de l’instance. Dans postgresql.conf :

Exemple 5-434 Exemple extrait du PDF, page 403.
autovacuum = on

postgres@pgbench=# SELECT pg_reload_conf() ;
pg_reload_conf
----------------
 t
Tableau 5-133 Tableau ou résultat de commande extrait du PDF, page 403.
Exemple 5-435 Exemple extrait du PDF, page 403.
postgres@pgbench=# SHOW autovacuum;
autovacuum
------------
 on
Tableau 5-134 Tableau ou résultat de commande extrait du PDF, page 403.
Attendre quelques secondes et voir si copie1 change de taille. Après quelques instants, la taille de copie1 (qui avait été vidée plus tôt avec DELETE ) va redescendre à quelques kilooctets. Le passage de l’autovacuum en arrière-plan est tracé dans last_autovacuum :
Exemple 5-436 Exemple extrait du PDF, page 403.
postgres@pgbench=# SELECT * FROM pg_stat_user_tables WHERE relname ='copie1' \gx

-[ RECORD 1 ]-------+------------------------------
relid               | 18920
schemaname          | public
relname             | copie1
seq_scan            | 1
seq_tup_read        | 100000
idx_scan            |
idx_tup_fetch       |
n_tup_ins           | 100000
n_tup_upd           | 0
n_tup_del           | 100000
n_tup_hot_upd       | 0
n_live_tup          | 0
n_dead_tup          | 0
n_mod_since_analyze | 0
last_vacuum         |
last_autovacuum     | 2019-07-17 14:04:21.238296+01
last_analyze        |
last_autoanalyze    | 2019-07-17 14:04:21.240525+01
vacuum_count        | 0
autovacuum_count    | 1
analyze_count       | 0
autoanalyze_count   | 1
Tableau 5-135 Tableau ou résultat de commande extrait du PDF, page 403.

5.12.9 Statistiques des données

But : Savoir trouver les statistiques des données et les mettre à jour Créer une table copie3 , copie de pgbench_accounts .

Exemple 5-437 Exemple extrait du PDF, page 404.
CREATE TABLE copie3 AS SELECT * FROM pgbench_accounts ;
SELECT 100000 Dans la vue système pg_stats , afficher les statistiques collectées pour la table copie3 .
Exemple 5-438 Exemple extrait du PDF, page 404.
postgres@pgbench=# SELECT * FROM pg_stats WHERE tablename = 'copie3' ;
(0 ligne) L’autovacuum n’est pas passé, les statistiques ne sont pas encore présentes. Noter que, même activé, il n’aurait pas forcément eu le temps de passer entre les deux ordres précédents. Lancer la collecte des statistiques pour cette table uniquement. La collecte se déclenche avec la commande ANALYZE :
Exemple 5-439 Exemple extrait du PDF, page 404.
postgres@pgbench=# ANALYZE VERBOSE copie3 ;

INFO:   analyzing "public.copie3"
INFO:   "copie3": scanned 1640 of 1640 pages,
        containing 100000 live rows and 0 dead rows;
        30000 rows in sample, 100000 estimated total rows
ANALYZE
Exemple 5-440 Exemple extrait du PDF, page 404.
30 000 lignes font partie de l’échantillonnage.
  Afficher de nouveau les statistiques.

SELECT * FROM pg_stats WHERE tablename = 'copie3' ;
Cette fois, la vue pg_stats renvoie des informations, colonne par colonne. Le champ aid est la clé primaire, ses valeurs sont toutes différentes. L’histogramme des valeurs compte 100 valeurs qui délimite autant de buckets. Ils sont là régulièrement répartis, ce qui indique une répartition homogène des valeurs.
Exemple 5-441 Exemple extrait du PDF, page 404.
SELECT * FROM pg_stats WHERE tablename = 'copie3' ;

-[ RECORD 1 ]-------------------------------------------------
schemaname             | public
tablename              | copie3
attname                | aid
Tableau 5-136 Tableau ou résultat de commande extrait du PDF, page 404.

inherited              | f
null_frac              | 0
avg_width              | 4
n_distinct             | -1
most_common_vals       |
most_common_freqs      |
histogram_bounds       | {2,1021,2095,3098,4058,5047,6120,
Tableau 5-137 Tableau ou résultat de commande extrait du PDF, page 405.
7113,8058,9075,10092,11090,12061,13064,14053,15091,16106, 17195,18234,19203,20204,21165,22183,23156,24162,25156,26192, 27113,28159,29193,30258,31260,32274,33316,34346,35350,36281, 37183,38158,39077,40007,41070,42084,43063,44064,45101,46089, 47131,48189,49082,50100,51157,52113,53009,54033,55120,56114, 57066,58121,59111,60122,61088,62151,63217,64195,65168,66103, 67088,68126,69100,70057,71104,72105,73092,73994,75007,76067, 77092,78141,79180,80165,81100,82085,83094,84107,85200,86242, 87246,88293,89288,90286,91210,92197,93172,94084,95070,96086, 97067,98031,99032,99998}
correlation            | 1
most_common_elems      |
most_common_elem_freqs |
elem_count_histogram   |
Tableau 5-138 Tableau ou résultat de commande extrait du PDF, page 405.
Autre exemple, le champ bid : on voit qu’il ne possède qu’une seule valeur.
Exemple 5-442 Exemple extrait du PDF, page 405.
-[ RECORD 2 ]-------------------------------------------------
schemaname             | public
tablename              | copie3
attname                | bid
inherited              | f
null_frac              | 0
avg_width              | 4
n_distinct             | 1
most_common_vals       | {1}
most_common_freqs      | {1}
histogram_bounds       |
correlation            | 1
most_common_elems      |
most_common_elem_freqs |
elem_count_histogram   |
Tableau 5-139 Tableau ou résultat de commande extrait du PDF, page 405.
De même, on pourra vérifier que le champ filler a une taille moyenne de 85 octets, ou voir la répartition des valeurs du champ abalance .

5.12.10 Réindexation

But : Réindexer Recréer les index de la table pgbench_accounts . La réindexation d’une table se fait de la manière suivante :

Exemple 5-443 Exemple extrait du PDF, page 406.
postgres@pgbench=# REINDEX (VERBOSE) TABLE pgbench_accounts ;

INFO: index "pgbench_accounts_pkey" was reindexed
DÉTAIL : CPU: user: 0.05 s, system: 0.00 s, elapsed: 0.08 s
REINDEX Comment recréer tous les index de la base pgbench?
Exemple 5-444 Exemple extrait du PDF, page 406.
postgres@pgbench=# REINDEX (VERBOSE) DATABASE pgbench ;

...
INFO: index "pg_shseclabel_object_index" was reindexed
DÉTAIL : CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.01 s
INFO: index "pg_toast_3592_index" was reindexed
DÉTAIL : CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.01 s
INFO: table "pg_catalog.pg_shseclabel" was reindexed
INFO: index "pgbench_branches_pkey" was reindexed
DÉTAIL : CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.01 s
INFO: table "public.pgbench_branches" was reindexed
INFO: index "pgbench_tellers_pkey" was reindexed
DÉTAIL : CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.01 s
INFO: table "public.pgbench_tellers" was reindexed
INFO: index "pgbench_accounts_pkey" was reindexed
DÉTAIL : CPU: user: 0.07 s, system: 0.01 s, elapsed: 0.12 s
INFO: table "public.pgbench_accounts" was reindexed
REINDEX Comment recréer uniquement les index des tables systèmes? Pour réindexer uniquement les tables systèmes :
Exemple 5-445 Exemple extrait du PDF, page 406.
postgres@pgbench=# REINDEX SYSTEM pgbench ;
Quelle est la différence entre la commande REINDEX et la séquence DROP INDEX + CREATE INDEX ? REINDEX est similaire à une suppression et à une nouvelle création de l’index. Cependant, les conditions de verrouillage sont différentes :

  • REINDEX verrouille les écritures mais pas les lectures de la table mère de l’index. Il prend aussi un verrou exclusif sur l’index en cours de traitement, ce qui bloque les lectures qui tentent d’utiliser l’index.
  • Au contraire, DROP INDEX crée temporairement un verrou exclusif sur la table parent, bloquant ainsi écritures et lectures. Le CREATE INDEX qui suit verrouille les écritures mais pas les lectures; comme l’index n’existe pas, aucune lecture ne peut être tentée, signifiant qu’il n’y a aucun blocage et que les lectures sont probablement forcées de réaliser des parcours séquentiels complets.

5.12.11 Traces

But : Gérer les fichiers de traces Quelle est la méthode de gestion des traces utilisée par défaut? Par défaut, le mode de journalisation est stderr :

Exemple 5-446 Exemple extrait du PDF, page 407.
postgres@pgbench=# SHOW log_destination ;
log_destination
-----------------
 stderr
Tableau 5-140 Tableau ou résultat de commande extrait du PDF, page 407.
Paramétrer le programme interne de rotation des journaux :

  • modifier le fichier postgresql.conf pour utiliser le logging collector ;
  • les traces doivent désormais être sauvegardés dans le répertoire /var/lib/pgsql/traces ;
  • la rotation des journaux doit être automatisée pour générer un nouveau fichier de logs toutes les 30 minutes, quelle que soit la quantité de logs archivés; le nom du fichier devra donc comporter les minutes.
  • Tester en forçant des rotations avec la fonction pg_rotate_logfile .
  • Augmenter la trace (niveau info ). Sur Red Hat/CentOS/Rocky Linux, le collecteur des traces (logging collector) est activé par défaut dans postgresql.conf (mais ce ne sera pas le cas sur un environnement Debian ou avec une installation compilée, et il faudra redémarrer pour l’activer) :
    Exemple 5-447 Exemple extrait du PDF, page 407.
    logging_collector = on
    On crée le répertoire, où postgres doit pouvoir écrire :
    Exemple 5-448 Exemple extrait du PDF, page 407.
    $ sudo mkdir -m700 /var/lib/pgsql/traces
    $ sudo chown postgres: /var/lib/pgsql/traces
    
    Puis paramétrer le comportement du récupérateur :
    Exemple 5-449 Exemple extrait du PDF, page 407.
    log_directory = '/var/lib/pgsql/traces'
    log_filename = 'postgresql-%Y-%m-%d_%H-%M.log'
    log_rotation_age = 30min
    log_rotation_size = 0
    log_min_messages = info
    Recharger la configuration et voir ce qui se passe dans /var/lib/pgsql/traces :
    Exemple 5-450 Exemple extrait du PDF, page 407.
    $ sudo systemctl reload postgresql-12
    $ sudo watch -n 5 ls -lh /var/lib/pgsql/traces
    

Dans une autre fenêtre, générer un peu d’activité, avec pgbench ou tout simplement avec :

Exemple 5-451 Exemple extrait du PDF, page 408.
postgres@pgbench=# SELECT 1 ;
postgres@pgbench=# \watch 1
Les fichiers générés doivent porter un nom ressemblant à postgresql-2019-08-02_16-55.log . Pour forcer le passage à un nouveau fichier de traces :
Exemple 5-452 Exemple extrait du PDF, page 408.
postgres@pgbench=# SELECT pg_rotate_logfile() ;
Comment éviter l’accumulation des fichiers?

  • La première méthode consiste à avoir un log_filename cyclique. C’est le cas par défaut sur Red Hat/CentOS/Rocky Linux avec postgresql-%a , qui reprend les jours de la semaine. Noter qu’il n’est pas forcément garanti qu’un postgresql-%H-%M.log planifié toutes les 5 minutes écrase les fichiers de la journée précédente. En pratique, on descend rarement en-dessous de l’heure.
  • Utiliser l’utilitaire logrotate , fourni avec toute distribution Linux, dont le travail est de gérer rotation, compression et purge. Il est activé par défaut sur Debian.
  • Enfin, on peut rediriger les traces vers un système externe.

Source : manuel complet PDF, révision 26.09, pages PDF 279-408.