CL

170. QUSLRCDを使わないレコード名検索

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の進化のおかげである。
\