"CREATE FUNCTION" SQL 11/05/95 PostgreSQL PostgreSQL

名前

create function - 新しい関数を作成する

書式

create function 関数名
    ([type1 {, type-n}])
    returns type-r
    as {'オブジェクトファイルへのフルパス' | 'SQL クエリー'}
    language {'c' \ 'sql' \ 'internal' \ 'plname'}
    

説明

このコマンドにより、Postgres のユーザが Postgres に対して関数を登録できる。これに伴って、このユーザはその関数の所有者となる。

引数を持つ関数を定義する場合は、入力データ型type-1 ,type-2 ,...,type-n ,と返り値のデータ型type-rを、"c""sql""internal""plname"という言語にしたがって指定しなければならない。(plnameは、作成された手続き型言語の言語名である。詳細についてはlanguage(l) を参照のこと)。(関数が引数を持たない場合はarg is句が残ってしまうかもしれない。そうしないと、引数リストが空になってしまう)。入力型はベース型、複合型、またはopaqueのいずれかである。opaqueは、その関数が (char *) のような無効な型の引数であっても受け付けることを示す。出力型はベース型、複合型、setof <型>またはopaqueのいずれかである。setof修飾子は、その関数が単一項目ではなく項目のセットを返すことを示す。コマンドのas句は、以下に示すように、C と SQL で異なって扱われることを示す。

C 関数

C で書かれた関数を Postgres に定義できるが、これらはアドレス空間に対して動的にロードされる。ローディングは、load(l) を使ったり、その関数が最初に必要になった時に行われる。関数を何回も続けて実行する場合でも、その関数がメモリキャッシュに入っているためオーバーヘッドはほとんどかからない。

内部関数は C で書かれ、postgres のバックエンドプロセスに静的にリンクされている。内部関数を定義する場合でも、なお as 句は必要である。指定しないと関数の内容が無視されてしまう。

C の関数を書く

as に続く C 関数本体は、関数のオブジェクトコード (.o ファイル)への フルパス を、引用符で括って指定する。(Postgres は、関数を自動的にコンパイルしたりはしない。その関数は、 define function コマンドで使われる前にコンパイルされていなければならない。)

ベース型を持つ C 関数は、素直な書き方で書かれていなければならない。C と同等の Postgres 組込み型は、

.../src/backend/utils/builtins.h
    
がヘッダファイルとしてインクルードされていれば C ファイルでアクセス可能である。これを行うには、C のソースファイルの先頭に
#include <utils/builtins.h>
    
を入れておき、すべての C ファイルを以下のオプションでコンパイルする。

ccコマンドラインにおけるすべての ".c" プログラムの前に

-I.../src/backend
-I.../src/backend/port/<プラットフォーム名>
-I.../src/backend/obj
    
を指定する。たとえば、以下のようになる。
cc -I.../src/backend \
   -I.../src/backend/port/<プラットフォーム名> \
   -I.../src/backend/obj \
   -c progname.c
    
ここで、"..." はインストールされた Postgres のソースツリーへのパス、 "<プラットフォーム名>" はソースツリーがビルドされたプラットフォームの名前である。

ユーザ C 関数との引数の受け渡しは、慣習的に、32 ビット(4 バイト)などのデータ型では「値渡し」を使い、32 ビットを超えるものを要求するデータ型では「参照渡し」を使う。

C 関数への複雑な引数は、

.../src/libpq/libpq-fe.h
    
で定義される特別な C の型 タプル(TUPLE) として C 関数に渡される。この型で与えられた変数tを、C 関数は以下の関数コールにより抽出することができる。
GetAttributeByName(t, フィールド名, &isnull)
    
ここでisnullは、そのフィールドが NULL の場合関数がにセットする型へのポインタである。この関数の結果は、以下の例で示すように適切にキャストされる場合もある。

動的にロードされる C 関数をコンパイルする

異なったオペレーティングシステムでは C のソースファイルをコンパイルするのに異なった手続きを要求するので、Postgresはそれらを動的にロードできる。この章では、各システムにおいて必要なコンパイラとローダのオプションを解説する。

Linux ELF においては、コンパイラフラグとして -fpic を指定することによりオブジェクトファイルを生成することができる。

