片岡裕生 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
© Copyright 2024 Paperzz