PostgreSQLLa base de données la plus sophistiquée au monde.
Documentation PostgreSQL 12.22 » Internes » Index SP-GiST » Extensibilité

65.3. ExtensibilitĂ©

SP-GiST offre une interface avec un haut niveau d'abstraction, imposant au dĂ©veloppeur des mĂ©thodes d'accĂšs de n'implĂ©menter que des mĂ©thodes spĂ©cifiques Ă  un type de donnĂ©e spĂ©cifiĂ©. Le cƓur de SP-GiST est responsable de l'efficacitĂ© du stockage sur le disque et de la recherche dans la structure arborescente. Il s'occupe aussi de la concurrence d'accĂšs et des journaux.

Les lignes des feuilles d'un arbre SP-GiST contiennent des valeurs du mĂȘme type de donnĂ©es que la colonne indexĂ©e. Les lignes des feuilles Ă  la racine contiendront toujours la valeur originale de la donnĂ©e indexĂ©e, mais les lignes des feuilles Ă  des niveaux infĂ©rieurs peuvent en contenir seulement des reprĂ©sentations rĂ©duites, comme un suffixe. Dans ce cas, les classes d'opĂ©rateur des fonctions supportĂ©es devront ĂȘtre capables de reconstruire la valeur originale en utilisant les informations accumulĂ©es dans les lignes intermĂ©diaires au travers du parcours de l'arbre et vers le niveau le plus bas.

Les lignes intermĂ©diaires sont plus complexes car elles relient des points dans l'arbre de recherche. Chaque ligne intermĂ©diaire contient un ensemble d'au moins un nƓud, qui reprĂ©sente des groupes de valeurs similaires de feuilles. Un nƓud contient un lien qui mĂšne vers un autre nƓud de niveau infĂ©rieur, ou une petite liste de lignes de feuilles qui appartiennent toutes Ă  la mĂȘme page d'index. Chaque nƓud a un label qui le dĂ©crit. Par exemple, dans un arbre radix, le label du nƓud peut ĂȘtre le caractĂšre suivant de la chaĂźne de caractĂšre. (Sinon, une classe d'opĂ©rateur peut omettre les labels des nƓuds si elle fonctionne avec un ensemble fixe de nƓuds pour les enregistrements internes ; voir Section 65.4.2.) En option, une ligne intermĂ©diaire peut avoir une valeur de prĂ©fixe qui dĂ©crit tous ses membres. Dans un arbre radix, cela peut ĂȘtre le prĂ©fixe commun des chaĂźnes reprĂ©sentant les donnĂ©es. La valeur du prĂ©fixe n'est pas nĂ©cessairement rĂ©ellement un prĂ©fixe, mais peut ĂȘtre toute donnĂ©e utilisĂ©e par la classe d'opĂ©rateur. Par exemple, pour un quadtree, il peut stocker le barycentre des quatre points reprĂ©sentĂ© par chaque feuille. Une ligne intermĂ©diaire d'un quadtree contiendra aussi quatre nƓuds correspondants Ă  des points autour de ce point central.

Quelques algorithmes de recherche arborescente nĂ©cessite la connaissance du niveau (ou profondeur) de la ligne en cours, et ainsi le cƓur de SP-GiST fournit aux classes d'opĂ©rateur la possibilitĂ© de gĂ©rer le dĂ©compte des niveaux lors du parcours de l'arbre. Il fournit aussi le moyen de reconstruire de façon incrĂ©mentale la valeur reprĂ©sentĂ©e lorsque cela est nĂ©cessaire, et pour passer des donnĂ©es supplĂ©mentaires (appelĂ©es valeurs traverses) lors de la descente de l'arbre.

Note

Le code du cƓur de SP-GiST tient aussi compte des valeurs NULL. Bien que les index SP-GiST stockent des entrĂ©es pour les valeurs NULL dans les colonnes indexĂ©es, cette implĂ©mentation reste non apparente au code de l'index de classe d'opĂ©rateur : aucune valeur NULL d'index ou de condition de recherche ne sera jamais transmis aux mĂ©thodes de la classe d'opĂ©rateur (il est convenu que les opĂ©rateurs SP-GiST sont stricts et ainsi ne peuvent trouver des valeurs NULL). Le cas des valeurs NULL n'est ainsi plus abordĂ© dans les paragraphes qui suivent.

