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

こんにちは。毎日オンライン画面のチューニングや、夜間バッチのABEND(異常終了)対応にお疲れ様です。

今回は、CICS環境下におけるPL/IプログラムからのVSAM(KSDSなど)操作、すなわち `EXEC CICS READ` / `WRITE` / `REWRITE` / `DELETE` によるCRUD操作 について、実務で絶対に知っておくべき勘所を徹底的に解説します。

近年のオープン系へのマイグレーション案件でも、このCICS+VSAMのロジックを正確に読めないと、データ整合性の設計で痛い目を見ます。ベテランの視点から、現場のリアルなノウハウを伝授しましょう。

—

1. PL/IとCICSファイル制御の基本思想

まず大前提として、PL/I自体にはCICSのコマンドを実行するネイティブな構文はありません。そのため、コンパイル時に CICSトランスレータ(DFHEDPライン) を通し、`EXEC CICS` 構文をPL/IのCALL文(CICSスタブへのインターフェース)に展開するという前処理を行います。

ここで、今回のテーマである 「PL/Iの予約語を持たない構文規則」 が非常に重要になってきます。
PL/Iには、COBOLのような「固定的な予約語」という概念がほとんどありません。多くのキーワードは「文脈依存(Contextual Keywords)」です。つまり、変数名に `READ` や `WRITE`、さらには `CICS` と名付けても、コンパイラは前後の文脈からそれが何であるかを判断できます(※もちろん可読性のためにそんな命名は避けるべきですが)。この「何でも変数に使える柔軟さ」ゆえに、CICSのマクロや変数定義とコンフリクトを起こさないよう、コーディング規約で厳しく自衛する必要があるのです。

—

2. VSAM CRUD操作と排他制御(ENQ/DEQ)の自動適用

CICSのファイルコントロール(FCT定義されたVSAMファイル)を叩くとき、私たちが最も気を付けなければならないのは 「排他制御(ENQ)」 です。

CICS環境下では、以下のような挙動の原則があります。

  • 自動排他制御の範囲:

`EXEC CICS READ … UPDATE` を発行した瞬間、CICSはそのレコード(またはKSDSのCI/CA)に対して排他ロック(ENQ)をかけます。このロックは、同一タスク内で後続の `REWRITE` または `DELETE` が実行されるか、あるいは `EXEC CICS SYNCPOINT`(またはタスク終了による暗黙のコミット)が発行されるまで絶対に解放されません。

  • デッドロック(AICAやAKTx)の恐怖:

`READ UPDATE` をかけたまま、不要に長い処理(例えば外部APIの呼び出しや、画面からの入力待ちなど)を挟むと、リソースがロックされたままになり、他のオンラインユーザーがデッドロックやタイムアウト(AICA等)の犠牲になります。「取得したら速やかに更新して解放する」これがCICSプログラミングの鉄則です。

—

3. 実践:CICS/PL/IによるVSAM操作サンプルコード

それでは、実際の現場で即戦力となるCICS/PL/Iのソースコードを見てみましょう。KSDSのマスターファイルを読み込み、更新(REWRITE)し、別レコードを削除(DELETE)する一連の流れを実装しています。

/================================================================/
/ プログラムID: CUSTUPD1 /
/ 概要 : CICS/VSAM(KSDS) クライアントマスタ保守 /
/================================================================/
CUSTUPD1: PROC OPTIONS(MAIN) REENTRANT;

/— CICS通信エリア(DFHCOMMAREA)の定義 —/
DCL 1 CWA,
5 CA-REQ-TYPE CHAR(1), (‘1’:更新, ‘2’:削除)
5 CA-CUST-ID CHAR(8), (顧客ID – キー項目)
5 CA-CUST-NAME CHAR(30), (顧客名)
5 CA-RESP-CODE PIC ‘9(4)’ BIN; (CICS応答コード)

/— VSAMレコード様式(LCCK: 顧客マスタ構造体) —/
DCL 1 CUST-REC,
5 R-CUST-ID CHAR(8), (キー)
5 R-CUST-NAME CHAR(30), (名前)
5 R-CUST-ADDR CHAR(50), (住所)
5 R-UPDATE-DATE CHAR(8); (更新日付)

DCL WK-LEN PIC ‘9(4)’ BIN INIT(88);
DCL 1 EIB EXT; (CICSタスク実行ブロック)

