Documentation PostgreSQL 8.3.23 > Programmation serveur > Ătendre SQL > Interfacer des extensions d'index | |
![]() |
Déclencheurs (triggers)![]() |
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.
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.
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 ».
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.
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.
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 :
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.
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é.
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.
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.
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.
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.