Un index de classe d'opĂ©rateur pour SP-GiST peut proposer cinq mĂ©thodes personnalisĂ©es, et une optionnelle. Chacune de ces cinq mĂ©thodes obligatoires doit suivre la convention qui consiste Ă  accepter deux arguments de type internal, le premier Ă©tant un pointeur vers une structure C contenant les valeurs en entrĂ©e de cette mĂ©thode, et le second Ă©tant un pointeur vers une structure C oĂč les valeurs en sortie seront placĂ©es. Quatre de ces mĂ©thodes retournent void car leurs rĂ©sultats sont prĂ©sent dans la structure en sortie. Mais la mĂ©thode leaf_consistent retourne en complĂ©ment une valeur de type boolean. Les mĂ©thodes ne doivent modifier aucun des champs de la structure en entrĂ©e. Dans tous les cas, la structure en sortie est initialisĂ©e avec des zĂ©ros avant l'appel Ă  la mĂ©thode personnalisĂ©e. La sixiĂšme mĂ©thode, optionnelle, compress accepte une donnĂ©e Ă  indexer comme seul argument et renvoie la valeur convenable pour un enregistrement physique dans un enregistrement feuille.

Les cinq mĂ©thodes personnalisĂ©es sont :

config

Retourne des informations statiques concernant l'implĂ©mentation des index, incluant les OID du type de donnĂ©es du prĂ©fixe et le type de donnĂ©es du label du nƓud.

La dĂ©claration SQL de la fonction doit ressembler Ă  :

CREATE FUNCTION ma_configuration(internal, internal) RETURNS void ...
      

Le premier argument est un pointeur vers une structure C spgConfigIn, qui contient les données en entrée de la fonction. Le second argument est un pointeur vers une structure C spgConfigOut, qui permet à la fonction d'y spécifier les données en sortie.

typedef struct spgConfigIn
{
    Oid         attType;        /* Le type de donnée à indexer */
} spgConfigIn;

typedef struct spgConfigOut
{
    Oid         prefixType;     /* Le type de donnée des préfixe des tuples intermédiaires */
    Oid         labelType;      /* Le type de donnĂ©e des labels de nƓud des tuples intermĂ©diaires */
    Oid         leafType;       /* Type de données pour les valeurs de tuple feuille */
    bool        canReturnData;  /* Opclass peut reconstruire les données originales */
    bool        longValuesOK;   /* Opclass sait gérer les valeurs plus grandes qu'une page */
} spgConfigOut;
      

attType est fourni pour gĂ©rer les index polymorphiques de classe d'opĂ©rateur. Pour les types de donnĂ©es ordinaires de classe d'opĂ©rateur (fixĂ©s), il aura toujours la mĂȘme valeur et peut ainsi ĂȘtre ignorĂ©.

Pour les classes d'opĂ©rateurs qui n'utilisent pas de prĂ©fixe, prefixType peut ĂȘtre dĂ©fini Ă  VOIDOID. De la mĂȘme façon, pour les classes d'opĂ©rateurs qui n'utilisent pas de label de nƓud, labelType peut ĂȘtre dĂ©fini Ă  VOIDOID. canReturnData peut ĂȘtre dĂ©fini Ă  true si la classe d'opĂ©rateur est capable de reconstruire la valeur d'index fournie initialement. longValuesOK doit ĂȘtre dĂ©fini Ă  true uniquement lorsque attType est de longueur variable et que la classe d'opĂ©rateur est capable de segmenter les grandes valeurs en rĂ©pĂ©tant les suffixes (voir Section 65.4.1).

leafType est gĂ©nĂ©ralement identique Ă  attType. Pour des raisons de compatibilitĂ© ascendante, la mĂ©thode config peut laisser leafType non initialisĂ© ; cela donnerait le mĂȘme effet que de configurer leafType Ă  la mĂȘme valeur que attType. Quand attType et leafType sont diffĂ©rents, alors la mĂ©thode optionnelle compress doit ĂȘtre fournie. La mĂ©thode compress est responsable de la transformation des datums pour les indexer de attType vers leafType. Note : les deux fonctions cohĂ©rentes obtiendront scankeys non modifiĂ©, sans transformation utilisant compress.

choose

Choisit une méthode pour insérer une nouvelle valeur dans une ligne intermédiaire.

La dĂ©claration SQL de la fonction doit ressembler Ă  :

CREATE FUNCTION mon_choix(internal, internal) RETURNS void ...
      

