【入門編】DB2埋め込みSQLにおけるホスト変数マッピングとSQLDAの構造 – PL/Iの基本構文とデータ制御実践ガイド

こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといった他のモダンな言語の経験がある方にとって、突然目の前に現れるPL/I(ピーエルアイ)や、さらにその中で動くDB2の埋め込みSQLという組み合わせは、なんだか黒魔術のように難解に見えるかもしれませんよね。

「変数の名前の付け方に予約語がないってどういうこと?」
「COBOLのピクチャ句とは違う、なんだか独特なデータ宣言は一体なに?」
「ホスト変数って、DB2とどうやってメモリをやり取りしているの?」

大丈夫です、怖くありませんよ。一つずつ、蓋を開けて中身を覗いていけば、実はとっても合理的で美しい仕組みになっていることが分かります。今回は、JavaやCOBOLの常識を少しだけ脇に置いて、PL/IとDB2が織り出す「データ通信の裏側」を一緒に紐解いていきましょう!

—

1. PL/Iの優しい世界:実は「予約語」がない!?

他のプログラミング言語(JavaやCOBOLなど)を触ったことがある方なら、「予約語(Keyword)」の存在に悩まされた経験があるはずです。例えば、`IF`や`SELECT`といったシステムが命令として使う単語は、変数名(識別子)としては使えないのが普通ですよね。

ところが、PL/Iの仕様書をめくると、驚くべき事実に出会います。
「PL/Iには、厳密な意味での『予約語』が存在しない」のです。

どういうことかと言うと、PL/Iは文脈(コンテキスト)によって言葉の意味を判断してくれます。例えば、極端な話、以下のようなコードを書いたとしてもコンパイラは怒りません。

1
/ 「IF」という名前の変数を宣言して、そこに「IF」という文字列を入れる狂気の沙汰(笑) /
DCL IF CHAR(2) INIT(‘OK’);

※実務でこんな名前をつけると後続のプログラマに怒られるので絶対にやめましょうね!

なぜこんなことができるかと言うと、PL/Iは前後の文脈から「あ、ここは命令の `IF` じゃなくて、変数の `IF` だな」と空気を読んで解釈してくれるからです。他言語の経験者からすると「なんて自由奔放なんだ…!」と驚くポイントですが、これは設計当時の「プログラマの表現力を縛らない」という優しい思想の表れなんですね。

—

2. DB2埋め込みSQLとホスト変数のマッピング

さて、本題のDB2とPL/Iの連携についてです。
オンラインやバッチ処理の中でデータベースをいじる際、PL/IのプログラムとDB2の間でデータをやり取りするための変数を「ホスト変数(Host Variable)」と呼びます。

JavaのJDBCなら `PreparedStatement` や `ResultSet` を使いますが、PL/Iではもっとダイレクトに、メモリ上の変数をそのままSQLの入出力に使います。ここで重要になるのが「データ型の翻訳」です。

JavaやCOBOLの型と、PL/Iの型、そしてDB2の型がどう対応しているのか、まずは一覧で見てみましょう。

| DB2のデータ型 | PL/Iのデータ宣言(属性) | COBOLのイメージ | Javaのイメージ |
| :— | :— | :— | :— |
| INTEGER (4バイト整数) | `DCL 変数名 FIXED BIN(31);` | `PIC S9(9) COMP` | `int` / `Integer` |
| SMALLINT (2バイト整数) | `DCL 変数名 FIXED BIN(15);` | `PIC S9(4) COMP` | `short` |
| CHAR(n) (固定長文字列) | `DCL 変数名 CHAR(n);` | `PIC X(n)` | `String` |
| VARCHAR(n) (可変長文字列)| `DCL 1 変数名, 2 宿主長 FIXED BIN(15), 2 宿主文字列 CHAR(n);` | `PIC S9(4) COMP` + `PIC X(n)` | `String` (長さ保持) |

ちょっぴり特殊な「可変長文字列(VARCHAR)」の正体

特に初心者が「うわっ」と怯えてしまうのが、DB2の `VARCHAR` を受けるときの宣言です。
PL/Iでは、DB2の可変長文字列を以下のような構造体(レベル番号付きのデータ)で受け取るお作法があります。

1
/ 顧客名を格納するVARCHAR(50)のホスト変数宣言 /
Dcl 1 顧客名_AREA,
3 顧客名_LEN Fixed Bin(15), / 実際のデータの長さ(2バイトバイナリ) /
3 顧客名_TEXT Char(50); / 文字列本体(50バイト) /

初めてこれを見たとき、「えっ、1つの変数を取るのに勝手に構造体になるの?」と戸惑いますよね。
仕組みは簡単です。先頭の `2 顧客名_LEN` に「今から何文字分のデータを入れるか」を数字でセットし、続く `3 顧客名_TEXT` に文字列本体を詰め込みます。DB2側もこのレイアウトを理解しているので、通信時に「あ、先頭の2バイトが長さね、了解!」と綺麗に解釈してくれます。

