CL

101. CLP でファイルをキーで検索をするには ?

OVRDBF を F4キーでいつもプロンプト表示していると
「ファイルの開始桁」キー・ワードがあるので、これを指定すれば
キーでレコードを CHAIN して検索することができるのだろうと
思ってみても実際に試してみた人は少ないのではないだろうか ?

また実際に CLP でファイルをキー指定で検索しても
エラーに悩まされて諦めた人もいるかも知れない。
ここでは実際に CLP で DCLF で指定されたキーつきのファイルを
キー・フィールドを指定して検索する方法とそのテクニックを紹介する。
さらに IBM が推薦している方法との比較も考察してみよう。
RPGプログラムではなく CLP でもファイルに CHAIN できるのなら
不要な RPG を書かなくて済むし何よりスマートである。
是非、このテクニックは覚えて欲しい。

【 サンプルCLP: TESTKEYR 】
0001.00              PGM
0002.00 /*-------------------------------------------------------------------*/
0003.00 /*   TESTKEYR  :  KEY レコードの検索                                 */
0004.00 /*                                                                   */
0005.00 /*   2017/05/02  作成                                                */
0006.00 /*-------------------------------------------------------------------*/
0007.00              DCL        VAR(&MSG) TYPE(*CHAR) LEN(132)
0008.00              DCL        VAR(&MSGID) TYPE(*CHAR) LEN(7)
0009.00              DCL        VAR(&MSGF) TYPE(*CHAR) LEN(10)
0010.00              DCL        VAR(&MSGFLIB) TYPE(*CHAR) LEN(10)
0011.00              DCL        VAR(&MSGDTA) TYPE(*CHAR) LEN(132)
0012.00              DCL        VAR(&TYPE) TYPE(*CHAR) LEN(1)
0013.00              DCL        VAR(&TOPGMQ) TYPE(*CHAR) LEN(10)
0014.00              DCL        VAR(&MSGTYPE) TYPE(*CHAR) LEN(10) +
0015.00                           VALUE('*ESCAPE   ')
0016.00              DCL        VAR(&APIERR) TYPE(*CHAR) LEN(116) +
0017.00                           VALUE(X'000074') /* 2 進数  */
0018.00              DCL        VAR(&NULL4) TYPE(*CHAR) LEN(4) +
0019.00                           VALUE(X'00000000')
0020.00              DCLF       FILE(QTRFIL/SHOHIN) RCDFMT(*ALL)
0021.00              MONMSG     MSGID(CPF0000) EXEC(GOTO CMDLBL(ERROR))
0022.00
0023.00 /*( 環境の取得 )*/
0024.00              RTVJOBA    TYPE(&TYPE)
0025.00              IF         COND(&TYPE *EQ '0') THEN(DO) /*  バッチ  */
0026.00              CHGVAR     VAR(&TOPGMQ) VALUE('*SYSOPR   ')
0027.00              ENDDO      /*  バッチ  */
0028.00              ELSE       CMD(DO) /*  対話式  */
0029.00              CHGVAR     VAR(&TOPGMQ) VALUE('*TOPGMQ   ')
0030.00              ENDDO      /*  対話式  */
0031.00
0032.00 /*( キーのセット )*/
0033.00              CHGVAR     VAR(&SHCODE) VALUE('NV-CF1    ')
0034.00              OVRDBF     FILE(SHOHIN) TOFILE(QTRFIL/SHOHIN) +
0035.00                           POSITION(*KEYAE 1 SHOHINR &SHCODE) +
0036.00                           SECURE(*YES) OVRSCOPE(*JOB)
0037.00              RCVF       RCDFMT(SHOHINR)
0038.00              MONMSG     MSGID(CPF0864) EXEC(GOTO CMDLBL(REDEND))
0039.00              SNDPGMMSG  MSG(' 見つかったキーは SHCODE=' *CAT +
0040.00                           &SHCODE *CAT ' です。 ') MSGTYPE(*DIAG)
0041.00              DLTOVR     FILE(SHOHIN) LVL(*JOB)
0042.00              RETURN
0043.00
0044.00  REDEND:
0045.00              DLTOVR     FILE(SHOHIN) LVL(*JOB)
0046.00              RETURN
0047.00
0048.00  ERROR:      RCVMSG     MSGTYPE(*LAST) RMV(*NO) MSG(&MSG) +
0049.00                           MSGDTA(&MSGDTA) MSGID(&MSGID) MSGF(&MSGF) +
0050.00                           MSGFLIB(&MSGFLIB)
0051.00              DLTOVR     FILE(SHOHIN) LVL(*JOB)
0052.00  SNDMSG:     IF         COND(&MSGID *EQ ' ') THEN(DO)
0053.00              SNDPGMMSG  MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA(&MSG) +
0054.00                           TOMSGQ(&TOPGMQ) MSGTYPE(&MSGTYPE)
0055.00              ENDDO
0056.00              ELSE       CMD(DO)
0057.00              SNDPGMMSG  MSGID(&MSGID) MSGF(&MSGFLIB/&MSGF) +
0058.00                           MSGDTA(&MSGDTA) TOMSGQ(&TOPGMQ) +
0059.00                           MSGTYPE(&MSGTYPE)
0060.00              ENDDO
0061.00              ENDPGM
【解説】

