オブジェクト指向 RDBMS 豊富な拡張性

片岡裕生 KATAOKA Hiroki kataoka@interwiz.koganei.tokyo.jp
ブルに格納されている情報を書き換えれば,
オブジェクト指向 RDBMS
PostgreSQL の動作そのものを簡単に変更できるこ
とになります.
PostgreSQL はオブジェクト指向のリレーショナ
実はその通りなのです.PostgreSQL は非常に強
ルデータベース管理システムと言われています.
力な拡張性を備えたリレーショナルデータベース
そう言えるのも,PostgreSQL のデータベースエン
管理システムなのです.ただ,何を拡張するにも
ジン内部ではあらゆるものが抽象化されて扱われ
システムテーブルを書き換えるというのも大変で
ているからです.
すし,なにより危険でもあります.そこで
たとえば,それぞれのデータ型の特徴や演算の
方法などはすべてシステムテーブルに保管されて
います.つまりデータベースエンジン自身は,
「int4 型はメモリ上で 4 バイトを占める」というこ
とさえも知らないのです.
抽象化はデータ型においてだけではありません.
PostgreSQL では,比較的需要の多い拡張内容に対
して専用の SQL 命令を用意しています(表 1)
.
関数の登録などは,あえて拡張というほどの内
容でもないと思えますが,PostgreSQL では C 言語
で関数を作成することもできます.ですから無限
に近い可能性を秘めた立派な拡張と言えるわけで
集合関数やアクセスメソッド(インデックス)な
す.専用の SQL 命令を使わないで直接システムテ
ども抽象化されています.たとえば PostgreSQL に
ーブルを変更するのであれば,もう少し踏み込ん
は“btree”や“hash”
,
“rtree”などのアクセスメ
だ拡張も可能です(表 2)
.
ソッドが搭載されていますが,実はデータベース
それでは各拡張内容についてもう少し詳しく説
エンジン自身はこれらのアクセスメソッドの仕組
明したいと思いますが,これらの拡張性を利用し
みについてはまったく知らないのです.
た例として PostgreSQL 用の多次元幾何オブジェク
PostgreSQL ではアクセスメソッドの定義でさえも
トがありますので,この例を持ち出しながら解説
システムテーブルに保管されているのです.
●表 1 SQL 命令で行える拡張
豊富な拡張性
PostgreSQL ではあらゆる物が抽象化されており,
個々の特徴などはシステムテーブルに保管されて
いると述べました.ということは,システムテー
44 - Software Design
拡張内容
SQL 命令
データ型の登録
CREATE TYPE
関数の登録
CREATE FUNCTION
演算子の登録
CREATE OPERATOR
集合関数の登録
CREATE AGGREGATE
c h a p t e r
4
PostgreSQL に最初から搭載されている 2 次元幾
したいと思います.
何データ型では固定されていた許容誤差が,多
多次元幾何オブジェクト
多次元幾何オブジェクトとは,その名の通り多
次元の幾何データを扱うための PostgreSQL 用のデ
ータ型です.PostgreSQL には最初から 2 次元の幾
何データを扱う機能が搭載されていますが,決し
て十分な内容とはいえませんし,もちろん3 次元の
データを扱うこともできません.そこでこれらの
問題を解決するために作成されたのが,多次元幾
次元幾何オブジェクトでは自由に設定できます.
最新版の多次元幾何オブジェクトのソースファ
イル一式は,次の URL にて公開されています.
『インターウィズ PostgreSQL 用 多次元幾何オブジ
ェクト』
http://www.interwiz.koganei.tokyo.jp/sof
tware/geometric/index.html
何オブジェクトなのです.もちろん PostgreSQL 本
それでは,この多次元幾何オブジェクトを例に
体には一切変更を加えることなく,あくまでも
PostgreSQL を拡張する方法を解説します.なお誌
PostgreSQL の拡張性を活用するというスタイルで
面の都合もあり,多次元幾何オブジェクト自体の
作成されています.
アルゴリズムなどに関しては割愛させていただき
多次元幾何オブジェクトで実現している機能に
ます.
は次のようなものがあります.
新しいデータ型の登録
¡任意の次元数のデータに対応
2 次元はもちろんのこと,3 次元以上のデータも
PostgreSQL に新しいデータ型(ユーザ定義デー
タ型)を追加するには,2 つの関数を作成する必要
扱えます.
¡単純な幾何学的形状が認識可能
があります.一対の入出力関数です.この関数の
たとえば 2 次元データとしては点/線分/三角
役割は,バイナリデータ(ユーザ定義データ型の
形/長方形などが,3 次元データとしては点/線
内部形式)とテキストデータを相互変換すること
分/三角形/長方形/四面体/直方体などが扱
です(図 1)
.
えます.4 次元以上については…ややこしいだけ
PostgreSQL は,ユーザ定義データ型がどのよう
ですから説明はやめておきます.
な内部形式になっているかを知りません.そのた
これらすべての形状が単一のデータ型で扱われ
め,このような入出力関数が必要になります.た
ます.
とえば SQL 文の中にユーザ定義データ型の値が記
¡インデックスが利用可能
入されていた場合,SQL 文中のテキスト表現を内
PostgreSQL に最初から搭載されているアクセス
部形式に変換するために,PostgreSQL はユーザ定
メソッド“rtree”の改良版が利用できます.
義データ型の入力関数を呼び出します.逆にユー
PostgreSQL 本体の再コンパイルは必要ありませ
ザ定義データ型の内容を表示しなければならない
ん.実行時にダイナミックに入れ替わります.
場合などには,出力関数を呼び出して内部形式を
¡演算上の許容誤差を指定可能
テキスト形式に変換させるのです.
●表 2 システムテーブルを直接変更する拡張
拡張内容
説明
アクセスメソッドの定義
btree や hash などのインデックスの種類を新たに定義する
オペレータクラスの定義
新しい比較の方法を定義して,アクセスメソッドに対応づける
Jun.
2000 - 45
多次元幾何オブジェクトでは入力関数として
“geometric_in”関数を,出力関数として“geometr
ic_out”関数を用意しています.これらの関数は C
¡create type geometric
データ型“geometric”を登録します.
¡internallength = variable
言語で作成されていて,コンパイルされたものが
このデータ型のサイズは可変長です(ちなみに
“libgeometric.so.2.0”という共有オブジェクトファ
固定長の場合には“variable”の代わりにバイト
イルに格納されています.多次元幾何オブジェク
ト用のユーザ定義データ型を登録するためには,
数を指定します)
.
¡input = geometric_in, output = geometric_out
先にこの 2 つの関数を PostgreSQL に登録する必要
このデータ型の入力関数は geometric_in で,出
があります.その後,新しいデータ型“geometric”
力関数は“geometric_out”です.
を登録します.リスト 1 に SQL 文の例を示します.
新しい関数の登録には create function 文を利用し,
この作業が済むと,PostgreSQLで geometric型が
新しいデータ型の登録には create type 文を利用し
利用できるようになります.でもまだテーブルへ
ます.この SQL 文の大まかな意味は次のような内
の格納と取り出ししかできません.
容です.
新しい関数の登録
¡create function ∼
「新しい関数の登録」の項を参照してください.
●リスト 1
テーブルへの格納と取り出しができるようにな
入出力関数の登録
create function geometric_in(opaque) returns geometric
as '/usr/local/pgsql/lib/libgeometric.so.2.0'
language 'c';
create function geometric_out(opaque) returns opaque
as '/usr/local/pgsql/lib/libgeometric.so.2.0'
language 'c';
create type geometric (
internallength = variable,
input = geometric_in, output = geometric_out
);
●図 1
入出力関数
各種データ型
PostgreSQL
テキスト
データ
入力関数
バイナリ
データ
エンジン部
テキスト
データ
46 - Software Design
出力関数
バイナリ
データ
デ
ー
デタ
ー型
タ独
構自
造の
c h a p t e r
ったところで,比較用の関数を登録してみましょ
う.geometric 型データを比較することができない
と,何かと不便です.
多次元幾何オブジェクトでは,比較用の関数と
していくつかを用意しています.たとえば 2 つの
geometric 型データが等しいかどうかを判断する
“geometric_same”関数があります.この関数も C
言語で作成されていて,コンパイルされたものが
共有オブジェクトファイルに格納されています.
PostgreSQL 上でこの関数を使えるようにするた
4
も,次のような記述しかできないからです.
SELECT * FROM xxxx WHERE
geometric_same(column1, column2);
できることなら次のように記述したいでしょう.
SELECT * FROM xxxx WHERE
column1 = column2;
これを実現するために行うことは,オペレータ
(演算子)の登録です.
めには,先ほどと同じように create function 文を利
新しいオペレータの登録
用して関数を登録する必要があります.リスト 2 が
その SQL 文です.この SQL 文の大まかな意味は次
のような内容です.
PostgreSQL では,関数と演算子を対応づけるこ
とができます.具体的には,ある演算子を使った
¡create function geometric_same (geometr
ic, geometric) returns bool
式があったら,それを既存の関数呼び出しに置き
換えることができるのです.
関数“geometric_same”を登録します.関数の
それでは先ほどの比較関数 geometric_same を,
引数は 2 つ,共に geometric 型で,関数の返り値
演算子“~=”で呼び出せるようにしてみましょう.
は bool 型です.
¡as '/usr/local/pgsql/lib/libgeometric.s
リスト 3 がその SQL 文です.オペレータの登録
には create operater 文を利用します.リスト内の
o.2.0'
create operater 文の大まかな意味は次のようになっ
関数の処理内容は共有オブジェクトファイル
ています.
“∼ libgeometric.so.2.0”に入っています.
¡language 'c'
関数の処理内容は C言語で作成されています.
¡create operator ˜=
演算子“~=”を登録します.
¡leftarg = geometric, rightarg = geometric
これで geometric 型データの比較が行えるように
なったわけですが,まだ十分ではありません.と
いうのも,この時点ではまだ普通の関数でしかあ
りませんから,SQL 文の中で比較しようと思って
この演算子の左右の引数は共に geometric 型で
す.
¡procedure = geometric_same
この演算子によって呼び出すべき関数は
●リスト 2 関数の登録
create function geometric_same(geometric, geometric) returns bool
as '/usr/local/pgsql/lib/libgeometric.so.2.0'
language 'c';
●リスト 3 オペレータの登録
create operator ˜= (
leftarg = geometric, rightarg = geometric,
procedure = geometric_same,
commutator = ˜=,
restrict = eqsel, join = eqjoinsel
);
Jun.
2000 - 47
回数をカウントしていき,最終的にデータ件数
geometric_same です.
¡commutator = ~=
を求めます.
この演算子の左右の引数が入れ替わった場合に
同じ結果を返せる演算子は“~=”です.
¡restrict = eqsel, join = eqjoinsel
¡後処理関数
最後に 1 度だけ呼び出され,上記 2 つの関数が集
計した結果から答えを求めます.たとえば平均
問い合わせコストを計算する場合に利用すべき
値を求めるavg関数の場合なら,データの値の合
関数は eqsel とeqjoinsel です(詳細は省略)
.
計をデータ件数で割り,平均値を求めます.
これで演算子“~=”による比較が行えるように
上記 3 つの関数を必要に応じて用意するわけです
が,たとえば sum 関数にはデータ集計関数しかあ
なります.
りません.というのもデータの値の合計を求める
新しい集合関数の登録
だけで済むからです.
多次元幾何オブジェクトでは2 つの集合関数を用
PostgreSQL では,count 関数や avg 関数で知られ
意しています.1 つは“geometric_union”集合関数
ている集合関数でさえも新しく登録することがで
で,すべてのデータを含む最小の領域を計算しま
きます.それには,必要に応じて次の3 つの関数を
す.もう 1 つは“geometric_intersect”集合関数で,
作成する必要があります(図 2)
.
すべてのデータに内包される最大の領域を計算し
ます.
¡データ集計関数
geometric_union 集合関数を例に取ると,今説明
レコード1 件ごとに呼び出され,データの値に関
した 3 つの関数のうち必要なのは“ag_geometric_u
する集計を行います.たとえば平均値を求める
nion_s1”というデータ集計関数だけです.そこで
avg 関数の場合なら,データの値を変数に加算し
この ag_geometric_union_s1 関数を PostgreSQL に登
ていき,最終的に合計を求めます.
録してから,geometric_union という名前の集合関
¡件数集計関数
数を登録します.
レコード 1 件ごとに呼び出され,呼び出し回数
リスト 4 がその SQL 文です.集合関数の登録に
(データ件数)に関する集計を行います.たとえ
は create aggregate 文を利用します.リスト内の
ば平均値を求めるavg 関数の場合なら,呼び出し
create aggregate 文の大まかな意味は次のようにな
●図 2
集合関数の登録
開始
繰レ
りコ
返ー
しド
が
続
く
間
データ集計関数
データ
変数1
件数集計関数
変数2
テーブル
後処理関数
終了
結果
※後処理関数がない場合には,
変数1の値が結果になる
48 - Software Design
c h a p t e r
4
●リスト 4 集合関数の登録
create function ag_geometric_union_s1(geometric, geometric) returns geometric
as '/usr/local/pgsql/lib/libgeometric.so.2.0'
language 'c';
create aggregate geometric_union (
bsetype = geometric,
sfunc1 = ag_geometric_union_s1, stype1 = geometric
);
っています.
このように,物の比較の方法にしてもいろいろ
な尺度が考えられるわけです.PostgreSQL では,
¡create aggregate geometric_union
集合関数“geometric_union”を登録します.
¡bsetype = geometric
この尺度をオペレータクラスで表現しています.
PostgreSQL のアクセスメソッドは,登録済みの
オペレータクラスの尺度をもとに,データを並び
この集合関数が扱うデータ型は geometric 型で
替えてインデックスを作成します.ですから新し
す.
いオペレータクラスさえ作成すれば,PostgreSQL
¡sfunc1 = ag_geometric_union_s1, stype1
= geometric
データ集計関数は“ ag_geometric_union_s1”で,
のアクセスメソッドは柔軟に対応してくれるので
す.
それでは,geometric型のデータをPostgreSQLに
変数 1(図 2 参照)のデータ型は geometric 型で
最初から搭載されている rtree アクセスメソッドに
す.
扱わせることを考えてみます.rtree アクセスメソ
ッドとは多次元データ向けのアクセスメソッドで
アクセスメソッドに関して
す.他のアクセスメソッドとしては btree が有名で
すが,こちらは1 次元のデータしか扱うことができ
PostgreSQL では,最初にも述べたように新規ア
ません.geometric 型は多次元のデータですから,
クセスメソッドの登録でさえも可能です.ただ今
rtree アクセスメソッドを利用することになります.
回は誌面の関係もありそこまでは紹介できません
まずはオペレータクラスを登録します.名前は
が,既存の rtree アクセスメソッドに geometric 型
“geometric_ops”とします.オペレータクラスを登
を扱わせる方法についてだけは紹介しておこうと
録する SQL 命令などはとくにありませんので,直
思います.
接システムテーブルを書き換えます.オペレータ
新たなデータ型や比較方法に対してアクセスメ
クラスを格納するシステムテーブルは“pg_opclass”
ソッドを利用できるようにするためには,オペレ
で,
“opcname”カラムにオペレータクラスの名称
ータクラスというものを登録しなければなりませ
を,
“opcdeftype”カラムにデータ型の oid(オブジ
ん.オペレータクラスは「物の尺度」と表現すれ
ェクト ID)を指定します.
ば良いかもしれません(
「尺度」とは筆者がわかり
オペレータクラス geometric_ops を登録する SQL
やすいと思って利用している用語であり,決して
文はリスト 5 のようになります.geometric 型の oid
PostgreSQL の用語ではありません)
.たとえば単純
は環境により異なりますので,この SQL 文ではデ
な数値を比較する場合,誰もが「数値が大きいか
ータ型の情報が格納されているシステムテーブル
小さいか」という尺度で考えると思います.とこ
ろが日本語の文字列ならどうでしょうか.コンピ
ュータで扱う場合なら「文字列の内部コード順」
という尺度もありますし,それ以外の代表的なも
のとしては「辞書順」という尺度もあります.
“pg_type”から oid を取り出すようにしています.
●リスト 5
オペレータクラスの登録
insert into pg_opclass (opcname, opcdeftype)
select 'geometric_ops', pg_type.oid
from pg_type
where pg_type.typname = 'geometric';
Jun.
2000 - 49
さて,オペレータクラスは登録できたのですが,
“pg_amop”です.尺度の定義はアクセスメソッド
まだ尺度となる情報自体がどこにもありません.
ごとに異なりますので,どのアクセスメソッドを
というわけで,これから行う作業がこの尺度の指
利用するかによって登録する内容も決まります.
定です.
このテーブルに含まれるカラムは多いので,表 3 に
簡単な説明と共にまとめました.そして表 4 が,ア
尺度の情報を登録するのはシステムテーブル
●表 3
クセスメソッドごとの登録すべき情
pg_amop テーブルのカラム
報です.
カラム名
説明
amopid
この尺度で使用するアクセスメソッドの oid
amopclaid
この尺度を表すオペレータクラスの oid
amopstrategy
ストラテジ(尺度の意味のコード番号)(表 4 参照)
amopopr
ストラテジに対応した演算子の oid(表 4 参照)
amopselect
問い合わせコストに関する指定
geometric_ops の尺度情報を登録する
amopnpages
問い合わせコストに関する指定
SQL 文はリスト 6 のようになります.
●表 4
多次元幾何オブジェクトでは rtree
アクセスメソッドを利用しますので,
表 4 から,登録すべき尺度の情報は 8
つになります.
先ほど作成したオペレータクラス
ただし 8 つの情報を登録する SQL 文は
pg_amop テーブルに登録すべき尺度の情報
非常に長くなりますので,リスト 6 に
amopid
amopstrategy
amopopr(例)
尺度の意味
hash
1
=
等しい
btree
1
<
未満
2
<=
以下
3
=
等しい
4
>=
以上
の登録」の項で取り上げた“~=”演
5
>
超える
算子を指定しています.なお,各カ
1
<<
より左
ラムに設定する oid の値は環境により
2
&<
より左 or 交差
異なってきますので,この SQL 文で
3
&&
交差
もシステムテーブルから必要な oid を
4
&>
より右 or 交差
取得するようにしています.
5
>>
より右
6
~=
等しい
7
~
含む
8
@
含まれる
は 6 番目の情報を登録している部分だ
けを載せています.rtree アクセスメ
rtree
ソッドの 6 番目の情報とは「等しい」
ことを意味する演算子の情報です
(表 4 参照)ので,
「新しいオペレータ
そして最後に残された作業が rtree
アクセスメソッド自体に関すること
です.geometric 型のデータを比較す
るための「尺度」は登録できました
●リスト 6
が,まだ rtree アクセスメソッド自体
尺度情報の登録
:
insert into pg_amop (amopid, amopclaid, amopopr, amopstrategy, amopselect, amopnpages)
select pg_am.oid, pg_opclass.oid, pg_operator.oid, 6, 'rtsel'::regproc, 'rtnpage'::regproc
from pg_am, pg_opclass, pg_operator, pg_type pg_type_left, pg_type pg_type_right
where
pg_am.amname = 'rtree' and
pg_opclass.opcname = 'geometric_ops' and
pg_operator.oprname = '˜=' and
pg_operator.oprleft = pg_type_left.oid and
pg_type_left.typname = 'geometric' and
pg_operator.oprright = pg_type_right.oid and
pg_type_right.typname = 'geometric';
:
50 - Software Design
c h a p t e r
4
は geometric 型のことを良くは知りませんから,イ
SQL 上では関数として認識されません.次の insert
ンデックスを構築することができません.ですか
文が,サポート関数の登録です.他の例を同じよ
ら geometric 型のデータを扱うのに必要な情報(サ
うに,各 oid をシステムテーブルから取得していま
ポート関数)を,rtree アクセスメソッドに教えて
す.
あげる必要があります.
PostgreSQL の可能性
これらの情報を登録するするシステムテーブル
は“pg_amproc”です.表 5 に各カラムの説明を載
以上の手順で,めでたく多次元幾何オブジェク
せておきます.各アクセスメソッドが必要として
トが利用可能になります.PostgreSQL 本体のソー
いる情報を表 5に示します.
rtree アクセスメソッドで geometric 型のデータを
スコードをとくに触ることなく注 1 これだけの拡張
扱わせるためには,表 6 から,3 つのサポート関数
が可能なのも,ひとえに PostgreSQL の設計思想の
を登録すれば良いことになります.
たまものだと思います.
その SQL 文はリスト 7 のようになります.なお
「皆さんもぜひ試してみてください」とまでは申
このリストも,1 番目のサポート関数の登録部分の
しませんが,PostgreSQL の柔軟性を理解していた
みを掲載しています.最初に行っているのは関数
だければ幸いです.
そのものの登録です.これを行わない限り Postgre
●表 5 pg_amproc テーブルのカラム
カラム名
説明
amid
アクセスメソッドの oid
amopclaid
オペレータクラスの oid
amprocnum
サポート関数の番号(表 6 参照)
amproc
サポート関数(表 6 参照)
●表 6 pg_amproc テーブルに登録すべき情報
amid
amprocnum
サポート関数の意味
hash
1
データをハッシュコード化する関数
btree
1
2 つのデータを比較する関数
rtree
1
2 つのデータを含む最小の領域を取得する関数
2
2 つのデータに含まれる最大の領域を取得する関数
3
データの大きさを取得する関数
●リスト 7 サポート関数の登録
create function rt_geometric_union(geometric, geometric) returns geometric
as '/usr/local/pgsql/lib/libgeometric.so.2.0'
language 'c';
:
insert into pg_amproc (amid, amopclaid, amproc, amprocnum)
select pg_am.oid, pg_opclass.oid, pg_proc.oid, 1
from pg_am, pg_opclass, pg_proc
where
pg_am.amname = 'rtree' and
pg_opclass.opcname = 'geometric_ops' and
pg_proc.proname = 'rt_geometric_union';
:
注 1)多次元幾何オブジェクトではメモリ消費効率を良くするために,rtree の改良版 nrtree を利用しています.ただし実行時に動的に
nrtree に差し替えますので,やはりソースコードには触れていません.
Jun.
2000 - 51