PostgreSQLでは,いろいろな言語を使ってユーザー定義関数を書くことができる。代表的なものとしては,PL/pgSQLというSQLに制御構造を追加したような独自言語で記述するユーザー定義関数がある。他のDBMSで言えば,「ストアド・プロシジャ」と呼ばれているものに相当する。

 一方,最近Javaでエンタープライズ向のアプリケーションが多くなるにつれ,商用DBMSベンダーではJavaによるストアド・プロシジャがサポートされるようになってきた。Javaでストアド・プロシジャが書けるようになれば,開発言語がJavaに統一されるので開発者の負担が軽くなるばかりでなく,アプリケーションとストアド・プロシジャでJavaのクラスを共有することによって開発効率の向上を狙えるからである。

 こうした背景を受けて登場したのがPL/Javaである。その名の通り,JavaでPostgreSQLのユーザー定義関数が書ける。ただし,実行できるJavaメソッドはクラス・メソッドに限られている。

PL/Javaの構造を見る

写真1●PL/Javaの開発ページ
図1●JVMを別プロセスとして起動する
図2●JNIを使ってバックエンド・プロセス内にJVMを取り込む
 PL/Javaを開発したのはスウェーデン在住の独立コンサルタントThomas Hallgren氏で,今年の1月7日に最初のバージョンが公開され,その後1月27日にはLinuxやWindows用のバイナリの配布も始まった。開発はPostgreSQL開発チームが提供するPostgreSQLのアプリケーション開発支援サイトgborg。postgresql.org上で行われており,ソースコードやバイナリ,ドキュメント類もここから入手できる。PL/Javaの開発ページ(写真1[拡大表示])は(http://gborg.postgresql.org/project/pljava/projdisplay.php)である。

 PL/JavaのライセンスはPostgreSQLと同じBSDライセンスである.そのため,どのような用途でも無償で使え,また改造して商用製品に組み込んで使った場合にもソースコード公開などの義務は発生しない。

 PL/Javaの実行環境としては,PostgreSQL 7.4以降と,JRE 1.4が必要である。もし自前でコンパイルするのであれば,JDKとantも必要になる。

 ユーザー定義関数はPostgreSQLのバックエンド・プロセス(データベースエンジン・プロセス)で実行される。バックエンド・プロセスは,データベースへの接続セッションごとに作成される。バックエンドはC言語で開発されており,当然内部にJavaの実行系(JVM)を含まないため,そのままではJavaは実行できない。対策としては以下のような方法が考えられる.

(1) JVMを別プロセスとして起動し,バックエンドからJavaをリモート実行する(図1[拡大表示])

 この方法は,Apache+Tomcatなどで使用されているのと同様の手法である。バックエンド・プロセスにJVMを抱え込む必要がない分だけバックエンド・プロセスが軽くなるが,プロセス間の通信のオーバーヘッドが発生するという問題がある。

(2) JVMをバックエンド・プロセスに動的にリンクする(図2[拡大表示])

 共有ライブラリとして提供されているJVMを必要に応じてバックエンド・プロセスに動的にリンクする方法である。バックエンドとJVMの連携にはJNI(JavaNative Interface)を使う。通信のオーバーヘッドがなく,またJVMプロセスで複雑なセッション毎のスレッド管理をする必要がないメリットがある。ただし,巨大なJVMのライブラリをリンクするためにどうしてもバックエンドの起動は重くなる。

 PL/Javaはこの方法を採用している。初回起動時の重さの問題については,開発者は「コネクション・プール(PostgreSQLへの接続をキャッシュする技術)を使うことによって回避できる」としている。(http://gborg.postgresql.org/project/pljava/genpage.php?jni_rationale)に詳細な説明があるので,興味のある方は読んでみるとよいだろう。

PL/Javaを構築する

 検証は手元のLinux(Vine Linux 2。6r3)上で行った。最初PL/Javaのバイナリ版を試したが,glibc2.3が要求されたために実行できなかった。そこで次にソースコードを取得,コンパイルを試みたが,Vineに付属するコンパイラ(gcc 2.95)ではコンパイルできなかった。Thomas Hallgren氏に連絡したところ,gcc 3以上が必要とのことであったが,より多くの環境でPL/Javaが利用されるには古いバージョンのgccへの対応も必要と訴えたところ,快くgcc 2.95対応を実施していただけた。氏のご協力に深く感謝したい。

 以上のような経緯から,本記事で検証したのは,2004年2月6日現在の最新のCVSのスナップショットである。以下,PL/Javaの構築方法も含め,導入方法を解説する。

 x86アーキテクチャでglibc 2.3系のLinuxを使っている方は,前記Webページからバイナリが導入できるので,それを使っても良い。その場合は以下のステップの2)-6)を省略し,代りに(ftp://gborg.postgresql.org/pub/pljava/stable/pljava_linux_i386_1.0.0.b.tar.gzを取得して/usr/local/src/pgsql/7.4.1/lib/pljava_linux_i386_1.0.0.bに展開しておく。

(1) 準備

  • PostgreSQL 7.4.x

     PostgreSQL 7.4系列であればどのバージョンでも構わないが,今回は7.4.1を使用した。ソースコードはhttp://www.postgresql.orgのほか,各種ミラー・サイトから入手できる。インストール方法については,付属のファイルINSTALLや,日本PostgreSQLユーザー会が配布する翻訳ドキュメント(http://www.postgresql.jp/document/pg74doc/index.html)を参照されたい。

     以下,/usr/local/src/pgsql/7.4.1以下にPostgreSQLがインストールされているものとする(configureのときに,--prefix=/usr/local/src/pgsql/7.4.1 とする)。

     また,ソースコードは/usr/local/src/pgsql/7.4.1/postgresql-7.4.1以下に展開した。なお,JDBCドライバが必要になるので,必ずconfigureのときに--with-javaを付けて実行しよう。

  • JDK  1.4.2を使用した。

    $ java -version
    java version "1.4.2_01"
    Java(TM) 2 Runtime Environment,Standard Edition (build 1.4.2_01-b06)
    Java HotSpot(TM) Client VM (build 1.4.2_01-b06,mixed mode)

  • ant  1.5.4を使用した。

    $ ant -version
    Apache Ant version 1.5.4 compiled on August 12 2003

    (2) PL/Javaソースコードの入手

     CVSから最新のソースコードを入手する。以下,PostgreSQLのスーパーユーザー(通常postgres)で実行する。

    $ cvs -d :pserver:anonymous@gborg.postgresql.org:/usr/local/cvsroot/pljava login

     パスワードを聞かれるので,"anonymous"と入力する。

    この操作は最初の一度だけ行えば良い.いよいよソースコードを取得する。

    $ cd /usr/local/src/pgsql/7.4.1
    $ cvs -d :pserver:anonymous@gborg.postgresql.org:/usr/local/cvsroot/pljava export -r HEAD org.postgresql.pljava

     これで,カレントディレクトリに"org.postgresql.pljava"というディレクトリができたはずだ。この下にソースコード一式が入っている。

    (3) Makefileの修正

     /usr/local/src/pgsql/7.4.1/org.postgresql.pljava/src/C/pljava/Makefileと/usr/local/src/pgsql/7.4.1/org.postgresql.pljava/src/C/pljava/type/Makefileのtop_builddir = ... で始まる行を以下のように変更する。

    top_builddir = /usr/local/src/pgsql/7.4.1/postgresql-7.4.1

    (4) antの実行

     antを実行し,Javaのクラスや,必要なファイルを生成する。

    $ cd /usr/local/src/pgsql/7.4.1/org.postgresql.pljava
    $ ant -buildfile build.xml all

    (5) Cのソースコードのコンパイル

    $ cd /usr/local/src/pgsql/7.4.1/org.postgresql.pljava/src/C/pljava
    $ make

    (6) インストール

     以上コンパイルがすべて終了したので,生成した.soファイルやjarファイルをコピーする。コピー先はどこでもよいが,今回はバイナリ配布のPL/Javaに合わせ,/usr/local/src/pgsql/7.4.1/lib/pljava_linux_i386_1.0.0.b/ にインストールする。

    $ mkdir /usr/local/src/pgsql/7.4.1/lib/pljava_linux_i386_1.0.0.b
    $ cp /usr/local/src/pgsql/7.4.1/org.postgresql.pljava/bin/*.{jar,so}
    /usr/local/src/pgsql/7.4.1/lib/pljava_linux_i386_1.0.0.b

    PL/Javaの各種設定

     PL/Javaを使うためには,PostgreSQLに設定が必要である.

    (1) postgrsql.conf

     以下を設定する.

    tcpip_socket = true
    dynamic_library_path = '/usr/local/src/pgsql/7.4.1/lib/pljava_linux_i386_1.0.0.b:$libdir'

    (2) postgresの環境設定

    postgresユーザーの.bashrcなどで以下を設定する.

    PG=/usr/local/src/pgsql/7.4.1
    export PGPORT=5432
    export PGLIB=$PG/lib
    export PGDATA=$PG/data
    export LD_LIBRARY_PATH=$PG/lib:$JAVA_HOME/jre/lib/i386:$JAVA_HOME/jre/lib/i386/client
    export CLASSPATH=$PG/share/java/postgresql.jar:$PG/lib/pljava_linux_i386_1.0.0.b/pljava.jar
    :$PG/lib/pljava_linux_i386_1.0.0.b/deploy.jar:$PG/lib/pljava_linux_i386_1.0.0.b/test.jar
    PATH=$PG/bin:$PATH
    export LD_LIBRARY_PATH=$PG/lib:$PG/lib/pljava_linux_i386_1.0.0.b

     以上が終わったら,postgresユーザーで再度ログインし,postmasterを起動しておこう。

    データベースのPL/Javaの導入

     PL/Javaでは,使用するデータベースごとにPL/Javaの導入手続きが必要になる。これは,PL/pgSQLなどで,createlangを行う操作に対応する。

     今回はテスト用に,pljavaというデータベースを新たに作成する。

    $ createdb -E EUC_JP pljava

     次にPL/Java付属のツールを使って導入設定を行う。

    $ java org.postgresql.pljava.deploy.Deployer -install -database pljava -user postgres

     この結果sqljというスキーマが作られる.sqljにはサポート用のテーブルや関数などが登録される。

    以上でPL/Javaが利用可能になった。

    サンプル・プログラムを実行する

     早速付属サンプルを実行してみよう。

     PL/Javaでは,まずユーザーが開発したPL/Javaのコードを含むjarファイルを認識させる必要がある。それがinstall_jar関数である。

    pljava=# SELECT sqlj.install_jar('examples','file:///usr/local/src/pgsql/7.4.1/lib/pljava_linux_i386_1.0.0.b/examples.jar',true);

     以後,examples.jarは,examplesという名前で参照できるようになる。次にclass pathをセットするが,サンプルではjavatestというスキーマを使うので,それを定義しておこう。

    pljava=# CREATE SCHEMA javatest;

    pljava=# SELECT sqlj.set_classpath('javatest','examples');

     これによって,javatestというスキーマでexamplesというjarファイルを使うためのクラスパスが設定された。

     以上で準備ができたので,付属のサンプルを実行する。以下のように出力されれば正常に動作している。

    $ java org.postgresql.pljava.test.Tester -database pljava -user postgres
    Timestamp = 2004-02-06 19:05:30.662,Timestamptz = 2004-02-06 19:05:30.664
    54 + 2 = 56
    nullOnEven(1) = 1
    nullOnEven(2) = null
    Name = "nothing",username = "thomas"
    Name = "null",username = "t-ishii"(t-ishiiは環境によって実行環境によって変わる)
    Name = "empty string",username = ""
    Name = "space",username = " "
    Name = "tab",username = " "
    Name = "name",username = "name"
    Id = "1",idesc = "first",moddate = "2004-02-06 19:05:31.48561"
    Id = "2",idesc = "second",moddate = "2004-02-06 19:05:31.490048"
    Id = "3",idesc = "third",moddate = "2004-02-06 19:05:31.492819"
    Id = "4",idesc = "first",moddate = "2004-02-06 19:05:32.034"
    Id = "5",idesc = "second",moddate = "2004-02-06 19:05:32.044"
    Id = "6",idesc = "third",moddate = "2004-02-06 19:05:32.052"
    Id = "3",name = "Rebecka Shawn",salary = "30000"
    Id = "4",name = "Priscilla Johnson",salary = "25000"
    Base = "1",incbase = "6",ctime = "2004-02-06 19:05:32.36"
    Base = "1",incbase = "1",ctime = "2004-02-06 19:05:32.396"
    Base = "1",incbase = "6",ctime = "2004-02-06 19:05:32.396"
    Base = "1",incbase = "11",ctime = "2004-02-06 19:05:32.396"
    Base = "1",incbase = "16",ctime = "2004-02-06 19:05:32.396"
    Base = "1",incbase = "21",ctime = "2004-02-06 19:05:32.398"
    Base = "1",incbase = "26",ctime = "2004-02-06 19:05:32.398"
    Base = "1",incbase = "31",ctime = "2004-02-06 19:05:32.398"
    Base = "1",incbase = "36",ctime = "2004-02-06 19:05:32.398"
    Base = "1",incbase = "41",ctime = "2004-02-06 19:05:32.398"
    Base = "1",incbase = "46",ctime = "2004-02-06 19:05:32.398"
    Base = "1",incbase = "51",ctime = "2004-02-06 19:05:32.4"
    Base = "1",incbase = "56",ctime = "2004-02-06 19:05:32.4"

    PL/Javaの使い方

     最後にさきほどのサンプルを例にとって,ごく簡単なPL/Javaの使い方を説明しよう。

     PL/Javaを使うためには,Javaのクラスメソッドを定義する必要がある。例えば,引数に1を足して返すごく簡単なクラスがサンプルの中にあるが,以下のように定義されている。

    package org.postgresql.pljava.example;

    public class Parameters
    {

    public static int addOne(int value)
    {
    return value + 1;
    }
    }

     addOneをPL/Javaから呼び出すようにするためには,以下のCREATE FUNCTION文を実行する。

    CREATE FUNCTION javatest.java_addOne(INTEGER)
    RETURNS INTEGER AS
    'org.postgresql.pljava.example.Parameters.addOne(int)'
    LANGUAGE java;

     Javaの既存のメソッドを利用することもできる。

    pljava=# CREATE FUNCTION getsysprop(VARCHAR) RETURNS VARCHAR AS 'java.lang.System.getProperty' LANGUAGE java;
    CREATE FUNCTION
    pljava=# SELECT getsysprop('user.home');
     getsysprop 
    ---------------
    /home/t-ishii
    (1 row)

     もちろんPL/Javaでは,もっと複雑なこともできる。例えば,行を返す関数を書くことも可能だし,トリガ用の関数を定義することもできる。詳しくは,ユーザーガイド(http://gborg.postgresql.org/project/pljava/genpage.php?userguide)もしくはソース付属のuserguide.htmlを参照されたい。

    *      *      *

     PostgreSQLは拡張が容易なデータベースシステムである。そのため,いろいろな言語に対応したユーザー定義関数(正確には「言語ハンドラ」)を作ることができる。例えば,gborg.postgresql.orgをちょっと漁っただけで,plpq(libpqを呼び出せるようにしたもの),PL/Rなど,毛色の変ったものが見つかり,興味深い。

     そうしたものの中にあって,PL/Javaは久々に実用性の高いものであると感じた。Javaプログラマが最近増えているが,そうしたエンジニアにも歓迎されるに違いない。

    石井達夫(Tasuo Ishii)

    ■著者紹介
    石井達夫(いしい・たつお)氏
    1984年,SRA入社。主にUNIX関連の開発に従事するかたわら,95年からPostgreSQLのメーリング・リストを主宰。現在はオープンソースソリューション部でPostgreSQL関連のビジネス活動を技術支援。著書に『PostgreSQL完全攻略ガイド』(技術評論社),『PHPxPostgreSQLで作る最強Webシステム』(技術評論社),『PostgreSQL構築・運用ガイド』(日経BP,共著)などがある。日本PostgreSQLユーザ会理事長。