PostgreSQLLa base de données la plus sophistiquée au monde.

34.14. Interfacer des extensions d'index

Les procĂ©dures dĂ©crites jusqu'Ă  maintenant permettent de dĂ©finir de nouveaux types, de nouvelles fonctions et de nouveaux opĂ©rateurs. NĂ©anmoins, nous ne pouvons pas encore dĂ©finir un index sur une colonne d'un nouveau type de donnĂ©es. Pour cela, nous devons dĂ©finir une classe d'opĂ©rateur pour le nouveau type de donnĂ©es. Plus loin dans cette section, nous illustrerons ce concept avec un exemple : une nouvelle classe d'opĂ©rateur pour la mĂ©thode d'indexation B-tree qui enregistre et trie des nombres complexes dans l'ordre ascendant des valeurs absolues.

Les classes d'opĂ©rateur peuvent ĂȘtre groupĂ©es en familles d'opĂ©rateur pour afficher les relations entre classes compatibles sĂ©mantiquement. Quand un seul type de donnĂ©es est impliquĂ©, une classe d'opĂ©rateur est suffisant, donc nous allons nous fixer sur ce cas en premier puis retourner aux familles d'opĂ©rateur.

34.14.1. Méthodes d'indexation et classes d'opérateurs

La table pg_am contient une ligne pour chaque mĂ©thode d'indexation (connue en interne comme mĂ©thode d'accĂšs). Le support pour l'accĂšs normal aux tables est implĂ©mentĂ© dans PostgreSQLℱ mais toutes les mĂ©thodes d'index sont dĂ©crites dans pg_am. Il est possible d'ajouter une nouvelle mĂ©thode d'indexation en dĂ©finissant les routines d'interface nĂ©cessaires et en crĂ©ant ensuite une ligne dans la table pg_am -- mais ceci est au-delĂ  du sujet de ce chapitre (voir le Chapitre 50, DĂ©finition de l'interface des mĂ©thodes d'accĂšs aux index).

Les routines pour une mĂ©thode d'indexation n'ont pas Ă  connaĂźtre directement les types de donnĂ©es sur lesquels opĂšre la mĂ©thode d'indexation. Au lieu de cela, une classe d'opĂ©rateur identifie l'ensemble d'opĂ©rations que la mĂ©thode d'indexation doit utiliser pour fonctionner avec un type particulier de donnĂ©es. Les classes d'opĂ©rateurs sont ainsi dĂ©nommĂ©es parce qu'une de leur tĂąche est de spĂ©cifier l'ensemble des opĂ©rateurs de la clause WHERE utilisables avec un index (c'est-Ă -dire, qui peuvent ĂȘtre requalifiĂ©s en balayage d'index). Une classe d'opĂ©rateur peut Ă©galement spĂ©cifier des procĂ©dures d'appui, nĂ©cessaires pour les opĂ©rations internes de la mĂ©thode d'indexation mais sans correspondance directe avec un quelconque opĂ©rateur de clause WHERE pouvant ĂȘtre utilisĂ© avec l'index.

Il est possible de dĂ©finir plusieurs classes d'opĂ©rateurs pour le mĂȘme type de donnĂ©es et la mĂȘme mĂ©thode d'indexation. Ainsi, de multiples ensembles de sĂ©mantiques d'indexation peuvent ĂȘtre dĂ©finis pour un seul type de donnĂ©es. Par exemple, un index B-tree exige qu'un tri ordonnĂ© soit dĂ©fini pour chaque type de donnĂ©es auquel il peut s'appliquer. Il peut ĂȘtre utile pour un type de donnĂ©e de nombre complexe de disposer d'une classe d'opĂ©rateur B-tree qui trie les donnĂ©es selon la valeur absolue complexe, une autre selon la partie rĂ©elle, etc. Typiquement, une des classes d'opĂ©rateur sera considĂ©rĂ©e comme plus utile et sera marquĂ©e comme l'opĂ©rateur par dĂ©faut pour ce type de donnĂ©es et cette mĂ©thode d'indexation.

Le mĂȘme nom de classe d'opĂ©rateur peut ĂȘtre utilisĂ© pour plusieurs mĂ©thodes d'indexation diffĂ©rentes (par exemple, les mĂ©thodes d'index B-tree et hash ont toutes les deux des classes d'opĂ©rateur nommĂ©es int4_ops) mais chacune de ces classes est une entitĂ© indĂ©pendante et doit ĂȘtre dĂ©finie sĂ©parĂ©ment.

34.14.2. Stratégies des méthode d'indexation

