インデックス拡張機能へのインタフェースこれまでのところでは、新しい型や新しい関数、および新しい演算子をどの様に定義するかについて説明してきました。新しい型の列に対するインデックスをまだ作成することができません。このためには、新しいデータ型に対する演算子クラスを定義する必要があります。本節では、複素数を値の絶対値の昇順にソートし格納するB-treeインデックスメソッドを使った新しい演算子クラスについての実行例を用いて、演算子クラスの概念を説明します。PostgreSQLリリース7.3より前のバージョンでは、ユーザ定義の演算子クラスを作成するために、pg_amop、pg_amproc、およびpg_opclassに手動での追加を行う必要がありました。CLASSが使用されています。CLASSを使用すると、必要なカタログエントリを、より簡単に、よりエラーが発生しにくい方法で作成することができます。インデックスメソッドと演算子クラスpg_amテーブルには各インデックスメソッド(内部ではアクセスメソッドとして知られています)に対して1行ずつ含まれています。テーブルへの通常のアクセスのサポートはPostgreSQLに組み込まれていますが、全てのインデックスメソッドは、pg_amで記述されています。必要とされるインタフェースルーチンを定義した後、pg_amに行を作成することによって、新しいインデックスメソッドを追加することが可能です。この方法についての説明は本章での範囲を超えています(第48章を参照してください)。インデックスメソッドのルーチンには、直接的にインデックスメソッドが演算するデータ型の情報は何も与えられていません。代わりに、演算子クラスが、特定のデータ型の操作においてインデックスメソッドを使用する必要がある演算の集合を識別します。演算子クラスという名前の由来は、それらが指定するものの1つにインデックスで使用できる(つまり、インデックススキャン条件に変換できる)WHERE句演算子の集合があるからです。演算子クラスは、インデックスメソッドの内部演算で必要な、しかしインデックスで使用できるWHERE句演算子には直接的には対応しない、サポートプロシージャをいくつか指定することができます。同じ入力データ型およびインデックスメソッドに対して複数の演算子クラスを定義することが可能です。これにより、1つのデータ型に対して、複数のインデックス付けセマンティックの集合を定義することができます。B-treeインデックスでは、処理するデータ型ごとにソート順を定義する必要があります。複素数データ型では、複素数の絶対値によりデータをソートするB-tree演算子クラスと、実部の数値によりソートするB-tree演算子クラスを持つといった方法は、有用かもしれません。通常は演算子クラスの1つが一般的に最も有用であると判断され、そのデータ型およびインデックメソッドに対するデフォルトの演算子クラスとして設定されます。複数の異なるインデックスメソッドに、同一の演算子クラス名を使用することができます(例えば、B-treeとハッシュインデックスメソッドは、両方ともint4_opsという名前の演算子クラスを持つことができます)。そのような各クラスは独立した実体であり、別々に定義される必要があります。インデックスメソッドのストラテジ演算子クラスに関連付けられている演算子は、"ストラテジ番号"により識別されます。"ストラテジ番号"は、演算子クラスのコンテキスト内における各演算子のセマンティクスを識別するためのものです。B-treeの場合、キーが小さい方から大きい方へ厳密に並んでいなければなりません。したがって、B-treeに関しては、"より小さい"および"以上"のような演算子は興味深いと言えます。PostgreSQLではユーザが演算子を定義できるため、PostgreSQLは演算子の名前(例えば<や>=)を見つけても、その演算子がどのような比較を行うかを判断することはできません。その代わり、インデックスメソッドは"ストラテジ"の集合を定義します。"ストラテジ"は、汎用演算子と考えることができます。各演算子クラスは、特定のデータ型およびインデックスセマンティックスの解釈において、実際のどの演算子が各ストラテジに対応しているかを指定します。表32-2に示すように、B-treeインデックスメソッドではストラテジを5つ定義します。5ハッシュインデックスは、ビットごとの等価性のみを表します。したがって、表32-3に示すように、ストラテジを1つのみ定義します。1R-treeインデックスは二次元空間の関係を表します。表32-4に示すように、R-treeインデックスはストラテジを12個定義します。残り8個の内4個はX方向に対する、残り4個はY方向に対する同一の試験を提供します。12GiSTインデックスは、より柔軟です。固定のストラテジの集合をまったく持ちません。代わりに、特定のGiST演算子クラスの"一貫性"サポートルーチンが、ストラテジ番号が何を意味するかを解釈します。全てのストラテジ演算子は論理値を返すことに注意してください。実際、インデックスで使用されるためにWHEREの最上位レベルで現れなければなりませんので、インデックスメソッドストラテジとして定義された全ての演算子の戻り値の型はbooleanでなければなりません。ところで、pg_amのamorderstrategy列は、インデックスメソッドがシーケンシャルスキャンをサポートするかどうかを示します。ゼロはサポートしないことを意味します。サポートする場合は、amorderstrategyが順序付け用演算子に対応するストラテジ番号になります。1となっており、これは"小なり"のストラテジ番号です。インデックスメソッドのサポートルーチンストラテジは通常、システムがインデックスを使う方法を判断するために十分な情報ではありません。実際には、インデックスメソッドは、作業のために追加のサポートルーチンを必要とします。B-treeインデックスメソッドは、2つのキーを比較し、より大きいのか、等しいのか、より小さいのかを決定できなければなりません。サイズなどを計算できなければなりません。これらの操作はSQLコマンドの条件内で使用される演算子とは対応しません。これらはインデックスメソッドで内部的に使用される管理用ルーチンです。ストラテジと同じように、演算子クラスにより、与えられたデータ型およびセマンティックス解釈に対して、どの特定の関数がこれらの各役割を果たすべきであるかが識別されます。インデックスメソッドは必要な関数の集合を定義し、演算子クラスは、これらを"サポート関数番号"に代入することによって、使用すべき正しい関数を識別します。表32-5に示すように、B-treeには単一のサポート関数が必要です。関数サポート番号2つのキーを比較し、最初のキーが2番目のキーより小さいか、等しいか、大きいかを示す、0未満、0、もしくは0より大きい整数を返します。1表32-6に示すように、ハッシュインデックスも同様に単一のサポート関数が必要です。1表32-7に示すように、R-treeインデックスには3つのサポート関数が必要です。3表32-8に示すように、GiSTインデックスには7つのサポート関数が必要です。7ストラテジ演算と異なり、サポート関数は特定のインデックスメソッドが想定するデータ型、例えばB-tree用の比較関数の場合、符号付き整数を返します。例ここまでで概念について説明してきました。(この例を作業できるように、ソース配布物内のsrc/tutorial/complex.cとsrc/tutorial/complex.sqlにコピーがあります)。この演算子クラスは、複素数をその絶対値による順番でソートする演算子をカプセル化します。その名前にcomplex_abs_opsを選びました。最初に、演算子の集合が必要になります。演算子を定義する処理は項32.12で説明しました。B-tree上の演算子クラスでは、以下の演算子が必要です。絶対値による、小なり(ストラテジ1)絶対値による、以下(ストラテジ2)絶対値による、等しい(ストラテジ3)絶対値による、以上(ストラテジ4)絶対値による、大なり(ストラテジ5)比較演算子の関連する集合を定義する時にエラーを発生を最小にする方法は、まず、B-tree比較サポート関数を作成し、その後に、他の関数をサポート関数に対する1行のラッパとして作成することです。これにより、重箱の隅で一貫性のない結果を得る確率が減少します。この手法に従って、まず以下を作成します。0;}これで、小なり関数は以下のようになります。0);}他の4関数での違いは、内部関数の結果とゼロとをどのように比べるかだけです。次に、関数と、この関数に基づく演算子をSQLで宣言します。scalarltjoinsel);正しく交換演算子と否定子演算子を指定する他、適切な制限選択性関数と結合関数を指定することが重要です。ないと、オプティマイザはインデックスを効率的に使用することができません。小なり、等価、大なりの場合に異なる選択性関数を使用しなければならないことに注意してください。他にも、価値がないことがここで発生します。complex型を両オペランドに取る=という名前の演算子を1つしか作成できません。この場合、complex用の他の=演算子を持てません。実際にデータ型を作成しているとしたら、おそらく、複素数の(絶対値の等価性ではない)通常の等価性演算を行う=を欲するでしょう。この場合、complex_abs_eq用の演算子名に別の名前を使用しなければなりません。PostgreSQLでは、異なる引数のデータ型であれば同じSQL名の演算子を使うことができますが、Cでは1つの名前で1つのグローバル関数が使えるだけです。C関数はabs_eqのような単純な名前にするべきではありません。通常は、他のデータ型の関数と衝突しないように、C関数名にデータ型名を入れておくことを勧めます。abs_eq関数のSQL名は、PostgreSQLが引数のデータ型によって同じ名前を持つ他のSQL関数から区別してくれることを期待して作ることができます。ここでは例を簡単にするために、関数にCレベルとSQLレベルで同じ名前を与えています。次のステップは、B-treeに必要なサポートルーチンの登録です。これを実装するCコードは、演算子関数と同じファイルに入っています。以下は、関数をどのように宣言するかを示します。STRICT;これまでで、必要な演算子およびサポートルーチンを持つようになりました。最後に演算子クラスを作成することができます。これでcomplex列にB-treeインデックスを作って使用することが可能になったはずです。以下のように、演算子エントリをより冗長に記述することができます。演算子が、演算子クラスの定義と同一のデータ型である場合、このような記述をする必要はありません。上記の例は、ユーザがこの新しい演算子クラスをcomplexデータ型のデフォルトのB-tree演算子クラスにしようとしていると仮定しています。このようにしない場合、DEFAULTという単語を取り除いてください。データ型をまたがる演算子クラスこれまでは暗黙的に、演算子クラスは1つのデータ型のみを扱うものと仮定してきました。確かに特定のインデックス列にはたった1つのデータ型しかあり得ませんが、異なるデータ型の値とインデックス列の比較を行うインデックス操作はよく役に立ちます。現在、B-treeとGiSTインデックスメソッドでサポートされています。B-treeは、各演算子の左辺オペランドがインデックス対象のデータ型であることを要求しています。右辺オペランドは異なる型を取ることができます。サポート関数が一致するシグネチャを持っている必要があります。bigint(int8)型用の組み込みの演算子クラスでは、int4とint2との間で型をまたがった比較を行うことができます。以下の定義により多重化されています。;この定義は演算子ストラテジ関数番号とサポート関数番号を"上書き"していることに注意してください。(B-tree演算子クラスにおいてのみ)特定の番号のインスタンスがそれぞれ異なる右辺のデータ型を持つ限り、これを行うことができます。型をまたがっていないインスタンスがその演算子クラスのデフォルト演算子もしくは主演算子です。GiSTインデックスではストラテジ関数番号やサポート関数番号の上書きを行うことができません。サポートを必要とする各演算子に別のストラテジ番号を割り当てることにより、複数の右辺データ型をサポートする効果を持つことができます。consistentサポート関数は、ストラテジ番号に基づいて何を行う必要があるかを決定しなければなりず、適切なデータ型の値の比較を受け付けられるように準備されていなければなりません。システムの演算子クラスに対する依存性PostgreSQLは演算子クラスを、単にインデックスで使用できるかどうかだけではなく、多くの方式で演算子の性質を推定するために使用します。したがって、データ型の列をインデックス付けするつもりがなくても、演算子クラスを作成した方が良い可能性があります。BYやDISTINCTなど、値の比較とソートを必要とするSQL機能があります。ユーザ定義のデータ型に対してこの機能を実装するために、PostgreSQLはそのデータ型用のデフォルトのB-tree演算子クラスを検索します。BY順序を定義します。ユーザ定義型の配列の比較は、デフォルトのB-tree演算子クラスによって定義されるセマンティックに依存します。データ型用のデフォルトのB-tree演算子クラスが存在しないと、システムはデフォルトのハッシュ演算子クラスを検索します。この種類の演算子クラスは等価性のみを提供しますので、実際にこれは、配列等価性のサポートだけに対して十分です。データ型用のデフォルトの演算子クラスが存在しない場合に、こうしたSQL機能をデータ型に使用しようとすると、"演算子クラスを識別できなかった"といったエラーとなります。PostgreSQLバージョン7.4より前まででは、ソートやグループ化操作は暗黙的に=、<、>という名前の演算子を使用していました。この新しい、デフォルトの演算子クラスに依存する振舞いによって、特定の名前を持つ演算子の振舞いについて何らかの仮定を立てることを防止しています。演算子クラスには、まだ説明していない2つの特殊な機能があります。説明していない主な理由は、最もよく使用するインデックスメソッドでは、これらがあまり有用ではないためです。通常、演算子を演算子クラスのメンバとして宣言すると、インデックスメソッドでその演算子を使用して、WHERE条件を満たす行の集合を正確に抽出することができます。4;この式は、整数列にB-treeインデックスを使用することにより、正確に満たすことができます。一致する行へ厳密ではなくとも導く手段としてインデックスが有用である場合があります。R-treeインデックスで、オブジェクトの境界ボックスのみを格納したとします。その結果、多角形のような長方形でないオブジェクトとの重なりをテストするWHERE条件は正確に満たすことができません。このインデックスを使用して、対象オブジェクトの境界ボックスに重なる境界ボックスを持つオブジェクトを検索し、さらに、検索されたオブジェクトのみに対して正確に重なるかどうかをテストすることはできます。このような場合、インデックスは、演算子に対して"無駄が多い"ということになります。CLASSコマンドのOPERATOR句に、RECHECKを追加します。RECHECKは、インデックスが、全ての必要な行、およびおそらくいくつかの追加の行を返すことを保証されている場合に有効です。この追加の行は、元の演算子呼び出しを実行することによって除外できます。再度、多角形のような複雑なオブジェクトの境界ボックスのみをインデックスに格納している状況を考えてみてください。この場合、インデックスエントリに多角形全体を格納するのは、それほど有用なことではありません。単純なbox型のオブジェクトを格納した方が良いかもしれません。CLASSのSTORAGEオプションによって表現することができます。以下のように記述します。box;現時点では、GiSTアクセスメソッドのみが、列のデータ型と異なるSTORAGE型をサポートしています。STORAGEが使用された場合、GiSTのcompressおよびdecompressサポートルーチンは、データ型を変換する必要があります。