ページの先頭行へ戻る
Enterprise Postgres 10 アプリケーション開発ガイド
FUJITSU Software

D.4.5 非プリミティブSQLデータ型の扱い方

本節では、非スカラー型およびユーザ定義のSQLデータ型をECOBPGアプリケーションで扱う方法を示します。 この内容は、前の項で説明した非プリミティブ型のホスト変数の扱い方とは別のものです。

配列

SQLの配列は、ECOBPGにおいては直接的にはサポートされていません。 SQL配列をCOBOLの配列のホスト変数に簡単に対応させることはできません。 定められていない振る舞いを引き起こすでしょう。 但し、いくつかの回避策があります。

もし、クエリが配列の 要素 に対して個別にアクセスした場合、ECOBPGにおける配列の利用を避けることができます。 その際、要素に対応させることができる型のホスト変数を利用しなければなりません。 例えば、カラムの型が integer の配列の場合、PIC S9(9) COMP 型のホスト変数を使用することができます。 同様に、要素の型が varchar または text の場合、VARCHAR 型のホスト変数を使用することができます。

以下に例を示します。次のようなテーブルを仮定します:

CREATE TABLE t3 (
ii integer[]
);
testdb=> SELECT * FROM t3;
ii
-------------
{1,2,3,4,5}
(1 row)

以下のプログラム例は、配列の4番目の要素を取得し、それを PIC S9(9) COMP-5 型のホスト変数に保存します:

EXEC SQL BEGIN DECLARE SECTION END-EXEC.
01 II PIC S9(9) COMP.
EXEC SQL END DECLARE SECTION END-EXEC.

EXEC SQL DECLARE cur1 CURSOR FOR SELECT ii[4] FROM t3 END-EXEC.
EXEC SQL OPEN cur1 END-EXEC.

EXEC SQL WHENEVER NOT FOUND GOTO END-FETCH END-EXEC.

PERFORM NO LIMIT
    EXEC SQL FETCH FROM cur1 INTO :II  END-EXEC
    DISPLAY "ii=" II
END-PERFORM.
END-FETCH.
EXEC SQL CLOSE cur1 END-EXEC.

複数の配列の要素を、配列型のホスト変数の複数の要素にマッピングするためには、配列型のカラムの各要素とホスト変数配列の各要素は、以下の例のように別々に管理されなければなりません。以下に例を示します。

EXEC SQL BEGIN DECLARE SECTION END-EXEC.
01 GROUP-ITEM.
    05 II_A PIC S9(9) COMP OCCURS 8.
EXEC SQL END DECLARE SECTION END-EXEC.

EXEC SQL DECLARE cur1 CURSOR FOR SELECT ii[1], ii[2], ii[3], ii[4] FROM t3 END-EXEC.
EXEC SQL OPEN cur1 END-EXEC.

EXEC SQL WHENEVER NOT FOUND GOTO END-FETCH END-EXEC.

PERFORM NO LIMIT
    EXEC SQL FETCH FROM cur1 INTO :II_A[1], :II_A[2], :II_A[3], :II_A[4] END-EXEC
    ...
END-PERFORM.

繰り返しになりますが、以下の例は正しく動作しません。なぜなら、配列型のカラムをホストの配列変数に直接対応させることはできないからです。

EXEC SQL BEGIN DECLARE SECTION END-EXEC.
01 GROUP-ITEM.
    05 II_A PIC S9(9) COMP OCCURS 8.
EXEC SQL END DECLARE SECTION END-EXEC.

EXEC SQL DECLARE cur1 CURSOR FOR SELECT ii FROM t3 END-EXEC.
EXEC SQL OPEN cur1 END-EXEC.

EXEC SQL WHENEVER NOT FOUND GOTO END-FETCH END-EXEC.
PERFORM NO LIMIT
*     WRONG
    EXEC SQL FETCH FROM cur1 INTO :II_A END-EXEC
    ...
END-PERFORM.

もうひとつの回避策は、配列をホスト変数のVARCHAR 型に文字列表現として保存することです。この表現についてより詳細があります。

参照

この表現の詳細については、”PostgreSQL 文書”の“チュートリアル”の”配列”を参照してください。

これは、配列にはホストプログラム内で自然な形ではアクセスできないことを意味しています(文字列表現を解析する追加処理が無ければですが)。

複合型

複合型はECOBPGでは直接はサポートされていませんが、簡単な回避方法が利用可能です。 利用可能な回避方法は、先に配列において説明されたものと似ています。つまり、各属性に個別にアクセスするか、外部の文字列表現を使います。

以降の例のため、以下の型とテーブルを仮定します:

CREATE TYPE comp_t AS (intval integer, textval varchar(32));
CREATE TABLE t4 (compval comp_t);
INSERT INTO t4 VALUES ( (256, 'PostgreSQL') );

もっとも分かりやすい解決法は、各属性に個別にアクセスすることです。 以下のプログラムは、 comp_t 型の各要素を個別に選択することによってサンプルのテーブルからデータを受け取ります:

EXEC SQL BEGIN DECLARE SECTION END-EXEC.
01 INTVAL PIC S9(9) COMP.
01 TEXTVAL PIC X(33) VARYING.
EXEC SQL END DECLARE SECTION END-EXEC.

* Put each element of the composite type column in the SELECT list.
EXEC SQL DECLARE cur1 CURSOR FOR SELECT (compval).intval, (compval).textval FROM t4 END-EXEC.
EXEC SQL OPEN cur1 END-EXEC.

EXEC SQL WHENEVER NOT FOUND GOTO END-FETCH END-EXEC.