Ultrix では、Postgres が動的にロードすることを期待しているすべてのオブジェクトファイルは、/bin/ccを使い、"-G 0" オプションを有効にしてコンパイルされていなければならない。また、as句で指定されるオブジェクトファイルは ".o" で終わっている必要がある。

HP-UX, DEC OSF/1, AIX および SunOS 4 においては、すべてのオブジェクトファイルは、オペレーティングシステムネイティブのオブジェクトファイル・ローダであるld(1) を使って共有ライブラリになっていなければならない。

HP-UX では、オブジェクトファイルはネイティブの HP-UX C コンパイラである/bin/ccを使い、フラグ "+z" と "+u" を両方有効にしてコンパイルされている必要がある。最初のフラグは、オブジェクトファイルを "位置独立コード" (PIC) にし、2 番目のフラグは PA-RISC アーキテクチャにおいて通常行われるアラインメント制限を取り除くものである。その後、オブジェクトファイルは HP-UX ローダ/bin/ldを使って共有ライブラリに変換される。C ソースファイル"foo.c" をコンパイルする際のコマンドラインは以下のようになる。

cc <その他のフラグ> +z +u -c foo.c
ld <その他のフラグ> -b -o foo.sl foo.o
    
as 句におけるオブジェクトファイル名は ".sl" で終わっていなければならない。

HP-UX の 9.00 より前のバージョンでは、さらに余分なステップが必要となる。もしソースファイル中に Postgres のヘッダファイル

include/c.h
    
が含まれていないと、さらに以下の行を各ソースファイルの先頭に追加しなければならない。
#pragma HP_ALIGN HPUX_NATURAL_S500
    
しかしながらこの行は、HP-UX の 9.00 以降でコンパイルする場合はあってはならない。

DEC OSF/1 では、オブジェクトファイルはコンパイルされた後 OSF/1 のローダ/bin/ldを使って共有ライブラリに変換しなければならない。この場合、コマンドラインは以下のようになる。

cc <その他のフラグ> -c foo.c
ld <その他のフラグ> -shared -expect_unresolved '*' -o foo.so foo.o
    
as 句におけるオブジェクトファイル名は ".so" で終わっていなければならない。

SunOS 4 では、オブジェクトファイルはコンパイルされた後 SunOS 4のローダ/bin/ldを使って共有ライブラリに変換しなければならない。この場合、コマンドラインは以下のようになる。

cc <その他のフラグ> -PIC -c foo.c
ld <その他のフラグ> -dc -dp -Bdynamic -o foo.so foo.o
    
as 句におけるオブジェクトファイル名は ".so" で終わっていなければならない。

AIX では、オブジェクトファイルは普通にコンパイルしてよいが、共有ライブラリを作るのにいくつかのステップが必要である。まず、オブジェクトファイルを作る。

cc <その他のフラグ> -c foo.c
    
次に、オブジェクトファイルのためのシンボルを"エクスポート" するファイルを作らなければならない。
mkldexport foo.o `pwd` > foo.exp
    
最後に共有ライブラリを作る。
ld <other flags> -H512 -T512 -o foo.so -e _nostart \
   -bI:.../lib/postgres.exp -bE:foo.exp foo.o \
   -lm -lc 2>/dev/null
    
Postgres User's Manual で、この手続きに関する説明を読んでおいた方がよいだろう。

SQL 関数

SQL 関数は SQL 問い合わせの調停リストを実行し、リストの中の最後の問い合わせに対する結果を返す。一般的に、SQL 関数はセットを返す。戻り値の型がsetofとして指定されていないと、最後の問い合わせの結果のうちの、要素の何れかが返されてしまう。

