Chapter 14. SQLの拡張:データ型

先に述べられているように、Postgresには 基本データ型(プログラミング言語で定義されています。)と複合データ型という2種類のデータ型が あります。この章からインデックスへのインタフェース章までで使用している例は、 complex.sqlcomplex.cに あります。複合データ型の例はfuncs.sqlにあります。

14.1. ユーザ定義データ型

14.1.1. ユーザ定義データ型に必要な関数

ユーザ定義データ型では、必ず入力関数と出力関数が必要です。 これらの関数は、型がユーザによる入力とユーザへの出力のための 文字列中にどのような形式で表示されるかと、その型がメモリ中で どう構成されるかを決定します。入力関数はその入力として NULL終端文字列を取り、その型の(メモリ中の)内部表現を返します。 出力関数はその型の内部表現を取り、NULL終端文字列を返します。 例えば、複素数を表現する複素数型を定義したい場合は、

typedef struct Complex {
    double      x;
    double      y;
} Complex;
     
のように、C構造体のように、メモリ中の複素数表現と、 外部文字列表現として(x,y)という形式の文字列を使用する場合が自然でしょう。 これらの関数、特に出力関数を記述するのは困難ではありません。 しかしながら、いくつかの注意点があります。

  • 外部(文字列)表現を定義する時には、その表現のための完全で堅牢なパーサを 入力関数として書かなければなりません。

    Complex *
    complex_in(char *str)
    {
        double x, y;
        Complex *result;
        if (sscanf(str, " ( %lf , %lf )", &x, &y) != 2) {
            elog(ERROR, "complex_in: error in parsing %s", str);
            return NULL;
        }
        result = (Complex *)palloc(sizeof(Complex));
        result->x = x;
        result->y = y;
        return (result);
    }
    	
    出力関数は単に下記のようになるでしょう。
    char *
    complex_out(Complex *complex)
    {
        char *result;
        if (complex == NULL)
            return(NULL);
        result = (char *) palloc(60);
        sprintf(result, "(%g,%g)", complex->x, complex->y);
        return(result);
    }
    	

  • 入出力関数は、各々の逆関数になるようにするべきです。 そうしないと、データをファイルにダンプし、例えばそれを 他のコンピュータ上戻にある別の誰かのデータベースなどに戻そう とする際に、深刻な問題が発生するでしょう。

複素数(complex)型を定義する時、その型を生成する前に 2つのユーザ定義関数complex_inとcomplex_outを生成する必要が あります。

CREATE FUNCTION complex_in(opaque)
    RETURNS complex
    AS 'PGROOT/tutorial/obj/complex.so'
    LANGUAGE 'c';

CREATE FUNCTION complex_out(opaque)
    RETURNS opaque
    AS 'PGROOT/tutorial/obj/complex.so'
    LANGUAGE 'c';

CREATE TYPE complex (
    internallength = 16,
    input = complex_in,
    output = complex_out
);
     

前に述べたように、Postgresは基本型の配列を フルにサポートしています。これに加え、Postgres ではユーザ定義型の配列もサポートしています。型を定義する時、 Postgresは自動的にその型の配列のサポートを提供します。 歴史的な理由により、配列型はユーザ型の名前の前にアンダースコア文字"_" が 付いた名前になります。複合型の場合は、すでにシステムはそれらが内部的に どのようになっているかを把握しているので、関数を定義する必要はありません。

14.1.2. ラージオブジェクト

そのデータ型の値が内部形式で数百バイトを越える畏れがあるならば、 それらをTOASTで使用できるようにしなければなりません。 この仕方は、内部表現は可変長型の標準レイアウト(最初の4バイトはint32型で、 そこにはそれ自身を含めたデータの全大きさのバイト数が記述されている)に 沿っていなければなりません。その後、その型の値を受け入れる関数は、 与えられた値にpg_detoast_datum()に対して呼び出すとき(その値がNULLでは 無かったと照合され、その関数が厳密ではなかった場合)に注意を しなければなりません。最後に、CREATE TYPEコマンドを実行する際に、 適当な保存オプションを選択して下さい。