

Cette section fournit un aperçu du format des pages utilisées par les tables et index de PostgreSQL.[16] Les séquences et les tables TOAST sont formatées comme des tables standards.
Dans l'explication qui suit, un octet contient huit bits. De plus, le terme élément fait référence à une valeur de données individuelle qui est stockée dans une page. Dans une table, un élément est une ligne ; dans un index, un élément est une entrée d'index.
   Chaque table et index est stocké comme un tableau de pages d'une
   taille fixe (habituellement 8 ko, bien qu'une taille de page différente
   peut être sélectionnée lors de la compilation du serveur). Dans une table,
   toutes les pages sont logiquement équivalentes pour qu'un élément (ligne)
   particulier puisse être stocké dans n'importe quelle page. Dans les index, la
   première page est généralement réservée comme métapage contenant
   des informations de contrôle, et il peut exister différents types de pages à
   l'intérieur de l'index, suivant la méthode d'accès à l'index. Les tables ont
   aussi une carte de visibilité dans un fichier de suffixe _vm,
   pour tracer les pages dont on sait qu'elles ne contiennent pas de lignes mortes
   et qui n'ont pas du coup besoin de VACUUM.
  
Tableau 69.2 affiche le contenu complet d'une page. Il existe cinq parties pour chaque page.
Tableau 69.2. Disposition générale d'une page
| Élément | Description | 
|---|---|
| PageHeaderData | Longueur de 24 octets. Contient des informations générales sur la page y compris des pointeurs sur les espaces libres. | 
| ItemIdData | Tableau d'identifiants d'élément pointant vers les éléments réels. Chaque enregistrement est une paire (décalage, longueur). 4 octets par élément. | 
| Free space | L'espace non alloué. Les pointeurs de nouveaux éléments sont alloués à partir du début de cette région, les nouveaux éléments à partir de la fin. | 
| Items | Les éléments eux-mêmes. | 
| Special space | Données spécifiques des méthodes d'accès aux index. Différentes méthodes stockent différentes données. Vide pour les tables ordinaires. | 
   Les 24 premiers octets de chaque page consistent en un en-tête de page
   (PageHeaderData). Son format est détaillé dans
   Tableau 69.3. Le premier champ trace l'entrée la
   plus récente dans les journaux de transactions pour cette page. Le deuxième
   champ contient la somme de contrôle de la page si data checksums est activé. Ensuite se trouve un champ
   sur deux octets contenant des drapeaux. Il est suivi de champs entiers sur
   deux octets (pd_lower,
   pd_upper et
   pd_special). Ils contiennent les décalages en
   octets du début de page vers le début de l'espace non alloué, vers la fin de
   l'espace non alloué et vers le début de l'espace spécial. Les deux octets
   suivants de l'en-tête de page,
   pd_pagesize_version, enregistrent la taille de la
   page et un indicateur de version. À partir de la version 8.3 de
   PostgreSQL, le numéro de version est 4 ;
   PostgreSQL 8.1 et 8.2 ont utilisé le numéro de version 3 ;
   PostgreSQL 8.0 a utilisé le numéro de version 2 ;
   PostgreSQL 7.3 et 7.4 ont utilisé le numéro de
   version 1 ; les versions précédentes utilisaient le numéro de version 0.
   (La disposition fondamentale de la page et le format de l'en-tête n'ont pas changé
   dans la plupart de ces versions mais la disposition de l'en-tête des lignes de tête a
   changé.) La taille de la page est seulement présente comme vérification
   croisée ; il n'existe pas de support pour avoir plus d'une taille de
   page dans une installation.
   Le dernier champ est une aide indiquant si traiter la page serait
   profitable : il garde l'information sur le plus vieux XMAX non traité
   de la page.
  
Tableau 69.3. Disposition de PageHeaderData
| Champ | Type | Longueur | Description | 
|---|---|---|---|
| pd_lsn | PageXLogRecPtr | 8 octets | LSN : octet suivant le dernier octet de l'enregistrement WAL pour la dernière modification de cette page | 
| pd_checksum | uint16 | 2 octets | Somme de contrôle de la page | 
| pd_flags | uint16 | 2 octets | Bits d'état | 
| pd_lower | LocationIndex | 2 octets | Décalage jusqu'au début de l'espace libre | 
| pd_upper | LocationIndex | 2 octets | Décalage jusqu'à la fin de l'espace libre | 
| pd_special | LocationIndex | 2 octets | Décalage jusqu'au début de l'espace spécial | 
| pd_pagesize_version | uint16 | 2 octets | Taille de la page et disposition de l'information du numéro de version | 
| pd_prune_xid | TransactionId | 4 bytes | Plus vieux XMAX non traité sur la page, ou zéro si aucun | 
   Tous les détails se trouvent dans
   src/include/storage/bufpage.h.
  
   Après l'en-tête de la page se trouvent les identificateurs d'élément
   (ItemIdData), chacun nécessitant quatre octets. Un identificateur
   d'élément contient un décalage d'octet vers le début d'un élément, sa
   longueur en octets, et quelques bits d'attributs qui affectent son
   interprétation. Les nouveaux identificateurs d'éléments sont alloués si
   nécessaire à partir du début de l'espace non alloué. Le nombre d'identificateurs
   d'éléments présents peut être déterminé en regardant
   pd_lower, qui est augmenté pour allouer un nouvel
   identificateur. Comme un identificateur d'élément n'est jamais déplacé tant qu'il
   n'est pas libéré, son index pourrait être utilisé sur une base à long terme
   pour référencer un élément, même quand l'élément lui-même est déplacé le long de
   la page pour compresser l'espace libre. En fait, chaque pointeur vers un
   élément (ItemPointer, aussi connu sous le nom de
   CTID), créé par PostgreSQL consiste
   en un numéro de page et l'index de l'identificateur d'élément.
  
   Les éléments eux-mêmes sont stockés dans l'espace alloué en marche arrière,
   à partir de la fin de l'espace non alloué. La structure exacte varie
   suivant le contenu de la table. Les tables et les séquences utilisent toutes
   les deux une structure nommée HeapTupleHeaderData, décrite
   ci-dessous.
  
   La section finale est la « section spéciale » qui pourrait
   contenir tout ce que les méthodes d'accès souhaitent stocker. Par exemple,
   les index b-tree stockent des liens vers les enfants gauche et droit de la
   page ainsi que quelques autres données sur la structure de l'index. Les
   tables ordinaires n'utilisent pas du tout de section spéciale (indiquée
   en configurant pd_special à la taille de la page).
  