—

3. 怖くない!SQLCAとSQLDAの裏側

データベースを操作すると、必ずと言っていいほどお世話になるのが通信領域です。

  • SQLCA (SQL Communication Area)
  • SQLの実行結果(成功したのか、データがもう無いのか、エラーなのか)を受け取るお馴染みのエリアです。SQL文の直後に `EXEC SQL INCLUDE SQLCA;` と書くことで自動的に展開されます。
  • SQLDA (SQL Descriptor Area)
  • 動的SQL(プログラム実行時までどんなSQL文が来るか分からない、検索条件が可変のバッチなど)を扱うための「動的記述子エリア」です。

メモリレイアウトとアライメントの罠

メインフレームの世界では、メモリの「境界調整(アライメント)」が非常にシビアです。
例えば、奇数バイトのCHAR変数の直後に `FIXED BIN(31)` (4バイト整数)を配置すると、ハードウェアの効率的なアクセス(偶数境界や4バイト境界)のために、コンパイラが勝手に「パディング(隙間の埋め草バイト)」を挿入することがあります。

SQLDAや構造体を使ったホスト変数群を定義する際、このアライメントを意識していないと、DB2との間でデータのズレが生じ、「S0C4(ストレージ保護例外)」というおそろしいシステム異常終了を引き起こす原因になります。

> 💡 シニアアーキテクトからのアドバイス
> 「なんかよく分からないけど落ちる!」というときは、大体アライメントのズレか、ポインタの指し先ミスです。PL/Iで構造体を定義するときは、`UNALIGNED` 属性を明示的に指定して、パディングを勝手に入れないようにコントロールするテクニックも覚えておくと、実務ですごく役に立ちますよ。

—

4. 実践!PL/IとDB2の埋め込みSQLコード例

それでは最後に、ここまでの知識を総動員して、実際にDB2からデータを安全に取得するPL/Iのサンプルプログラム(の断片)を見てみましょう。
COBOLやJavaの経験があれば、「あ、やっていることは同じだな」とスッと入ってくるはずです。

1
/ ========================================================== /
/ DB2ホスト変数マッピングおよびデータ取得のサンプル /
/ ========================================================== /
Pgm_Main: Proc Options(Main);

/ 1. SQL通信エリア(SQLCA)の取り込み /
Exec Sql Include Sqlca;

/ 2. ホスト変数の定義 /
Dcl W_社員番号 Char(6) Init(‘ ‘); / 社員番号(入力用) /

/ 氏名はDB2側がVARCHAR(30)なので、PL/Iの構造体で受ける /
Dcl 1 W_社員氏名,
3 Len Fixed Bin(15), / 格納文字長 /
3 Text Char(30); / 文字列本体 /

Dcl W_給与 Fixed Bin(31); / 給与(4バイト整数) /
Dcl W_インジケータ Fixed Bin(15); / NULL値判定用インジケータ変数 /

/ 3. 検索条件の社員番号を設定 /
W_社員番号 = ‘A12345’;

/ 4. 埋め込みSQLの実行(単行SELECT) /
Exec Sql
Select EMPLOYEE_NAME, SALARY
Into :W_社員氏名, :W_給与 :W_インジケータ
From EMPLOYEE_TBL
Where EMPLOYEE_ID = :W_社員番号;

/ 5. 結果の判定 /
If Sqlcode = 0 Then Do
/ 正常終了時の処理 /
Put Skip List(‘社員名: ‘ || Substr(W_社員氏名.Text, 1, W_社員氏名.Len));
End;
Else If Sqlcode = 100 Then Do
/ データなし /
Put Skip List(‘該当する社員がいません。’);
End;
Else Do
/ 異常終了 /
Put Skip List(‘DB2エラー発生! SQLCODE = ‘ || Sqlcode);
End;

End Pgm_Main;

—

おわりに

いかがでしたでしょうか?
PL/Iの変数宣言や、DB2のホスト変数マッピング、そして少し特殊に見える可変長文字列(VARCHAR)の構造体。一見するとレガシーで取っ付きにくく感じるかもしれませんが、一つひとつの仕様には「なぜそう作られているのか」というハードウェアやミドルウェアの歴史的背景がしっかりとあります。

「予約語がない自由な文法」や「メモリを直接意識するデータ構造」は、裏を返せば、プログラマがコンピュータの性能を極限まで引き出すための強力な武器でもあります。

レガシーシステムの移行や改修プロジェクトに直面しても、怖がる必要は全くありません。ぜひ、この解説を思い出して、一つずつ紐解いていってくださいね。あなたのメインフレームライフが実り多いものになるよう、陰ながら応援しています!

タイトルとURLをコピーしました