Les opĂ©rateurs associĂ©s Ă  une classe d'opĂ©rateur sont identifiĂ©s par des « numĂ©ros de stratĂ©gie Â», servant Ă  identifier la sĂ©mantique de chaque opĂ©rateur dans le contexte de sa classe d'opĂ©rateur. Par exemple, les B-trees imposent un classement strict selon les clĂ©s, du plus petit au plus grand. Ainsi, des opĂ©rateurs comme « plus petit que Â» et « plus grand que Â» sont intĂ©ressants pour un B-tree. Comme PostgreSQLℱ permet Ă  l'utilisateur de dĂ©finir des opĂ©rateurs, PostgreSQLℱ ne peut pas rechercher le nom d'un opĂ©rateur (par exemple, < ou >=) et rapporter de quelle comparaison il s'agit. Au lieu de cela, la mĂ©thode d'indexation dĂ©finit un ensemble de « stratĂ©gies Â», qui peuvent ĂȘtre comprises comme des opĂ©rateurs gĂ©nĂ©ralisĂ©s. Chaque classe d'opĂ©rateur spĂ©cifie l'opĂ©rateur effectif correspondant Ă  chaque stratĂ©gie pour un type de donnĂ©e particulier et pour une interprĂ©tation de la sĂ©mantique d'index.

La mĂ©thode d'indexation B-tree dĂ©finit cinq stratĂ©gies, qui sont exposĂ©es dans le Tableau 34.2, « StratĂ©gies B-tree Â».

Tableau 34.2. StratĂ©gies B-tree

Opération Numéro de stratégie
plus petit que 1
plus petit ou égal 2
égal 3
plus grand ou égal 4
plus grand que 5

Les index de dĂ©coupage permettent seulement des comparaisons d'Ă©galitĂ© et utilisent ainsi une seule stratĂ©gie exposĂ©e dans le Tableau 34.3, « StratĂ©gies de dĂ©coupage Â».

Tableau 34.3. StratĂ©gies de dĂ©coupage

Opération Numéro de stratégie
égal à 1

Les index GiST sont plus flexibles : ils n'ont pas du tout un ensemble fixe de stratĂ©gies. À la place, la routine de support de « cohĂ©rence Â» de chaque classe d'opĂ©rateur GiST interprĂšte les numĂ©ros de stratĂ©gie comme elle l'entend. Comme exemple, plusieurs des classes d'opĂ©rateurs GiST indexe les objets gĂ©omĂ©triques Ă  deux dimensions fournissant les stratĂ©gies « R-tree Â» affichĂ©es dans Tableau 34.4, « StratĂ©gies « R-tree Â» pour GiST Ă  deux dimensions Â». Quatre d'entre elles sont des vrais tests Ă  deux dimensions (surcharge, identique, contient, contenu par) ; quatre autres considĂšrent seulement la direction X ; et les quatre derniĂšres fournissent les mĂȘmes tests dans la direction Y.

Tableau 34.4. StratĂ©gies « R-tree Â» pour GiST Ă  deux dimensions

Opération Numéro de stratégie
strictement Ă  gauche de 1
ne s'étend pas à droite de 2
surcharge 3
ne s'étend pas à gauche de 4
strictement Ă  droite de 5
identique 6
contient 7
contenu par 8
ne s'étend pas au dessus 9
strictement en dessous 10
strictement au dessus 11
ne s'étend pas en dessous 12

Les index GIN sont similaires aux index GiST en flexibilitĂ© : ils n'ont pas un ensemble fixe de stratĂ©gie. À la place, les routines de support de chaque classe d'opĂ©rateur interprĂštent les numĂ©ros de stratĂ©gie suivant la dĂ©finition du classe d'opĂ©rateur. Comme exemple, les numĂ©ros des stratĂ©gies utilisĂ©s par les classes d'opĂ©rateur sur des tableaux sont affichĂ©s dans Tableau 34.5, « StratĂ©gies des tableaux GIN Â».

Tableau 34.5. StratĂ©gies des tableaux GIN

Opération Numéro de stratégie
surcharge 1
contient 2
est contenu par 3
identique 4

Notez que tous les opĂ©rateurs de stratĂ©gie renvoient des valeurs de type boolĂ©en. Dans la pratique, tous les opĂ©rateurs dĂ©finis comme stratĂ©gies de mĂ©thode d'indexation doivent renvoyer un type boolean puisqu'ils doivent apparaĂźtre au plus haut niveau d'une clause WHERE pour ĂȘtre utilisĂ©s avec un index.

34.14.3. Routines d'appui des méthodes d'indexation

