ファイヤープロジェクト
メタデータの取得と記述子領域
2004-05-14T06:30+09:00   matsu
動的SQLだと,スキーマを事前に予測できない場合がある.記述子領域は,メタデータとデータのデータセットで,これを使用すると,埋め込みSQLでもメタデータを扱えるようになる.
以下を使用すればプログラム実行時にいかなるスキーマのテーブルをも作成することができる.
EXEC SQL EXECUTE IMMEDIATE 文字列;
問題はこのプログラム実行時にスキーマが決定されるテーブルから,どうやってデータを取り出すかである.静的な埋め込みSQLではスキーマがあらかじめ分かっている状況を扱うので,取り出したデータを格納する変数の型と数をコードにあらかじめ記述できた.こうした問題を解決するために,記述子領域というものを使用する.記述子領域には以下が含まれている.
  • ヘッダ
  • メタデータ
  • データ(検索結果1行分)
フェッチの埋め込みSQLで,記述子領域に結果を格納するようにして,メタデータをチェックした上で値の取り出しを行うことができるようになり,プログラム実行時にスキーマが決定される状況に対応できるようになる.
記述子領域を使用するサンプルを以下にしめす.
上のサンプルではinit.pcgとfin.pcgをEXEC SQL INCLUDEしている.init.pcgでテーブルhogeを作成しデータを投入する.上のサンプル本体はテーブルhogeのスキーマがどうなっているを調べるので,init.pcgでテーブルhogeのスキーマは好きなようにできる.今回はinit.pcgを以下のようにした.
fin.pcgは,サンプルを繰り返し実行できるよう,init.pcgで作成したテーブルをドロップするようにした.
実行結果を以下に示す(今回はecpgのデバッグ文は抑止した).
$> ./descriptor  -d tcp:postgresql://dbserver/firstdb -u matsu -p hogefuga
record 01
        fieldno = 1 / fieldname = "id" / fieldtype = 4 data="1"
                indicator = 0 / datalength = -5 
        fieldno = 2 / fieldname = "name" / fieldtype = 1 data="foovar01"
                indicator = 0 / datalength = 8 
record 02
        fieldno = 1 / fieldname = "id" / fieldtype = 4 data="3"
                indicator = 0 / datalength = -5 
        fieldno = 2 / fieldname = "name" / fieldtype = 1 data="foovar03"
                indicator = 0 / datalength = 8 
record 03
        fieldno = 1 / fieldname = "id" / fieldtype = 4 data="5"
                indicator = 0 / datalength = -5 
        fieldno = 2 / fieldname = "name" / fieldtype = 1 data="foovar05"
                indicator = 0 / datalength = 8 
record 04
        fieldno = 1 / fieldname = "id" / fieldtype = 4 data="7"
                indicator = 0 / datalength = -5 
        fieldno = 2 / fieldname = "name" / fieldtype = 1 data="foovar07"
                indicator = 0 / datalength = 8 
record 05
        fieldno = 1 / fieldname = "id" / fieldtype = 4 data="9"
                indicator = 0 / datalength = -5 
        fieldno = 2 / fieldname = "name" / fieldtype = 1 data="foovar09"
                indicator = 0 / datalength = 8 
record 06
        fieldno = 1 / fieldname = "id" / fieldtype = 4 data="0"
                indicator = 0 / datalength = -5 
        fieldno = 2 / fieldname = "name" / fieldtype = 1 data=""
                indicator = -1 / datalength = 8 
record 07
        fieldno = 1 / fieldname = "id" / fieldtype = 4 data="2"
                indicator = 0 / datalength = -5 
        fieldno = 2 / fieldname = "name" / fieldtype = 1 data=""
                indicator = -1 / datalength = 8 
record 08
        fieldno = 1 / fieldname = "id" / fieldtype = 4 data="4"
                indicator = 0 / datalength = -5 
        fieldno = 2 / fieldname = "name" / fieldtype = 1 data=""
                indicator = -1 / datalength = 8 