SQL 関数の本体は、ホワイトスペースで区切られ、引用符でくくられた問い合わせのリストでなければならない。問い合わせ中に引用符を使う場合は、2 つのバックスラッシュを前に置いてエスケープされていなければならない(すなわち \')。

SQL 関数への引数は、問い合わせの中で $n 書式を使って参照される。$1 は最初の引数、$2 は 2 番目の引数というように。引数が複雑な場合は引数の属性(たとえば "$1.emp") にアクセスするのに"ドット" 記法が使われたり、ネストしたドットの書式を通して関数が呼び出される。

PL 関数

手続き型言語は Postgres には組み込まれておらず、ローダブル・モジュールとして提供される。PL のための書式に関する詳細や、PL ハンドラがどのようにas句を解釈するかに関しては、関連の文書を参照してほしい。

例:C の関数

以下のコマンドは、引数として 2 つのベース型を持つ C の関数 overpaid を定義する。

create function overpaid (float8, int4) returns bool
    as '/usr/postgres/src/adt/overpaid.o'
    language 'c'
    
C のファイル overpaid.c は以下のようになるだろう。
#include <utils/builtins.h>

bool overpaid(salary, age) float8 *salary; int4 age; { if (*salary > 200000.00) return(TRUE); if ((age < 30) & (*salary > 100000.00)) return(TRUE); return(FALSE); }
関数 overpaid は、問い合わせの中で以下のような使い方をされる。
select name from 従業員 where overpaid(給与, 年齢)
    
また別の人が、従業員型の 1 つの引数を取る以下のような関数を書くこともできる。
create function overpaid_2 (EMP)
    returns bool
    as '/usr/postgres/src/adt/overpaid_2.o'
    language 'c'    
    
これで以下のような問い合わせが可能となる。
select name from 従業員 where overpaid_2(EMP)
    
このケースでは、overpaid_2 関数の中で従業員レコードを取り出さなければならない。C のファイル overpaid_2.c は以下のようになるだろう。
#include <utils/builtins.h>
#include <libpq-fe.h>

bool overpaid_2(t) TUPLE t; { float8 *salary; int4 age; bool salnull, agenull;
salary = (float8 *)GetAttributeByName(t, salary, &salnull); age = (int4)GetAttributeByName(t, age, &agenull); if (!salnull && *salary > 200000.00) return(TRUE); if (!agenull && (age<30) && (*salary > 100000.00)) return(TRUE); return(FALSE) }

例:SQL 関数

ある単純な SQL 関数について説明をするために、以下のことを考えてみる。これは銀行口座で預金の引き出しが発生した時に使えるかもしれない。

create function TP1 (int4, float8) returns int4
    as 'update 銀行口座 set 残高 = 銀行口座.残高 - $2
        where 銀行口座.口座番号 = $1
        select(x = 1)'
        language 'sql'
    
あるユーザは、17 番口座から $100.00 引き出す時に、この関数を以下のように実行するだろう。
select (x = TP1( 17,100.0))
    
以下にもっとおもしろい例を挙げてみる。ここでは従業員型の 1 つの引数を与えて複数の結果を取り出す。
select function hobbies (従業員) returns set of 趣味
    as 'select (趣味.all) from 趣味
        where $1.名前 = 趣味.人名'
    language 'sql'
    

関連事項

information(1) load(l) function(l) language(l) .

注意

名前空間の競合

引数が異なっているような 1 つ以上の関数が、同じ名前で定義されているかもしれない。言い換えると、関数名はオーバーロードされうる。関数はまた、属性として同じ名前を持つこともできる。複雑な型の関数と複雑な型の属性との間に曖昧さがある場合、常に属性が使われる。

制限事項

C の関数名は正式な C 関数名でなくてはならず、また C のコード内部の関数名は create function で指定された関数名と完全に一致していなければならない。この制限事項に関する困った実装系がある。ほとんどのオペレーティングシステムにおける動的ローディングのルーチンでは、(一意の名前を持つ)関数名が衝突するような、任意の数の共有ライブラリをローディングすることができてしまうため、事実上とんでもないところでローディングに失敗してしまうことがありうる。たとえば、Postgres に組み込まれている関数と同じ名前を持つ、動的にローディングされる関数が定義された場合、DEC OSF/1 の動的ローダはPostgres にその関数を呼ばせず、組み込み関数の方が呼ばれてしまったりする。そのため、異なったアーキテクチャ上で自前の関数を呼びたい場合は、C の関数名をオーバーロードしない方がよいだろう。

この問題を回避するための巧妙なトリックがある。SQL 関数をオーバーロードするのは問題ないので、C 関数のセットを違った名前で定義しておき、さらに、適切な引数の型を受け取って対応する C 関数を呼ぶような、SQL 関数のラッパーを SQL 関数と同じ名前で定義するのである。

opaqueは SQL 関数に引数として渡すことはできない。

バグ

C 関数は値のセットを返すことはできない。

翻訳者

堀田 倫英 <hotta@net-newbie.com>