GĂ©nĂ©ralement, les stratĂ©gies n'apportent pas assez d'informations au systĂšme pour indiquer comment utiliser un index. Dans la pratique, les mĂ©thodes d'indexation demandent des routines d'appui additionnelles pour fonctionner. Par exemple, les mĂ©thodes d'index B-tree doivent ĂȘtre capables de comparer deux clĂ©s et de dĂ©terminer laquelle est supĂ©rieure, Ă©gale ou infĂ©rieure Ă  l'autre. De la mĂȘme façon, la mĂ©thode d'indexation hash doit ĂȘtre capable de calculer les codes de hachage pour les valeurs de clĂ©s. Ces opĂ©rations ne correspondent pas Ă  des opĂ©rateurs utilisĂ©s dans les commandes SQL ; ce sont des routines administratives utilisĂ©es en interne par des mĂ©thodes d'index.

Comme pour les stratĂ©gies, la classe d'opĂ©rateur Ă©numĂšre les fonctions spĂ©cifiques et le rĂŽle qu'elles doivent jouer pour un type de donnĂ©e donnĂ© et une interprĂ©tation sĂ©mantique donnĂ©e. La mĂ©thode d'indexation dĂ©finit l'ensemble des fonctions dont elle a besoin et la classe d'opĂ©rateur identifie les fonctions exactes Ă  utiliser en les assignant aux « numĂ©ros de fonction d'appui Â» spĂ©cifiĂ©s par la mĂ©thode d'indexage.

Les B-trees demandent une seule fonction d'appui, exposĂ©e dans le Tableau 34.6, « Fonctions d'appui de B-tree Â».

Tableau 34.6. Fonctions d'appui de B-tree

Fonction Numéro d'appui
Comparer deux clés et renvoyer un entier inférieur à zéro, zéro ou supérieure à zéro indiquant si la premiÚre clé est inférieure, égale ou supérieure à la deuxiÚme. 1

De façon identique, les index de dĂ©coupage requiĂšrent une fonction d'appui exposĂ©e dans le Tableau 34.7, « Fonctions d'appui pour dĂ©coupage Â».

Tableau 34.7. Fonctions d'appui pour dĂ©coupage

Fonction Numéro d'appui
Calculer la valeur de découpage pour une clé 1

Les index GiST requiĂšrent sept fonctions d'appui exposĂ©es dans le Tableau 34.8, « Fonctions d'appui pour GiST Â».

Tableau 34.8. Fonctions d'appui pour GiST

Fonction Numéro d'appui
consistency - dĂ©termine si la clĂ© satisfait le qualifiant de la requĂȘte 1
union - calcule l'union d'un ensemble de clés 2
compress - calcule une représentation compressée d'une clé ou d'une valeur à indexer 3
decompress - calcule une représentation décompressée d'une clé compressée 4
penality - calcule la pénalité pour l'insertion d'une nouvelle clé dans un sous-arbre avec la clé du sous-arbre indiqué 5
picksplit - détermine les entrées d'une page qui sont à déplacer vers la nouvelle page et calcule les clés d'union pour les pages résultantes 6
equal - compare deux clés et renvoie true si elles sont identiques 7

Les index GIN requiĂšrent quatre fonctions d'appui exposĂ©es dans le Tableau 34.9, « Fonctions d'appui GIN Â».

Tableau 34.9. Fonctions d'appui GIN

Fonction Numéro d'appui
compare - Compare deux clés et renvoie un entier plus petit que zéro, zéro ou plus grand que zéro, indiquant si la premiÚre clé est plus petit, égal à ou plus grand que la seconde. 1
extractValue - extrait les clĂ©s Ă  partir d'une condition de requĂȘte 2
extractQuery - extrait les clĂ©s Ă  partir d'une condition de requĂȘte 3
consistent - dĂ©termine la valeur correspondant Ă  la condition de requĂȘte 4

Contrairement aux opĂ©rateurs de stratĂ©gie, les fonctions d'appui renvoient le type de donnĂ©e, quelqu'il soit, que la mĂ©thode d'indexation particuliĂšre attend, par exemple, dans le cas de la fonction de comparaison des B-trees, un entier signĂ©. Le nombre et le type des arguments pour chaque fonction de support peuvent dĂ©pendre de la mĂ©thode d'indexage. Pour les B-tree et les hash, les fonctions de support prennent les mĂȘme types de donnĂ©es en entrĂ©e comme le font les opĂ©rateurs incluant dans la classe d'opĂ©rateur, bien que ce ne soit pas le cas pour la plupart des fonctions de support GIN et GiST.

34.14.4. Exemple

