関連するオブジェクトを拡張としてパッケージ化

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

# 35.15. 関連するオブジェクトを拡張としてパッケージ化

PostgreSQLへの有用な拡張は通常、複数のSQLオブジェクトを含んでいます。 例えば、新しいデータ型は新しい関数、新しい演算子、おそらく新しいインデックス演算子クラスを必要とします。 これらのオブジェクトをすべて単一のパッケージとしてまとめることは、データベース管理を単純化するために役に立ちます。 PostgreSQLではこうしたパッケージを拡張とよびます。 拡張を定義するためには、少なくとも、拡張のオブジェクトを作成するためのSQLコマンドを含むスクリプトファイル、拡張自身の数個の基本属性を指定する制御ファイルが必要です。 また拡張がCコードを含む場合、通常Cコードで構築された共有ライブラリが存在します。 これらのファイルがあれば、単純なCREATE EXTENSIONコマンドがそのオブジェクトをデータベース内に読み込みます。

拡張を使用する主な利点は、SQLスクリプトを実行するだけでデータベースに"粗な"なオブジェクトの群をロードできることではなく、PostgreSQLが拡張のオブジェクトをまとまったものと理解できることです。 単一のDROP EXTENSIONコマンドでオブジェクトすべてを削除することができます（個々の"アンインストール"スクリプトを保守する必要はありません）。 もっと有用なことは、pg_dumpが拡張の個々のメンバオブジェクトを削除してはならないことを把握していることです。 代わりにダンプ内には`CREATE EXTENSION`コマンドだけが含まれます。 これは、古いバージョンよりも多くのまたは異なるオブジェクトを含む可能性がある、拡張の新しいバージョンへの移行を大きく単純化します。 しかし、こうしたダンプを新しいデータベースにロードする際には、拡張の制御ファイル、スクリプトファイル、その他のファイルが利用できるようにしておく必要があります。

PostgreSQLはユーザに、拡張全体を削除させる以外に、拡張内に含まれる個々のオブジェクトを削除させません。 また、拡張のメンバオブジェクトの定義を変更する（例えば関数では`CREATE OR REPLACE FUNCTION`を介して変更する）ことはできますが、変更した定義はpg_dumpによりダンプされないことに留意してください。 こうした変更は通常、同時に拡張のスクリプトファイルにも同じ変更を行った場合のみ認識することができます。 （しかし後述するように設定データを持つテーブルに対しては特殊な準備があります。）

また拡張機構は、拡張に含まれるSQLオブジェクトの定義を調整するパッケージ調整スクリプトを準備しています。 例えば、拡張のバージョン1.1でバージョン1.0と比べて１つの関数を追加し、他の関数本体を変更する場合、拡張の作成者はこれらの２つの変更のみを行う更新スクリプトを提供することができます。 そして`ALTER EXTENSION UPDATE`コマンドを使用して、これらの変更を適用し、指定されたデータベース内に実際にインストールされた拡張のバージョンが何かを記録します。

拡張のメンバとなり得るSQLオブジェクトの種類をALTER EXTENSIONで説明します。 拡張は１つのデータベースの中でのみ認識されますので、データベース、ロール、テーブル空間などデータベースクラスタ全体のオブジェクトは拡張のメンバにすることができないことに注意してください。 （拡張のスクリプトでこうしたオブジェクトを生成することは禁止されていませんが、作成したとしても、拡張の一部として記録されません。） また、テーブルは拡張のメンバになることができますが、インデックスなどそれに付随するオブジェクトは拡張の直接的なメンバとはみなされません。

## 35.15.1. 拡張のファイル

CREATE EXTENSIONコマンドは各拡張に関して、拡張と同じ名前に`.control`という拡張子を持つファイル名である必要がある、制御ファイルに依存します。 また、このファイルはインストレーションの`SHAREDIR/extension`ディレクトリ内に存在しなければなりません。 また少なくとも１つの、``extension`--`version`.sql`という命名規約（例えば`foo`拡張のバージョン`1.0`では`foo--1.0.sql`）に従ったSQLスクリプトファイルが存在しなければなりません。 デフォルトでは、このスクリプトファイルも`SHAREDIR/extension`ディレクトリに格納されますが、制御ファイルでスクリプトファイルを別のディレクトリに指定することができます。

