QSAMでPDSメンバーにアクセスする
順次データセットは、QSAMを利用すれば論理レコード単位で簡単にアクセスできます。区分データセット用にはBPAMが提供されています。しかしながら、BPAMの場合はブロック(物理レコード)単位でないとアクセスできず、ブロックを論理レコードにバラしたり(あるいは論理レコードをまとめてブロックにまとめたり)バッファリングの制御なども自分でやらねばなりません。バッファリング制御はオプション処理的なものなので、やらなくてもアクセス自体はできます。しかし、ブロッキングとデブロッキングの処理は避けることができません。決して難しい処理ではありませんが、面倒であることは確かで、やらずに済むならそれに越したことはありません。
JCLでは、DSN=dsname(member)のようにデータセット名の後ろに()でメンバー名まで指定すれば、区分データセットのメンバーをQSAMでアクセスすることができます。これは、区分データセットのメンバー自体が、順次データセットと同じ構造を持っているからです。予めアクセスするメンバーが決まっているならJCL側の定義と組み合わせてQSAMでアクセスすることもできます。例えば、パラメーター・メンバーなどはその代表的なものの1つです。ところが、1つや2つのメンバーではなく、数十あるいは全部のメンバーに順番にアクセスしたい、となった場合はどうでしょうか?JCL側ではメンバー名まで定義できないことになります。そのような場合は、アクセスするメンバーを、JCLで定義する代わりに動的割り振り(DYNALLOC)でアロケーションしてQSAMでアクセス方法もあります。しかし、DYNALLOCは比較的オーバーヘッドが大きい処理なので、アクセスするメンバー数が多くなるとパフォーマンスの問題が出てきます。ISPFの起動プロシージャー内で、沢山のデータセットをALLOCコマンドで割り振っているとISPFが起動されるまでに時間が掛かることと同じです。
ここでは、メンバーをQSAMでアクセスでき、アロケーションのオーバーヘッドも掛からない方法として、JFCBを使用した区分データセットのオープン方法を紹介します。
//STEP1 EXEC PGM=PDSPRINT //SYSPRINT DD SYSOUT=* //SYSUT1 DD DISP=SHR,DSN=USR1.JCLLIB //
このようなJCLで、SYSUT1 DDステートメントに定義された区分データセットの全メンバーを、SYSPRINT DDステートメントに定義されたデータセットへプリントするプログラムの例です。
DDステートメント定義情報の変更を行ってからデータセットをオープンする
JFCBを使用したデータセットのオープン処理を行うため、最初にJFCBを読み込みます。JFCBは、JCLのDDステートメントの定義情報だと考えればいいです。読み込んだJFCBには、JCLのDSNパラメーターで指定されたデータセット名は格納されていますが、メンバー名は当然ながら入っていません。メンバー名指定なしの区分データセットをQSAMでオープンすると、ディレクトリー部の先頭に位置付いてしまい、レコード形式などが不一致となるのでS013でABENDします。DSNパラメーターにメンバー名が入っていれば、オープンした時にそのメンバーの先頭に位置付けられてQSAMでアクセスができます。
従って、読み込んだJFCBのメンバー名フィールドにアクセスしたいメンバー名をセットすれば、JCL DDステートメントのDSNパラメーターに(メンバー名)まで指定したことと同じ状況を作り出せます。オープン前にJFCBを読み込み、その内容(JCL DDステートメント情報)を変更してからオープンすることもできます。
USING INFMJFCB,JFCBAREA ADDRESS TO JFCB READ AREA RDJFCB UT1DCB READ SYSUT1 DD STMT JFCB OI JFCBIND1,JFCPDS INDICATES DATASET IS PDS MEMBER MVC JFCBELNM,MEMBER1 SET NEXT MEMBER NAME BAS RA,MEMPROC MEMBER PROCESSING FOR MEMBER1 MVC JFCBELNM,MEMBER2 SET NEXT MEMBER NAME BAS RA,MEMPROC MEMBER PROCESSING FOR MEMBER2 : : MEMPROC DS 0H OPEN (UT1DCB,INPUT),TYPE=J OPEN DATASET WITH MODIFIED JFCB+ (OPEN THE SPECIFIED PDS MEMBER) : : UT1DCB DCB DDNAME=SYSUT1, QSAM DCB + DSORG=PS,MACRF=GL, + EXLST=UT1EXLST,EODAD=UT1EOD UT1EXLST DS 0F DC XL1'87',AL3(JFCBAREA) JFCBAREA DS 0F DC (JFCBLGTH)X'00' JFCB READ AREA : : DJFCB DSECT , IEFJFCBN LIST=YES JFCB DSECT :
ポイントになる箇所は太字で示してあります。メンバー名はJFCBELEMフィールドに設定します。DDステートメントにDSN=dsname(member)と定義されると、JFCBのJFCBDSNMにdsnameが、JFCBELEMにmemberが格納されます。従って、オープン前にJFCBELEMにメンバー名を設定すれば、DDステートメントでメンバー名まで指定したことと同じになります。なお、オープン時に区分データセットのメンバー部に位置付けさせるためには、メンバー名の設定に加えて、JFCBIND1フィールドのJFCPDSフラグをオン(1)にする必要があります。JFCPDSフラグは1度オンにすれば、MVS(DFSMSdfp)側で変更されることはありません。
JFCBの準備ができたら、データセットをQSAMでオープンします。RDJFCBマクロで使用したDCBを指定してOPENマクロを発行します。この時、OPENマクロにTYPE=Jパラメーターを追加します。TYPE=Jは、JFCBがユーザー側で提供されたことを示します。TYPE=Jパラメーターを忘れると変更後のJFCBは反映されません。メンバーの読み込みならGETマクロ、メンバーの書き込みならPUTマクロを使用します。QSAMなので論理レコード単位でのアクセスです。1つのメンバーの処理を終えたら、データセット(メンバー)をクローズします。別のメンバーへの処理を行うのであれば、JFCBELEMに次のメンバー名を設定してオープンから繰り返します。
なお、メンバー内容を書き込みする場合、クローズ時にメンバー・ディレクトリーのユーザー・データ部分は消えてしまいます。ISPFエディターで保管したメンバーを、アプリケーション・プログラムのQSAMアクセスで書き直すような場合、メンバーのISPF統計データは消えてしまうことは知っておく必要があります。必要であれば、BLDLマクロなどでメンバー・ディレクトリーの情報を読み取っておき、後でBPAMでオープンし直してSTOWマクロで必要なユーザー・データを書き込む、などの処理を追加します。
※JFCBの読み込み方とRDJFCBマクロの使い方は、「DD文定義情報を得る(DEVTYPEとRDJFCB)」に解説してあります。
サンプル・プログラム(PDSPRINT)ソース・コード
PDS内メンバーのQSAMアクセスをセットアップした後、PDSデータセットのディレクトリー部をやはりQSAMでオープンします。
GETマクロでPDSディレクトリーブロックを1ブロック読み込み、その中に登録されているメンバーエントリーを取り出します。メンバー名をJFCBにセットしたら、PDSメンバーをオープンしてGETマクロで1レコード読み込み出力側のデータセットに書き出します。メンバーがEODになるまで処理を繰り返し、PDSメンバーをクローズします。
読み込み済みディレクトリーブロック内の次のメンバーエントリーに位置付けて、メンバー名をJFCBにセットする処理から繰り返します。ディレクトリーブロック内の全てのメンバーを処理し終えたら、次のディレクトリーブロックの読み込みから繰り返します。次に処理すべきメンバーエントリーのメンバー名がxFFFFFFFFFFFFFFFFであれば(これ以上メンバーがないことを示す)、PDSデータセットのディレクトリー部と出力側のデータセットをクローズしてプログラム処理を終了します。
※PDSディレクトリーの読み込み方は、「アセンブラーで区分データセットのディレクトリー部を読み込む」に解説してあります。
----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- MAINENTR CSECT DEFINE CONTROL SECTION USING *,12 DEFINE BASE REGISTER SAVE (14,12),, SAVE CALLER REGISTERS + 'MAINENTR &SYSDATE &SYSTIME' LR 12,15 GR12 --> OUR 1ST BASE ADDRESS LR 15,13 SAVE CALLER SAVEAREA CNOP 0,4 INSURE FULL WORD BOUNDARY BAS 13,*+4+72 AROUND OUR SAVEAREA DC 18F'-1' OUR GPR SAVEAREA ST 15,4(,13) SAVE CALLER SAVEAREA POINTER ST 13,8(,15) FORWARD CHAIN FOR LINK TRACE B MAINPROC DO MAINLINE PROCESSING * *----------------------------------* * * EXIT PROCESSING * * *----------------------------------* EXIT8 DS 0H LA 15,8 SET CC=8 B EXITPROC EXIT0 DS 0H SLR 15,15 SET CC=0 EXITPROC DS 0H L 13,4(,13) RESTORE CALLER SAVEAREA ST 15,16(,13) PASS RETURN CODE TO CALLER RETURN (14,12),T RESTORE CALLER REGISTERS + AND RETURN TO CALLER EJECT , *********************************************************************** * SAMPLE CODE FOR PRINTING PDS/PDSE ALL MEMBERS * * ===================================================== * * READ PDS/PDSE MEMBER BY QSAM. * *********************************************************************** MAINPROC DS 0H * *----------------------------------* * * OPEN OUTPUT DATASET * * *----------------------------------* OPEN (PRTDCB,OUTPUT) OPEN THE PRINT DATASET LTR R15,R15 SUCCESSFUL ? BNZ EXIT8 NO, MAY BE DD STMT NOT DEFINED * *----------------------------------* * * SETUP FOR PDS QSAM ACCESSING * * *----------------------------------* USING INFMJFCB,JFCBAREA ADDRESS TO JFCB READ AREA RDJFCB UT1DCB READ SYSUT1 DD STMT JFCB LTR R15,R15 SUCCESSFUL ? BNZ EXIT8 NO, MAY BE DD STMT NOT DEFINED OI JFCBIND1,JFCPDS INDICATES DATASET IS PDS MEMBER * *----------------------------------* * * OPEN PDS DIRECTORY BY QSAM * * *----------------------------------* OPEN (DIRDCB,INPUT) OPEN THE PDS DIRECTORY READDIR DS 0H GET DIRDCB READ NEXT DIR BLOCK RECORD LH R7,0(,R1) GR7 <--- remaining length ahi r7,-2 adjust it(subtract count fld ln la r6,2(,r1) gr6 <--- dir block record using pds2,r6 address it * *----------------------------------* * * print next pds member * * *----------------------------------* memloop ds 0h clc pds2name,=8X'FF' end of directory ? be closdir yes, processing mvc jfcbelnm,pds2name set name bas r10,prtmembr record * locate to entry *----------------------------------* ni pds2indc,pds2lusr drop other bits slr r15,r15 clear workreg ic r15,pds2indc load user data length indicator sll r15,1 get r6,pds2usrd(r15) member r7,r15 update r7,-(pds2usrd-pds2) it bp memloop loop for b readdir block r6 forget block closdir close (dirdcb) the directory * output dataset *----------------------------------* doclose (prtdcb) used dataset exit0 all processing done eject , *********************************************************************** , *********************************************************************** * internal sub routines * *********************************************************************** prtmembr memname,jfcbelnm put prtdcb,title1 title-1 line prtdcb,title2 title-2 line open (ut1dcb,input),type =J with modified jfcb+ (open specified member) prtloop ut1dcb read lr r0,r1 record address prtdcb,(0) it sysprint prtloop until eod ut1eod (ut1dcb) input 0(,r10) return mainline area * *********************************************************************** prtdcb dcb ddname =SYSPRINT, qsam out + dsorg =PS,MACRF=PM, recfm =FB,LRECL=80DIRDCB>