Ce chapitre définit l'interface entre le cœur du système de PostgreSQL™ et les méthodes d'accès aux index, qui gérent chaque type d'index. Le système principal ne sait rien des index en dehors de ce qui est spécifié ici. Il est donc possible de développer des types d'index entièrement nouveaux en écrivant du code supplémentaire.
Tous les index de PostgreSQL™ sont techniquement des index secondaires ; c'est-à-dire que l'index est séparé physiquement du fichier de la table qu'il décrit. Chaque index est stocké dans sa propre relation physique et est donc décrit par une entrée dans le catalogue pg_class. Le contenu d'un index est entièrement contrôlé par la méthode d'accès à l'index. En pratique, toutes les méthodes d'accès aux index les divisent en pages de taille standard de façon à utiliser le gestionnaire de stockage et le gestionnaire de tampon pour accéder au contenu de l'index. (De plus, toutes les méthodes existantes d'accès aux index utilisent la disposition de page standard décrite dans Section 66.6, « Emplacement des pages de la base de données » et la plupart ont le même format pour les en-têtes de ligne de l'index ; mais ce ne sont pas des obligations pour toutes les méthodes d'accès.)
Dans les faits, un index est une correspondance entre certaines valeurs de données clés et les identifiants des lignes (tuple identifiers, ou TIDs), dans leurs différentes versions, dans la table parente de l'index. Un TID consiste en un numéro de bloc et un numéro d'élément dans ce bloc (voir Section 66.6, « Emplacement des pages de la base de données »). L'information est suffisante pour récupérer une version d'une ligne particulière à partir de la table. Les index n'ont pas directement connaissance de l'existence éventuelle, à cause du MVCC, de plusieurs versions de la même ligne logique ; pour un index, chaque ligne est un objet indépendant qui a besoin de sa propre entrée. En conséquence, la mise à jour d'une ligne crée toujours de nouvelles entrées dans l'index pour cette ligne, même si les valeurs de la clé ne changent pas. (Les lignes HOT sont une exception ; mais les index ne s'en occupent pas). Les entrées d'index pour les lignes mortes sont nettoyées (par le VACUUM) lorsque les lignes mortes elles-même sont nettoyées.
Chaque méthode d'accès à un index est décrite par une ligne dans le catalogue système pg_am. Elle indique un nom et une fonction gestionnaire pour la méthode d'accès. Ces entrées peuvent être créées et supprimées en utilisant les commandes SQL CREATE ACCESS METHOD(7) et DROP ACCESS METHOD(7) respectivement.
Une fonction gestionnaire de méthode d'accès aux index doit être déclarée avec un seul argument de type internal et en retour le pseudo-type index_am_handler. L'argument est une valeur sans utilité sinon pour empêcher les fonctions gestionnaires d'être appelées directement à partir d'une commande SQL. Le résultat de la fonction doit être une structure, allouée avec palloc, de type IndexAmRoutine, et contenant tout ce que le code interne a besoin de savoir pour utiliser la méthode d'accès à l'index. La structure IndexAmRoutine, aussi appelée API struct de la méthode, inclut les champs spécifiant les propriétés fixes de la méthode d'accès, comme le support des index multi-colonnes. Plus important, elle contient les pointeurs vers les fonctions de la méthode d'accès, qui se chargent de tout le travail d'accès aux index. Ces fonctions de support sont de simples fonctions en C et ne sont ni visibles ni appelables au niveau SQL. Elles sont décrites dans Section 60.2, « Fonctions des méthode d'accès aux index ».
La structure IndexAmRoutine est définie ainsi :
typedef struct IndexAmRoutine { NodeTag type; /* * Nombre total de stratégies (opérateurs) par lequels nous pouvons * traverser la méthode d'accès ou chercher dedans. Zéro si la méthode * n'a pas de jeu de stratégies fixé. */ uint16 amstrategies; /* nombre total de fonctions support utilisées par cette méthode d'accès */ uint16 amsupport; /* la méthode supporte-t-elle un ORDER BY sur la colonne indexée ? */ bool amcanorder; /* la méthode supporte-t-elle un ORDER BY sur le résultat d'un opérateur appliqué à une colonne indexée ? */ bool amcanorderbyop; /* la méthode supporte-t-elle le parcours à rebours ? */ bool amcanbackward; /* la méthode supporte-t-elle les index UNIQUE ? */ bool amcanunique; /* la méthode supporte-t-elle les index multi-colonnes ? */ bool amcanmulticol; /* la méthode exige-t-elle un parcours pour une contrainte sur la première colonne de l'index ? */ bool amoptionalkey; /* la méthode gère-t-elle les qualificatifs ScalarArrayOpExpr ? */ bool amsearcharray; /* la méthode gère-elle les qualificatifs IS NULL/IS NOT NULL ? */ bool amsearchnulls; /* le type de la valeur dans l'index peut-elle différer du type de la colonne ? */ bool amstorage; /* un index de ce type peut-il être la cible de la commande CLUSTER ? */ bool amclusterable; /* la méthode gère-t-elle les verrous sur prédicat ? */ bool ampredlocks; /* does AM support parallel scan? */ bool amcanparallel; /* type de données stocké dans l'index, ou InvalidOid si variable */ Oid amkeytype; /* fonctions d'interfaçage */ ambuild_function ambuild; ambuildempty_function ambuildempty; aminsert_function aminsert; ambulkdelete_function ambulkdelete; amvacuumcleanup_function amvacuumcleanup; amcanreturn_function amcanreturn; /* peut être NULL */ amcostestimate_function amcostestimate; amoptions_function amoptions; amproperty_function amproperty; /* peut être NULL */ amvalidate_function amvalidate; ambeginscan_function ambeginscan; amrescan_function amrescan; amgettuple_function amgettuple; /* peut être NULL */ amgetbitmap_function amgetbitmap; /* peut être NULL */ amendscan_function amendscan; ammarkpos_function ammarkpos; /* peut être NULL */ amrestrpos_function amrestrpos; /* peut être NULL */ /* interface functions to support parallel index scans */ amestimateparallelscan_function amestimateparallelscan; /* can be NULL */ aminitparallelscan_function aminitparallelscan; /* can be NULL */ amparallelrescan_function amparallelrescan; /* can be NULL */ } IndexAmRoutine;
Pour être utile, une méthode d'accès à l'index doit aussi avoir une ou plusieurs familles d'opérateurs et classes d'opérateurs définies dans pg_opfamily, pg_opclass, pg_amop et pg_amproc. Ces entrées permettent au planificateur de déterminer les types de requêtes qui peuvent être utilisés avec les index de cette méthode d'accès. Les familles et classes d'opérateurs sont décrites dans Section 37.14, « Interfacer des extensions d'index », qui est un élément requis pour comprendre ce chapitre.
Un index individuel est défini par une entrée dans pg_class en tant que relation physique, et une entrée dans pg_index affichant son contenu logique -- c'est-à-dire ses colonnes et leur sémantique, telles que récupérées par les classes d'opérateurs associées. Les colonnes de l'index (valeurs clés) peuvent être de simples colonnes de la table sous-jacente ou des expressions des lignes. Habituellement, la méthode d'accès ne s'intéresse pas à la provenance des valeurs clés (elles lui arrivent toujours pré-calculées), mais plutôt aux informations de la classe d'opérateurs dans pg_index. On peut accéder aux entrées de ces deux catalogues via la structure de données Relation passée à toute opération sur l'index.
Certains champs de IndexAmRoutine ont des implications peu évidentes. Les besoins de amcanunique sont discutés dans Section 60.5, « Vérification de l'unicité par les index ». L'option amcanmulticol indique que la méthode d'accès supporte les index multi-colonnes alors que amoptionalkey autorise des parcours lorsqu'aucune restriction indexable n'est fournie pour la première colonne de l'index. Quand amcanmulticol est faux, amoptionalkey indique essentiellement que la méthode d'accès autorise les parcours complets de l'index sans clause de restriction. Les méthodes d'accès supportant les colonnes multiples doivent supporter les parcours sans restriction sur une ou toutes les colonnes après la première ; néanmoins, elles peuvent imposer une restriction sur la première colonne de l'index, ce qui est signalé par amoptionalkey à false . Une raison pour une méthode d'accès d'index d'initialiser amoptionalkey à false est de ne pas indexer les valeurs NULL. Comme la plupart des opérateurs indexables sont stricts et ne peuvent donc pas renvoyer true pour des entrées NULL, à première vue on ne voudra pas stocker d'entrées pour les valeurs NULL : un parcours d'index ne peut de toute façon pas les retourner. Néanmoins, cette raison ne vaut pas pour un parcours d'index sans restriction pour une colonne d'index donnée. En pratique, cela signifie que les index avec amoptionalkey à true doivent indexer les valeurs NULL, car le planificateur peut décider de les utiliser sans aucune clé de parcours. Une limite liée : une méthode d'accès qui supporte des colonnes multiples doit supporter l'indexation des NULL dans les colonnes après la première, car le planificateur supposera l'index utilisable avec les requêtes ne restreignant pas ces colonnes. Par exemple, considérons un index sur (a,b) et une requête avec WHERE a = 4. Le système supposera que l'index est utilisable pour les lignes où a = 4, ce qui est faux si l'index omet les lignes où b est NULL. Néanmoins, on peut omettre les lignes où la première colonne indexée est NULL. Du coup, une méthode d'accès d'index ne s'occupant pas des valeurs NULL peut aussi affecter amsearchnulls à true, indiquant ainsi qu'elle supporte les clauses IS NULL et IS NOT NULL dans les conditions de recherche.