/— 初期処理:通信エリアのコピー —/
IF EIBCALEN = 0 THEN DO;
/ 領域なし起動のエラー処理 /
RETURN;
END;

/ DFHCOMMAREAの内容をローカル変数へ退避 /
CWA = DFHCOMMAREA;

/————————————————————/
/ 1. READ with UPDATE (排他ロックの取得) /
/————————————————————/
EXEC CICS READ
DATASET(‘CUSTFILE’)
INTO(CUST-REC)
LENGTH(WK-LEN)
RIDFLD(CA-CUST-ID)
KEYLENGTH(8)
UPDATE / ここで排他ロックが掛かる /
RESP(CA-RESP-CODE);

IF CA-RESP-CODE ^= DFHRESP(NORMAL) THEN DO;
IF CA-RESP-CODE = DFHRESP(NOTFND) THEN
/ レコード未存在時の処理 /
GOTO NOT-FOUND-RTN;
ELSE
/ その他のシステム異常処理 /
GOTO CICS-ERR-RTN;
END;

/————————————————————/
/ 2. 分岐処理 (更新 or 削除) /
/————————————————————/
IF CA-REQ-TYPE = ‘1’ THEN DO;

/— データの加工 —/
R-CUST-NAME = CA-CUST-NAME;
R-UPDATE-DATE = ‘20231025’; / 例としての固定値 /

/ 3. REWRITE (更新実行:ここでロックが自動解放される) /
EXEC CICS REWRITE
DATASET(‘CUSTFILE’)
FROM(CUST-REC)
LENGTH(WK-LEN)
RESP(CA-RESP-CODE);

IF CA-RESP-CODE ^= DFHRESP(NORMAL) THEN
GOTO CICS-ERR-RTN;

END;
ELSE IF CA-REQ-TYPE = ‘2’ THEN DO;

/ 4. DELETE (削除実行:ここでロックが自動解放される) /
EXEC CICS DELETE
DATASET(‘CUSTFILE’)
RIDFLD(CA-CUST-ID)
KEYLENGTH(8)
RESP(CA-RESP-CODE);

IF CA-RESP-CODE ^= DFHRESP(NORMAL) THEN
GOTO CICS-ERR-RTN;

END;

/ 正常終了 /
RETURN;

NOT-FOUND-RTN:
/ 該当データ無しの場合のハンドリング /
CA-RESP-CODE = 1001; / 独自エラーコード設定 /
RETURN;

CICS-ERR-RTN:
/ 異常終了時の緊急処理(必要に応じてABEND発行) /
EXEC CICS ABEND
ABCODE(‘C999’)
NODUMP;
RETURN;

END CUSTUPD1;

—

4. ベテランからのデバッグのコツと注意点

現場でこの手のプログラムを保守していて、最もハマりやすいポイントをいくつか共有しておきます。

1. `RESP` オプションを必ず記述する
うっかり `RESP` を省略すると、CICSファイル制御エラーが発生した瞬間にタスクが異常終了(ASRAやDFHZC2400など)してしまいます。必ず `RESP(CA-RESP-CODE)` を受け取り、`DFHRESP(NORMAL)` や `DFHRESP(NOTFND)` と比較する防衛的コーディングを徹底してください。
2. `LENGTH` の罠
`FROM()` や `INTO()` で渡すレコード長は、VSAMの定義(FCT)と完全に一致している必要があります。構造体のパディング(境界調整)によって意図せぬバイト数ズレが起きることがあるため、PL/Iの `LENGTH` 組み込み関数(例: `LENGTH(CUST-REC)`)を動的に指定するか、明確に固定長を計算して渡す癖をつけましょう。
3. READ UPDATE と DELETE のペア性
`READ … UPDATE` を発行したなら、必ず対応する `REWRITE` か `DELETE`、またはエラー時の `EXEC CICS UNLOCK`(またはロールバック)を行わないと、リソースが宙ぶらりんになり、CICS領域全体を巻き込む大障害に発展します。

メインフレームの基幹系を支えるPL/IとCICSの連携は、一見古臭く見えても、極めて堅牢で洗練された仕組みの上に成り立っています。仕様の裏側にある「なぜその構文になっているのか」を理解すれば、どんな大規模改修でも怖くありません。

日々のコーディングに誇りを持って、頑張っていきましょう!

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