Le premier argument est un pointeur vers une structure C spgChooseIn, qui contient les données en entrée de la fonction. Le second argument est un pointeur vers une structure C spgChooseOut, qui permet à la fonction d'y spécifier les données en sortie.

typedef struct spgChooseIn
{
    Datum       datum;          /* donnée initiale à indexer */
    Datum       leafDatum;      /* donnée en cours à stocker dans la feuille */
    int         level;          /* niveau en cours (Ă  partir de 0) */

    /* Données issues de la ligne intermédiaire */
    bool        allTheSame;     /* la ligne contient des valeurs équivalentes ? */
    bool        hasPrefix;      /* la ligne a-t-elle un préfixe? */
    Datum       prefixDatum;    /* si c'est le cas, la valeur de ce préfixe */
    int         nNodes;         /* nombre de nƓuds dans la ligne intermĂ©diaire */
    Datum      *nodeLabels;     /* valeurs du label du nƓud (NULL sinon) */
} spgChooseIn;

typedef enum spgChooseResultType
{
    spgMatchNode = 1,           /* descend dans le nƓud existant */
    spgAddNode,                 /* ajoute un nƓud dans la ligne intermĂ©diaire */
    spgSplitTuple               /* scinde une ligne intermédiaire (modifie son préfixe) */
} spgChooseResultType;

typedef struct spgChooseOut
{
    spgChooseResultType resultType;     /* code d'action, voir plus bas */
    union
    {
        struct                  /* resultats de spgMatchNode */
        {
            int         nodeN;      /* descend dans ce nƓud (à partir de 0) */
            int         levelAdd;   /* incrémente le niveau de cette valeur */
            Datum       restDatum;  /* nouvelle valeur de la feuille */
        }           matchNode;
        struct                  /* résultats de spgAddNode */
        {
            Datum       nodeLabel;  /* nouveau label du nƓud */
            int         nodeN;      /* lĂ  oĂč l'insĂ©rer (Ă  partir de 0) */
        }           addNode;
        struct                  /* résultats pour spgSplitTuple */
        {
	    /* Information pour former une ligne de niveau supĂ©rieur avec un nƓud fils */
            bool        prefixHasPrefix;    /* la ligne doit-elle avoir un préfixe ? */
            Datum       prefixPrefixDatum;  /* si oui, sa valeur */
            int         prefixNNodes;       /* nombre de nƓuds */
            Datum      *prefixNodeLabels;   /* leurs labels (ou NULL si
                                             * aucun label) */
            int         childNodeN;         /* quel nƓud a un nƓud fils */

            /* Informations pour former une nouvelle ligne intermédaire de niveau inférieur
			à partir de tous les anciens nƓuds */
            bool        postfixHasPrefix;   /* la ligne doit-elle avoir un préfixe ? */
            Datum       postfixPrefixDatum; /* si oui, sa valeur */
        }           splitTuple;
    }           result;
} spgChooseOut;
      

datum est la valeur initiale de type spgConfigIn.attType de la donnĂ©e qui a Ă©tĂ© insĂ©rĂ©e dans l'index. leafDatum est une valeur de type spgConfigOut.leafType qui est initialement un rĂ©sultat de la mĂ©thode compress appliquĂ©e Ă  datum quand la mĂ©thode compress est fournie, ou la mĂȘme valeur que datum dans le cas contraire. leafDatum peut changer Ă  des niveaux infĂ©rieurs de l'arbre si la fonction choose ou picksplit change cette valeur. Lorsque la recherche liĂ©e Ă  l'insertion atteint une feuille, la valeur actuelle de leafDatum sera stockĂ©e dans la nouvelle ligne de feuille créée. level est le niveau actuel de la ligne intermĂ©diaire, en considĂ©rant que 0 est le niveau racine. allTheSame est true si la ligne intermĂ©diaire actuelle est marquĂ©e comme contenant plusieurs nƓuds Ă©quivalents. (voir Section 65.4.3). hasPrefix est vrai si la ligne intermĂ©diaire actuelle contient un prĂ©fixe ; si c'est le cas, prefixDatum est sa valeur. nNodes est le nombre de nƓuds enfants contenus dans la ligne intermĂ©diaire, et nodeLabels est un tableau des valeurs de leurs labels, ou NULL s'il n'y a pas de labels.

