Documentation PostgreSQL 14.22 » Internes » Définition de l'interface des méthodes d'accès aux index » Fonctions des méthodes d'accès aux index

Précédent | Niveau supérieur | Suivant
62.1. Structure basique de l'API pour les index | Sommaire | 62.3. Parcours d'index

## 62.2. Fonctions des méthodes 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.

    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).

    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 renvoie 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` renvoie `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` renvoie `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érateurs 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érateurs 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érateurs 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` renvoie 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` renvoie 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 (void);

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`.)

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.