Maintenant que nous avons vu les idĂ©es, voici l'exemple promis de crĂ©ation d'une nouvelle classe d'opĂ©rateur. Cette classe d'opĂ©rateur encapsule les opĂ©rateurs qui trient les nombres complexes selon l'ordre de la valeur absolue, aussi avons-nous choisi le nom de complex_abs_ops. En premier lieu, nous avons besoin d'un ensemble d'opĂ©rateurs. La procĂ©dure pour dĂ©finir des opĂ©rateurs a Ă©tĂ© discutĂ©e dans la Section 34.12, « OpĂ©rateurs dĂ©finis par l'utilisateur Â». Pour une classe d'opĂ©rateur sur les B-trees, nous avons besoin des opĂ©rateurs :

  • valeur absolue less-than (stratĂ©gie 1) ;
  • valeur absolue less-than-or-equal (stratĂ©gie 2) ;
  • valeur absolue equal (stratĂ©gie 3) ;
  • valeur absolue greater-than-or-equal (stratĂ©gie 4) ;
  • valeur absolue greater-than (stratĂ©gie 5) ;

Le plus simple moyen de dĂ©finie un ensemble d'opĂ©rateurs de comparaison est d'Ă©crire en premier la fonction de comparaison B-tree, puis d'Ă©crire les autres fonctions en tant que wrapper de la fonction de support. Ceci rĂ©duit les risques de rĂ©sultats incohĂ©rents pour les cas spĂ©cifiques. En suivant cette approche, nous devons tout d'abord Ă©crire :

#define Mag(c) ((c)->x*(c)->x + (c)->y*(c)->y)

bool
complex_abs_eq(Complex *a, Complex *b)
{
    double amag = Mag(a), bmag = Mag(b);
    return (amag == bmag);
}

Maintenant, la fonction plus-petit-que ressemble Ă  ceci :

PG_FUNCTION_INFO_V1(complex_abs_lt);

Datum
complex_abs_lt(PG_FUNCTION_ARGS)
{
    Complex    *a = (Complex *) PG_GETARG_POINTER(0);
    Complex    *b = (Complex *) PG_GETARG_POINTER(1);

    PG_RETURN_BOOL(complex_abs_cmp_internal(a, b) < 0);
}

Les quatre autres fonctions diffÚrent seulement sur la façon dont ils comparent le résultat de la fonction interne au zéro.

Maintenant, dĂ©clarons en SQL les fonctions et les opĂ©rateurs basĂ©s sur ces fonctions :

CREATE FUNCTION complex_abs_lt(complex, complex) RETURNS bool
    AS 'nom_fichier', 'complex_abs_lt'
    LANGUAGE C IMMUTABLE STRICT;

CREATE OPERATOR < (
   leftarg = complex, rightarg = complex, procedure = complex_abs_lt,
   commutator = > , negator = >= ,
   restrict = scalarltsel, join = scalarltjoinsel
);

Il est important de spécifier les fonctions de sélectivité de restriction et de jointure, sinon l'optimiseur sera incapable de faire un usage effectif de l'index. Notez que les cas 'less-than', 'equal' et 'greater-than' doivent utiliser des fonctions différentes de sélectivité.