La fonction choose peut dĂ©terminer si la nouvelle valeur correspond Ă  un des nƓuds enfants existants, ou si un nouvel enfant doit ĂȘtre ajoutĂ©, ou si la nouvelle valeur n'est pas consistante avec les prĂ©fixes de ligne et qu'ainsi la ligne intermĂ©diaire doit ĂȘtre dĂ©coupĂ©e pour crĂ©er un prĂ©fixe moins restrictif.

Si la nouvelle valeur correspond Ă  un des nƓuds enfants existants, dĂ©finir resultType Ă  spgMatchNode. et dĂ©finir nodeN Ă  l'index (Ă  partir de 0) du nƓud dans le tableau de nƓud. DĂ©finir levelAdd Ă  l'incrĂ©ment de level nĂ©cessaire pour descendre au travers de ce nƓud, ou le laisser Ă  0 si la classe d'opĂ©rateur n'utilise pas de niveaux. DĂ©finir restDatum Ă  la valeur de leafDatum si la classe d'opĂ©rateur ne modifie pas les valeurs d'un niveau au suivant, ou dans le cas contraire, dĂ©finir la valeur modifiĂ©e pour ĂȘtre utilisĂ©e comme valeur de leafDatum au niveau suivant.

Si un nouveau nƓud enfant doit ĂȘtre ajoutĂ©, dĂ©finir resultType Ă  spgAddNode. DĂ©finir nodeLabel au label Ă  utiliser pour le nouveau nƓud, et dĂ©finir nodeN Ă  l'index (de 0) auquel insĂ©rer le nƓud dans le tableau de nƓud. AprĂšs que ce nƓud ait Ă©tĂ© ajoutĂ©, la fonction choose sera appelĂ©e Ă  nouveau avec la ligne intermĂ©diaire modifiĂ©e. Cet appel devrait produire un rĂ©sultat spgMatchNode.

Si la nouvelle valeur est cohĂ©rente avec le prĂ©fixe de ligne, dĂ©finir resultType Ă  spgSplitTuple. Cette action dĂ©place tous les nƓuds existants dans le nouveau niveau infĂ©rieur de la ligne intermĂ©diaire, et remplace la ligne intermĂ©diaire existant avec une ligne qui dispose d'un unique nƓud qui est liĂ© Ă  la nouvelle ligne intermĂ©diaire de niveau infĂ©rieur. DĂ©finir prefixHasPrefix pour indiquer si les nouvelles lignes supĂ©rieures doivent avoir un prĂ©fixe, et si c'est le cas, dĂ©finir prefixPrefixDatum Ă  la valeur du prĂ©fixe. Cette nouvelle valeur de prĂ©fixe doit ĂȘtre suffisamment moins restrictive que l'original pour accepter que la nouvelle valeur soit indexĂ©e. DĂ©finir prefixNNodes au nombre de nƓuds nĂ©cessaires pour la nouvelle ligne et dĂ©finir prefixNodeLabels Ă  un tableau allouĂ© avec palloc de leurs labels, ou Ă  NULL si les labels des nƓuds ne sont pas nĂ©cessaires. Noter que la taille totale de la nouvelle ligne supĂ©rieure ne doit pas dĂ©passer la taille totale de la ligne qu'elle remplace ; cela contraint les longueurs des nouveaux prĂ©fixes et labels. DĂ©finir postfixHasPrefix pour indiquer si la nouvelle ligne intermĂ©diaire de niveau infĂ©rieur aura un prĂ©fixe, et dans ce cas dĂ©finir postfixPrefixDatum Ă  la valeur du prĂ©fixe. La combinaison de ces deux prĂ©fixes et le label additionnel doit avoir la mĂȘme signification que le prĂ©fixe original car il n'y a pas de moyen de modifier le label du nƓud qui est dĂ©placĂ© vers la nouvelle ligne de niveau infĂ©rieur, ni de modifier une quelconque entrĂ©e d'index enfant. AprĂšs que ce nƓud ait Ă©tĂ© dĂ©coupĂ©, la fonction choose sera appelĂ©e Ă  nouveau avec la ligne intermĂ©diaire de remplacement. Cet appel devrait retourner un spgAddNode car, Ă  priori, le label du nƓud ajoutĂ© lors de l'Ă©tape de dĂ©coupage ne correspondra pas Ă  la nouvelle valeur. Ainsi, aprĂšs cette Ă©tape, il y aura une troisiĂšme Ă©tape qui retournera finalement spgMatchNode et permettra l'insertion pour descendre au niveau feuille.

