インデックス拡張機能へのインタフェース

PostgreSQL 9.0.0文書
前のページ | 巻戻し | 第 35章SQLの拡張 | 早送り | 次のページ

# 35.14. インデックス拡張機能へのインタフェース

これまでのところでは、新しい型や新しい関数、および新しい演算子をどの様に定義するかについて説明してきました。 しかしながら、新しい型の列に対するインデックスをまだ作成することができません。 このためには、新しいデータ型に対する演算子クラスを定義する必要があります。 本節では、複素数を値の絶対値の昇順にソートし格納するB-treeインデックスメソッドを使った新しい演算子クラスについての実行例を用いて、演算子クラスの概念を説明します。

演算子クラスを演算子族にまとめ、意味的に互換性を持つクラス間の関係を表すことができます。 1つのデータ型のみが含まれる場合、演算子クラスで十分です。 そこでまずこうした状況かどうかに注目し、その後で演算子族に戻ります。

## 35.14.1. インデックスメソッドと演算子クラス

`pg_am`テーブルには各インデックスメソッド（内部ではアクセスメソッドとして知られています）に対して1つの行が含まれています。 テーブルへの通常のアクセスのサポートはPostgreSQLに組み込まれていますが、全てのインデックスメソッドは、`pg_am`で記述されています。 必要とされるインタフェースルーチンを定義した後、`pg_am`に行を作成することによって、新しいインデックスメソッドを追加することが可能です。 しかし、この方法についての説明は本章での範囲を超えています（第51章を参照してください）。

インデックスメソッドのルーチンには、直接的にインデックスメソッドが演算するデータ型の情報は何も与えられていません。 代わりに、演算子クラスが、特定のデータ型の操作においてインデックスメソッドを使用する必要がある演算の集合を識別します。 演算子クラスという名前の由来は、それらが指定するものの1つにインデックスで使用できる（つまり、インデックススキャン条件に変換できる）`WHERE`句演算子の集合があるからです。 また、演算子クラスは、インデックスメソッドの内部演算で必要な、しかしインデックスで使用できる`WHERE`句演算子には直接的には対応しない、サポートプロシージャをいくつか指定することができます。

同じ入力データ型およびインデックスメソッドに対して複数の演算子クラスを定義することが可能です。 これにより、1つのデータ型に対して、複数のインデックス付けセマンティックの集合を定義することができます。 例えば、B-treeインデックスでは、処理するデータ型ごとにソート順を定義する必要があります。 複素数データ型では、複素数の絶対値によりデータをソートするB-tree演算子クラスと、実部の数値によりソートするB-tree演算子クラスを持つといった方法は、有用かもしれません。 通常は演算子クラスの1つが一般的に最も有用であると判断され、そのデータ型およびインデックスメソッドに対するデフォルトの演算子クラスとして設定されます。

複数の異なるインデックスメソッドに、同一の演算子クラス名を使用することができます（例えば、B-treeとハッシュインデックスメソッドは、両方とも`int4_ops`という名前の演算子クラスを持つことができます）。 ただし、そのような各クラスは独立した実体であり、別々に定義される必要があります。

## 35.14.2. インデックスメソッドのストラテジ

演算子クラスに関連付けられている演算子は、"ストラテジ番号"により識別されます。 "ストラテジ番号"は、演算子クラスのコンテキスト内における各演算子のセマンティクスを識別するためのものです。 例えば、B-treeの場合、キーが小さい方から大きい方へ厳密に並んでいなければなりません。 したがって、B-treeに関しては、"より小さい"および"以上"のような演算子は興味深いと言えます。 PostgreSQLではユーザが演算子を定義できるため、PostgreSQLは演算子の名前（例えば`<`や`>=`）を見つけても、その演算子がどのような比較を行うかを判断することはできません。 その代わり、インデックスメソッドは"ストラテジ"の集合を定義します。 "ストラテジ"は、汎用演算子と考えることができます。 各演算子クラスは、特定のデータ型およびインデックスセマンティックスの解釈において、実際のどの演算子が各ストラテジに対応しているかを指定します。

