【テクニカル・上級編】CICSにおけるEXEC CICS READ/WRITE/REWRITE/DELETEのVSAM操作 – PL/Iの基本構文とデータ制御実践ガイド

基幹の暗部を支配するVSAMとCICSオンラインの作法

メインフレームの現場で長く生きていると、「なぜ今のオープン系エンジニアは、ファイルアクセス一つでこれほどまでに苦労するのか」と慨嘆したくなる瞬間がある。JavaやC#のORM(オブジェクト関係マッピング)や、抽象化されたリポジトリパターンに慣れ親しんだ頭脳にとって、CICS(Customer Information Control System)環境下でのVSAM(Virtual Storage Access Method)操作は、まるで地雷原を裸足で歩くような恐怖に映るらしい。

しかし、PL/I(Programming Language One)とCICS、そしてVSAMが織りなすトライアングルは、極限まで無駄を削ぎ落とした「純粋なデータ駆動の美学」の結晶である。今回は、CICSオンライン処理におけるEXEC CICSコマンド群(READ, WRITE, REWRITE, DELETE)を用いたCRUD操作の本質と、オープン系移行を見据えたアーキテクチャの急所を、コンパイラの裏側まで踏み込んで解き明かす。

—

1. CICS/VSAM操作の基本と「予約語を持たない」PL/Iの優位性

PL/Iの最も特異で、かつベテランアーキテクトを唸らせる仕様は、「PL/Iには厳密な意味での予約語(Reserved Words)が存在しない」という点にある。COBOLのように `READ` や `WRITE` が言語の文法上の縛りとして予約されているわけではない。すべてはコンテキスト(文脈)によって解釈される。

これが何を意味するか。CICSプリプロセッサ(DFHEAP1Iなど)がソースコードを走査する際、`EXEC CICS` で始まるブロックはホスト言語の枠外として解釈され、適切なIBMファミリのランタイムサービス呼び出し(DFHICVST等のスタブ)に展開される。この「言語仕様の緩やかさ」と「CICSの厳格なトランザクション管理」の同居こそが、メインフレームの安定稼働を支える基盤なのだ。

実践:VSAM KSDSに対するCRUD処理のPL/Iコード

百聞は一見に如かず。CICSオンラインプログラムにおいて、VSAMのKSDS(Key-Sequenced Data Set)を操作する典型的なPL/Iコードを見てほしい。ベース変数とポインタ、そしてCICSコマンドの連係がどのように行われているか、実務のシビアな視点で記述している。

1
/================================================================/
/ 顧客マスタ更新オンラインプログラム(CICS / VSAM CRUD) /
/================================================================/
CUST_UPDATE_PGM: PROC OPTIONS(MAIN) REENTRANT;

/— 1. ワークエリアおよびポインタの定義 —/
DCL CUST_PTR POINTER;
DCL 1 CUST_REC BASED(CUST_PTR),
3 C_KEY CHAR(8), / 顧客番号(KSDSキー) /
3 C_NAME CHAR(30), / 顧客名 /
3 C_BALANCE FIXED DEC(9,2), / 預金残高(パック)/
3 C_FILLER CHAR(62); / 予備領域 /

DCL W_LENG FIXED BIN(31) INIT(100);
DCL EIBTRMID CHAR(4) EXTERNAL; / 端末ID /

/— 2. READ操作:レコードの排他取得(UPDATE指定) —/
EXEC CICS READ
DATASET(‘CUSTFILE’)
INTO(CUST_REC)
RIDFLD(C_KEY)
KEYLENGTH(8)
UPDATE
LENGTH(W_LENG)
RESP(CICS_RESP)
RESP2(CICS_RESP2);

IF CICS_RESP = DFHRESP(NORMAL) THEN DO;
/ 正常取得:残高に加算処理 /
C_BALANCE = C_BALANCE + 1000.00;

/— 3. REWRITE操作:更新の確定 —/
EXEC CICS REWRITE
DATASET(‘CUSTFILE’)
FROM(CUST_REC)
LENGTH(W_LENG)
RESP(CICS_RESP)
RESP2(CICS_RESP2);

IF CICS_RESP ¬= DFHRESP(NORMAL) THEN DO;
/ REWRITE異常時の処理 /
CALL HANDLE_ERROR(‘REWRITE’, CICS_RESP, CICS_RESP2);
END;
END;
ELSE IF CICS_RESP = DFHRESP(NOTFND) THEN DO;
/— 4. WRITE操作:新規レコードの追加 —/
C_KEY = ‘C9999999’;
C_NAME = ‘NEWCUSTOMER’;
C_BALANCE = 5000.00;

EXEC CICS WRITE
DATASET(‘CUSTFILE’)
FROM(CUST_REC)
RIDFLD(C_KEY)
LENGTH(W_LENG)
RESP(CICS_RESP);
END;
ELSE DO;
/ 読み込みエラー /
CALL HANDLE_ERROR(‘READ’, CICS_RESP, CICS_RESP2);
END;

/— 5. 終了処理と同期点(SYNCPOINT) —/
EXEC CICS RETURN;

HANDLE_ERROR: PROC(P_CMD, P_RESP, P_RESP2);
DCL P_CMD CHAR();
DCL P_RESP FIXED BIN(31);
DCL P_RESP2 FIXED BIN(31);
/ ここにログ出力およびABEND処理を記述 /
EXEC CICS ABEND ABCODE(‘CERR’) NODUMP;
END HANDLE_ERROR;

END CUST_UPDATE_PGM;

—

2. 排他制御(ENQ/DEQ)の自動適用範囲と「見えない罠」