picksplit

Décide de la maniÚre à suivre pour créer une ligne intermédiaire à partir d'un ensemble de lignes de feuilles.

La dĂ©claration de fonction SQL doit ressembler Ă  :

CREATE FUNCTION mon_decoupage(internal, internal) RETURNS void ...
      

Le premier argument est un pointeur vers une structure C spgPickSplitIn, qui contient les données en entrée de la fonction. Le second argument est un pointeur vers une structure C spgPickSplitOut, qui permet à la fonction d'y spécifier les données en sortie.

typedef struct spgPickSplitIn
{
    int         nTuples;        /* nombre de lignes feuilles */
    Datum      *datums;         /* leur données (tableau de taille nTuples) */
    int         level;          /* niveau actuel (Ă  partir de 0) */
} spgPickSplitIn;

typedef struct spgPickSplitOut
{
    bool        hasPrefix;      /* les nouvelles lignes intermédiaires doivent-elles avoir un préfixe ? */
    Datum       prefixDatum;    /* si oui, la valeur du préfixe */

    int         nNodes;         /* nombre de nƓud pour une nouvelle ligne intermĂ©diaire */
    Datum      *nodeLabels;     /* leurs labels (ou NULL s'il n'y a aucun label) */

    int        *mapTuplesToNodes;   /* index du nƓud de chaque ligne feuille */
    Datum      *leafTupleDatums;    /* données à stocker dans chaque nouvelle ligne feuille */
} spgPickSplitOut;
      

nTuples est le nombre de lignes feuilles fournies. datums est un tableau de leurs données de type spgConfigOut.leafType. level est le niveau actuel que les lignes feuille concernées partagent, qui deviendra le niveau de la nouvelle ligne intermédiaire.

DĂ©finir hasPrefix pour indiquer que la nouvelle ligne intermĂ©diaire doit avoir un prĂ©fixe, et dans ce cas, dĂ©finir prefixDatum Ă  la valeur de ce prĂ©fixe. DĂ©finir nNodes pour indiquer le nombre de nƓuds que contiendra la nouvelle ligne intermĂ©diaire, et spĂ©cifier dans nodeLabels un tableau de leurs labels, ou NULL si les labels ne sont pas nĂ©cessaires. Attribuer Ă  mapTuplesToNodes un tableau des index (Ă  partir de zĂ©ro) des nƓuds auquels seront assignĂ©s chaque ligne feuille. Attribuer Ă  leafTupleDatums un tableau des valeurs Ă  stocker dans la nouvelle ligne de feuilles (ces valeurs seront les mĂȘmes que celles des donnĂ©es datums fournies en paramĂštre si la classe d'opĂ©rateur ne modifie pas les donnĂ©es d'un niveau Ă  un autre). À noter que la fonction picksplit est responsable de l'allocation de mĂ©moire des tableaux nodeLabels, mapTuplesToNodes et leafTupleDatums.

Si plus d'une ligne de feuille est fournie, il est nĂ©cessaire que la fonction picksplit les classent en plus d'un nƓud. Dans le cas contraire, il ne sera pas possible de rĂ©partir les lignes des feuilles sur des pages diffĂ©rentes, ce qui est pourtant l'objectif de cette opĂ©ration. À cet effet, si la fonction picksplit se termine aprĂšs avoir rĂ©parti toutes les lignes des feuilles dans le mĂȘme nƓud, le code du moteur de SP-GiST ne tiendra pas compte de cette dĂ©cision, et gĂ©nĂ©rera une ligne intermĂ©diaire dans lequel chaque ligne de feuille sera assignĂ© alĂ©atoirement Ă  plusieurs nƓuds de labels identiques. De telles lignes sont marquĂ©es allTheSame pour garder une trace de cette dĂ©cision. Les fonctions choose et inner_consistent doivent tenir compte de ces lignes intermĂ©diaires. Voir Section 65.4.3 pour plus d'informations.

picksplit peut ĂȘtre appliquĂ© Ă  une unique ligne de feuille lorsque la fonction config dĂ©finit longValuesOK Ă  true et qu'une valeur plus large qu'une page est donnĂ©e en paramĂštre. Dans ce cas, l'objectif de la fonction est d'extraire un prĂ©fixe et de produire une donnĂ©e de feuille moins longue. Cet appel sera rĂ©pĂ©tĂ© jusqu'Ă  ce que la donnĂ©e de la feuille soit suffisamment petite pour tenir dans une page. Voir Section 65.4.1 pour plus d'information.