Figure 69.1 illustre comment ces parties sont placées dans un bloc.
Figure 69.1. Disposition d'une page
    Toutes les lignes de la table sont structurées de la même façon. Il existe
    un en-tête à taille fixe (occupant 23 octets sur la plupart des machines),
    suivi par un bitmap NULL optionnel, un champ ID de l'objet optionnel et les
    données de l'utilisateur. L'en-tête est détaillé dans Tableau 69.4. Les données réelles de l'utilisateur
    (les colonnes de la ligne) commencent au décalage indiqué par
    t_hoff, qui doit toujours être un multiple de la distance
    MAXALIGN pour la plateforme. Le bitmap NULL est seulement présent si le bit
    HEAP_HASNULL est initialisé dans
    t_infomask. S'il est présent, il commence juste
    après l'en-tête fixe et occupe suffisamment d'octets pour avoir un bit par colonne
    de données (c'est-à-dire le nombre de bits identique au nombre d'attribut dans
    t_infomask2). Dans cette
    liste de bits, un bit 1 indique une valeur non NULL, un bit 0 une valeur
    NULL. Quand le bitmap n'est pas présent, toutes les colonnes sont supposées
    non NULL. L'ID de l'objet est seulement présent si le bit
    HEAP_HASOID_OLD est initialisé dans
    t_infomask. S'il est présent, il apparaît juste
    avant la limite t_hoff. Tout ajout nécessaire pour faire
    de t_hoff un multiple de MAXALIGN apparaîtra entre le
    bitmap NULL et l'ID de l'objet. (Ceci nous assure en retour que l'ID de
    l'objet est convenablement aligné.)
   
Tableau 69.4. Disposition de HeapTupleHeaderData
| Champ | Type | Longueur | Description | 
|---|---|---|---|
| t_xmin | TransactionId | 4 octets | XID d'insertion | 
| t_xmax | TransactionId | 4 octets | XID de suppression | 
| t_cid | CommandId | 4 octets | CID d'insertion et de suppression (surcharge avec t_xvac) | 
| t_xvac | TransactionId | 4 octets | XID pour l'opération VACUUM déplaçant une version de ligne | 
| t_ctid | ItemPointerData | 6 octets | TID en cours pour cette version de ligne ou pour une version plus récente | 
| t_infomask2 | uint16 | 2 octets | nombre d'attributs et quelques bits d'état | 
| t_infomask | uint16 | 2 octets | différents bits d'options (flag bits) | 
| t_hoff | uint8 | 1 octet | décalage vers les données utilisateur | 
    Tous les détails sont disponibles dans
    src/include/access/htup_details.h.
   
    Interpréter les données réelles peut seulement se faire avec des informations
    obtenues à partir d'autres tables, principalement
    pg_attribute. Les valeurs clés nécessaires pour
    identifier les emplacements des champs sont
    attlen et attalign.
    Il n'existe aucun moyen pour obtenir directement un attribut particulier,
    sauf quand il n'y a que des champs de largeur fixe et aucune colonne NULL.
    Tout ceci est emballé dans les fonctions
    heap_getattr, fastgetattr
    et heap_getsysattr.
   
    Pour lire les données, vous avez besoin d'examinez chaque attribut à son
    tour. Commencez par vérifier si le champ est NULL en fonction du bitmap NULL.
    S'il l'est, allez au suivant. Puis, assurez-vous que vous avez le bon
    alignement. Si le champ est un champ à taille fixe, alors tous les octets
    sont placés simplement. S'il s'agit d'un champ à taille variable
    (attlen = -1), alors c'est un peu plus compliqué. Tous les types de données
    à longueur variable partagent la même structure commune d'en-tête,
    struct varlena, qui inclut la longueur totale de la valeur stockée
    et quelques bits d'option. Suivant les options, les données pourraient être
    soit dans la table de base soit dans une table TOAST ;
    elles pourraient aussi être compressées (voir Section 69.2).
   
[16] 
     Actuellement, l'utilisation de ce format de page n'est pas requis, que ce
     soit pour les méthodes d'accès aux tables comme aux index. La méthode
     d'accès aux tables heap utilise toujours ce format.
     Toutes les méthodes existantes d'accès aux index utilisent le format
     basique mais les données conservées dans les pages de métadonnées ne
     suivent généralement pas les règles de placement des éléments.