拡張の制御ファイルのファイル書式は`postgresql.conf`ファイルと同じです。 すなわち、`parameter_name` `=` `value`という代入を１行当たり１つ記述します。 空行および`#`から始まるコメントが許されます。 単一の単語または数字ではない値にはすべて引用符で確実にくくってください。

制御ファイルは以下のパラメータを設定することができます。

`directory` (`string`)

  拡張のSQLスクリプトファイルを含むディレクトリです。 絶対パスで指定されていない限り、この名前はインストレーションの`SHAREDIR`ディレクトリからの相対バスになります。 デフォルトの動作は`directory = 'extension'`と指定した場合と同じです。

`default_version` (`string`)

  拡張のデフォルトのバージョン（`CREATE EXTENSION`でバージョン指定がない場合にインストールされるバージョン）です。 これは省略することができますが、その場合`VERSION`オプションがない`CREATE EXTENSION`は失敗します。 ですので通常省略しようとは思わないでしょう。

`comment` (`string`)

  拡張に関するコメント（任意の文字列）です。 この他の方法として、スクリプトファイル内でCOMMENTコマンドを使用してコメントを設定することができます。

`encoding` (`string`)

  スクリプトファイルで使用される文字セット符号化方式です。 スクリプトファイルに何らかの非ASCII文字が含まれる場合に指定しなければなりません。 指定がなければ、ファイルはデータベース符号化方式であると仮定されます。

`module_pathname` (`string`)

  このパラメータの値でスクリプトファイル内の`MODULE_PATHNAME`の出現箇所が置換されます。 設定されていない場合は置換は行われません。 通常これは、スクリプトファイル内で共有ライブラリの名前を直接書き込む必要がなくなるように`$libdir/`shared_library_name``に設定され、C言語関数では`CREATE FUNCTION`コマンド中で`MODULE_PATHNAME`を使用します。

`requires` (`string`)

  拡張が依存する拡張の名前のリストです。 例えば`requires = 'foo, bar'`です。 対象の拡張がインストールできるようになる前に、これらの拡張がインストールされていなければなりません。

`superuser` (`boolean`)

  このパラメータが`true`（デフォルト）の場合、スーパーユーザのみが拡張を作成または新しいバージョンに更新することができます。 `false`に設定されている場合は、インストレーション内でコマンドを実行するまたはスクリプトを更新するために必要な権限のみが必要とされます。

`relocatable` (`boolean`)

  拡張を最初に作成した後に拡張により含まれるオブジェクトを別のスキーマに移動することができる場合、拡張は再配置可能です。 デフォルトは`false`、つまり、拡張は再配置可能ではありません。 詳しくは後で説明します。

`schema` (`string`)

  このパラメータは再配置可能ではない拡張に対してのみ設定することができます。 拡張が指名したスキーマのみにロードされ、他にはロードされないことを強制します。 詳しくは後で説明します。

主制御ファイル``extension`.control`に加え、拡張は``extension`--`version`.control`という形の名前の副制御ファイルを持つことができます。 これらを提供する場合は、スクリプトファイルディレクトリに格納しなければなりません。 副制御ファイルは主制御ファイルと同じ書式に従います。 拡張の対応するバージョンをインストールまたは更新する時、副制御ファイル内で設定されるパラメータはいずれも、主制御ファイルを上書きします。 しかし`directory`および`default_version`パラメータは副制御ファイルで設定することはできません。

拡張のSQLスクリプトファイルにはトランザクション制御コマンド（`BEGIN`、`COMMIT`など）およびトランザクションブロックの内側で実行することができないコマンド（`VACUUM`など）を除く任意のSQLコマンドを含めることができます。 スクリプトファイルが暗黙的にトランザクションブロック内で実行されるためです。

スクリプトファイルは指定した符号化方式で認められる任意の文字を含めることができますが、PostgreSQLが制御ファイルの符号化方式が何かを把握する方法がありませんので、制御ファイルにはASCII文字のみを含めなければなりません。 実際には、拡張のコメントに非ASCII文字を含めたい場合にのみ、これが問題になります。 このような場合には、制御ファイルの`comment`を使用せず、代わりにコメントを設定するためにスクリプトファイル内で`COMMENT ON EXTENSION`を使用することを勧めます。

## 35.15.2. 拡張の再配置性

