PostgreSQLLa base de données la plus sophistiquée au monde.
Documentation PostgreSQL 17.1 » Internes » Définition de l'interface des méthodes d'accès aux index » Fonctions des méthode d'accès aux index

62.2. Fonctions des méthode d'accès aux index #

Les fonctions de construction et de maintenance que doit fournir une méthode d'accès aux index dans IndexAmRoutine sont :

IndexBuildResult *
ambuild (Relation heapRelation,
         Relation indexRelation,
         IndexInfo *indexInfo);
   

Construit un nouvel index. La relation de l'index a été créée physiquement mais elle est vide. Elle doit être remplie avec toute donnée figée nécessaire à la méthode d'accès, ainsi que les entrées pour toutes les lignes existant déjà dans la table. Habituellement, la fonction ambuild appelle table_index_build_scan() pour parcourir la table à la recherche des lignes existantes et calculer les clés à insérer dans l'index. La fonction doit renvoyer une structure allouée par palloc contenant les statistiques du nouvel index. Le drapeau amcanbuildparallel indique si la méthode d'accès accepte les constructions parallélisées d'index. Quand il est configuré à true, le système tentera d'allouer des workers parallélisés pour la construction. Les méthodes d'accès supportant uniquement des contructions d'index non parallélisées devraient laisser ce drapeau à false.

bool
void
ambuildempty (Relation indexRelation);
   

Construit un index vide et l'écrit dans le fichier d'initialisation (INIT_FORKNUM) de la relation. Cette méthode n'est appelée que pour les index non journalisés ; l'index vide écrit dans ce fichier écrasera le fichier de la relation à chaque redémarrage du serveur.

bool
aminsert (Relation indexRelation,
          Datum *values,
          bool *isnull,
          ItemPointer heap_tid,
          Relation heapRelation,
          IndexUniqueCheck checkUnique,
          IndexInfo *indexInfo);
   

Insère une nouvelle ligne dans un index existant. Les tableaux values et isnull donnent les valeurs de clés à indexer et heap_tid est le TID à indexer. Si la méthode d'accès supporte les index uniques (son drapeau amcanunique vaut true), alors checkUnique indique le type de vérification d'unicité nécessaire. Elle varie si la contrainte unique est déferrable ou non ; voir Section 62.5 pour les détails. Normalement, la méthode d'accès a seulement besoin du paramètre heapRelation lors de la vérification d'unicité (car elle doit vérifier la visibilité de la ligne dans la table).

La valeur booléenne indexUnchanged donne une idée sur la nature de la ligne à indexer. Quand elle vaut true, la ligne est un duplicat d'une ligne existant dans l'index. La nouvelle ligne est une version ultérieure de ligne MVCC logiquement non modifiée. Ceci survient quand un UPDATE a lieu et qu'il ne modifie par les colonnes couvertes par l'index, mais qu'une nouvelle version est malgré tout nécessaire. La méthode d'accès de l'index peut utiliser cette information pour décider d'application la suppression du bas vers le haut dans certaines parties de l'index où de nombreuses versions de la même ligne logique s'accumulent. Notez que la mise à jour d'une colonne non clé ou d'une colonne qui apparaît seulement dans un prédicat d'index partiel n'affecte pas la valeur de indexUnchanged. Le code détermine la valeur de indexUnchanged de chaque ligne utilisant une approche avec une surcharge faible qui permet à la fois les faux positifs et les faux négatifs. Les méthodes d'accès des index ne doivent pas traiter indexUnchanged comme une source d'informations faisant autorité sur la visibilité ou le versionnement des lignes.