Voici d'autres choses importantes Ă  noter :

  • Il ne peut y avoir qu'un seul opĂ©rateur nommĂ©, disons, = et acceptant un type complex pour ses deux opĂ©randes. Dans le cas prĂ©sent, nous n'avons aucun autre opĂ©rateur = pour complex mais, si nous construisons un type de donnĂ©e fonctionnel, nous aurions certainement dĂ©sirĂ© que = soit l'opĂ©ration ordinaire d'Ă©galitĂ© pour les nombres complexes (et non pour l'Ă©galitĂ© de leurs valeurs absolues). Dans ce cas, nous aurions eu besoin d'utiliser un autre nom d'opĂ©rateur pour notre fonction complex_abs_eq.

  • Bien que PostgreSQLℱ puisse se dĂ©brouiller avec des fonctions ayant le mĂȘme nom SQL, tant qu'elles ont en argument des types de donnĂ©es diffĂ©rents, en C il ne peut exister qu'une fonction globale pour un nom donnĂ©. Aussi ne devons-nous pas donner un nom simple comme abs_eq. Habituellement, inclure le nom du type de donnĂ©es dans le nom de la fonction C est une bonne habitude pour ne pas provoquer de conflit avec des fonctions pour d'autres types de donnĂ©e.

  • Nous aurions pu faire de abs_eq le nom SQL de la fonction, en laissant Ă  PostgreSQLℱ le soin de la distinguer de toute autre fonction SQL de mĂȘme nom par les types de donnĂ©es en argument. Pour la simplicitĂ© de l'exemple, nous donnerons Ă  la fonction le mĂȘme nom au niveau de C et au niveau de SQL.

La prochaine Ă©tape est l'enregistrement de la routine d'appui nĂ©cessaire pour les B-trees. Le code exemple C qui implĂ©mente ceci est dans le mĂȘme fichier qui contient les fonctions d'opĂ©rateur. Voici comment dĂ©clarer la fonction :

CREATE FUNCTION complex_abs_cmp(complex, complex)
    RETURNS integer
    AS 'filename'
    LANGUAGE C;

Maintenant que nous avons les opérateurs requis et la routine d'appui, nous pouvons enfin créer la classe d'opérateur.

CREATE OPERATOR CLASS complex_abs_ops
    DEFAULT FOR TYPE complex USING btree AS
        OPERATOR        1       < ,
        OPERATOR        2       <= ,
        OPERATOR        3       = ,
        OPERATOR        4       >= ,
        OPERATOR        5       > ,
        FUNCTION        1       complex_abs_cmp(complex, complex);

Et c'est fait ! Il devrait ĂȘtre possible maintenant de crĂ©er et d'utiliser les index B-tree sur les colonnes complex.

Nous aurions pu Ă©crire les entrĂ©es de l'opĂ©rateur de façon plus explicite comme dans :

        OPERATOR        1       < (complex, complex) ,

mais il n'y a pas besoin de faire ainsi quand les opĂ©rateurs prennent le mĂȘme type de donnĂ©e que celui pour lequel la classe d'opĂ©rateur a Ă©tĂ© dĂ©finie.

Les exemples ci-dessus supposent que vous voulez que cette nouvelle classe d'opérateur soit la classe d'opérateur B-tree par défaut pour le type de donnée complex. Si vous ne voulez pas, supprimez simplement le mot DEFAULT.

34.14.5. Classes et familles d'opérateur

Jusqu'Ă  maintenant, nous avons supposĂ© implicitement qu'une classe d'opĂ©rateur s'occupe d'un seul type de donnĂ©es. Bien qu'il ne peut y avoir qu'un seul type de donnĂ©es dans une colonne d'index particuliĂšre, il est souvent utile d'indexer les opĂ©rations qui comparent une colonne indexĂ©e Ă  une valeur d'un type de donnĂ©es diffĂ©rent. De plus, s'il est intĂ©ressant d'utiliser un opĂ©rateur inter-type en connexion avec une classe d'opĂ©rateur, souvent cet autre type de donnĂ©e a sa propre classe d'opĂ©rateur. Rendre explicite les connexions entre classes en relation est d'une grande aide pour que le planificateur optimise les requĂȘtes SQL (tout particuliĂšrement pour les classes d'opĂ©rateur B-tree car le planificateur sait bien comme les utiliser).

Pour gĂ©rer ces besoins, PostgreSQLℱ utilise le concept d'une famille d'opĂ©rateur . Une famille d'opĂ©rateur contient une ou plusieurs classes d'opĂ©rateur et peut aussi contenir des opĂ©rateurs indexables et les fonctions de support correspondantes appartenant Ă  la famille entiĂšre mais pas Ă  une classe particuliĂšre de la famille. Nous disons que ces opĂ©rateurs et fonctions sont « lĂąches Â» Ă  l'intĂ©rieur de la famille, en opposition Ă  ĂȘtre liĂ© Ă  une classe spĂ©cifique. Typiquement, chaque classe d'opĂ©rateur contient des opĂ©rateurs de types de donnĂ©es simples alors que les opĂ©rateurs inter-type sont lĂąches dans la famille.

Tous les opĂ©rateurs et fonctions d'une famille d'opĂ©rateurs doivent avoir une sĂ©mantique compatible oĂč les prĂ©-requis de la compatibilitĂ© sont dictĂ©s par la mĂ©thode indexage. Du coup, vous pouvez vous demander la raison pour s'embarrasser de distinguer les sous-ensembles de la famille en tant que classes d'opĂ©rateur. En fait, dans beaucoup de cas, les divisions en classe sont inutiles et la famille est le seul groupe intĂ©ressant. La raison de la dĂ©finition de classes d'opĂ©rateurs est qu'ils spĂ©cifient Ă  quel point la famille est nĂ©cessaire pour supporter un index particulier. S'il existe un index utilisant une classe d'opĂ©rateur, alors cette classe d'opĂ©rateur ne peut pas ĂȘtre supprimĂ©e sans supprimer l'index -- mais les autres parties de la famille d'opĂ©rateurs, donc les autres classes et les opĂ©rateurs lĂąches, peuvent ĂȘtre supprimĂ©es. Du coup, une classe d'opĂ©rateur doit ĂȘtre indiquĂ©e pour contenir l'ensemble minimum d'opĂ©rateurs et de fonctions qui sont raisonnablement nĂ©cessaire pour travailler avec un index sur un type de donnĂ©es spĂ©cifique, et ensuite les opĂ©rateurs en relation mais peuvent ĂȘtre ajoutĂ©s en tant que membres lĂąches de la famille d'opĂ©rateur.