ユーザは拡張に含まれるオブジェクトを拡張の作成者が考えていたスキーマとは別のスキーマにロードしたいとよく考えます。 再配置性に関して３つのレベルがサポートされます。

- 完全な再配置可能な拡張は、いつでも、データベースにロードされた後であっても、他のスキーマに移動させることができます。 これは、自動的にすべてのメンバオブジェクトを新しいスキーマに名前を変更する、`ALTER EXTENSION SET SCHEMA`を用いて行います。 通常これは、拡張がオブジェクトが含まれるスキーマが何かに関して内部的な仮定を持たない場合のみ可能です。 また、拡張のオブジェクト（手続き言語など何らかのスキーマに属さないオブジェクトは無視して）はすべて最初に１つのスキーマ内に存在しなければなりません。 制御ファイル内で`relocatable = true`と設定することで、完全な再配置可能と印付けます。

- 拡張はインストール処理の間再配置可能ですが、その後再配置することはできません。 通常これは、拡張のスクリプトファイルが、SQL関数用の`search_path`属性の設定など、対象のスキーマを明示的に参照する必要がある場合です。 こうした拡張では、制御ファイルで`relocatable = false`と設定し、スクリプトファイル内で対象のスキーマを参照するために`@extschema@`を設定してください。 この文字列の出現箇所はすべて、スクリプトが実行される前に、実際の対象のスキーマ名に置換されます。 ユーザは`CREATE EXTENSION`の`SCHEMA`オプションを使用して対象のスキーマを設定することができます。

- 拡張が再配置をまったくサポートしない場合、制御ファイルで`relocatable = false`を設定し、かつ、`schema`を意図している対象スキーマの名前に設定してください。 これは、制御ファイル内で指定されたスキーマと同じ名前が指定されていない限り、`CREATE EXTENSION`の`SCHEMA`オプションの指定を阻止します。 この選択は通常、拡張が`@extschema@`を使用して置き換えることができないスキーマ名について内部的な仮定を持つ場合に必要です。 `@extschema@`置換機構はこの場合でも使用することができますが、スキーマ名が制御ファイルによって決定されますので、用途は限定されます。

すべての場合において、スクリプトファイルは対象のスキーマを指し示すようにあらかじめ設定したsearch_pathを用いて実行されます。 つまり`CREATE EXTENSION`は以下と同じことを行います。

    SET LOCAL search_path TO @extschema@;

これによりスクリプトファイルで作成されるオブジェクトを対象のスキーマ内に格納することができます。 スクリプトファイルは要望に応じて`search_path`を変更することができますが、一般的には望まれません。 `CREATE EXTENSION`の実行後、`search_path`は以前の設定に戻されます。

対象のスキーマは制御ファイル内の`schema`パラメータがあればこのパラメータにより決定されます。 このパラメータがなければ、`CREATE EXTENSION`の`SCHEMA`があればこの値で決まり、これ以外の場合は現在のデフォルトのオブジェクト生成用スキーマ（呼び出し元の`search_path`の最初のもの）になります。 制御ファイルの`schema`パラメータが使用される時、対象のスキーマが存在しない場合は作成されますが、これ以外の２つの場合ではすでに存在しなければなりません。

何らかの事前に必要な拡張が制御ファイル内の`requires`に列挙されていた場合、それらの対象スキーマが`search_path`の初期設定に追加されます。 これにより新しい拡張のスクリプトファイルからそれらのオブジェクトが可視になります。

再配置不可能な拡張は複数スキーマにまたがるオブジェクトを含めることができますが、通常、外部使用を意図したオブジェクトはすべて単一スキーマに格納することが望まれます。 この単一スキーマが拡張の対象のスキーマとみなされます。 こうした調整は依存する拡張を作成する間、デフォルトの`search_path`設定を都合に合わせて扱います。

## 35.15.3. 拡張設定テーブル

一部の拡張は、拡張をインストールした後でユーザにより追加または変更される可能性があるデータを持つ設定テーブルを含みます。 通常、テーブルが拡張の一部である場合、テーブル定義もその内容もpg_dumpによりダンプされません。 しかしこの振舞いは設定テーブルの場合望まれません。 ユーザによってなされたデータ変更はダンプ内に含まれなければなりません。 さもないとダンプしリストアした後で拡張の動作が変わってしまいます。

