PL/Iの「STRING」関数が引き起こすメモリの深淵:構造体連結とアライメントの罠
メインフレームの現場で、PL/Iの`STRING`組み込み関数ほど、その「便利さ」と「凶暴さ」が表裏一体の機能は他にありません。構造体全体を一括して文字列として連結し、出力や転送に用いる――一見すると、個別のフィールドを一つずつMOVEする苦労から解放される魔法のように思えます。
しかし、我々のようなシステムアーキテクトにとって、この関数は「ブラックボックス」と同義です。特にマイグレーション案件において、JavaやC#のオブジェクト指向的なデータ構造へ変換する際、この関数が隠蔽していた「メモリレイアウト」の歪みが、致命的な不整合となって現れます。
STRING関数が暴くメモリの真実
`STRING(structure_name)` は、構造体に含まれる全要素を、メモリ上の配置順序に従って連続した文字列として抽出します。ここで重要なのは、「アライメント(境界調整)」がデータ構造に与える不可視の空白です。
/i
DCL 1 MY_REC,
2 ID CHAR(4),
2 AMOUNT FIXED BIN(31), / 4バイト境界にアライメントされる可能性 /
2 STATUS CHAR(1);
/
- このとき、AMOUNTの前にコンパイラが自動挿入した
- 埋め草(PADDING)バイトが含まれるかどうかは、
- コンパイラオプション(ALIGNED/UNALIGNED)に依存する。
/
DCL BUFFER CHAR(20) VAR;
BUFFER = STRING(MY_REC);
もし、この構造体が `ALIGNED` で定義されていれば、フィールド間に意図せぬバイト列が挿入されます。マイグレーション先のJavaで `ByteBuffer` を使い、生のバイト列をそのままキャストしようとすれば、この「見えない空白」のせいで、後続のフィールドがずれて読み込まれる――まさに悪夢の始まりです。
構造体連結における「パックデシマル」の地雷
特に危険なのが、構造体内に `FIXED DEC(N, M)` (パックデシマル)が含まれている場合です。`STRING` 関数は、パックデシマルの内部表現(ニブル単位の符号部)をそのまま連結します。
もし、DB2のホスト変数として定義した構造体を `STRING` で読み出し、外部ファイルへ出力した後にJava側で符号の解釈に失敗すれば、数値が狂うだけでなく、最悪の場合、アプリケーションが計算例外(`SOC7`)でアベンドします。
推奨される対応策:明示的なアンパック
移行設計においては、`STRING` 関数に頼るのではなく、構造体を個別に変換するシリアライザを実装すべきです。どうしても既存資産を流用する場合、以下の点に注意してください。
1. UNALIGNED属性の明示: 全ての構造体に `UNALIGNED` を指定し、アライメントによるパディングを強制排除する。
2. ダンプ解析の徹底: アベンド時の `SYSUDUMP` を読み込み、`OFFSET` と `STORAGE` コマンドで、期待するバイト列と実際のメモリ状態を比較する。
3. DB2ホスト変数の分離: 埋め込みSQLで使う構造体と、ファイルIOで使う構造体は物理的に分離する。
ポインタによる動的メモリ操作の極意
`STRING` 関数を多用するコードを保守する場合、往々にして `BASED` 変数とポインタを用いた動的なバッファ操作が組み合わされています。
/i
DCL P_REC PTR;
DCL MY_REC BASED(P_REC) LIKE TEMPLATE_REC;
/ 動的にメモリを確保し、STRINGで上書きする際の注意点 /
ALLOCATE MY_REC;
/
- 構造体のサイズを SIZEOF(MY_REC) で正しく取得しているか?
- コンパイラ最適化(OPTIMIZE(3))により、
- 変数のロード順序が変更されているケースがある。
/
最適化レベルが `OPTIMIZE(2)` 以上の場合、コンパイラはコードの効率化のために変数のロード・ストア順序を入れ替えます。この環境下で「メモリ領域を直接操作する」コードを書くのは、地雷原でタップダンスを踊るようなものです。CICSのオンライン処理であれば、`DFHEIBLK` を介したメモリ管理と混ざり合い、再現性のない断続的な不具合(Intermittent Error)を引き起こします。
アーキテクトとしての提言:マイグレーションの針路
レガシー移行において、PL/IのコードをJava等のモダン言語へ書き換える際、「機能の同値性」を求めるあまり、PL/I独特のメモリレイアウトを模倣しようとする設計者がいます。これは百害あって一利なしです。
- データ構造を再定義する: 構造体を `STRING` で連結するような設計は、本来「シリアライズ」として切り出すべきロジックです。
- ビジネスロジックとIOの分離: 構造体のメモリレイアウトに依存したロジックを排除し、JSONやProtobufのような「言語非依存のデータ交換形式」へ移行する。
PL/Iが我々に教えてくれたのは、メモリを直接叩くことの強さと、その裏側にある脆さです。技術の転換期において、その「脆さ」をコードの奥深くに埋め込まないこと。それが、次の10年を支えるシステムを設計する我々の責務ではないでしょうか。
もし、貴方の現場で `STRING` 関数由来のアベンドが頻発しているなら、まずは `DCL` ステートメントの `ALIGNED` 属性をすべて剥がし、バイナリダンプを16進数で追うことから始めてください。PL/Iのコンパイラは、嘘をつきません。嘘をついているのは、いつも我々人間なのです。