上記のコードで最も注目すべきは、`EXEC CICS READ` に付与された `UPDATE` オプションである。

オープン系エンジニアから「なぜわざわざREADの時点でUPDATEと宣言するのか?」という質問をよく受ける。ここにメインフレームの真骨頂がある。CICSにおいて `UPDATE` を指定してVSAMレコードを読み込んだ瞬間、CICSのファイルコントロール管理下でそのレコード(またはCI/CAレベルのロック)に対する排他制御(ENQ)が自動的にかけられる。

自動排他制御のライフサイクルとリスク

1. ロックの起点: `EXEC CICS READ … UPDATE` が成功した瞬間。
2. ロックの持続: タスクが終了するか、明示的な `EXEC CICS SYNCPOINT`(またはROLLBACK)が発行されるまで持続する。
3. ロックの解放: `REWRITE` や `DELETE` が実行されるか、タスクが終了(`RETURN`)した瞬間に自動的にDEQ(解放)される。

ここでアーキテクトとして警鐘を鳴らしたいのは、「オンライン画面からの入力待ち(CONVERSE/SEND MAP)を挟むロングランニング・トランザクション内でUPDATE読み込みを維持する設計」の愚かしさである。
オペレータがコーヒーブレイクに入っている間、VSAMの該当レコード、あるいはバッファプールが丸ごとロックされ、他のオンライン処理やバッチがデッドロックの渦に巻き込まれる。CICS環境における排他制御は、可能な限り「瞬殺(短時間で完結)」が鉄則なのだ。

—

3. コンパイラの最適化とパックデシマルの内部符号反転バグ

PL/I(Enterprise PL/Iコンパイラ)で数値演算を行う際、`FIXED DEC(p, q)`(パックデシマル)の扱いには特段の注意が必要となる。特に、DB2の埋め込みSQLやVSAM経由で外部システムから渡ってきたデータを処理する際、CICSタスク間でメモリを直接キャスト(ベース変数によるポインタ参照)すると、符号ニブル(Zone/Sign半バイト)の不整合に起因する致命的なアベンド(S0C7など)に直面する。

パックデシマルのメモリレイアウトとアベンド解析

IBMメインフレームのパックデシマルは、1バイトに2桁の数字を格納し、最下位バイトの後半4ビットに符号(正なら `C` や `F`、負なら `D`)を持つ。
もしオープン系から移行したデータや、外部連携の不備でこの符号ニブルが破損(例: `0x1234567E` のはずが `0x12345678` になるなど)した場合、PL/Iが演算を行った瞬間に ASRA (S0C7) アベンド が発生し、ストレージダンプの海に放り出されることになる。

対策としてのコンパイラオプション:
本番運用においては、コンパイラオプションに `TEST` や不適切な最適化(極端な `OPT(3)` などでポインタ位置のズレが生じるケースは稀だが、浮動小数点やアライメントで挙動が変わる)を避け、実務では以下のような防御的コーディングを徹底すべきだ。

1
/ パックデシマルの符号およびデータ妥当性チェックの例 /
IF ¬(C_BALANCE IS DATA) THEN DO;
/ データ破損時の例外処理 /
EXEC CICS ABEND ABCODE(‘DAT1’);
END;

(※実際には `TEST(ERROR)` オプションを付与したロードモジュールと、CEEDUMPの解析スキルがシニアアーキテクトの腕の見せ所となる)

—

4. マイグレーション(レガシー移行)における設計の急所

金融機関や大手流通の基幹システムを、Java(Spring Boot)やC#(.NET Core)へリライト、あるいはマイグレーションするプロジェクトにおいて、このCICS/VSAMのCRUDロジックは最大の難所となる。

オープン系へのトランスレーションにおける3つの壁

1. トランザクション境界の解体:
CICSのタスク単位の暗黙的コミット/ロールバック(SYNCPOINT)の概念は、RDB(PostgreSQLやOracle)のトランザクション管理や、分散トランザクション(XA)とは非対称である。単純なAPI化を行うと、整合性ロジックが破綻する。
2. VSAM KSDSのRDBマッピング:
VSAMの可変長レコードや、重複キー(Alt-Index)、ジェネリック検索(`START` / `READNEXT`)の挙動を、リレーショナルデータベースのインデックスやプレフィックス検索にそのまま置き換えるのは容易ではない。特に `START` コマンドによるブラウズ処理(カーソル処理)の再現には、SQL側の適切な `ORDER BY` とフェッチ制御が不可欠となる。
3. 排他制御の粒度:
VSAMのレコード単位ロックと、RDBの行ロック(SELECT … FOR UPDATE)では、タイムアウトの挙動やデッドロック検知メカニズムが異なる。移行設計では、CICSのレスポンスタイム前提(サブ秒応答)を前提としたロック解放タイミングを、オープン系のコネクションプール設計に再マッピングする必要がある。

—

結びにかえて

PL/IとCICS、そしてVSAMの組み合わせは、レガシーの代名詞として語られがちだが、そこに宿る思想は「高スループットと圧倒的な堅牢性」そのものである。

変数名に予約語の縛りがない自由な文法を持ちながら、背後ではミリ秒単位のトランザクションと厳密なメモリ管理をこなす。このアーキテクチャの本質を理解せずして、真に信頼性の高いモダンシステムへの移行などあり得ない。

コードの表面的な構文を追うのではなく、コンパイラが生成する機械語、そしてCICSが裏で握るリソースの息吹を感じ取ること――それこそが、真のシステムアーキテクトに求められる姿勢なのである。

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