この問題を解消するために、拡張のスクリプトファイルでは設定テーブルとして作成されるテーブルに印を付け、pg_dumpにテーブルの内容をダンプに含める（定義は含まれません）ようにさせることができます。 このためには、以下の例のようにテーブルを作成した後に`pg_extension_config_dump(regclass, text)`関数を呼び出してください。

    CREATE TABLE my_config (key text, value text);

    SELECT pg_catalog.pg_extension_config_dump('my_config', '');

任意数のテーブルをこの方法で印付けることができます。

`pg_extension_config_dump`の第２引数が空文字列である場合、テーブルのすべての内容がpg_dumpによりダンプされます。 これは、拡張のスクリプトによって作成された初期段階においてテーブルが空である場合のみ正しいものです。 テーブルの中で初期データとユーザが提供したデータが混在する場合、`pg_extension_config_dump`の第２引数においてダンプすべきデータを選択する`WHERE`条件を提供します。 以下に例を示します。

    CREATE TABLE my_config (key text, value text, standard_entry boolean);

    SELECT pg_catalog.pg_extension_config_dump('my_config', 'WHERE NOT standard_entry');

このようにした後、拡張のスクリプトで作成される行のみで`standard_entry`が確実に真になるようにします。

初期状態で提供される行がユーザによって変更されるようなもっと複雑な状況では、設定テーブルに対するトリガを作成して、変更された行が正しく印付けられることを確実にするように取り扱うことができます。

## 35.15.4. 拡張の更新

拡張機構の１つの利点は、拡張のオブジェクトを定義するSQLコマンドの更新を簡便に管理する方法を提供していることです。 これは、拡張のインストール用スクリプトのリリース版それぞれにバージョン名称またはバージョン番号を関連付けることで行われます。 さらに、ユーザにあるバージョンから次のバージョンへ動的にデータベースを更新させることができるようにしたい場合、あるバージョンから次のバージョンまでの間に行われる必要な変更を行う更新スクリプトを提供しなければなりません。 更新スクリプトは``extension`--`oldversion`--`newversion`.sql`というパターンに従った名前（例えば、`foo--1.0--1.1.sql`は`foo`拡張のバージョン`1.0`からバージョン`1.1`に変更するコマンドを含みます。）を持たなければなりません。

適切な更新スクリプトが利用可能である場合、`ALTER EXTENSION UPDATE`コマンドはインストール済みの拡張を指定した新しいバージョンへ更新します。 更新スクリプトは、`CREATE EXTENSION`がインストール用スクリプト向けに提供する環境と同じ環境で実行されます。 具体的には`search_path`は同じ方法で設定され、スクリプトにより作成される新しいオブジェクトはすべて自動的に拡張に追加されます。

拡張が副制御ファイルを持つ場合、更新スクリプトで使用される制御パラメータは、スクリプトの対象の（新しい）バージョンに関連付けされたものになります。

更新機構を使用して、オブジェクトの"粗"集合から拡張に変換するという、特別かつ重大な状況を解消することができます。 拡張機構がPostgreSQLに（9.1で）追加されるようになる前では、パッケージ化されずに単に詰めあわされたオブジェクトを作成する拡張モジュールを多くのユーザが作成していました。 こうしたオブジェクトを持つデータベースが存在する場合、どのようにすればこれらのオブジェクトを適切にパッケージ化された拡張に変換できるでしょうか？ 削除した後で普通に`CREATE EXTENSION`を行うことも１つの方法ですが、オブジェクトに依存関係がある（例えば拡張により作成されたデータ型のテーブル列が存在する場合など）場合は好まれません。 こうした状況を解消する方法は、空の拡張を作成し、`ALTER EXTENSION ADD`を使用して、既存のオブジェクトそれぞれを拡張に関連づけ、最後にパッケージ化されていないリリースに存在しないが現在のバージョンの拡張には存在する新しいオブジェクトを作成するという方法です。 `CREATE EXTENSION`は`FROM` `old_version`オプションでこの状況をサポートします。 この場合、通常のインストール用スクリプトは実行されず、代わりに``extension`--`old_version`--`target_version`.sql`という名前の更新スクリプトが実行されるようになります。 `old_version`として使用するダミーのバージョン名の選択は拡張の作成者に任せられていますが、`unpackaged`がよく使われる規約です。 拡張形式に更新できるようにしたい過去のバージョンが複数存在する場合、それらを識別できるように複数のダミーバージョン番号を使用していください。