表35-2に示すように、B-treeインデックスメソッドではストラテジを5つ定義します。

表 35-2. B-treeストラテジ

演算 | ストラテジ番号
--- | ---
小なり | 1
以下 | 2
等しい | 3
以上 | 4
大なり | 5

ハッシュインデックスは等価性のみをサポートします。 したがって、表35-3に示すように、ストラテジを1つのみ定義します。

表 35-3. ハッシュストラテジ

演算 | ストラテジ番号
--- | ---
等しい | 1

GiSTインデックスはより柔軟です。 固定のストラテジの集合をまったく持ちません。 代わりに、特定のGiST演算子クラスの"consistent"サポートルーチンが、ストラテジ番号が何を意味するかを解釈します。 たとえば、複数の組み込みのGiSTインデックス演算子クラスは、2次元幾何オブジェクトをインデックス付けし、表35-4で示される"R-tree"ストラテジを提供します。 この内4個は2次元に対する（重複、合同、含有、被含有）試験です。 残りの内4個はX方向のみに対する、残り4個はY方向のみに対する同一の試験を提供します。

表 35-4. GiSTによる2次元の"R-tree"ストラテジ

演算 | ストラテジ番号
--- | ---
完全に左側 | 1
右側にはみ出さない | 2
重なる | 3
左側にはみ出さない | 4
完全に右側 | 5
同じ | 6
含む | 7
含まれる | 8
上側にはみ出さない | 9
完全に下側 | 10
完全に上側 | 11
下側にはみ出さない | 12

GINインデックスは柔軟性という点でGiSTと似ており、固定のストラテジ群を持ちません。 その代わりに、各演算子クラスのサポートルーチンが演算子クラスの定義に従ってストラテジ番号を解釈します。 例えば、組み込みの配列に対する演算子クラスのストラテジ番号を表35-5に示します。

表 35-5. GIN 配列のストラテジ

操作 | ストラテジ番号
--- | ---
重複 | 1
含有 | 2
含有される | 3
等しい | 4

全てのストラテジ演算子は論理値を返すことに注意してください。 実際、インデックスで使用されるために`WHERE`の最上位レベルで現れなければなりませんので、インデックスメソッドストラテジとして定義された全ての演算子の戻り値の型は`boolean`でなければなりません。

## 35.14.3. インデックスメソッドのサポートルーチン

ストラテジは通常、システムがインデックスを使う方法を判断するために十分な情報ではありません。 実際には、インデックスメソッドが動作するためには、さらにサポートルーチンを必要とします。 例えばB-treeインデックスメソッドは、2つのキーを比較し、より大きいのか、等しいのか、より小さいのかを決定できなければなりません。 同様に、ハッシュインデックスは、キー値のハッシュコードを計算できなければなりません。 これらの操作はSQLコマンドの条件内で使用される演算子とは対応しません。 これらはインデックスメソッドで内部的に使用される管理用ルーチンです。

ストラテジと同じように、演算子クラスにより、与えられたデータ型およびセマンティックス解釈に対して、どの特定の関数がこれらの各役割を果たすべきであるかが識別されます。 インデックスメソッドは必要な関数の集合を定義し、演算子クラスは、これらをインデックスメソッドで指定された"サポート関数番号"に代入することによって、使用すべき正しい関数を識別します。

表35-6に示すように、B-treeには単一のサポート関数が必要です。

表 35-6. B-treeサポート関数

関数 | サポート番号
--- | ---
2つのキーを比較し、最初のキーが2番目のキーより小さいか、等しいか、大きいかを示す、0未満、0、もしくは0より大きい整数を返します。 | 1

表35-7に示すように、ハッシュインデックスも同様に単一のサポート関数が必要です。

表 35-7. ハッシュサポート関数

関数 | サポート番号
--- | ---
キーのハッシュ値を計算 | 1

表35-8に示すように、GiSTインデックスには7つのサポート関数が必要です。

表 35-8. GiSTサポート関数