La valeur booléenne résultante n'a de sens que si checkUnique vaut UNIQUE_CHECK_PARTIAL. Dans ce cas, un résultat true signifie que la nouvelle entrée est reconnue comme unique alors que false indique qu'elle pourrait ne pas être unique (et une vérification d'unicité déferrée doit être planifiée). Dans les autres cas, renvoyer une constante false est recommandé.

Certains index peuvent ne pas indexer toutes les lignes. Si la ligne ne doit pas être indexée, aminsert devrait s'achever sans rien faire.

Si l'AM de l'index souhaite mettre en cache des données entre plusieurs insertions successives dans l'index au sein d'un même ordre SQL, il peut allouer de l'espace dans indexInfo->ii_Context et stocker un pointeur vers les données dans indexInfo->ii_AmCache (qui sera initialement NULL). Si des ressources autres que la mémoire doivent être rendues après des insertions dans l'index, aminsertcleanup peut être fourni, elle sera appelée quand la mémoire est rendue.

void
aminsertcleanup (Relation indexRelation,
                 IndexInfo *indexInfo);

Nettoie l'état qui a été maintenu au travers de plusieurs insertions successives dans indexInfo->ii_AmCache. C'est utile si les données nécessitent des étapes supplémentaires de nettoyage (par exemple relacher des buffers verrouillés), et si rendre la mémoire n'est pas suffisant.

IndexBulkDeleteResult *
ambulkdelete (IndexVacuumInfo *info,
              IndexBulkDeleteResult *stats,
              IndexBulkDeleteCallback callback,
              void *callback_state);
   

Supprime un ou des tuple(s) de l'index. Il s'agit d'une opération de « suppression en masse » à implémenter par un parcours complet de l'index et la vérification de chaque entrée pour voir si elle doit être supprimée. La fonction callback en argument doit être appelée, sous la forme callback(TID, callback_state) returns bool, pour déterminer si une entrée d'index particulière, identifiée par son TID, doit être supprimée. Elle doit retourner NULL ou une structure issue d'un palloc contenant des statistiques sur les effets de la suppression. La fonction peut renvoyer NULL si aucune information n'a besoin d'être envoyée à amvacuumcleanup.

À cause d'un maintenance_work_mem limité, la suppression de nombreux tuples peut nécessiter d'appeler ambulkdelete à plusieurs reprises. L'argument stats est le résultat du dernier appel pour cet index (il est NULL au premier appel dans une opération VACUUM). Ceci permet à la méthode d'accumuler des statistiques sur toute l'opération. Typiquement, ambulkdelete modifie et renvoie la même structure si le stats fourni n'est pas NULL.

IndexBulkDeleteResult *
amvacuumcleanup (IndexVacuumInfo *info,
                 IndexBulkDeleteResult *stats);
   

Nettoyer après une opération VACUUM (zéro à plusieurs appels à ambulkdelete). La fonction n'a pas d'autre but que de retourner des statistiques sur les index, mais elle peut réaliser un nettoyage en masse (réclamer les pages d'index vides, par exemple). stats est le retour de l'appel à ambulkdelete, ou NULL si ambulkdelete n'a pas été appelée car aucune ligne n'avait besoin d'être supprimée. Si le résultat n'est pas NULL, il s'agit d'une structure allouée par palloc. Les statistiques qu'elle contient seront utilisées pour mettre à jour pg_class, et sont rapportées par VACUUM si VERBOSE est indiqué. La fonction peut retourner NULL si l'index n'a pas été modifié lors de l'opération de VACUUM mais, dans le cas contraire, il faut retourner des statistiques correctes.

amvacuumcleanup sera aussi appelé à la fin d'une opération ANALYZE. Dans ce cas, stats vaut toujours NULL et toute valeur de retour sera ignorée. Ce cas peut être repéré en vérifiant info->analyze_only. Il est recommandé que la méthode d'accès ne fasse rien en dehors du nettoyage après insertion pour ce type d'appel, et cela seulement au sein d'un processus autovacuum.

bool
amcanreturn (Relation indexRelation, int attno);
   

Vérifie si l'index supporte les parcours d'index seul sur la colonne indiquée, en renvoyant la valeur originale indexée de la colonne. Le numéro d'attribut est indexé à partir de 1, c'est-à-dire que le champ attno de la première colonne est 1. Renvoie true si supporté, false sinon. Cette fonction renvoit toujours true pour les colonnes inclues (si elles sont supportées) car il y a peu d'intérêt à une colonne inclue qui ne peut pas être récupérée. Si la méthode d'accès ne supporte pas du tout les parcours d'index seul, le champ amcanreturn de la structure IndexAmRoutine peut être mis à NULL.

void
amcostestimate (PlannerInfo *root,
                IndexPath *path,
                double loop_count,
                Cost *indexStartupCost,
                Cost *indexTotalCost,
                Selectivity *indexSelectivity,
                double *indexCorrelation,
                double *indexPages);
   

Estime les coûts d'un parcours d'index. Cette fonction est décrite complètement dans Section 62.6 ci-dessous.

bytea *
amoptions (ArrayType *reloptions,
           bool validate);
   

Analyse et valide le tableau reloptions d'un index. Cette fonction n'est appelée que lorsqu'il existe un tableau reloptions non NULL pour l'index. reloptions est un tableau avec des entrées de type text et de la forme nom=valeur. La fonction doit construire une valeur de type bytea à copier dans le rd_options de l'entrée relcache de l'index. Les données contenues dans le bytea sont définies par la méthode d'accès. La plupart des méthodes d'accès standard utilisent la structure StdRdOptions. Lorsque validate est true, la fonction doit remonter un message d'erreur clair si une des options n'est pas reconnue ou a des valeurs invalides ; quand validate est false, les entrées invalides sont ignorées silencieusement. (validate est faux lors du chargement d'options déjà stockées dans pg_catalog ; une entrée invalide ne peut se trouver que si la méthode d'accès a modifié ses règles pour les options et, dans ce cas, il faut ignorer les entrées obsolètes.) Pour obtenir le comportement par défaut, il suffit de retourner NULL.

bool
amproperty (Oid index_oid, int attno,
            IndexAMProperty prop, const char *propname,
            bool *res, bool *isnull);
   

Permet aux méthodes d'accès de surcharger le comportement par défaut de pg_index_column_has_property et des fonctions liées. Si la méthode d'accès n'a pas de comportement spécial lors des demandes de propriétés d'index, le champ amproperty dans sa structure IndexAmRoutine peut être NULL. Dans le cas contraire, la méthode amproperty sera appelée avec index_oid et attno tous les deux à zéro pour les appels à pg_indexam_has_property, ou avec un index_oid valide et attno à zéro pour les appels à pg_index_has_property, ou avec un index_oid valide et un attno supérieur à zéro pour les appels à pg_index_column_has_property. prop est une valeur enum identifiant la propriété testée, alors que propname est le nom de la propriété originale. Si le code principal ne reconnaît pas le nom de la propriété, alors prop vaut AMPROP_UNKNOWN. Les méthodes d'accès peuvent définir les noms de propriétés personnalisées en cherchant une correspondance avec propname (utilisez pg_strcasecmp pour être cohérent avec le code principal) ; pour les noms connus du code principal, il est préférable d'inspecter prop. Si la méthode amproperty renvoit true, alors elle a passé le test de propriété : elle doit renvoyer le booléen *res ou mettre *isnull à true pour renvoyer un NULL. (Les deux variables référencées sont initialisées à false avant l'appel.) Si la méthode amproperty renvoit false, alors le code principal continuera avec sa logique habituelle pour tester la propriété.

Les méthodes d'accès supportant les opérateurs de tri devraient implémenter le test de AMPROP_DISTANCE_ORDERABLE car le code principal ne sait pas le faire et renverra NULL. Il pourrait aussi être avantageux d'implémenter un test sur AMPROP_RETURNABLE si cela peut être fait de façon plus simple que d'ouvrir l'index et d'appeler amcanreturn, comme le fait le code principal. Le comportement par défaut devrait être satisfaisant pour toutes les autres propriétés standard.

char *
ambuildphasename (int64 phasenum);
   

Retourne le nom textuel du numéro de phase de construction donnée. Les numéros de phase sont ceux reportés durant une construction d'index via l'interface pgstat_progress_update_param. Les noms de phase sont ensuite exposés dans la vue pg_stat_progress_create_index.

bool
amvalidate (Oid opclassoid);
   

Valide les entrées du catalogue pour la classe d'opérateur indiquée, à condition que la méthode d'accès puisse le faire raisonnablement. Par exemple, ceci pourrait inclure le test de la présence de toutes les fonctions de support. La fonction amvalidate doit renvoyer false si la classe d'opérateur est invalide. Les problèmes devraient être rapportés avec les messages ereport, typiquement au niveau INFO.

void
amadjustmembers (Oid opfamilyoid,
                 Oid opclassoid,
                 List *operators,
                 List *functions);

Valide l'opérateur new proposé et les membres fonctions d'une famille d'opérateur, si la méthode d'accès peut raisonnablement le faire, et configure leur types de dépendance si le défaut n'est pas satisfaisant. C'est appelée lors du CREATE OPERATOR CLASS et lors du ALTER OPERATOR FAMILY ADD ; dans ce dernier cas, opclassoid vaut InvalidOid. Les arguments List sont des listes de structures OpFamilyMember, comme définies dans amapi.h. Les tests réalisés par cette fonction seront typiquement un sous-ensemble de ceux réalisés par amvalidate, parce que amadjustmembers ne peut pas assumer qu'elle voit un ensemble complet de membres. Par exemple, il serait raisonnable de vérifier la signature d'une fonction d'appui, mais pas de vérifier si toutes les fonctions d'appui requises sont fournies. Tout problème peut être rapporté en renvoyant une erreur. Les champs relatifs aux dépendances des structures OpFamilyMember sont initialisés par le cœur de PostgreSQL pour créer les dépendances dures sur la classe d'opérateur si c'est CREATE OPERATOR CLASS, ou des dépendances douces sur la famille d'opérateur si c'est ALTER OPERATOR FAMILY ADD. amadjustmembers peut ajuster ces champs si d'autres comportements sont plus appropriés. Par exemple, GIN, GiST et SP-GiST configurent toujours les membres opérateurs pour avoir des dépendances douces sur la famille d'opérateur car la connexion entre un opérateur et une classe d'opérateur est relativement faible dans ces types d'index ; donc il est raisonnable de permettre l'ajout et la suppression libres de membres d'opérateurs. Les fonctions d'appui optionnelles ont typiquement des dépendances douces, pour être supprimées si nécessaire.

Le but d'un index est bien sûr de supporter les parcours de lignes qui correspondent à une condition WHERE indexable, souvent appelée qualificateur (qualifier) ou clé de parcours (scan key). La sémantique du parcours d'index est décrite plus complètement dans Section 62.3, ci-dessous. Une méthode d'accès à l'index peut supporter les parcours d'accès standards (« plain index scan »), les parcours d'index « bitmap » ou les deux. Les fonctions liées au parcours qu'une méthode d'accès à l'index doit ou devrait fournir sont :

IndexScanDesc
ambeginscan (Relation indexRelation,
             int nkeys,
             int norderbys);
   

Prépare un parcours d'index. Les paramètres nkeys et norderbys indiquent le nombre de qualificateurs et d'opérateurs de tri qui seront utilisés dans le parcours. Ils peuvent servir pour l'allocation d'espace. Notez que les valeurs réelles des clés de parcours ne sont pas encore fournies. Le résultat doit être une structure palloc. Pour des raisons d'implémentation, la méthode d'accès à l'index doit créer cette structure en appelant RelationGetIndexScan(). Dans la plupart des cas, ambeginscan ne fait pas grand-chose d'autre que cet appel et parfois l'acquisition de verrous ; les parties intéressantes du début du parcours sont dans amrescan.

void
amrescan (IndexScanDesc scan,
          ScanKey keys,
          int nkeys,
          ScanKey orderbys,
          int norderbys);
   

Démarre ou relance un parcours d'index, possiblement avec de nouvelles clés d'index. (Pour relancer en utilisant des clés déjà passées, passer NULL à keys et/ou orderbys.) Notez que le nombre de clés ou d'opérateurs de tri ne doit pas être plus grand que ce qui a été passé à la fonction ambeginscan. En pratique, le relancement est utilisé quand une nouvelle ligne externe est sélectionnée par une jointure de boucle imbriquée, donc avec une nouvelle valeur de comparaison, mais la structure de clé de parcours reste la même.

bool
amgettuple (IndexScanDesc scan,
            ScanDirection direction);
   

Récupérer la prochaine ligne d'un parcours donné, dans la direction donnée (vers l'avant ou l'arrière de l'index). Renvoie true si une ligne a été obtenue, false s'il ne reste aucune ligne. Dans le cas true, le TID de la ligne est stocké dans la structure scan. « success » signifie juste que l'index contient une entrée qui correspond aux clés de parcours, pas que la ligne existe toujours dans la table ou qu'elle sera visible dans l'instantané (snapshot) de l'appelant. En cas de succès, amgettuple doit passer scan->xs_recheck à true ou false. True signifie que ce n'est pas certain et que les conditions représentées par les clés de parcours doivent être de nouveau vérifiées sur la ligne dans la table après récupération. Cette différence permet de supporter les opérateurs d'index « à perte ». Notez que cela ne s'appliquera qu'aux conditions de parcours ; un prédicat partiel d'index n'est jamais revérifié par les appelants à amgettuple.

Si l'index supporte les parcours d'index seul (c'est-à-dire que amcanreturn renvoit true pour chacune de ces colonnes), alors, en cas de succès, la méthode d'accès doit aussi vérifier scan->xs_want_itup, et si ce dernier est true, elle doit renvoyer les données indexées originales de cette entrée d'index. Les colonnes pour lesquelles amcanreturn renvoit false peuvent êre renvoyées comme NULL. Les données peuvent être retournées sous la forme d'un pointeur d'IndexTuple stocké dans scan->xs_itup, avec un descripteur de lignes dans scan->xs_itupdesc; ou sous la forme d'un pointeur HeapTuple stocké dans scan->xs_hitup, avec le descripteur de ligne scan->xs_hitupdesc. (Le second format devrait être utilisé lors de la reconstruction des données qui pourraient ne pas tenir dans un IndexTuple.) Dans tous les cas, la gestion de la donnée référencée par le pointeur est de la responsabilité de la méthode d'accès. Les données doivent rester bonnes au moins jusqu'au prochain appel à amgettuple, amrescan ou amendscan pour le parcours.

La fonction amgettuple a seulement besoin d'exister si la méthode d'accès supporte les parcours d'index standards. Si ce n'est pas le cas, le champ amgettuple de la structure IndexAmRoutine doit être NULL.

int64
amgetbitmap (IndexScanDesc scan,
             TIDBitmap *tbm);
   

Récupère toutes les lignes du parcours sélectionné et les ajoute au TIDBitmap fourni par l'appelant (c'est-à-dire un OU de l'ensemble des identifiants de ligne dans l'ensemble où se trouve déjà le bitmap). Le nombre de lignes récupérées est renvoyé (cela peut n'être qu'une estimation car certaines méthodes d'accès ne détectent pas les duplicats). Lors de l'insertion d'identifiants de ligne dans le bitmap, amgetbitmap peut indiquer que la vérification des conditions du parcours est requis pour des identifiants précis de transactions. C'est identique au paramètre de sortie xs_recheck de amgettuple. Note : dans l'implémentation actuelle, le support de cette fonctionnalité est fusionné avec le support du stockage à perte du bitmap lui-même, et du coup les appelants revérifient à la fois les conditions du parcours et le prédicat de l'index partiel (si c'en est un) pour les lignes à revérifier. Cela ne sera pas forcément toujours vrai. amgetbitmap et amgettuple ne peuvent pas être utilisés dans le même parcours d'index ; il existe d'autres restrictions lors de l'utilisation de amgetbitmap, comme expliqué dans Section 62.3.

En plus de supporter des parcours d'index ordinaires, certains types d'index peuvent souhaiter supporter des parcours d'index parallèle, qui permettent à de multiples processus clients de coopérer afin de réaliser un parcours d'index. La méthode d'accès à l'index devrait s'arranger pour que chaque processus participant au parcours retourne un sous-ensemble des lignes qui devraient être traitées par un parcours d'index ordinaire, non parallèle, mais de telle façon que l'union de tous ces sous ensembles soit identique aux ensemble de lignes qui seraient retournés par un parcours d'index ordinaire, non parallèle. En outre, bien qu'il n'y ait pas besoin d'un ordre de tri global des lignes retournée par un parcours parallèle, l'ordre du sous ensemble de lignes retourné par chaque processus participant au parcours d'index doit correspondre à l'ordre demandé. Les fonctions suivantes peuvent être implémentée pour supporter les parcours d'index parallèles :

Size
amestimateparallelscan (int nkeys,
                        int norderbys);
   

Estime et retourne le nombre d'octets de mémoire partagée dynamique dont la méthode d'accès aura besoin pour effectuer le parcours d'index. (Ce chiffre est en plus, et non à la place, de la quantité d'espace nécessaire pour les données indépendantes de l'AM dans ParallelIndexScanDescData.)

Les paramètres nkeys et norderbys indiquent le nombre de qualificatifs et d'opérateurs de tri qui seront utilisés dans le parcours ; les mêmes valeurs seront passées à amrescan. Notez que les valeurs réels des clés du parcours ne sont pas encore fournies.

Il n'est pas nécessaire d'implémenter cette fonction pour les méthodes d'accès qui ne supportent pas les parcours d'index parallèles, où pour lesquelles le nombre d'octets de stockage additionnels vaut zéro.

void
aminitparallelscan (void *target);
   

Cette fonction sera appelée pour initialiser la mémoire partagée dynamique au début du parcours parallèle. target pointera vers au moins le nombre d'octets précédemment retourné par amestimateparallelscan, et cette fonction pourra utiliser cette quantité d'espace pour stocker n'importe quelle donnée dont elle a besoin.

Il n'est pas nécessaire d'implémenter cette fonction pour les méthodes d'accès qui ne spportent pas les parcours d'index parallèles ou dans le cas où l'espace de mémoire partagé requis ne nécessite pas d'initialisation.

void
amparallelrescan (IndexScanDesc scan);
   

Si implémentée, cette fonction sera appelée lorsqu'un parcours d'index parallèle doit recommencer. Elle devrait réinitialiser tout état partagé mis en place par aminitparallelscan de telle manière à ce que le parcours sera recommencé depuis le début.

La fonction amgetbitmap ne doit exister que si la méthode d'accès supporte les parcours d'index « bitmap ». Dans le cas contraire, le champ amgetbitmap de la structure IndexAmRoutine doit être NULL.

void
amendscan (IndexScanDesc scan);
   

Terminer un parcours et libérer les ressources. La structure scan elle-même ne doit pas être libérée, mais tout verrou posé en interne par la méthode d'accès doit être libéré, ainsi qu'à tout autre mémoire allouée par ambeginscan et les autres fonctions relatives aux parcours.

void
ammarkpos (IndexScanDesc scan);
   

Marquer la position courante du parcours. La méthode d'accès n'a besoin de mémoriser qu'une seule position par parcours.

La fonction ammarkpos n'a besoin d'être fournie que si la méthode supporte les parcours ordonnés. Dans le cas contraire, le champ ammarkpos dans sa structure IndexAmRoutine peut être NULL.

void
amrestrpos (IndexScanDesc scan);
   

Restaurer le parcours à sa plus récente position marquée.