`ALTER EXTENSION`は、要求される更新を実現するために更新スクリプトを連続して実行することができます。 例えば`foo--1.0--1.1.sql`と`foo--1.1--2.0.sql`のみが利用可能であるとすると、現在`1.0`がインストールされている時にバージョン`2.0`への更新が要求された場合、`ALTER EXTENSION`はこれらを順番に適用します。

PostgreSQLはバージョン名称の特性についてまったく仮定を行いません。 例えば`1.0`の次が`1.1`であるかどうかを把握しません。 これは利用可能なバージョン名をかみ合わせ、もっとも少ない数の更新スクリプトを適用するために必要な経路を続けるだけです。 （バージョン名には、`--`を含まず先頭または最後に`-`が付かなければ、任意の文字を取ることができます。）

"ダウングレード"スクリプトを提供することが便利な場合があります。 例えば`foo--1.1--1.0.sql`は、バージョン`1.1`に関連した変更を基に戻すことができます。 この場合、ダウングレードスクリプトがより短いパスを生成するために、予期せず適用されてしまう可能性に注意してください。 複数のバージョンをまたがって更新する"近道"更新スクリプトと近道の開始バージョンへのダウングレードスクリプトが存在する場合に危険性があります。 ダウングレードしてから近道となる更新スクリプトを実行する方が、バージョンを１つずつ進めるよりも少ない処理で済んでしまうかもしれません。 ダウングレードスクリプトが取り返しがつかないオブジェクトを何か削除してしまう場合、望まない結果になってしまいます。

想定外の更新経路かどうかを検査するためには、以下のコマンドを使用してください。

    SELECT * FROM pg_extension_update_paths('extension_name');

これは指定した拡張の個々の既知のバージョン名の組み合わせをそれぞれ、元のバージョンから対象のバージョンへ進む時に取られる更新経路順、またはもし利用できる更新経路がなければ`NULL`を付けて、表示します。 経路は`--`を区切り文字として使用したテキスト形式で表示されます。 配列形式の方が良ければ`regexp_split_to_array(path,'--')`を使用することができます。

## 35.15.5. 拡張の例

ここでは、SQLのみの拡張の完全な例を示します。 "k"と"v"という名称の２つの要素からなる複合型であり、そのスロットには任意の型の値を格納することができるものです。 格納の際テキスト以外の値は自動的にテキストに変換されます。

`pair--1.0.sql`スクリプトファイルは以下のようになります。

    CREATE TYPE pair AS ( k text, v text );

    CREATE OR REPLACE FUNCTION pair(anyelement, text)
    RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';

    CREATE OR REPLACE FUNCTION pair(text, anyelement)
    RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';

    CREATE OR REPLACE FUNCTION pair(anyelement, anyelement)
    RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';

    CREATE OR REPLACE FUNCTION pair(text, text)
    RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair;';

    CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = anyelement, PROCEDURE = pair);
    CREATE OPERATOR ~> (LEFTARG = anyelement, RIGHTARG = text, PROCEDURE = pair);
    CREATE OPERATOR ~> (LEFTARG = anyelement, RIGHTARG = anyelement, PROCEDURE = pair);
    CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = text, PROCEDURE = pair);

`pair.control`制御ファイルは以下のようになります。

    # pair extension
    comment = 'A key/value pair data type'
    default_version = '1.0'
    relocatable = true

これらの２つのファイルを正しいディレクトリにインストールするためにメークファイルを作成する必要はほとんどありませんが、以下を含む`Makefile`を使用することができます。

    EXTENSION = pair
    DATA = pair--1.0.sql

    PG_CONFIG = pg_config
    PGXS := $(shell $(PG_CONFIG) --pgxs)
    include $(PGXS)

このメークファイルは項35.16で説明するPGXSに依存します。 `make install`コマンドは制御ファイルとスクリプトファイルをpg_configで報告される正しいディレクトリにインストールします。

ファイルがインストールされた後、CREATE EXTENSIONコマンドを使用してオブジェクトを任意の特定のデータベースにロードしてください。

前のページ | ホーム | 次のページ
インデックス拡張機能へのインタフェース | 上に戻る | 拡張構築基盤