関数 | サポート番号
--- | ---
consistent - キーが問い合わせ条件を満たすかどうかを決定します。 | 1
union - キーの集合の和集合を計算します。 | 2
compress - キーまたはインデックス付けされる値の圧縮表現を計算します。 | 3
decompress - 圧縮されたキーを伸張した表現を計算します。 | 4
penalty - 指定された副ツリーキーを持つ副ツリーに新しいキーを挿入する時のペナルティを計算します。 | 5
picksplit - ページのどのエントリを新しいページに移動させるかを決定し、結果ページ用の統合キーを計算します。 | 6
equal -2つのキーを比較し、等しければ真を返します。 | 7

GINインデックスは、表35-9に示す4つのサポート関数を必要とします。

表 35-9. GINサポート関数

関数 | 説明 | サポート数
--- | --- | ---
`compare` | 2つのキーを比較し、0未満、0、0より大きな整数を返します。 それぞれ最初のキーの方が大きい、等しい、小さいを示します。 | 1
`extractValue` | インデックス付けされる値からキーを抽出します。 | 2
`extractQuery` | 問い合わせ条件からキーを抽出します。 | 3
`consistent` | 問い合わせ条件に一致する値かどうかを決定します。 | 4
`comparePartial` | comparePartial（省略可能なメソッド）。問い合わせからの部分キーとインデックスからのキーを比較し、それぞれ、GINがこのインデックス項目を無視しなければならないか、一致する項目として扱わなければならないか、インデックススキャンを中止しなければならないかを示す、ゼロより小さい、ゼロ、ゼロより大きい整数値のいずれかを返します。 | 5

ストラテジ演算と異なり、サポート関数は特定のインデックスメソッドが想定するデータ型、例えばB-tree用の比較関数の場合、符号付き整数を返します。 同様に各サポート関数に渡す引数の数と型はインデックスメソッドに依存します。 B-Treeとハッシュでは、サポート関数はその演算子クラスに含まれる演算子と同じ入力データ型を取りますが、GINおよびGiSTサポート関数のほとんどはそうではありません。

## 35.14.4. 例

ここまでで概念について説明してきました。 ここで、新しい演算子クラスを作成する有用な例を紹介します。 （この例を作業できるように、ソース配布物内の`src/tutorial/complex.c`と`src/tutorial/complex.sql`にコピーがあります。） この演算子クラスは、複素数をその絶対値による順番でソートする演算子をカプセル化します。 ですので、その名前に`complex_abs_ops`を選びました。 最初に演算子の集合が必要になります。 演算子を定義する処理は項35.12で説明しました。 B-tree上の演算子クラスでは、以下の演算子が必要です。

- 絶対値による、小なり（ストラテジ1）
- 絶対値による、以下（ストラテジ2）
- 絶対値による、等しい（ストラテジ3）
- 絶対値による、以上（ストラテジ4）
- 絶対値による、大なり（ストラテジ5）

比較演算子の関連する集合を定義する時にエラーを発生を最小にする方法は、まず、B-tree比較サポート関数を作成し、その後に、他の関数をサポート関数に対する1行のラッパとして作成することです。 これにより、境界となる条件で一貫性のない結果を得る確率が減少します。 この手法に従って、まず以下を作成します。

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

    static int
    complex_abs_cmp_internal(Complex *a, Complex *b)
    {
        double      amag = Mag(a),
                    bmag = Mag(b);

        if (amag < bmag)
            return -1;
        if (amag > bmag)
            return 1;
        return 0;
    }

これで、小なり関数は以下のようになります。

    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);
    }

他の4関数での違いは、内部関数の結果とゼロとをどのように比べるかだけです。