inner_consistent

Retourne un ensemble de nƓuds (branches) à suivre durant une recherche arborescente.

La dĂ©claration SQL de cette fonction doit ressembler Ă  :

CREATE FUNCTION ma_suite_de_nƓuds(internal, internal) RETURNS void ...
      

Le premier argument est un pointeur vers une structure C spgInnerConsistentIn, qui contient les données en entrée de la fonction. Le second argument est un pointeur vers une structure C spgInnerConsistentOut, qui permet à la fonction d'y spécifier les données en sortie.

typedef struct spgInnerConsistentIn
{
    ScanKey     scankeys;       /* tableau d'opérateurs et de valeurs de comparaison */
    ScanKey     orderbys;       /* tableau d'opérateurs de tri et comparaison */
                                 * de valeur */
    int         nkeys;          /* taille du tableau scankeys */
    int         norderbys;      /* taille du tableau orderbys */

    Datum       reconstructedValue;     /* valeur reconstruite au niveau parent */
    MemoryContext traversalMemoryContext;   /* placer les nouvelles valeurs ici */
    int         level;          /* niveau actuel (à partir de zéro) */
    bool        returnData;     /* retourner la valeur originale ? */

    /* Données du tuple intermédiaire en cours */
    bool        allTheSame;     /* la ligne est-elle identifiée comme all-the-same ? */
    bool        hasPrefix;      /* la ligne a-t-elle un préfixe ? */
    Datum       prefixDatum;    /* dans ce cas, la valeur du préfixe */
    int         nNodes;         /* nombre de nƓuds dans la ligne intermĂ©diaire */
    Datum      *nodeLabels;     /* labels du nƓud (NULL si pas de labels) */
    void      **traversalValues;        /* valeurs traverses spécifiques de la classe d'opérateur */
    double    **distances;              /* distances associées */
} spgInnerConsistentIn;

typedef struct spgInnerConsistentOut
{
    int         nNodes;         /* nombre de nƓuds enfants à visiter */
    int        *nodeNumbers;    /* leurs index dans le tableau de nƓuds */
    int        *levelAdds;      /* l'incrément à apporter au niveau pour chaque enfant */
    Datum      *reconstructedValues;    /* valeurs reconstruites associées */
} spgInnerConsistentOut;
      

Le tableau scankeys, de longueur nkeys, dĂ©crit les conditions de recherche d'index. Ces conditions sont combinĂ©es avec un opĂ©rateur ET. Seuls les entrĂ©es d'index qui correspondent Ă  toutes ces conditions sont conservĂ©es (Ă  noter que nkeys = 0 implique que toutes les entrĂ©es d'index sont conservĂ©es). GĂ©nĂ©ralement, la fonction inner_consistent ne tient compte que des champs sk_strategy et sk_argument de chaque entrĂ©e de tableau, qui fournissent respectivement l'opĂ©rateur indexĂ© et la valeur de comparaison. En particulier, il n'est pas nĂ©cessaire de vĂ©rifier si sk_flags est NULL car le moteur de SP-GiST aura complĂ©tĂ© cette valeur. Le tableau orderbys, de longueur norderbys, dĂ©crit les opĂ©rateurs de tri (s'il y en a) de la mĂȘme maniĂšre. reconstructedValue est la valeur reconstruite pour la ligne parent. La valeur est (Datum) 0 au niveau le plus haut ou si la fonction inner_consistent ne fournit pas de valeur pour le niveau supĂ©rieur. reconstructedValue est toujours de type spgConfigOut.leafType type. traversalValue est un pointer vers toute donnĂ©e traverse passĂ©e Ă  l'appel prĂ©cĂ©dent de inner_consistent sur l'enregistrement parent de l'index, ou NULL Ă  la racine. traversalMemoryContext est le contexte mĂ©moire de stockage des valeurs traverses en sortie (voir ci-dessous). level est le niveau actuel de la ligne intermĂ©diaire, en commençant Ă  0 pour le niveau racine. returnData est true pour la valeur reconstruite pour cette requĂȘte. Ce n'est le cas que si la fonction config dĂ©finit canReturnData. allTheSame est true si la ligne intermĂ©diaire en cours est marquĂ©e « all-the-same Â». Dans ce cas, tous les nƓuds ont le mĂȘme label (si un label est dĂ©fini) et ainsi soit ils correspondent tous Ă  la requĂȘte, soit aucun ne correspond (voir Section 65.4.3). hasPrefix est true si la ligne intermĂ©diaire en cours contient un prĂ©fixe. Dans ce cas, prefixDatum est sa valeur. nNodes est le nombre de nƓuds enfants de la ligne intermĂ©diaire, et nodeLabels est un tableau de leurs labels, ou NULL si les nƓuds n'ont pas de labels.