PERFORM NO LIMIT
*    Fetch each element of the composite type column into host variables.
    EXEC SQL FETCH FROM cur1 INTO :INTVAL, :TEXTVAL END-EXEC

    DISPLAY "intval=" INTVAL ", textval=" ARR OF TEXTVAL
END-PERFORM.

END-FETCH.
EXEC SQL CLOSE cur1 END-EXEC.

この例を拡張して、 FETCH コマンドの値を格納するホスト変数を一つの集団項目にまとめることができます。 集団項目の形のホスト変数の詳細については、“集団項目”を参照してください。集団項目に変更するために、この例は以下のように変更することができます。 二つのホスト変数 intval と textval を comp_t 集団項目の従属項目とし、集団項目を FETCH コマンドで指定します。

EXEC SQL BEGIN DECLARE SECTION END-EXEC.
01 COMP-T TYPEDEF.
    02 INTVAL PIC S9(9) COMP.
    02 TEXTVAL PIC X(33) VARYING.

01 COMPVAL TYPE COMP-T.
EXEC SQL END DECLARE SECTION END-EXEC.

* Put each element of the composite type column in the SELECT list.
EXEC SQL DECLARE cur1 CURSOR FOR SELECT (compval).intval, (compval).textval FROM t4 END-EXEC.
EXEC SQL OPEN cur1 END-EXEC.
EXEC SQL WHENEVER NOT FOUND GOTO END-FETCH END-EXEC.

PERFORM NO LIMIT
*    Put all values in the SELECT list into one structure.
    EXEC SQL FETCH FROM cur1 INTO :COMPVAL END-EXEC

    DISPLAY "intval=" INTVAL ", textval=" ARR OF TEXTVAL
END-PERFORM.

END-FETCH.
EXEC SQL CLOSE cur1 END-EXEC.

集団項目が FETCH コマンドで使われていますが、属性名は SELECT 句において各々が指定されています。 これは、複合型の値のすべての属性を示す * を用いることで拡張することができます。

...
EXEC SQL DECLARE cur1 CURSOR FOR SELECT (compval).* FROM t4 END-EXEC.
EXEC SQL OPEN cur1 END-EXEC.
EXEC SQL WHENEVER NOT FOUND GOTO END-FETCH END-EXEC.

PERFORM NO LIMIT
*   Put all values in the SELECT list into one structure.
    EXEC SQL FETCH FROM cur1 INTO :COMPVAL END-EXEC

    DISPLAY "intval=" INTVAL ", textval=" ARR OF TEXTVAL
END-PERFORM.

この方法であれば、ECOBPGが複合型そのものを理解できないとしても、複合型はほぼシームレスに集団項目に対応させることができます。

最後に、VARCHAR 型のホスト変数に外部の文字列表現として複合型の値を格納することもできます。 しかし、この方法ではホストプログラムから値のフィールドにアクセスするのは簡単ではありません。

ユーザ定義の基本型

新しいユーザ定義の基本型は、ECOBPGでは直接的にはサポートされていません。 外部の文字列表現、VARCHAR 型のホスト変数を使うことができ、この解決法は多くの型について確かに適切かつ十分です。

以下に complex 型を使った例を示します。

参照

complex型の詳細は、“PostgreSQL文書”の“サーバプログラミング”の“ユーザ定義型”を参照してください。

この型の外部文字列表現は (%lf,%lf) で、complex_in() 関数および complex_out() 関数で定義されています。 以下の例は、カラム a と b に、complex 型の値 (1,1) および (3,3) を挿入し、その後、それらをテーブルからSELECTします。

EXEC SQL BEGIN DECLARE SECTION END-EXEC.
    01 A PIC X(64) VARYING.
    01 B PIC X(64) VARYING.
EXEC SQL END DECLARE SECTION END-EXEC.

EXEC SQL INSERT INTO test_complex VALUES ('(1,1)', '(3,3)') END-EXEC.

EXEC SQL DECLARE cur1 CURSOR FOR SELECT a, b FROM test_complex END-EXEC.
EXEC SQL OPEN cur1 END-EXEC.

EXEC SQL WHENEVER NOT FOUND GOTO END-FETCH END-EXEC.

PERFORM NO LIMIT
    EXEC SQL FETCH FROM cur1 INTO :A, :B END-EXEC
    DISPLAY "a=" ARR OF A ", b=" ARR OF B
END-PERFORM.

END-FETCH.
EXEC SQL CLOSE cur1 END-EXEC.

その他の回避方法は、ユーザ定義型をECOBPGにおいて直接的に使うことを避けることであり、ユーザ定義型とECOBPGが扱えるプリミティブ型を変換する関数またはキャストを作成することです。 ただし、型のキャスト、特に暗黙のものは型システムにおいて慎重に導入されなければなりません。

例を示します。

CREATE FUNCTION create_complex(r double precision, i double precision) RETURNS complex
LANGUAGE SQL
IMMUTABLE
AS $$ SELECT $1 * complex '(1,0)' + $2 * complex '(0,1)' $$;

この定義の後、以下の例は

EXEC SQL BEGIN DECLARE SECTION END-EXEC.
01 A COMP-2.
01 B COMP-2.
01 C COMP-2.
01 D COMP-2.
EXEC SQL END DECLARE SECTION END-EXEC.

MOVE 1 TO A.
MOVE 2 TO B.
MOVE 3 TO C.
MOVE 4 TO D.

EXEC SQL INSERT INTO test_complex VALUES (create_complex(:A, :B), create_complex(:C, :D))
END-EXEC.

以下と同じ効果をもたらします。

EXEC SQL INSERT INTO test_complex VALUES ('(1,2)', '(3,4)') END-EXEC.