Comme exemple, PostgreSQLℱ a une famille d'opĂ©rateur B-tree interne integer_ops, qui inclut les classes d'opĂ©rateurs int8_ops, int4_ops et int2_ops pour les index sur les colonnes bigint (int8), integer (int4) et smallint (int2) respectivement. La famille contient aussi des opĂ©rateurs de comparaison inter-type permettant la comparaison de deux de ces types, pour qu'un index parmi ces types puisse ĂȘtre parcouru en utilisant une valeur de comparaison d'un autre type. La famille peut ĂȘtre dupliquĂ© par ces dĂ©finitions :

CREATE OPERATOR FAMILY integer_ops USING btree;

CREATE OPERATOR CLASS int8_ops
DEFAULT FOR TYPE int8 USING btree FAMILY integer_ops AS
  -- comparaisons int8 standard
  OPERATOR 1 < ,
  OPERATOR 2 <= ,
  OPERATOR 3 = ,
  OPERATOR 4 >= ,
  OPERATOR 5 > ,
  FUNCTION 1 btint8cmp(int8, int8) ;

CREATE OPERATOR CLASS int4_ops
DEFAULT FOR TYPE int4 USING btree FAMILY integer_ops AS
  -- comparaisons int4 standard
  OPERATOR 1 < ,
  OPERATOR 2 <= ,
  OPERATOR 3 = ,
  OPERATOR 4 >= ,
  OPERATOR 5 > ,
  FUNCTION 1 btint4cmp(int4, int4) ;

CREATE OPERATOR CLASS int2_ops
DEFAULT FOR TYPE int2 USING btree FAMILY integer_ops AS
  -- comparaisons int2 standard
  OPERATOR 1 < ,
  OPERATOR 2 <= ,
  OPERATOR 3 = ,
  OPERATOR 4 >= ,
  OPERATOR 5 > ,
  FUNCTION 1 btint2cmp(int2, int2) ;

ALTER OPERATOR FAMILY integer_ops USING btree ADD
  -- comparaisons inter-types int8 vs int2
  OPERATOR 1 < (int8, int2) ,
  OPERATOR 2 <= (int8, int2) ,
  OPERATOR 3 = (int8, int2) ,
  OPERATOR 4 >= (int8, int2) ,
  OPERATOR 5 > (int8, int2) ,
  FUNCTION 1 btint82cmp(int8, int2) ,

  -- comparaisons inter-types  int8 vs int4
  OPERATOR 1 < (int8, int4) ,
  OPERATOR 2 <= (int8, int4) ,
  OPERATOR 3 = (int8, int4) ,
  OPERATOR 4 >= (int8, int4) ,
  OPERATOR 5 > (int8, int4) ,
  FUNCTION 1 btint84cmp(int8, int4) ,

  -- comparaisons inter-types int4 vs int2
  OPERATOR 1 < (int4, int2) ,
  OPERATOR 2 <= (int4, int2) ,
  OPERATOR 3 = (int4, int2) ,
  OPERATOR 4 >= (int4, int2) ,
  OPERATOR 5 > (int4, int2) ,
  FUNCTION 1 btint42cmp(int4, int2) ,

  -- comparaisons inter-types int4 vs int8
  OPERATOR 1 < (int4, int8) ,
  OPERATOR 2 <= (int4, int8) ,
  OPERATOR 3 = (int4, int8) ,
  OPERATOR 4 >= (int4, int8) ,
  OPERATOR 5 > (int4, int8) ,
  FUNCTION 1 btint48cmp(int4, int8) ,

  -- comparaisons inter-types int2 vs int8
  OPERATOR 1 < (int2, int8) ,
  OPERATOR 2 <= (int2, int8) ,
  OPERATOR 3 = (int2, int8) ,
  OPERATOR 4 >= (int2, int8) ,
  OPERATOR 5 > (int2, int8) ,
  FUNCTION 1 btint28cmp(int2, int8) ,

  -- comparaisons inter-types int2 vs int4
  OPERATOR 1 < (int2, int4) ,
  OPERATOR 2 <= (int2, int4) ,
  OPERATOR 3 = (int2, int4) ,
  OPERATOR 4 >= (int2, int4) ,
  OPERATOR 5 > (int2, int4) ,
  FUNCTION 1 btint24cmp(int2, int4) ;