record 09
        fieldno = 1 / fieldname = "id" / fieldtype = 4 data="6"
                indicator = 0 / datalength = -5 
        fieldno = 2 / fieldname = "name" / fieldtype = 1 data=""
                indicator = -1 / datalength = 8 
record 10
        fieldno = 1 / fieldname = "id" / fieldtype = 4 data="8"
                indicator = 0 / datalength = -5 
        fieldno = 2 / fieldname = "name" / fieldtype = 1 data=""
                indicator = -1 / datalength = 8 
各レコードについて全フィールド(といっても二つだけど)の名前や型などを取り出している.
記述子領域の使用の流れは大体以下のようになるだろう.
  1. 記述子領域の確保とカーソル定義/オープン
  2. フェッチ
  3. 記述子領域からメタデータを取り出す
  4. 記述子領域からデータを取り出す
  5. 記述子領域の開放とカーソルクローズ
記述子領域は明示的に確保と開放が必要である.記述子領域の確保は以下で行う.
EXEC SQL ALLOCATE DESCRIPTOR 記述子名;
そして領域確保した記述子をフェッチのINTO句に使用する.
EXEC SQL FETCH NEXT FROM カーソル名 INTO DESCRIPTOR 記述子名;
これで記述子領域にメタデータとともにフェッチ結果が格納される.記述子領域の使用が終ったら開放する.
EXEC SQL DEALLOCATE DESCRIPTOR 記述子名;
INTO句に記述子領域を指定してフェッチしたら,記述子領域からデータを取り出せる.まず以下で記述子領域のヘッダデータを取り出す.
EXEC SQL GET DESCRIPTOR 記述子領域名 格納変数 = フィールド名;
フィールドはCOUNT(列数)だけらしい.以下はサンプルでの記述.
/* 列数の取得 */
EXEC SQL GET DESCRIPTOR descriptor1 :fieldcount = COUNT;
上で列数が分かったので,サンプルではその列数分メタデータとデータの取り出しを行っている.
for (fieldno = 1; fieldno <= fieldcount; fieldno++)
  {
    EXEC SQL GET DESCRIPTOR descriptor1 VALUE :fieldno :fieldname = NAME;
    EXEC SQL GET DESCRIPTOR descriptor1 VALUE :fieldno :fieldtype = TYPE;
    EXEC SQL GET DESCRIPTOR descriptor1 VALUE :fieldno :fielddata = DATA;
    EXEC SQL GET DESCRIPTOR descriptor1 VALUE :fieldno :fieldindicator = INDICATOR;
    EXEC SQL GET DESCRIPTOR descriptor1 VALUE :fieldno :fielddatalength = LENGTH;
    fprintf (stderr,
             "\tfieldno = %d / fieldname = \"%s\" / fieldtype = %d data=\"%s\"\n",
             fieldno, fieldname, fieldtype, fielddata);
    fprintf (stderr, "\t\tindicator = %d / datalength = %d \n",
	     fieldindicator, fielddatalength);
  }
記述子領域本体のデータを取り出す構文は以下である(※).
EXEC SQL GET DESCRIPTOR 記述子領域名 VALUE 列番号 変数 = メタデータのフィールド名;
サンプルで取得しているメタデータのフィールドを以下に示す.
NAME
フィールド名(列名)
TYPE
フィールドの型
DATA
フィールドの値
INDICATOR
NULLなら負,正なら切り詰められた,0なら正常.
LENGTH
DATAの文字列としての長さ.サンプルの実行結果からすると,TYPEが文字列でなければ負になるようだ.
大体においてDATAが最終的に取得したいメタデータフィールドで,これを正しく取得するために,TYPEやINDICATORなどを参照する.メタデータフィールドは他にもいろいろある.この頁の執筆は「PostgreSQL 7.4.1 文書」の記述を参照した.
※ 「フィールド」というのがメタデータの項目のことなのか,テーブルの列,属性なのかに注意しないと混乱する.前者の場合は「メタデータの」とつけるようにした.
matsu(C)
Since 2002
Mail to matsu