これでアクセスメソッドと演算子クラスができました。次に、演算子の セットが必要になります。演算子を定義する処理はChapter 14で説明しました。 Btrees 上の complex_abs_ops 演算子クラスでは、以下 の演算子が必要です。
これらの関数を実装するコードは、 PGROOT/src/tutorial/complex.c に格納され、 PGROOT/src/tutorial/complex.so.にコンパイルしたものとします。C コードの一部を以下に示します。
#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); }
(注意:この例の残りの部分では、 イコール演算子のみを示すことにします。他の 4 つの演算子は良く似ています。詳細は、 complex.c または complex.source をご覧ください。)
PostgreSQL に関数を登録するために下記のようにします。
CREATE FUNCTION complex_abs_eq(complex, complex) RETURNS boolean AS 'PGROOT/src/tutorial/complex' LANGUAGE C;
ここで重要なことがいくつかあります。
まず、complex 用の「より小さい」、 「以下」、「等しい」、「以上」、 「より大きい」のための演算子が定義されようとしているということです。例えば、= という演算子は一つしかもつことが出来ず、両方のオペランドを complex 型とします。この場合complex に対してほかの演算子 = はありません。しかしもし実用的なデータ型を構築するなら、多分 = は複素数の通常の等しいという演算になってほしいかと思います。その場合は、complex_abs_eq に対しなにか他の演算子名を使う必要があります。
二番目に、PostgreSQL の場合は、違った入力データ型であれば同じ名前の演算子を使うことができますが、C ではネームスペース内で一つのグローバルルーチンが使えるだけです。ですから C 関数は abs_eq のような単純な名前にするべきではありません。通常は C 関数名にデータ型名を入れておけば、他のデータ型の関数と衝突することもありません。
三番目に、関数abs_eqの PostgreSQL 名は、 PostgreSQL が入力データ型によって同じ名前を持つ他の PostgreSQL 関数から区別してくれることを期待して作ることができます。ここでは例を簡単にするために、関数に C レベルと PostgreSQL レベルで同じ名前を与えます。
最後に、これらの演算子関数はブーリアンの値を返すことに注意して下さい。実際、インデックスアクセスメソッドストラテジとして定義した、全ての演算子は boolean 型を返さなければなりません。ですので、これらは、インデックスが使用されるために、WHERE のトップレベルに現れなければなりません。(一方で、サポート関数は特定のアクセスメソッドが期待するものを何でも返します。この場合は符号付き整数です)。
ファイルの最後のルーチンは、 pg_amテーブルの amsupport 列について説明したときに出てきた "サポートルーチン"です。これは後に使います。今のところは無視します。
これで演算子を定義する準備ができました。
CREATE OPERATOR = ( leftarg = complex, rightarg = complex, procedure = complex_abs_eq, restrict = eqsel, join = eqjoinsel );
ここで重要なのは、プロシージャ名(つまり上記で定義されるC 関数)と制約と結合選択関数です。例で使われている選択関数をただ使って下さい complex.sourceを参照)。これらは「より小さい」「等しい」そして「以上」の場合に使われる関数とは違うことに注意して下さい。これらが供給されなければ、オプティマイザはインデックスを効果的に利用できません。
次のステップはこれらの演算子の項目をpg_amop リレーションに追加することです。そうするためには、上記で定義したばかりの演算子の OID が必要です。ここでは二つのcomplex型をオペランドとして取る全ての演算子の名前を検索し、必要なものを選択します。
SELECT o.oid AS opoid, o.oprname INTO TEMP TABLE complex_ops_tmp FROM pg_operator o, pg_type t WHERE o.oprleft = t.oid and o.oprright = t.oid and t.typname = 'complex'; opoid | oprname --------+--------- 277963 | + 277970 | < 277971 | <= 277972 | = 277973 | >= 277974 | > (6 rows)
(ここでまた、OID 番号はほぼ確実に違ったものになるでしょう。)ここで重要な演算子は277970 から 277974 までの OID をもつものです。得られる値はシステムによって異なるでしょうから、以下ではその値に置き換えて下さい。ここでは SELECT 文で行います。
ここで、新しい演算子クラス用のpg_amop のエントリを挿入する準備ができました。このエントリは正確に、必要とする演算子の B-tree ストラテジ番号に関連づけられていなければなりません。「より小さい」演算子を挿入するコマンドは以下のようになります。
INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr) SELECT opcl.oid, 1, false, c.opoid FROM pg_opclass opcl, complex_ops_tmp c WHERE opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree') AND opcname = 'complex_abs_ops' AND c.oprname = '<';
次に、2行目の 1 と最後の < を置き換えて、他の演算子についても実行して下さい。順番に注意して下さい。 "より小さい" は 1、"以下" は 2、 "等しい" は 3、 "以上" は 4、そして、 "より大きい" は 5 です。
amopreqcheck フィールドについてはここで説明しません。B-tree 演算子では常に偽にすべきです。
最後のステップはpg_amの説明で以前に説明された "サポートルーチン" の登録です。このサポートルーチンのOID はpg_amprocテーブルに格納され、演算子クラスのOIDとサポートルーチン番号がキーになっています。
まず始めに、この関数をPostgresに登録する必要があります(演算子ルーチンを実装したファイルの最後にこのルーチンを実装するCコードを入れたことを思い出してください) 。
CREATE FUNCTION complex_abs_cmp(complex, complex) RETURNS integer AS 'PGROOT/src/tutorial/complex' LANGUAGE C; SELECT oid, proname FROM pg_proc WHERE proname = 'complex_abs_cmp'; oid | proname --------+----------------- 277997 | complex_abs_cmp (1 row)
(ここでまた、OIDが違っているでしょう。)
下記のように新しい行を追加することができます。
INSERT INTO pg_amproc (amopclaid, amprocnum, amproc) SELECT opcl.oid, 1, p.oid FROM pg_opclass opcl, pg_proc p WHERE opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree') AND opcname = 'complex_abs_ops' AND p.proname = 'complex_abs_cmp';
これで終りです! これでcomplex 列のbtree インデックスを作って使用することが可能になったはずです。