We know we can process an entire set of GDG datasets by allocating the GDG base name in the JCL So, for example, if we have a GDG with three generations,
GDG Base: XXXXXXXX.TEST.GDG Generations: XXXXXXXX.TEST.GDG.G0001V00 XXXXXXXX.TEST.GDG.G0002V00 XXXXXXXX.TEST.GDG.G0003V00
We can refer to the most current generation with XXXXXXXX.TEST.GDG(0)
or XXXXXXXX.TEST.GDG.G0003V00
, the second with XXXXXXXX.TEST.GDG(-1)
, the third with XXXXXXXX.TEST.GDG(-2)
, etc. So we could put all three in the JCL, concatenated together:
//FILE1 DD DISP=SHR,DSN=XXXXXXXX.TEST.GDG(-2) // DD DISP=SHR,DSN=XXXXXXXX.TEST.GDG(-1) // DD DISP=SHR,DSN=XXXXXXXX.TEST.GDG(0)
and in this way, process the oldest first and the newest last. But, often we don’t know how many generations there are to process from run to run and we certainly don’t want to recode the JCL every time, so instead we use:
//FILE1 DD DISP=SHR,DSN=XXXXXXXX.TEST.GDG
that is, we leave off the generation qualifier. This gives us all of the extant generations concatenated together automatically, but it gives them to us in reverse order with the (0) most current generation first and the (-2) oldest last.
Can we make use of our previous IGGCSI00 example to get the dataset names for all of the generations and process them in any order we’d like? Yes. Yes, we can.
In addition to using the Catalog Search Interface (CSI) routine, we will use COBOL’s dynamic allocation capability and to facilitate the dynamic allocation, we’ll use the POSIX setenv() function although there are other ways to set environment variables, including the Language Environment CEEENV function, which does not require a POSIX environment.
The demonstration program will take the GDG base name from a PARM, lookup the generation datasets associated with the base and put the dataset names into an array. Then the program will open, read a record and close each dataset in the array in turn.
Some notes:
- POSIX functions are called statically, so the program must be compiled with the NODYNAM option. I kept the IGGCSI00 call as dynamic, however.
- POSIX functions in general can be long, mixed-case names, so compile with PGMNAME(LONGMIXED).
- The Language Environment option POSIX(ON) must be on, so include that in the PARM or use a CEEUOPT. Or perhaps POSIX(ON) could be set as the default in your shop.
- The load module must be in a PDSE (or hfs). In my testing, I use the Compile-Link-Go PROC IGYWCLG and override the SYSLMOD with DSNTYPE=LIBRARY.
- The dynamic allocation is accomplished by setting an environment variable the name of which matches that of an ASSIGN clause (and not a DD name) and has the value “DSN(xxxx) SHR” where xxxx is the dataset name. This has to be done before the OPEN. We use setenv() for this.
So, PROCESS statements at the front of the source:
PROCESS LIST,LIB,TEST(NOHOOK,SEP),OPT,MAP,RENT PROCESS NODYNAM,PGMNAME(LONGMIXED)
and JCL like:
// JCLLIB ORDER=(IGY.SIGYPROC) //CEEUOPT EXEC ASMAC //C.SYSLIB DD DSN=CEE.SCEEMAC //C.SYSIN DD * CEEUOPT CSECT CEEUOPT AMODE ANY CEEUOPT RMODE ANY CEEXOPT POSIX=ON END CEEUOPT //C.SYSLIN DD DSN=&&LOADSET //COBGDG EXEC IGYWCLG,LNGPRFX='IGY',PARM.LKED=MAP, // PARM.GO='XXXXXXXX.TEST.GDG' //COBOL.SYSIN DD DISP=SHR,DSN=XXXXXXXX.SRC.COB(COBGDG) //COBOL.SYSLIB DD DISP=SHR,DSN=XXXXXXXX.SRC.COB //COBOL.SYSDEBUG DD DISP=SHR,DSN=XXXXXXXX.SYSDEBUG(COBGDG) //LKED.SYSLMOD DD DSNTYPE=LIBRARY //LKED.SYSIN DD * ENTRY COBGDG //GO.SYSOUT DD SYSOUT=*
Here’s the source: cobgdg.cob
Look for the copybooks in the earlier post.
For a non-POSIX version, which does not require the use of a PDSE and can be compiled DYNAM (a standard in many shops), use CEEENV,
77 ws-ceeenv pic x(8) value 'CEEENV '. 77 ws-ceeenv-funccode pic 9(9) binary. 77 ws-ceeenv-namelen pic 9(9) binary. 77 ws-ceeenv-name pic x(8). 77 ws-ceeenv-valuelen pic 9(9) binary. 77 ws-ceeenv-valptr pointer. 01 ws-ceeenv-fc. 02 CONDITION-TOKEN-VALUE. COPY CEEIGZCT. 03 CASE-1-CONDITION-ID. 04 SEVERITY PIC S9(4) BINARY. 04 MSG-NO PIC S9(4) BINARY. 03 CASE-SEV-CTL PIC X. 03 FACILITY-ID PIC XXX. 02 I-S-INFO PIC S9(9) BINARY.
…
string 'DSN(' delimited by size ws-ggt-name (ws-ggt-index) delimited by space ') SHR' delimited by size into ws-environment-string move 5 to ws-ceeenv-funccode move 6 to ws-ceeenv-namelen move 'GDGGEN' to ws-ceeenv-name move length of ws-environment-string to ws-ceeenv-valuelen set ws-ceeenv-valptr to address of ws-environment-string call ws-ceeenv using ws-ceeenv-funccode ws-ceeenv-namelen ws-ceeenv-name ws-ceeenv-valuelen ws-ceeenv-valptr ws-ceeenv-fc if severity not = 0 then display 'ceeenv return-code ' severity ' ' msg-no move 16 to return-code goback end-if
and compile and test with,
// JCLLIB ORDER=(IGY.SIGYPROC) //COBGDG2 EXEC IGYWCLG,LNGPRFX='IGY',PARM.LKED=MAP, // PARM.GO='XXXXXXXX.TEST.GDG' //COBOL.SYSIN DD DISP=SHR,DSN=XXXXXXXX.SRC.COBOL(COBGDG2) //COBOL.SYSLIB DD DISP=SHR,DSN=XXXXXXXX.SRC.COBOL // DD DISP=SHR,DSN=CEE.SCEESAMP //LKED.SYSIN DD * ENTRY COBGDG2 //GO.SYSOUT DD SYSOUT=*
Here is the source for this: cobgdg2.cob
Now, if we could process these datasets in parallel we might get some performance gains. Hmm. Perhaps a later post.