次に、関数と、この関数に基づく演算子をSQLで宣言します。

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

    CREATE OPERATOR < (
       leftarg = complex, rightarg = complex, procedure = complex_abs_lt,
       commutator = > , negator = >= ,
       restrict = scalarltsel, join = 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コードは、演算子関数と同じファイルに入っています。 以下は、関数をどのように宣言するかを示します。

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

これまでで、必要な演算子およびサポートルーチンを持つようになりました。 最後に演算子クラスを作成することができます。

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

これで終わりです！ これで`complex`列にB-treeインデックスを作って使用することが可能になったはずです。

以下のように、演算子エントリをより冗長に記述することができます。

            OPERATOR        1       < (complex, complex) ,

しかし、演算子が、演算子クラスの定義と同一のデータ型を取る場合、このような記述をする必要はありません。

上記の例は、ユーザがこの新しい演算子クラスを`complex`データ型のデフォルトのB-tree演算子クラスにしようとしていると仮定しています。 このようにしない場合、`DEFAULT`という単語を取り除いてください。

## 35.14.5. 演算子クラスと演算子族

これまでは暗黙的に、演算子クラスは1つのデータ型のみを扱うものと仮定してきました。 確かに特定のインデックス列にはたった1つのデータ型しかあり得ませんが、異なるデータ型の値とインデックス列の比較を行うインデックス操作はよく役に立ちます。 また、演算子クラスと関連したデータ型を跨る演算子を使用できる場合、他のデータ型は独自の関連した演算子クラスを持つことがよくあります。 SQL問い合わせを最適化する際にプランナを補助することができますので、関連したクラスを明示的に関連付けることは（どのように動作するかに関する知識をプランナは多く持ちますので、特にB-Tree演算子クラスで）有用です。

こうした要望に応えるためにPostgreSQLは演算子族という概念を使用します。 演算子族は1つ以上の演算子クラスから構成されます。 また、演算子族全体に属するが、演算子族内の個々のクラスには属さないインデックス可能演算子や対応するサポート関数を含めることもできます。 こうした演算子や関数を、特定のクラスに束縛されていないことから、演算子族内で"自由"であると呼びます。 通常、各演算子クラスは1つのデータ型演算子を持ちますが、データ型を跨る演算子は演算子族内で自由になります。

演算子族内の演算子と関数はすべて、意味的な互換性を持たなければなりません。 この互換性についての必要条件はインデックスメソッドによって設定されます。 このため、演算子族の特定の部分集合を演算子クラスとして選び出す方法に疑問を持つかもしれません。 実際多くの目的では、クラスの分類は不適切で、演算子族が唯一の興味深いグループ化です。 演算子クラスを定義する理由は、どれだけ多くの演算子族が何らかのインデックスをサポートするために必要かを指定することです。 ある演算子クラスを使用するインデックスが存在する場合、演算子クラスはそのインデックスを削除しない限り削除することができません。 しかし、演算子族の他の部分、すなわち、他の演算子クラスや自由な演算子を削除することができます。 したがって、演算子クラスは、特定のデータ型に対するインデックスを操作する上で理論上必要となる最少の演算子と関数の集合を含むように指定すべきです。 そして、関連するが基本的なものではな演算子を演算子族の自由なメンバとして追加することができます。

例えばPostgreSQLには`integer_ops`という組み込みのB-Tree演算子族があります。 ここには`bigint` (`int8`)、`integer` (`int4`)、`smallint` (`int2`)型の列上へのインデックスにそれぞれ対応した`int8_ops`、`int4_ops`、`int2_ops`という演算子クラスが含まれています。 また、上記の型の内任意の2つの型を比較できるように、この演算子族にはデータ型を跨る比較演算子も含まれます。 このため、上記の型のいずれかに対するインデックスを他の型の値との比較の際に使用することができます。 この演算子族は以下の定義により多重化されています。

    CREATE OPERATOR FAMILY integer_ops USING btree;

    CREATE OPERATOR CLASS int8_ops
    DEFAULT FOR TYPE int8 USING btree FAMILY integer_ops AS
      -- standard int8 comparisons
      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
      -- standard int4 comparisons
      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
      -- standard int2 comparisons
      OPERATOR 1 < ,
      OPERATOR 2 <= ,
      OPERATOR 3 = ,
      OPERATOR 4 >= ,
      OPERATOR 5 > ,
      FUNCTION 1 btint2cmp(int2, int2) ;

    ALTER OPERATOR FAMILY integer_ops USING btree ADD
      -- cross-type comparisons 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) ,

      -- cross-type comparisons 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) ,

      -- cross-type comparisons 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) ,

      -- cross-type comparisons 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) ,

      -- cross-type comparisons 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) ,

      -- cross-type comparisons 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) ;