nNodes doit ĂȘtre dĂ©fini comme le nombre de nƓuds enfants qui doivent ĂȘtre visitĂ©s durant la recherche, et nodeNumbers doit ĂȘtre dĂ©fini comme le tableau de leurs index. Si la classe d'opĂ©rateur effectue le suivi des niveaux, dĂ©finir levelAdds comme un tableau des incrĂ©ments Ă  ajouter aux niveaux pour descendre vers chaque nƓud Ă  visiter (dans la plupart des cas, les incrĂ©ments seront les mĂȘmes pour chaque nƓud, mais ce n'est pas systĂ©matique, et ainsi un tableau est employĂ©). Si la reconstruction de la valeur est nĂ©cessaire, dĂ©finir reconstructedValues comme le tableau des valeurs de type spgConfigOut.leafType reconstruites pour chaque nƓud enfant Ă  visiter. Sinon, laisser reconstructedValues Ă  la valeur NULL. Si une recherche triĂ©e est exĂ©cutĂ©e, initialise distances Ă  un tableau de valeurs de distance suivant le tableau orderbys (les nƓuds avec les plus petites distances seront traitĂ©es en premier). Laisse NULL sinon. S'il est souhaitable de passer les informations supplĂ©mentaires hors bande (« valeurs traverses Â») pour diminuer les niveaux de l'arbre de recherche, initialiser traversalValues en un tableau des valeurs traverses appropriĂ©es, un pour chaque nƓuds enfants Ă  visiter ; sinon laisser traversalValues Ă  NULL. Notez que la fonction inner_consistent est responsable de l'allocation mĂ©moire des tableaux nodeNumbers, levelAdds, distances, reconstructedValues et traversalValues dans le contexte mĂ©moire actuel. NĂ©anmoins, toute valeur traverse en sortie pointĂ©e par le tableau traversalValues devrait ĂȘtre allouĂ©e dans traversalMemoryContext. Chaque valeur traverse doit ĂȘtre un morceau simple allouĂ© avec la fonction palloc.

leaf_consistent

Retourne true si une ligne de feuille satisfait une requĂȘte.

La dĂ©claration SQL de cette fonction doit ressembler Ă  :

CREATE FUNCTION ma_fonction_leaf_consistent(internal, internal) RETURNS bool ...
      

Le premier argument est un pointeur vers une structure C spgLeafConsistentIn, qui contient les données en entrée de la fonction. Le second argument est un pointeur vers une structure C spgLeafConsistentOut, qui permet à la fonction d'y spécifier les données en sortie.

typedef struct spgLeafConsistentIn
{
    ScanKey     scankeys;       /* tableau d'opérateurs et de valeurs de comparaison */
    ScanKey     orderbys;       /* tableau d'opérateurs de tri et comparaison */
                                 * de valeurs */
    int         nkeys;          /* taille du tableau scankeys */
    int         norderbys;      /* taille du tableau orderbys */

    Datum       reconstructedValue;     /* valeur reconstruite au parent */
    void       *traversalValue; /* valeur traverse spécifique à la classe d'opérateur */
    int         level;          /* niveau actuel (à partir de zéro) */
    bool        returnData;     /* les donnĂ©es originales doivent-elles ĂȘtre reconstruites ? */

    Datum       leafDatum;      /* données de la ligne de feuille */
} spgLeafConsistentIn;

typedef struct spgLeafConsistentOut
{
    Datum       leafValue;      /* données originales reconstruites, le cas échéant */
    bool        recheck;        /* dĂ©finir Ă  true si l'opĂ©rateur doit ĂȘtre revĂ©rifiĂ© */
    Datum       leafValue;        /* valeur d'origine reconstruite, le cas échéant */
    bool        recheck;          /* positionnĂ© Ă  true si l'opĂ©rateur doit ĂȘtre revĂ©rifiĂ© */
    bool        recheckDistances; /* positionnĂ© Ă  true si les distances doivent ĂȘtre revĂ©rifiĂ©es */
    double     *distances;        /* associated distances */
} spgLeafConsistentOut;
      

