C言語で API: QDBRTVFD を使ってファイルノレコード名を検索する
サンプル・ソースを公開したが C言語ではわからない読者も多いと
予想して同じ処理をCLPで作成した。
CLPで API: QDBRTVFDを処理してみると C言語に比べて
驚くほど簡単になった。
それは CLPの機能が進化しているせいでもある。
_
[ TESTRCDCL: QUSLRCDを使わないレコード名検索 ]
ソースはこちらから
0001.00 PGM 0002.00 /*-------------------------------------------------------------------*/ 0003.00 /* TESTRCDCL : レコード名の検索 */ 0004.00 /* */ 0005.00 /* 2024/08/30 作成 */ 0006.00 /* API : QDBRTVFD を使って直接レコード名を検索します。 */ 0007.00 /* */ 0008.00 /*-------------------------------------------------------------------*/ 0009.00 DCL VAR(&FORMATBUF) TYPE(*CHAR) LEN(32727) 0010.00 DCL VAR(&FORMATLEN) TYPE(*CHAR) LEN(4) + 0011.00 VALUE(X'7FFF') /* =32767 */ 0012.00 DCL VAR(&FILSCOPE) TYPE(*CHAR) LEN(160) 0013.00 DCL VAR(&SIZE) TYPE(*DEC) LEN(4 0) VALUE(160) 0014.00 DCL VAR(&RECORD) TYPE(*CHAR) STG(*DEFINED) + 0015.00 LEN(10) DEFVAR(&FILSCOPE 69) 0016.00 DCL VAR(&RECNUM) TYPE(*CHAR) STG(*DEFINED) + 0017.00 LEN(2) DEFVAR(&FORMATBUF 15) /* BIN2 進数 */ 0018.00 DCL VAR(&POS) TYPE(*DEC) LEN(4 0) VALUE(512) 0019.00 DCL VAR(&N) TYPE(*DEC) LEN(4 0) 0020.00 DCL VAR(&FILFILLIB) TYPE(*CHAR) LEN(20) 0021.00 DCL VAR(&MSG) TYPE(*CHAR) LEN(132) 0022.00 DCL VAR(&STMMSG) TYPE(*CHAR) LEN(132) 0023.00 DCL VAR(&MSGID) TYPE(*CHAR) LEN(7) 0024.00 DCL VAR(&MSGF) TYPE(*CHAR) LEN(10) 0025.00 DCL VAR(&MSGFLIB) TYPE(*CHAR) LEN(10) 0026.00 DCL VAR(&MSGKEY) TYPE(*CHAR) LEN(4) 0027.00 DCL VAR(&MSGDTA) TYPE(*CHAR) LEN(132) 0028.00 DCL VAR(&ERRDTA) TYPE(*CHAR) LEN(132) 0029.00 DCL VAR(&TYPE) TYPE(*CHAR) LEN(1) 0030.00 DCL VAR(&TOPGMQ) TYPE(*CHAR) LEN(10) 0031.00 DCL VAR(&MSGTYPE) TYPE(*CHAR) LEN(10) + 0032.00 VALUE('*ESCAPE ') 0033.00 DCL VAR(&APIERR) TYPE(*CHAR) LEN(116) + 0034.00 VALUE(X'0000007400000000') /* 2 進数 */ 0035.00 DCL VAR(&ERR) TYPE(*CHAR) LEN(1) 0036.00 DCL VAR(&NULL4) TYPE(*CHAR) LEN(4) + 0037.00 VALUE(X'00000000') 0038.00 MONMSG MSGID(CPF9999) EXEC(GOTO CMDLBL(ERROR)) 0039.00 0040.00 /*( 環境の取得 )*/ 0041.00 RTVJOBA TYPE(&TYPE) 0042.00 IF COND(&TYPE *EQ '0') THEN(DO) /* バッチ */ 0043.00 CHGVAR VAR(&TOPGMQ) VALUE('*SYSOPR ') 0044.00 ENDDO /* バッチ */ 0045.00 ELSE CMD(DO) /* 対話式 */ 0046.00 CHGVAR VAR(&TOPGMQ) VALUE('*TOPGMQ ') 0047.00 ENDDO /* 対話式 */ 0048.00 0049.00 /*( API: QDBRTVFD の実行 )*/ 0050.00 CALL PGM(QDBRTVFD) PARM(&FORMATBUF &FORMATLEN + 0051.00 &FILFILLIB 'FILD0100' 'SEIKYU + 0052.00 QTRFIL ' '*FIRST ' '0' '*FILETYPE ' + 0053.00 '*EXT ' &APIERR) 0054.00 IF COND(%SST(&APIERR 5 4) *NE &NULL4) THEN(DO) 0055.00 SNDPGMMSG MSG('API: + 0056.00 QDBRTVFD の実行で次のエラーが発生しました。 + 0057.00 ') TOMSGQ(&TOPGMQ) MSGTYPE(*DIAG) 0058.00 GOTO APIERR 0059.00 ENDDO 0060.00 CHGVAR VAR(&N) VALUE(1) 0061.00 NXTREC: CHGVAR VAR(&FILSCOPE) VALUE(%SST(&FORMATBUF &POS + 0062.00 &SIZE)) 0063.00 SNDPGMMSG MSG('RECORD=' *CAT &RECORD) MSGTYPE(*DIAG) 0064.00 IF COND(&N < %BIN(&RECNUM)) THEN(DO) 0065.00 CHGVAR VAR(&N) VALUE(&N + 1) 0066.00 CHGVAR VAR(&POS) VALUE(&POS + &SIZE) 0067.00 GOTO NXTREC 0068.00 ENDDO 0069.00 RETURN 0070.00 0071.00 APIERR: 0072.00 CHGVAR VAR(&MSGID) VALUE(%SST(&APIERR 9 7)) 0073.00 CHGVAR VAR(&MSGDTA) VALUE(%SST(&APIERR 17 100)) 0074.00 CHGVAR VAR(&MSGF) VALUE('Q' *CAT %SST(&MSGID 1 + 0075.00 3) *CAT 'MSG') 0076.00 IF COND(&MSGF *EQ 'QCPEMSG') THEN(CHGVAR + 0077.00 VAR(&MSGF) VALUE('QCPFMSG')) 0078.00 CHGVAR VAR(&MSGFLIB) VALUE('QSYS ') 0079.00 GOTO SNDMSG 0080.00 0081.00 ERROR: RCVMSG MSGTYPE(*FIRST) RMV(*NO) KEYVAR(&MSGKEY) + 0082.00 MSGDTA(&MSGDTA) MSGID(&MSGID) MSGF(&MSGF) + 0083.00 SNDMSGFLIB(&MSGFLIB) 0084.00 IF COND(&MSGID *EQ 'CPF9999') THEN(DO) 0085.00 CHGVAR VAR(&ERRDTA) VALUE(&MSGDTA) 0086.00 RCVMSG MSGTYPE(*PRV) MSGKEY(&MSGKEY) RMV(*NO) + 0087.00 MSG(&MSG) MSGDTA(&MSGDTA) MSGID(&MSGID) + 0088.00 MSGF(&MSGF) MSGFLIB(&MSGFLIB) 0089.00 CHGVAR VAR(&STMMSG) VALUE(' プログラム ' *CAT + 0090.00 %SST(&ERRDTA 8 10) *TCAT + 0091.00 ' のステートメント ' *CAT %SST(&ERRDTA + 0092.00 24 8) *CAT ' で次のエラーが発生しました。 ') 0093.00 SNDPGMMSG MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA(&STMMSG) + 0094.00 TOMSGQ(&TOPGMQ) MSGTYPE(*DIAG) 0095.00 ENDDO 0096.00 SNDMSG: IF COND(&MSGID *EQ ' ') THEN(DO) 0097.00 SNDPGMMSG MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA(&MSG) + 0098.00 TOMSGQ(&TOPGMQ) MSGTYPE(&MSGTYPE) 0099.00 MONMSG MSGID(CPF2400) EXEC(RETURN) 0100.00 ENDDO 0101.00 ELSE CMD(DO) 0102.00 SNDPGMMSG MSGID(&MSGID) MSGF(&MSGFLIB/&MSGF) + 0103.00 MSGDTA(&MSGDTA) TOMSGQ(&TOPGMQ) + 0104.00 MSGTYPE(&MSGTYPE) 0105.00 MONMSG MSGID(CPF2400) EXEC(RETURN) 0106.00 ENDDO 0107.00 ENDPGM
[解説]
最初に変数の定義の解説から
0009.00 DCL VAR(&FORMATBUF) TYPE(*CHAR) LEN(32727) 0010.00 DCL VAR(&FORMATLEN) TYPE(*CHAR) LEN(4) + 0011.00 VALUE(X'7FFF') /* =32767 */
は ファイル仕様の検索API : QDBRTVFD に対して定義可能な最大のバッファー: &FORMATBUF を
定義している。
IBM iの使用可能な最大サイズは 32767バイトでありこれはヒーブ・サイズと呼ばれる。
&FORMATLEN の VALUE(X'7FFF') は 2進数の32767 を示している。
次に
0012.00 DCL VAR(&FILSCOPE) TYPE(*CHAR) LEN(160) 0013.00 DCL VAR(&SIZE) TYPE(*DEC) LEN(4 0) VALUE(160)
がファイル・スコープ配列と呼ばれるフォーマット・バッファーの中に存在している
メモリの配列でこれは160ハイトの長さであることを C言語のデバッグで
あらかじめ調べた。
さらに
0014.00 DCL VAR(&RECORD) TYPE(*CHAR) STG(*DEFINED) + 0015.00 LEN(10) DEFVAR(&FILSCOPE 69)
が求めたいレコード名であるがこれはファイル・スコープ配列のオフセット 69の位置から
10バイトで登録されていると QDBRTVFD の仕様書から判明している。
次に
0016.00 DCL VAR(&RECNUM) TYPE(*CHAR) STG(*DEFINED) + 0017.00 LEN(2) DEFVAR(&FORMATBUF 15) /* BIN2 進数 */
はファイル・スコープ配列の配列数でありフォーマット・バッファーの中の
15桁目から 2バイトの2進数として登録されていることが API : QDBRTVFD の仕様書によって
判明している。
ファイル・スコープ配列の個数はレコード名の個数であるのでLOOPして取り出す必要がある。
物理ファイルの場合はレコード数は 1個しか膨れないが論理ファイルの場合は
複数の物理ファイルで構成されている場合は複数個のレコードが存在することになる。
_
レコード名の検索は多くの場合は物理ファイルを検索することになる場合が多いが
そのために API: QUSLRCD を使うのであれば
(1) QUSCRTUSによってユーザー・スペースを作成
(2) QUSLRCDによってレコード情報をユーザー・スペースに出力
(3) QUSRTVUS によってユーザー・スペースを検索
と3回もの手順を踏まなければならない。
ソフトウェア製品を作る場合にこの2回も手順を踏むというのはパフォーマンスの低下を
招いてしまうことになりユーザー・スペースが QTEMPなどに残ってしまうのは
あまり格好の良いものではない。
本題に戻って
0049.00 /*( API: QDBRTVFD の実行 )*/ 0050.00 CALL PGM(QDBRTVFD) PARM(&FORMATBUF &FORMATLEN + 0051.00 &FILFILLIB 'FILD0100' 'SEIKYU + 0052.00 QTRFIL ' '*FIRST ' '0' '*FILETYPE ' + 0053.00 '*EXT ' &APIERR)
によって AI: QDBRTVFD はたった1回だけ無実行するだけである。
0060.00 CHGVAR VAR(&N) VALUE(1) 0061.00 NXTREC: CHGVAR VAR(&FILSCOPE) VALUE(%SST(&FORMATBUF &POS + 0062.00 &SIZE)) 0063.00 SNDPGMMSG MSG('RECORD=' *CAT &RECORD) MSGTYPE(*DIAG) 0064.00 IF COND(&N < %BIN(&RECNUM)) THEN(DO) 0065.00 CHGVAR VAR(&N) VALUE(&N + 1) 0066.00 CHGVAR VAR(&POS) VALUE(&POS + &SIZE) 0067.00 GOTO NXTREC 0068.00 ENDDO
が ファイル・スコープ配列をLOOPして取り出す部分でレコード名はファイル・スコープの
位置が
0014.00 DCL VAR(&RECORD) TYPE(*CHAR) STG(*DEFINED) + 0015.00 LEN(10) DEFVAR(&FILSCOPE 69)
によって決められていたのでそれを出力しているだけである。
実行の結果は
> call test.com/testrcdcl RECORD= SEIKYUR1 RECORD= SEIKYUR2
[解説]
このようにCLPでは短い演算にできるのは
*DEFINED による変数の相対位置の指定ができるようになったからで
IBM iの進化のおかげである。
\