この定義は演算子ストラテジ関数番号とサポート関数番号を"上書き"していることに注意してください。 各番号は演算子族内で複数回現れます。 特定番号のインスタンスがそれぞれ異なる入力データ型を持つ限り、これは許されます。 入力型の両方が演算子クラスの入力型と同じインスタンスは、演算子クラスの主演算子および主サポート関数であり、ほとんどの場合、演算子クラスの自由メンバではなく演算子クラスの一部として宣言しなければなりません。

B-Tree演算子族では、演算子族内のすべての演算子はソート互換、つまり、演算子族でサポートされるデータ型すべてに渡って過渡的規則、"A = BかつB = CならばA = C"および"A < BかつB < CならばA < C"を維持しなければなりません。 演算子族内の各演算子では、演算子と同じデータ型の2つのデータ型を取るサポート関数が存在しなければなりません。 演算子族を完結させること、つまり、データ型の組み合わせそれぞれに対する演算子をすべて含めることを推奨します。 各演算子クラスは、自身のデータ型に対してデータ型を跨らない演算子とサポート関数だけを含めなければなりません。

複数データ型のハッシュ演算子族を構築するには、演算子族でサポートされるデータ型それぞれに対する互換性を持つハッシュサポート関数を作成しなければなりません。 ここで、互換性とは、関数がその演算子族の等価性演算子で等価であるとみなされる任意の2つの値では同一のハッシュコードが生成されることを保証することを意味します。 通常、型が異なる物理表現を持つ場合、これを実現することは困難ですが、実現可能な場合もあります。 データ型1つに対してサポート関数が1つしか存在しないことに注意してください。 等価性演算子ごとに１つではありません。 演算子族を完結させること、つまり、データ型の組み合わせそれぞれに対する等価性演算子をすべて含めることを推奨します。 各演算子クラスは、自身のデータ型に対してデータ型を跨らない演算子とサポート関数だけを含めなければなりません。

GINおよびGiSTインデックスではデータ型を跨る操作についての明示的な記法はありません。 サポートされる演算子群は単に指定演算子クラスの主サポート関数が扱うことができるものです。

> 注意: PostgreSQL8.3より前のバージョンでは演算子族という概念はありませんでした。 そのため、インデックスで使用する予定のデータ型を跨る演算子はすべて、インデックスの演算子クラスに結びつけなければなりませんでした。 この手法もまだ使用できますが、インデックスの依存性を広げる点、および、両データ型が同一演算子族内で演算子を持つ場合、プランナがデータ型を跨った比較をより効率的に扱うことができる点より、廃止予定です。

## 35.14.6. システムの演算子クラスに対する依存性

PostgreSQLは演算子クラスを、単にインデックスで使用できるかどうかだけではなく、多くの方式で演算子の性質を推定するために使用します。 したがって、データ型の列をインデックス付けするつもりがなくても、演算子クラスを作成した方が良い可能性があります。

具体的には、`ORDER BY`や`DISTINCT`など、値の比較とソートを必要とするSQL機能があります。 ユーザ定義のデータ型に対してこの機能を実装するために、PostgreSQLはそのデータ型用のデフォルトのB-tree演算子クラスを検索します。 この演算子クラスの"等価判定"メンバが、`GROUP BY`や`DISTINCT`用の値の等価性についてのシステムの意向を定義し、この演算子クラスによって強制されるソート順序が、デフォルトの`ORDER BY`順序を定義します。

また、ユーザ定義型の配列の比較は、デフォルトのB-tree演算子クラスによって定義されるセマンティックに依存します。

データ型用のデフォルトのB-tree演算子クラスが存在しないと、システムはデフォルトのハッシュ演算子クラスを検索します。 しかし、この種類の演算子クラスは等価性のみを提供しますので、実際にこれは、配列等価性のサポートだけに対して十分です。