Notez que cette dĂ©finition « surcharge Â» la stratĂ©gie de l'opĂ©rateur et les numĂ©ros de fonction support : chaque numĂ©ro survient plusieurs fois dans la famille. Ceci est autorisĂ© aussi longtemps que chaque instance d'un numĂ©ro particulier a des types de donnĂ©es distincts en entrĂ©e. Les instances qui ont les deux types en entrĂ©e Ă©galent au type en entrĂ©e de la classe d'opĂ©rateur sont les opĂ©rateurs primaires et les fonctions de support pour cette classe d'opĂ©rateur et, dans la plupart des cas, doivent ĂȘtre dĂ©clarĂ©es comme membre de la classe d'opĂ©rateur plutĂŽt qu'en tant que membres lĂąches de la famille.

Dans une famille d'opĂ©rateur B-tree, tous les opĂ©rateurs de la famille doivent trier de façon compatible, ceci signifiant ques les lois transitives tiennent parmi tous les types de donnĂ©es supportĂ©s par la famille : « if A = B and B = C, then A = C Â» et « if A < B and B < C, then A < C Â». Pour chaque opĂ©rateur de la famille, il doit y avoir une fonction de support pour les deux mĂȘmes types de donnĂ©es en entrĂ©e que celui de l'opĂ©rateur. Il est recommandĂ© qu'une famille soit complĂšte, c'est-Ă -dire que pour chaque combinaison de types de donnĂ©es, tous les opĂ©rateurs sont inclus. Chaque classe d'opĂ©rateur doit juste inclure les opĂ©rateurs non inter-types et les fonctions de support pour ce type de donnĂ©es.

Pour construire une famille d'opĂ©rateurs de hachage pour plusieurs types de donnĂ©es, des fonctions de support de hachage compatibles doivent ĂȘtre créées pour chaque type de donnĂ©es supportĂ© par la famille. Ici, compatibilitĂ© signifie que les fonctions sont garanties de renvoyer le mĂȘme code de hachage pour toutes les paires de valeurs qui sont considĂ©rĂ©es Ă©gales par les opĂ©rateurs d'Ă©galitĂ© de la famille, mĂȘme quand les valeurs sont de type diffĂ©rent. Ceci est habituellement difficile Ă  accomplir quand les types ont diffĂ©rentes reprĂ©sentations physiques, mais cela peut se faire dans la plupart des cas. Notez qu'il y a seulement une fonction de support par type de donnĂ©es, pas une par opĂ©rateur d'Ă©galitĂ©. Il est recommandĂ© qu'une famille soit terminĂ©e, c'est-Ă -dire fournit un opĂ©rateur d'Ă©galitĂ© pour chaque combinaison de types de donnĂ©es. Chaque classe d'opĂ©rateur doit inclure l'opĂ©rateur d'Ă©galitĂ© non inter-type et la fonction de support pour ce type de donnĂ©es.

Les index GIN et GiST n'ont pas de notion explicite d'opérations inter-types. L'ensemble des opérateurs supportés est simplement ce que les fonctions de support primaire peuvent supporter pour un opérateur donné.

[Note]

Note

Avant PostgreSQLℱ 8.3, le concept des familles d'opĂ©rateurs n'existait pas. Donc, tous les opĂ©rateurs inter-type dont le but Ă©tait d'ĂȘtre utilisĂ©s avec un index Ă©taient liĂ©s directement Ă  la classe d'opĂ©rateur de l'index. Bien que cette approche fonctionne toujours, elle est obsolĂšte car elle rend trop importantes les dĂ©pendances de l'index et parce que le planificateur peut gĂ©rer des comparaisons inter-type avec plus d'efficacitĂ© que quand les typdes de donnĂ©es ont des opĂ©rateurs dans la mĂȘme famille d'opĂ©rateur.

34.14.6. Dépendances du systÚme pour les classes d'opérateur

PostgreSQLℱ utilise les classe d'opĂ©rateur pour infĂ©rer les propriĂ©tĂ©s des opĂ©rateurs de plusieurs autres façons que le seul usage avec les index. Donc, vous pouvez crĂ©er des classes d'opĂ©rateur mĂȘme si vous n'avez pas l'intention d'indexer une quelconque colonne de votre type de donnĂ©e.

En particulier, il existe des caractĂ©ristiques de SQL telles que ORDER BY et DISTINCT qui requiĂšrent la comparaison et le tri des valeurs. Pour implĂ©menter ces caractĂ©ristiques sur un type de donnĂ©e dĂ©fini par l'utilisateur, PostgreSQLℱ recherche la classe d'opĂ©rateur B-tree par dĂ©faut pour le type de donnĂ©e. Le membre « equals Â» de cette classe d'opĂ©rateur dĂ©finit pour le systĂšme la notion d'Ă©galitĂ© des valeurs pour GROUP BY et DISTINCT, et le tri ordonnĂ© imposĂ© par la classe d'opĂ©rateur dĂ©finit le ORDER BY par dĂ©faut.