Le tableau scankeys, de longueur nkeys, dĂ©crit les conditions de recherche dans l'index. Ces conditions sont uniquement combinĂ©es avec AND -- Seules les entrĂ©es d'index qui satisfont toutes les conditions satisfont la requĂȘte (Notez que nkeys = 0 implique que toutes les entrĂ©es de l'index satisfont la requĂȘte). GĂ©nĂ©ralement, la fonction de recherche ne tient compte que des champs sk_strategy et sk_argument de chaque entrĂ©e du tableau, qui correspondent respectivement Ă  l'opĂ©rateur indexable et Ă  la valeur de comparaison. En particulier, il n'est pas nĂ©cessaire de vĂ©rifier sk_flags pour savoir que la valeur de comparaison est NULL car le code du cƓur de SP-GiST filtre ces conditions. Le tableau orderbys, de taille norderbys, dĂ©crit les opĂ©rateurs de tri de la mĂȘme maniĂšre. reconstructedValue est la valeur reconstruite pour la ligne parent ; Il s'agit de (Datum) 0 au niveau racine ou si la fonction inner_consistent ne fournit pas de valeur au niveau parent. reconstructedValue est toujours de type spgConfigOut.leafType. traversalValue est un pointeur vers toute donnĂ©e traverse passĂ©e lors de l'appel prĂ©cĂ©dent Ă  inner_consistent de l'enregistrement parent de l'index ou NULL Ă  la racine. level est le niveau actuel de la ligne de feuille, qui commence Ă  zĂ©ro pour le niveau racine. returnData est true s'il est nĂ©cessaire de reconstruire les donnĂ©es pour cette requĂȘte. Cela ne sera le cas que lorsque la fonction config vĂ©rifie canReturnData. leafDatum est la valeur de la clĂ© stockĂ©e de spgConfigOut.leafType dans la ligne de feuille en cours.

La fonction doit retourner true si la ligne de feuille correspond Ă  la requĂȘte ou false sinon. Dans le cas oĂč la valeur serait true, et que returnData est true alors leafValue doit ĂȘtre dĂ©fini Ă  la valeur originale, de type spgConfigIn.attType fournie pour ĂȘtre indexĂ©e pour cette ligne de feuille. recheck peut ĂȘtre dĂ©fini Ă  true si la correspondance est incertaine et ainsi l'opĂ©rateur doit ĂȘtre rĂ©appliquĂ© Ă  la pile de ligne courante pour vĂ©rifier la correspondance. Si une recherche triĂ©e est effectuĂ©e, positionne distances Ă  un tableau de distance suivant le tableau orderbys Laisse NULL sinon. Si au moins une des distances retournĂ©es n'est pas exacte, positionne recheckDistances Ă  true. Dans ce cas, l'exĂ©cuteur recalculera la distance exacte aprĂšs avoir rĂ©cupĂ©rĂ© toutes les lignes de la table, et rĂ©ordonnera les lignes si besoin.

Ma mĂ©thode optionnelle dĂ©finie par l'utilisateur est :

Datum compress(Datum in)

Convertit l'Ă©lĂ©ment de donnĂ©es dans un format convenable pour un stockage physique dans un enregistrement feuille d'une page d'index. Elle accepte une valeur spgConfigIn.attType et renvoie une valeur spgConfigOut.leafType. La valeur en sortie ne doit pas ĂȘtre un TOAST.

Toutes les mĂ©thodes permettant d'utiliser SP-GiST sont normalement exĂ©cutĂ©es dans un contexte mĂ©moire de courte durĂ©e, c'est-Ă -dire que CurrentMemoryContext sera remis Ă  zĂ©ro aprĂšs le traitement de chaque ligne. Il n'est cependant pas rĂ©ellement important de se soucier de dĂ©sallouer la mĂ©moire allouĂ©e avec palloc (la mĂ©thode config est une exception : elle essaiera d'Ă©viter les fuites mĂ©moire. Mais gĂ©nĂ©ralement, la mĂ©thode config ne nĂ©cessite rien si ce n'est assigner des constantes aux structures passĂ©es en paramĂštre).

Si la colonne indexée a un type de donnée collationnable, l'index de collationnement sera passé à toutes les méthodes, en utilisant le mécanisme standard PG_GET_COLLATION().