READE ってのはキーが一致しない場合、次のレコードをロックする

RPGに `READE` という命令があるのは、みなさんご存知かと思いますが、実はキーが一致しない場合、**期待していないレコードがロックされている** と知っている方は、どれ位いるだろうか？当たり前の話という方は、この回はスルーして下さい。m(_ _)m

### 再現用データベース
事象再現用のデータベース「**ITEM**」が以下の状況で存在したとする。
- キー：商品コード（CODE）
- レコードの並び： 商品コード[00124] → 商品コード[99999]
![2022-11-16_184654.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100029/bbc1618d-d2e9-3e68-f5d0-151d1faccc6b.png)
---
### READE ロック再現コード

##### `READE` で期待していないレコード(商品コード：99999)をロックしてしまう側のコード

```rpgle:①QII0100R（ロック解放待ち側）
     H DATEDIT(*YMD)
     H*- ***************************************************************
     H*- * Ｆ仕様書                                                   **
     H*- ***************************************************************
     F*- ---<< ファイル定義    >>-----*
     FITEM      UF   E           K DISK
     D*- ---<< 変数定義    >>-----*
     D MSG             S             40A
     C*- ***************************************************************
     C*- *  ＫＬＩＳＴ                                                **
     C*- ***************************************************************
     C     ITEMKEY1      KLIST
     C                   KFLD                    K@CODE            5 0           商品コード
     C*- ***************************************************************
     C*- *  メインロジック                                            **
     C*- ***************************************************************
     C*
     C*---------------
     C*-  ＲＥＡＤＥ
     C*---------------
     C                   Z-ADD     00125         K@CODE                          商品コード
     C     ITEMKEY1      SETLL     ITEMR
     C     ITEMKEY1      READE     ITEMR
     C                   IF        %EOF
     C                   EVAL      MSG         = '[READE]-'
     C                                         + ' 商品 125 は存在しません '
     C     MSG           DSPLY
     C                   ENDIF
     C*
     C*---------------
     C*-  ＣＨＡＩＮ
     C*---------------
     C                   Z-ADD     00125         K@CODE                          商品コード
     C     ITEMKEY1      CHAIN     ITEMR
     C                   IF        NOT %FOUND
     C                   EVAL      MSG         = '[CHAIN]-'
     C                                         + ' 商品 125 は存在しません '
     C     MSG           DSPLY
     C                   ENDIF
     C*
     C                   SETON                                        LR
     C                   RETURN

```

##### 期待していないレコード(商品コード：99999)をワザとロックしているコード

```rpgle:②QII0101R（ロック側）
     H DATEDIT(*YMD)
     H*- ***************************************************************
     H*- * Ｆ仕様書                                                   **
     H*- ***************************************************************
     F*- ---<< ファイル定義    >>-----*
     FITEM      UF   E           K DISK
     D*- ---<< 変数定義    >>-----*
     D MSG             S             40A
     C*- ***************************************************************
     C*- *  ＫＬＩＳＴ                                                **
     C*- ***************************************************************
     C     ITEMKEY1      KLIST
     C                   KFLD                    K@CODE            5 0           商品コード
     C*- ***************************************************************
     C*- *  メインロジック                                            **
     C*- ***************************************************************
     C*
     C*---------------
     C*-  ＣＨＡＩＮ
     C*---------------
     C                   Z-ADD     99999         K@CODE                          商品コード
     C     ITEMKEY1      CHAIN     ITEMR
     C                   IF        %FOUND
     C                   EVAL      MSG         = '[CHAIN]-'
     C                                         + ' 商品 99999 をロック！ '
     C     MSG           DSPLY
     C                   ENDIF
     C*
     C                   SETON                                        LR
     C                   RETURN

```

上記①の`READE`と`CHAIN`は、共に存在しないキー **商品コード:00125** を更新読み取りを行う処理。
しかし、この2つの命令は、結果的に「同じ様な処理をしたいだけ」だとしても大きな違いが生じる。
②の処理を実行し、**商品コード：99999** がレコード・ロックされいてる場合、①`READE`でレコードロック解放待になり、`CHAIN`ではそれが発生しない。
つまり、`READE` の対象は **商品コード:00125** だとしても、それが存在しない場合、実際にレコード・ロックされるのは **商品コード:99999** だという事だ。
これは、`CHAIN` はランダムアクセス、`READE` は、順次アクセスだと言うこと証で、`READE`という命令は、内部的には、次の実装がされているだけだという事がわかる。

1. 位置づけされたキーで `READ`（※ここで期待したレコードが存在しない場合、次のレコードロックされる）
2. キーとフィールド値を比較
3. 「2」の結果で、**EOF** か否かを判定する

```rpgle:READEの実態
C                   READ      ITEMR
C                   IF        NOT %EOF                 AND
C                             K@CODE     <> CODE
C                   >>>>> ＥＯＦだよ
C                   ENDIF

```

### 結論
明確に更新したいキーがある場合は、意図しないレコードを「ロックさせてしまったり、ロック待ち」しないためには、ランダムアクセスである `CHAIN`を使用しましょうという事。`SETLL`でキーの位置づけを判定する方法もあるが、クリティカルに考えると「`SETLL` 時点では存在したが `READE` したら無かった」があり得るので、やはり個人的には最適解ではないと思います。

```rpgle:SETLL 判定実装例
C                   Z-ADD     00125         K@CODE                          商品コード
C     ITEMKEY1      SETLL     ITEMR
C                   IF        NOT %EQUAL
C                   EVAL      MSG         = '[SETLL]-'
C                                         + ' 商品 125 は存在しません '
C     MSG           DSPLY
C                   ELSE
C*- // ここでクリティカルに考えると結局ＮＧ
C     ITEMKEY1      READE     ITEMR
C                   IF        NOT %EOF
C                   EVAL      MSG         = '[READE]-'
C                                         + ' 商品 125 は存在しません '
C     MSG           DSPLY
C                   ENDIF
C                   ENDIF

```