La comparaison des tableaux de types définis par l'utilisateur repose sur les sémantiques définies par la classe d'opérateur B-tree par défaut.

S'il n'y a pas de classe d'opérateur B-tree par défaut pour le type de donnée, le systÚme cherchera une classe d'opérateur de découpage. Mais puisque cette classe d'opérateur ne fournit que l'égalité, c'est en pratique seulement suffisant pour établir l'égalité de tableau.

Quand il n'y a pas de classe d'opĂ©rateur par dĂ©faut pour un type de donnĂ©e, vous obtenez des erreurs telles que « could not identify an ordering operator Â» si vous essayez d'utiliser ces caractĂ©ristiques SQL avec le type de donnĂ©e.

[Note]

Note

Dans les versions de PostgreSQLℱ antĂ©rieures Ă  la 7.4, les opĂ©rations de tri et de groupement utilisaient implicitement les opĂ©rateurs nommĂ©s =, < et >. Le nouveau comportement qui repose sur les classes d'opĂ©rateurs par dĂ©faut Ă©vite d'avoir Ă  faire une quelconque supposition sur le comportement des opĂ©rateurs avec des noms particuliers.

Un autre point important est qu'un opérateur apparaissant dans une famille d'opérateur de hachage est un candidat pour les jointures de hachage, les agrégations de hachage et les optimisations relatives. La famille d'opérateur de hachage est essentiel ici car elle identifie le(s) fonction(s) de hachage à utiliser.

34.14.7. Caractéristiques spéciales des classes d'opérateur

Il y a deux caractéristiques spéciales des classes d'opérateur dont nous n'avons pas encore parlées, essentiellement parce qu'elles ne sont pas utiles avec les méthodes d'index les plus communément utilisées.

Normalement, dĂ©clarer un opĂ©rateur comme membre d'une classe ou d'une famille d'opĂ©rateur signifie que la mĂ©thode d'indexation peut retrouver exactement l'ensemble de lignes qui satisfait la condition WHERE utilisant cet opĂ©rateur. Par exemple :

SELECT * FROM table WHERE colonne_entier < 4;

peut ĂȘtre accompli exactement par un index B-tree sur la colonne entiĂšre. Mais il y a des cas oĂč un index est utile comme un guide inexact vers la colonne correspondante. Par exemple, si un index GiST enregistre seulement les rectangles limite des objets, alors il ne peut pas exactement satisfaire une condition WHERE qui teste le chevauchement entre des objets non rectangulaires comme des polygones. Cependant, nous pourrions utiliser l'index pour trouver des objets dont les rectangles limites chevauchent les limites de l'objet cible. Dans ce cas, l'index est dit ĂȘtre Ă  perte pour l'opĂ©rateur, et nous ajoutons RECHECK Ă  la clause OPERATOR dans la commande CREATE OPERATOR CLASS. RECHECK est valide si l'index garantie de retourner toutes les lignes requises, plus peut-ĂȘtre des lignes supplĂ©mentaires pouvant ĂȘtre Ă©liminĂ©es par le recours Ă  l'opĂ©rateur original.

ConsidĂ©rons Ă  nouveau la situation oĂč nous gardons seulement dans l'index le rectangle dĂ©limitant un objet complexe comme un polygone. Dans ce cas, il n'est pas trĂšs intĂ©ressant de conserver le polygone entier dans l'index - nous pouvons aussi bien conserver seulement un objet simple du type box. Cette situation est exprimĂ©e par l'option STORAGE dans la commande CREATE OPERATOR CLASS : nous aurons Ă  Ă©crire quelque chose comme :

CREATE OPERATOR CLASS polygon_ops
    DEFAULT FOR TYPE polygon USING gist AS
        ...
        STORAGE box;

Actuellement, seule les mĂ©thodes d'indexation GIN et GiST supportent un type STORAGE qui soit diffĂ©rent du type de donnĂ©e de la colonne. Les routines d'appui de GiST pour la compression (compress) et la dĂ©compression (decompress) doivent s'occuper de la conversion du type de donnĂ©e quand STORAGE est utilisĂ©. Avec GIN, le type STORAGE identifie le type des valeurs « key Â», qui est normalement diffĂ©rent du type de la colonne indexĂ©e -- par exemple, une classe d'opĂ©rateur pour des colonnes de tableaux d'entiers pourrait avoir des clĂ©s qui sont seulement des entiers. Les routines de support GIN extractValue et extractQuery sont responsables de l'extraction des clĂ©s Ă  partir des valeurs indexĂ©es.