この CLP はあらゆる環境や条件下でも適切な動作をするように考慮されている
基本パターンから構成されているので多少、冗長に見えるかも知れないが
中身はシンプルである。
まず

     DCLF       FILE(QTRFIL/SHOHIN) RCDFMT(*ALL)

によってファイルを定義しているが、ここではキーつきでオープンするとか
ファイル識別子を指定する必要はない。
次にキーのセットとして

     CHGVAR     VAR(&SHCODE) VALUE('NV-CF1    ')
     OVRDBF     FILE(SHOHIN) TOFILE(QTRFIL/SHOHIN) +
                         POSITION(*KEYAE 1 SHOHINR &SHCODE) +
                         SECURE(*YES) OVRSCOPE(*JOB)

を行っている。
DCLF でファイル定義して暗黙的にファイルをオープンしているはずなので
このファイル SHOHIN に対する OVRDBF が効くのか? と心配する人もいるかも知れないが
ファイルがオープンされるのは実は

     RCVF       RCDFMT(SHOHINR)

の時点なので OVRDBF は有効に効果を発揮する。
ところで

     OVRDBF     FILE(SHOHIN) TOFILE(QTRFIL/SHOHIN) +
                           POSITION(*KEYAE 1 SHOHINR &SHCODE) +
                           SECURE(*YES) OVRSCOPE(*JOB)

では POSITION*KEY ではなく *KEYAE を指定していることに注意して欲しい。
これはファイルの開始桁(POSITION) パラメータに問題があるからである。
実は POSITION パラメータのキー値の部分は
可変の長さ (VARY(*YES)) として定義されているので

     CHGVAR     VAR(&SHCODE) VALUE('NV-CF1    ')

のようにブランクを後続に含む値を渡すと後続のブランク文字列は切り捨てられて
この場合だと 「NV-CF1」 の 6桁の値として POSITION キー・ワードは解釈する。
さらにこの場合、キー: SHCODE は 10桁であるので
6桁の 「NV-CF1」 の指定は無条件にエラーになる。
しかも追いかけるように IBM が示すエラー・メッセージは

     CPF4137: メンバー SHOHIN の位置指定オプションが正しくない。

とのエラー・メッセージが出力される。
このメッセージから推測されるのは

     POSITION(*KEYAE 1 SHOHINR &SHCODE)  


1 が正しくないのかと思ってしまう。

     POSITION(*KEYAE 1 SHOHINR &SHCODE)  

ではなく

     POSITION(*KEYAE 1 SHOHINR 'NV-CF1     ')

と指定すれば正しく動作するのだが後続ブランクのあるキーを正しく指定することができない。
IBM は恐らく多くのユーザーからこの問題について質問と苦情を受けたのだろう。
IBM が行った回答は文字列を作って QCMDEXC で実行する、というものである。

【 IBM Knowledge Center : コマンド・パラメーターの末尾ブランク 】

https://www.ibm.com/support/knowledgecenter/ja/ssw_ibm_i_73/rbam6/tralb.htm

これは誠に不細工な方法であり他人に見られたくない方法である。
弊社の提案はキーが必ず存在するのであれば *KEYAE を使う方法である。
キーが存在しない場合も含めるなら RCVF の後で読んだレコードのキーを比較すれば良い。
どちらが見やすくスマートな方法に見えるだろうか?