データ型用のデフォルトの演算子クラスが存在しない場合に、こうしたSQL機能をデータ型に使用しようとすると、"演算子クラスを識別できなかった"といったエラーとなります。

> 注意: PostgreSQLバージョン7.4より前まででは、ソートやグループ化操作は暗黙的に`=`、`<`、`>`という名前の演算子を使用していました。 この新しい、デフォルトの演算子クラスに依存する振舞いによって、特定の名前を持つ演算子の振舞いについて何らかの仮定を立てることを防止しています。

他の重要な点として、ハッシュ演算子族内に現れる演算子がハッシュ結合、ハッシュ集約、関連する最適化の候補となることがあります。 使用するハッシュ関数を識別するため、ここでのハッシュ演算子族は基本的なものです。

## 35.14.7. 演算子クラスの特殊な機能

演算子クラスには、まだ説明していない2つの特殊な機能があります。 説明していない主な理由は、最もよく使用するインデックスメソッドでは、これらがあまり有用ではないためです。

通常、演算子を演算子クラス（または演算子族）のメンバとして宣言すると、インデックスメソッドでその演算子を使用して、`WHERE`条件を満たす行の集合を正確に抽出することができます。 以下に例を示します。

    SELECT * FROM table WHERE integer_column < 4;

この式は、整数列にB-treeインデックスを使用することにより、正確に満たすことができます。 しかし、一致する行へ厳密ではなくとも導く手段としてインデックスが有用である場合があります。 例えば、GiSTインデックスで、幾何オブジェクトの境界ボックスのみを格納したとします。 その結果、多角形のような長方形でないオブジェクトとの重なりをテストするWHERE条件は正確に満たすことができません。 もっとも、このインデックスを使用して、対象オブジェクトの境界ボックスに重なる境界ボックスを持つオブジェクトを検索し、さらに、検索されたオブジェクトのみに対して正確に重なるかどうかをテストすることはできます。 この筋書きを適用する場合、インデックスは演算子に対して"非可逆"と言われます。 非可逆インデックス検索は、ある行が問い合わせ条件を実際に満足するかしないかの時にrecheckフラグを返すインデックスメソッドを持つことで実装されます。 コアシステムは、そこで有効なマッチとして行が返されるか否かを確認するために、抽出された行に対して元の問い合わせ条件を検査します。 この手法はインデックスが全ての必要な行を返すことが保証された上で、元の演算子呼び出しを実行することによって除外することができる、いくつか余分な行を返す可能性がある場合に動作します。 非可逆検索を提供するインデックス方式（現時点ではGiSTとGIN）は個々の演算子クラスのサポート関数がrecheckフラグを設定することを許可します。 このためこれは原則的に演算子クラスの機能です。

再度、多角形のような複雑なオブジェクトの境界ボックスのみをインデックスに格納している状況を考えてみてください。 この場合、インデックスエントリに多角形全体を格納するのは、それほど有用なことではありません。 単に、より単純な`box`型のオブジェクトを格納した方が良いかもしれません。 このような状況は、`CREATE OPERATOR CLASS`の`STORAGE`オプションによって表現することができます。 例えば、以下のように記述します。

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

現時点では、GiSTインデックスメソッドとGINインデックスメソッドが、列のデータ型と異なる`STORAGE`型をサポートしています。 `STORAGE`が使用された場合、GiSTの`compress`および`decompress`サポートルーチンは、データ型を変換する必要があります。 GINでは、`STORAGE`型は"キー"の値の型を識別します。 通常これはインデックス付けされる列の型とは異なります。 例えば、整数配列の列用の演算子クラスは単なる整数をキーとして持つかもしれません。 GINの`extractValue`および`extractQuery`サポートルーチンが、インデックス付けされた値からキーを取り出す責任をおいます。

前のページ | ホーム | 次のページ
演算子最適化に関する情報 | 上に戻る | 拡張へのC++の利用