zOs/REXX/EXDB2LOG
/* REXX
!!____________________________________________________________________
!!
!! EXDB2LOG
!! --------
!! read mastlog output and insert the messages into tadm6* tables
!!
!! PARMS EXDB2LOG <PARM1>
!! PARM1 = DB2 SUBSYSTEM
!!
!! LOCATION DSN.DB2.EXEC ab 4.0
!! TSO.rz?.P0.USER.EXEC bis 3.1
!!
!! HISTORY:
!! 19.10.2015 V4.3 srchLi for deadlock/timeout search, cleanup
!! put '' in group without err if mbr unknown
!! 7.10.2015 V4.2 added support for jes2
!! 2.10.2015 V4.1 for timeout also use DSNT500I and store
!! these even without deadlock/timeout
!! 20.10.2014 V4.0 logE2 => logEx
!! 06.10.2014 V4.0 direkt aus Beta/eJes Extract DSNs lesen
!! member/Datum aus IAT6140 usw.
!! keine doppelte Ausgabe von Beta/eJes Logs
!! 09.04.2014 V3.1 Ergebnis zusätzlich ins DSN
!! 24.09.2012 V3.0 rewrite masterlog
!! 18.04.2012 V2.2 rz8 und rzz integriert
!! 17.04.2012 V2.1 truncate collids longer 18
!! 28.03.2008 V2.0 ABNORMAL EOT (G.KERN,A914227)
!! 27.03.2008 V1.1 UNCOMMITED UOW (G.KERN,A914227)
!! 27.03.2008 V1.2 CHECKPOINTS (G.KERN,A914227)
!! 27.03.2008 V1.3 LOCK ESCALATION (G.KERN,A914227)
!! 30.01.2008 V1.0 GRUNDVERSION (G.KERN,A914227)
!!
!!_____________________________________________________________________
*/
call errReset 'h'
PARSE UPPER arg SSID rest
if 1 then do /* prod settings */
m.debug = 0
m.tstRZ4 = 0
m.writeAblfPre = 'DSN.ABLF.LOGEX.'sysvar(sysnode)
end
else if 0 then do /* test settings */
m.debug = 0
m.tstRZ4 = 1
m.writeAblfPre = 'A540769.LOGEX.ABLF'
end
else if 0 then do /* old settings */
m.debug = 1
m.insertLocal = 1
m.writeABLF = 1
end
say "exDb2Log("ssid rest") version v4.3 vom 19.10.15"
if ssid == 1 then
return doFun1()
else if ssid == 2 then
return doFun2(rest)
else if ssid == 3 then
return doFun3()
else if 0 then
return workOld(ssid)
else do
o.1 = date('s') time() sysVar(sysNode) mvsvar('symdef', 'jobname') ,
'exDb2Log workOld deActivated'
call writeDsn 'mod dsn.ablf.logDeImp ::f', o., 1
say 'exDb2Log workOld deActivated'
return 0
end
endMainCode
/*--- write timestamp to dd parmNew ----------------------------------*/
doFun1: procedure expose m.
parse arg betaExt .
call ini 1
call readDD parmOld, i., '*'
call tsoClose parmOld
ix = i.0
say 'parmOld' ix strip(i.ix, 't')
w1 = word(i.ix, 1)
if i.0 = 0 then
old = '2014-01-01-00.00.00'
else if translate(w1, '999999999', '012345678') ,
\== '9999-99-99-99.99.99' then
call err 'bad to tst in parmOld 1:' i.ix
else if substr(w1, 15, 2) >= 15 then
old = overlay(right(substr(w1, 15, 2)-15, 2,0), w1, 15)
else if substr(w1, 12, 2) >= 1 then
old = overlay(right(substr(w1, 12, 2)-1, 2,0) ,
|| '.'right(substr(w1, 15, 2)+45, 2,0), w1, 12)
else
old = left(w1, 11)'00.00.00'
new = translate('1234-56-78', date('s'), '12345678') ,
|| '-'translate(time(), '.', ':')
if new <= old then
call err 'new' new '<=' old 'old'
o.1 = new old
call writeDD parmNew, o., 1
call tsoClose parmNew
say 'parmNew' strip(o.1, 't')
if substr(old, 6, 2) > 2 then
betaS = overlay(right(substr(old, 6, 2)-2, 2,0), old, 6)
else
betaS = overlay(left(old, 4)-1,
|| '-'right(substr(old, 6, 2)+10, 2,0), old, 1)
if substr(betaS, 9, 2) > 28 then
betaS = overlay(28, betaS, 9)
betaS = translate('78.56.1234', left(betaS, 10), '1234-56-78')
say 'betaStart' betaS 'betaExt' betaExt
o.1 = 'REPORT'
o.2 = ' SDATE('betaS')'
o.3 = ' STIME(00:00:00)'
o.4 = ' PDATE(TODAY)'
o.5 = ' PTIME(23:59:59)'
o.6 = ' JOBNAME(D***MSTR)'
call writeDD betaRePa, o., 6
call tsoClose betaRePa
a.1 = ' 00:00:00 ' /* idiotisches Rexx stuerzt ab auf leerem
konatiniertem Dataset | */
call writeDD 'betaExt', a., 1
call tsoClose 'betaExt'
call writeDD 'eJesExt', a., 1
call tsoClose 'eJesExt'
say 'written idiotic dummy row on betaExt and eJesExt'
call eJesJobExtDD 'D%%%MSTR', 'JESMSGLG'
return 0
endProcedure doFun1
/*--- select jobs from betaRep ---------------------------------------*/
doFun2: procedure expose m.
parse arg betaExt .
call ini 1
say 'fun2' betaExt
call parmNewRead
new = m.parm_new
old = m.parm_old
call readDD betaRep, b., '*'
call tsoClose betaRep
say 'dd betaRep' b.0 'lines'
do bx=1 to b.0
if substr(b.bx, 2, 8) == 'BETA 92 ' then do
bx = bx + 1
if substr(b.bx, 2, 17) == 'JOBNAME JES-ID ' then
leave
end
if pos('NO JOBS MATCHED SELECTION', b.bx) > 0 then do
say 'no jobs in Beta report:' b.bx
return 4
end
end
if bx > b.0 then
call err 'no title found in betaRep'
say b.bx
cJ = 2
cI = 11
cE = pos(' END DATE ', b.bx)
eE = cE + 10
cF = pos(' END TIME ', b.bx) + 1
eF = cF+8
m.o.0 = 0
if cE < 20 | cF < 20 then
call err 'bad end time/date in beta title' b.bx
cS = pos(' SUB DATE ', b.bx)
eS = cS + 10
cT = pos(' SUB TIME ', b.bx) + 1
eT = cT+8
m.o.0 = 0
m.f.0 = 0
if cS < 20 | cT < 20 then
call err 'bad end time/date in beta title' b.bx
jx = 0
jy = 0
do bx=bx to b.0
if substr(b.bx, 2, 8) == 'BETA 92 ' ,
| abbrev(substr(b.bx, 2), '-----') ,
| abbrev(substr(b.bx, 2), '=====') ,
| substr(b.bx, 2, 17) == 'JOBNAME JES-ID ' ,
| abbrev(substr(b.bx, 2), 'PROGRAM B92BFJBR ') then
iterate
if pos(' JOB(S) MATCHED SELECTI', b.bx) > 0 then do
jz = word(substr(b.bx, 2), 1)
iterate
end
say b.bx
parse var b.bx 2 vJ 10 11 vI 19 ,
=(cS) vS =(eS) =(cT) vT =(eT) ,
=(cE) vE =(eE) =(cF) vF =(eF)
if translate(vE, '999999999', '012345678') \== '99.99.9999' then
call err 'bad end date' vE 'in line' bx':' b.bx
if translate(vF, '999999999', '012345678') \= '99:99:99' then
call err 'bad end time' vF 'in line' bx':' b.bx
vG = translate('1234-56-78', vE, '78.56.1234') ,
|| '-'translate(vF, '.', ':')
jx = jx + 1
if vG << old then
iterate
jy = jy + 1
say ' selected' vJ vI', ended' vG '>>=' old 'old'
call mAdd f, 'BFIND' ,
, ' SDATE('vS')' ,
, ' STIME('vT')' ,
, ' PDATE('vS')' ,
, ' PTIME('vT')' ,
, ' JOBNAME('strip(vJ)')' ,
, ' JOBID('strip(vI)')' ,
, ' DDNAME1(JESMSGLG)' ,
, ' OPERATOR(OR)' ,
, ' OPTIONS(FIRST)' ,
, ' SCOPE(BOTH)' ,
, ' MESSAGE(LONG)' ,
, ' RELOAD(YES)' ,
, ' MIXEDMODE(NO)' ,
, ' SLINE(0)' ,
, ' PLINE(0)' ,
, ' STRING1(DATE)'
call mAdd o, 'PRINT' ,
, ' SDATE('vS')' ,
, ' STIME('vT')' ,
, ' PDATE('vS')' ,
, ' PTIME('vT')' ,
, ' MASK(MM/DD/YY)' ,
, ' AUTOSEL(NO)' ,
, ' JOBNAME('strip(vJ)')' ,
, ' JOBID('strip(vI)')' ,
, ' DDNAME1(JESMSGLG)' ,
, ' MESSAGE(LONG)' ,
, ' SCOPE(BOTH)' ,
, ' DISPOSITION(MOD)' ,
, ' DATASET('betaExt')'
end
if jx <> jz then
call err jx 'jobs read not' jz 'as beta says'
say jy 'jobs selected from' jz 'in list'
call writeDD betaExPa, 'M.O.'
call tsoClose betaExPa
call writeDD betaFiPa, 'M.F.'
call tsoClose betaFiPa
return 4 * (jy = 0)
endProcedure doFun2
/*--- read concatenated master logs and write load files -------------*/
doFun3: procedure expose m.
call ini 1
call parmNewRead
call readMstrLog
call writeAblfAll m.writeAblfPre
return 0
endProcedure doFun3
/*--- read parmNew, extract new and old timestamp --------------------*/
parmNewRead: procedure expose m.
call readDD parmNew, n., '*'
call tsoClose parmNew
parse var n.1 new old .
say 'parmNew' new old
if n.0 < 1 then
call err 'empty parmNew'
else if translate(new, '999999999', '012345678') ,
\== '9999-99-99-99.99.99' then
call err 'bad new in parmNew:' new
else if translate(old, '999999999', '012345678') ,
\== '9999-99-99-99.99.99' then
call err 'bad old in parmNew:' old
else if new <= old then
call err 'new <= old' new old
m.parm_new = new
m.parm_old = old
return
endProcedure parmNewRead
/*_____________________________________________________________________
!!
!! read the whole master log
!! and analyse each interesting msg
!!_____________________________________________________________________
*/
readMstrLog:
call logMsgBegin rd
m.to.0 = 0
m.uow.0 = 0
m.LoEs.0 = 0
m.ReEot.0 = 0
do mx=1
mid = logMsg(rd)
if mid == '' then do
say 'readMstrLog end:' readNxPos(rd)
call readNxEnd rd
return
end
else if m.info.tst <<= m.info.doneUntil then
nop /* already done yesterday or eJes <-> beta92 */
else if mid == 'DSNT375I' then
call anaTimeoutDeadlock rd, info, 'D'
else if mid == 'DSNT376I' then
call anaTimeoutDeadlock rd, info, 'T'
else if mid == 'DSNT500I' | mid == 'DSNT501I' then
call anaResourceNotAvailable rd, info, mid
else if mid == 'DSNJ031I' then
call anaUncommittedUOW rd, info, 'U'
else if mid == 'DSNR035I' then
call anaUncommittedUOW rd, info, 'C'
else if mid == 'DSNI031I' then
call anaLockEscalation rd, info, 'E'
else if mid == 'DSN3201I' then
call anaReadEot rd, info, 'A'
end
endProcedure readMstrLog
logMsgBegin: procedure expose m.
parse arg rd
call readNxBegin rd, '-', 'DDIN1'
do until m.li <> ' 00:00:00' & m.li <> ''
li = readNx(rd)
end
m.info.doneUntil = m.parm_old
m.info.head = left('? ^ # no no', 300, '}')
m.info.jobKey = ''
m.mOld = ''
m.rd.curIsMsg = 1
m.cLogMsg = 0
m.cCont = 0
m.cContCx = 0
m.cTONF = 0
m.cTONFX = 0
m.cTOFo = 0
m.cTOFoX = 0
return
endProcedure logMsgBegin
/*_____________________________________________________________________
!!
!! get next logMsg and put parts of msg into rd.cc.1, rd.cc.2 ....
!! at end return '' otherwise messageID (or ? if space)
!!_____________________________________________________________________
*/
logMsg: procedure expose m.
parse arg rd
m.cLogMsg = m.cLogMsg+1
li = readNxCur(rd)
if li == '' then do
say 'logMsg end:' readNxPos(rd)
if m.info.jobKey \== '' then
call logMstrEnd rd
return ''
end
line = m.li
if substr(line, 1, 20) == ' IAT6140 JOB ORIGIN ' then do
m.rd.jes2 = 0
return logMstrBegin(rd, line)
end
else if substr(strip(line), 1, 39) ,
== 'J E S 2 J O B L O G -- S Y S T E M ' then do
m.rd.jes2 = 1
m.info.j2Id = ''
return logMstrBegin(rd, line)
end
if m.rd.jes2 then do
if translate(substr(line, 1, 9), '999999999', '012345678') ,
\== '99.99.99 ' then do
if line = '------ JES2 JOB STATISTICS ------' then do
m.info.mid = '----stat'
/* achtung (unknown) hat space mehr | */
do cx=1 until li == '' | substr(m.li, 11, 3) = ' ' ,
| substr(m.li, 14, 1) <> ' ' ,
| substr(m.li, 15, 3) = ' '
m.rd.cc.cx = m.li
li = readNx(rd)
end
m.rd.cc.0 = cx
return m.info.mid
end
else
call err 'bad time in jes2 line' readNxPos(rd)
end
m.info.time = word(line, 1)
w2 = substr(line, 10, 8)
if w2 \== m.info.j2Id then do
if w2 = '' then
say 'jes2 empty id ???' readNxPos(rd)
else if m.info.j2Id \== '' then
call err 'jes2 id mismach' m.info.j2Id ,
'<>' readNxPos(rd)
else if pos(' ', w2) > 0 then
call err 'bad jes2 id' w2 'in' readNxPos(rd)
else
m.info.j2Id = w2
end
if substr(line, 18, 1) \== ' ' then
call err 'bad jes2 line' readNxPos(rd)
else if substr(line, 18, 6) == ' ---- ' then do
if word(line, 8) \== '----' then
call err 'bad jes2 ---- line' readNxPos(r)
call anaCurDate info, subword(substr(line, 24), 2, 3)
m.info.mid = '----date'
end
else do
m.info.mid = word(line, 3)
end
m.info.tst = m.info.date'-'m.info.time
call logMsgContJes2 rd, line
end
else do
if translate(substr(line, 1, 10), '999999999', '012345678') ,
\== ' 99:99:99 ' then
call err 'bad time in jes3 line' readNxPos(rd)
m.info.time = word(line, 1)
m.info.head = left(line, 9) /* no space in empty line | */
if substr(line, 10, 14) == ' ---- IAT6853 ' then do
if substr(line, 24, 20) \== 'THE CURRENT DATE IS ' then
call err 'bad IAT6853' readNxPos(rd)
call anaCurDate info, subword(substr(line, 44), 2, 3)
m.info.mid = 'IAT6853'
end
else do
m.info.mid = word(line, 2)
end
m.info.tst = m.info.date'-'translate(m.info.time, '.', ':')
call logMsgContJes3 rd, line
end
if m.info.mid = '' then
return '?'
else
return m.info.mid
endProcedure logMsg
logMsgContJes2: procedure expose m.
parse arg rd, line
m.cCont = m.cCont + 1
if translate(right(line, 5), 000000000,123456789)== ' 000' then do
mSeq = right(line, 3)
m.mOld = mSeq subWord(m.mOld, 1, 49)
m.rd.cc.1 = substr(line, 19, length(line)-23)
end
else do
mSeq = ''
m.rd.cc.1 = substr(line, 19)
end
cx = 1
do forever
li = readNx(rd)
if li == '' then
leave
else if mSeq \== '' & left(m.li, 19) = ' 'mSeq then do
cx = cx + 1
m.rd.cc.cx = substr(m.li, 19)
end
else if translate(left(m.li, 19), 000000000, 123456789) ,
= ' 000' then do /* out of sequence look next */
/* ??? ix = wordPos(substr(m.li, 4, 3), m.mOld)
if symbol('m.igno.ix') == 'VAR' then
m.igno.ix = m.igno.ix + 1
else
m.igno.ix = 1
if ix < 1 then
say 'ignoring after' m.info.mid'#'mSeq readNxPos(rd)
??? */ end
else if m.li = ' 00:00:00' then do
end /* ignore marker from fun1 */
else
leave
end
m.rd.cc.0 = cx
m.cContCx = m.cContCx + cx
return li
endProcedure logMsgContJes2
logMsgContJes3: procedure expose m.
parse arg rd, line
m.cCont = m.cCont + 1
m.rd.cc.1 = substr(line, 10)
cx = 1
do forever
li = readNx(rd)
if li == '' then
leave
if \ abbrev(m.li, m.info.head) then do
if translate(substr(m.li, 2, 9), '999999999', '012345678') ,
\== '99:99:99 ' then
leave
if translate(substr(m.info.head 2, 9) ,
, '999999999', '012345678') \== '99:99:99 ' then
leave
ds =((( substr(m.li, 2, 2) * 60) ,
+ substr(m.li, 5, 2) * 60) ,
+ substr(m.li, 8, 2)) ,
-((( substr(m.info.head, 2, 2) * 60) ,
+ substr(m.info.head, 5, 2) * 60) ,
+ substr(m.info.head, 8, 2))
if ds < 0 | ds > 3 then
leave
end
if substr(m.li, 10, 14) == ' ---- IAT6853 ' then
leave
vx = verify(m.li, ' ', 'N', 10)
if vx = 11 | vx = 12 then do
w2 = word(m.li, 2)
if (length(w2) == 7 | length(w2) == 8) ,
& verify(w2, m.ut_ucNum) = 0 then
if wordPos(left(w2, 3), 'IAT ACF DSN IEF IXL') > 0 then
leave
end
cx = cx + 1
m.rd.cc.cx = substr(m.li, 10)
end
m.rd.cc.0 = cx
m.cContCx = m.cContCx + cx
return li
endProcedure logMsgContJes3
logMstrEnd: procedure expose m.
parse arg rd
jKy = m.info.jobKey
p = readNxPos(rd)
p = left(p, pos(':', p)-1)
if m.rd.jes2 then
j = 'jes2'
else
j = 'jes3'
say j m.info.job jKy 'to' m.info.tst p
/* ????
ii = ''
o ix=0 to 99
if symbol('m.igno.ix') == 'VAR' then
ii = ii ix'='m.igno.ix
end
say ii
??? */
say 'logMsg='m.cLogMsg 'cont='m.cCont 'contCx='m.cContCx,
'toNf='m.cTONf 'toNFX='m.cTONfX 'toFo='m.cTOFo 'toFoX='m.ctoFoX
jKy = m.info.jobKey
jEnd = m.info.tst
if symbol('m.jobK2E.jKy') <> 'VAR' | jEnd >> m.jobK2E.jKy then
m.jobK2E.jKy = jEnd
m.info.jobKey = ''
return
endProcedure logMstrEnd
logMstrBegin: procedure expose m.
parse arg rd, line
if m.info.jobKey \== '' then
call logMstrEnd rd
m.info.dateTst = ''
do until m.li <> ''
li = readNx(rd)
end
do lx=1 to 50
mid = logMsg(rd)
if mid = '' then do
say 'eof in start of mstrLog' line
say ' @' readNxPos(rd)
return ''
end
if mid == 'IEF403I' then do
j1 = word(m.rd.cc.1, 2)
s1 = word(m.rd.cc.1, words(m.rd.cc.1))
end
else if mid == 'DSNY024I'then do
m2 = substr(word(m.rd.cc.1, 2), 2)
leave
end
else if abbrev(mid, 'DSN') then do
call err 'unexpected dsn' readNxPos(rd)
end
end
if lx > 50 then
call err 'mstr begin' readNxPos(rd)
if s1 == '' then
call err 'IEF403I not found' readNxPos(rd)
if m2 == '' then
call err 'DSNY024I not found' readNxPos(rd)
if j1 <> m2'MSTR' then
call err 'dbMember' m2 '<> job' j1
m.info.dbMb = m2
call errHandlerPushRet ''
m.info.dbSys = iiMbr2DbSys(m2)
call errHandlerPop ''
m.info.job = j1
m.info.sys = s1
m.info.wxTime = 1
m.info.cxTime = 2
m.to.mstrBegin = m.to.0 + 1
if m.info.dateTst == '' then
call err 'no date' readNxPos(rd)
jKy = m2 m.info.dateTst
if symbol('m.jobK2E.jKy') <> 'VAR' then
m.jobK2E.jKy = ''
else
say 'job' j1 jKy ,
'already done until' m.jobK2E.jKy
m.info.jobKey = jKy
if m.parm_old << m.jobK2E.jKy then
m.info.doneUntil = m.jobK2E.jKy
else
m.info.doneUntil = m.parm_old
return mid
endProcedure logMstrBegin
/*_____________________________________________________________________
!!
!! if this is not a dsn message return ''
!! otherwise, check it, collect infos into info and return id
!!_____________________________________________________________________
*/
isDsnMsg: procedure expose m.
parse arg line, info
if m.modeNew? then do
if translate(substr(line, 2, 9), '999999999', '012345678') ,
\== '99:99:99 ' then do
if substr(line, 1, 20) == ' IAT6140 JOB ORIGIN ' then do
m.rd.jes2 = 0
call err 'bad line' line
s1 = ''
m2 = ''
if m.info.jobKey \== '' then do
call sayJobEnd info
jKy = m.info.jobKey
jEnd = m.info.tst
if symbol('m.jobK2E.jKy') <> 'VAR' ,
| jEnd >> m.jobK2E.jKy then
m.jobK2E.jKy = jEnd
m.info.jobKey = ''
end
m.info.dateTst = ''
do lx=1 to 50
ln = readNx(rd)
if ln = '' then do
say 'eof in start of mstrLog' line
say ' @' readNxPos(rd)
return ''
end
if translate(substr(m.ln, 2, 9), '999999999',
, '012345678') \== '99:99:99 ' then do
say 'bad start of mstrLog after' line
say ' @' readNxPos(rd)
return isDsnMsg(m.ln, info)
end
if word(m.ln, 2) == 'IEF403I' then do
j1 = word(m.ln, 3)
s1 = word(m.ln, words(m.ln))
end
else do
d2 = isDsnMsg(m.ln, info)
if d2 = 'DSNY024I' then do
m2 = substr(word(m.ln, 3), 2)
leave
end
else if d2 \== '' then
call err 'unexpected dsn' readNxPos(rd)
end
end
if lx > 50 then
call err 'mstr begin' readNxPos(rd)
if s1 == '' then
call err 'IEF403I not found' readNxPos(rd)
if m2 == '' then
call err 'DSNY024I not found' readNxPos(rd)
if j1 <> m2'MSTR' then
call err 'dbMember' m2 '<> job' j1
m.info.dbMb = m2
call errHandlerPushRet ''
m.info.dbSys = iiMbr2DbSys(m2)
call errHandlerPop
m.info.job = j1
m.info.sys = s1
m.info.wxTime = 1
m.info.cxTime = 2
if m.info.dateTst == '' then
call err 'no date' readNxPos(rd)
jKy = m2 m.info.dateTst
if symbol('m.jobK2E.jKy') <> 'VAR' then
m.jobK2E.jKy = ''
else
say 'job' j1 jKy ,
'already done until' m.jobK2E.jKy
m.info.jobKey = jKy
if m.parm_old << m.jobK2E.jKy then
m.info.doneUntil = m.jobK2E.jKy
else
m.info.doneUntil = m.parm_old
return ''
end
mid = word(line, 2)
m.info.time = word(line, 1)
m.info.head = left(line, 9) /* no space in empty line | */
if \ abbrev(mid, 'DSN') | wordIndex(line, 2) <> 12 ,
| length(mid) > 8 then do
if mid = '----' then
if word(line, 3) = 'IAT6853' then
call anaCurDate info, line
return ''
end
end
else do
mid = word(line, 4)
parse var line m.info.dbMb m.info.date m.info.time .
call errHandlerPushRet ''
m.info.dbSys = iiMbr2DbSys(m.info.dbMb)
call errHandlerPop
if \ abbrev(mid, 'DSN') | wordIndex(line, 4) <> 29 ,
| length(mid) > 8 then do
if mid = '----' then
if word(line, 5) = 'IAT6853' then
call anaCurDate info, substr(line,18), word(line,2)
m.info.wxTime = 3
m.info.cxTime = 19
return ''
end
m.info.head = left(line,27)
end
/* diese Prüfung ist falsch, manche displays zeigen --------------
Infos aus anderen membern an, z.B. -dis indoubt ......
aMbr = word(line, 5)
if abbrev(aMbr, '-') then
if '-'m.info.dbMb \== aMbr then
call err 'dbMember mismatch:' m.info.dbMb ,
'<>' readNxPos(rd) -----------------------------*/
m.info.tst = m.info.date'-'translate(m.info.time, '.', ':')
return mid
endProcedure isDsnMsg
sayJobEnd: procedure expose m.
parse arg info
jKy = m.info.jobKey
p = readNxPos(rd)
p = left(p, pos(':', p)-1)
say 'job' m.info.job jKy 'to' m.info.tst p
return
endProcedure say JobEnd
/*_____________________________________________________________________
!!
!! analyse current date in iat6853 message
!! and check that it equals the header
!!_____________________________________________________________________
*/
anaCurDate: procedure expose m.
parse arg info, d1, compD
d2 = word(d1, 1) ,
translate(left(word(d1, 2), 1)),
|| translate(substr(word(d1, 2), 2),
, m.ut_AlfLC, m.ut_uc) ,
word(d1, 3)
do while abbrev(d2, 0) /* date does not accept leading zeroes ||||| */
d2 = substr(d2, 2)
end
d3 = date('s', d2)
m.info.date = translate('1234-56-78', d3, '12345678')
m.info.dateTst = m.info.date'-'translate(m.info.time,'.',':')
if compD \== '' then
if m.info.date <> compD then
call err 'date mismatch' compD '<>' d3 readNxPos(rd)
return
endProcedure anaCurDate
/*____________________________________________________________________
!!
!! analye msg: DSN3201I event type A - ABNORMAL EOT AUS INPUT-DS LESEN
!!____________________________________________________________________
*/
anaReadEot: procedure expose m.
parse arg rd, info, pEvTy
m.ReEot.0 = m.ReEot.0 +1
ux = 'REEOT.'m.ReEot.0 /*zähler */
m.ux.A = pEvty
m.ux.tst = m.info.tst
m.ux.dbMb = m.info.dbMb
m.ux.dbSys = m.info.dbSys
m.ux.corr = ''
m.ux.Jobname = ''
m.ux.conn = ''
m.ux.AuthID = '' /* AuthID = User column in db2 Table */
m.ux.AsID = ''
m.ux.tcb = ''
do lx = 1 to m.rd.cc.0
cx = pos(' CORRELATION-ID=', m.rd.cc.lx)
jx = pos(' JOBNAME=', m.rd.cc.lx)
if cx > 0 then do
if jx < cx then
m.ux.corr = cut18(strip(substr(m.rd.cc.lx,cx+16)))
else
m.ux.corr = cut18(strip(substr(m.rd.cc.lx,cx+16,
, jx-cx-16)))
end
if jx > 0 then
m.ux.Jobname = cut18(word(strip(substr(m.rd.cc.lx,jx+9)),1))
cx = pos(' CONNECTION-ID=', m.rd.cc.lx)
if cx > 0 then do
m.ux.conn = cut18(word(substr(m.rd.cc.lx,cx+15), 1))
end
cx = pos(' USER=', m.rd.cc.lx)
if cx > 0 then do
m.ux.AuthID = word(substr(m.rd.cc.lx,cx+6), 1)
end
cx = pos(' ASID=', m.rd.cc.lx)
if cx > 0 then
m.ux.AsID = word(substr(m.rd.cc.lx,cx+6), 1)
cx = pos(' TCB=', m.rd.cc.lx)
if cx > 0 then
m.ux.tcb = strip(substr(m.rd.cc.lx,cx+5))
/* if m.ux.tcb <> '' then
leave ????? */
end
return
endProcedure anaReadEot
/*____________________________________________________________________
!!
!! analye msg: DSNI031I event type E - LOCK ESCALATION
!!____________________________________________________________________
*/
anaLockEscalation: procedure expose m.
parse arg rd, info, pEvTy
m.LoEs.0 = m.LoEs.0 +1
ux = 'LOES.'m.LoEs.0 /*zähler */
m.ux.E = pEvty
m.ux.tst = m.info.tst
m.ux.dbMb = m.info.dbMb
m.ux.dbSys = m.info.dbSys
m.ux.plan = ''
m.ux.package = ''
m.ux.CollID = ''
m.ux.corr = ''
m.ux.conn = ''
m.ux.resource = ''
m.ux.LckSt = ''
m.ux.Statement = ''
do lx=1 to m.rd.cc.0
cx = pos(' RESOURCE NAME = ', m.rd.cc.lx)
if cx > 0 then
m.ux.resource = strip(word(m.rd.cc.lx, 4))
cx = pos(' LOCK STATE = ', m.rd.cc.lx)
if cx > 0 then
m.ux.LckSt = strip(word(m.rd.cc.lx, 4))
cx = pos(' PLAN NAME : PACKAGE NAME = ',m.rd.cc.lx)
if cx > 0 then do
PlanPack = substr(m.rd.cc.lx,cx+28)
cx = pos(':',planpack)
m.ux.plan = strip(left(planPack, cx-1))
m.ux.package = cut18(strip(substr(planPack,cx+1)))
end
cx = pos(' COLLECTION-ID = ', m.rd.cc.lx)
if cx > 0 then
m.ux.CollID = cut18(strip(substr(m.rd.cc.lx,cx+17)))
cx = pos(' STATEMENT NUMBER = ', m.rd.cc.lx)
if cx > 0 then
m.ux.Statement= strip(substr(m.rd.cc.lx,cx+20))
cx = pos(' CORRELATION-ID = ', m.rd.cc.lx)
if cx > 0 then
m.ux.corr = cut18(strip(substr(m.rd.cc.lx,cx+18)))
cx = pos(' CONNECTION-ID = ', m.rd.cc.lx)
if cx > 0 then
m.ux.conn = cut18(strip(substr(m.rd.cc.lx,cx+17)))
/* if m.ux.conn <> '' then
leave ???????? */
end
return
endProcedure anaLockEscalation
sayObj: procedure expose m.
parse arg ff, o
say o':' cl
do fx=1 to m.ff.0
f1 = m.ff.fx
say left(f1, 20) m.o.f1
end
return
endProcedure sayObj
/*____________________________________________________________________
!!
!! analye uncommit UOW msg: DSNJ031I / event type U and C
!!____________________________________________________________________
*/
anaUncommittedUOW: procedure expose m.
parse arg rd, info, pEvTy
m.uow.0 = m.uow.0 +1
ux = 'UOW.'m.uow.0 /* zähler */
m.ux.UC = pEvty
m.ux.tst = m.info.tst
m.ux.dbMb = m.info.dbMb
m.ux.dbSys = m.info.dbSys
m.ux.logRecs = ''
m.ux.corr = ''
m.ux.conn = ''
m.ux.plan = ''
m.ux.authid = ''
do lx = 1 to m.rd.cc.0
cx = pos(' CHECKPOINTS -', m.rd.cc.lx) /* for checkP */
if cx > 0 then
m.ux.logRecs = strip(word(m.rd.cc.lx, 2))
cx = pos(' LOG RECORDS -', m.rd.cc.lx) /* for UOW */
if cx > 0 then
m.ux.logRecs = strip(word(m.rd.cc.lx, 3))
cx = pos(' CORRELATION NAME =', m.rd.cc.lx)
if cx > 0 then
m.ux.corr = cut18(word(substr(m.rd.cc.lx,cx+19),1))
cx = pos(' CONNECTION ID =', m.rd.cc.lx)
if cx > 0 then
m.ux.conn = cut18(strip(substr(m.rd.cc.lx,cx+17)))
cx = pos(' PLAN NAME =', m.rd.cc.lx)
if cx > 0 then
m.ux.plan = strip(substr(m.rd.cc.lx,cx+13))
cx = pos(' AUTHID =', m.rd.cc.lx)
if cx > 0 then
m.ux.authid = strip(substr(m.rd.cc.lx,cx+9))
/* if m.ux.authid <> '' then
leave ???????????? */
end
return
endProcedure anaUncommittedUOW
/*____________________________________________________________________
!!
!! analye timeout, deadlock msg: DSNT375I, DSNT376I
!!____________________________________________________________________
*/
anaTimeoutDeadlock: procedure expose m.
parse arg rd, info, pEvTy
totx = newTimeout(info, pEvTy)
vs = 'V'
do lx=1 to m.rd.cc.0
if pos(' ONE HOLDER ', m.rd.cc.lx) > 0 then do
if pEvTy <> 'T' then
call err 'holder for evTy' pEvTy':'m.rd.cc.lx ,
readNxPos(r)
else if vs <> 'V' then
call err 'several holders:'m.rd.cc.lx readNxPos(r)
else
vs = 'H'
end
if pos(' IS DEADLOCKED ', m.rd.cc.lx) > 0 then do
if pEvTy <> 'D' then
call err 'is deadLocked for evTy' ,
pEvTy':'m.rd.cc.lx readNxPos(r)
else if vs <> 'V' then
call err 'several is deadLocked:'m.rd.cc.lx readNxPos(r)
else
vs = 'H'
end
cx = pos(' PLAN=', m.rd.cc.lx)
if cx > 0 then
m.toTx.vs.plan = word(substr(m.rd.cc.lx, cx+6,8), 1)
cx = pos(' CORRELATION-ID=', m.rd.cc.lx)
if cx > 0 then
m.toTx.vs.corr = cut18(strip(substr(m.rd.cc.lx, cx+16)))
cx = pos(' CONNECTION-ID=', m.rd.cc.lx)
if cx > 0 then
m.toTx.vs.conn = cut18(strip(substr(m.rd.cc.lx, cx+15)))
cx = pos(' ON MEMBER ', m.rd.cc.lx)
if cx > 0 then do
if vs <> 'H' then
call err 'on member in vs' vs':'m.rd.cc.lx readNxPos(rd)
else
m.toTx.vs.dbMb = word(substr(m.rd.cc.lx, cx+11, 8), 1)
end
end
return
endProcedure anaTimeOut
/*____________________________________________________________________
!!
!! make and initialise a new timeout/deadlock row
!!____________________________________________________________________
*/
newTimeout: procedure expose m.
parse arg info, pEvTy
toTy = 'TO.'m.to.0
m.to.0 = m.to.0 + 1
toTx = 'TO.'m.to.0
call clearFlds totx, ffTimeO
m.toTx.tst = m.info.tst
m.toTx.evTy = pEvTy
m.toTx.v.dbMb = m.info.dbMb
m.toTx.dbSys = m.info.dbSys
if m.to.0 <= m.to.mstrBegin then
m.toTx.srchLi = ''
else if m.toTy.name = '' then
m.toTx.srchLi = toTy
else
m.toTx.srchLi = m.toTy.srchLi
return toTx
endProcedure newTimeout
/*____________________________________________________________________
!!
!! analyse resourceNotAvailable msg DSNT501I and DSNT500I
!!____________________________________________________________________
*/
anaResourceNotAvailable: procedure expose m.
parse arg rd, info, mid
tCor = ''
tCon = ''
tRea = ''
tTyp = ''
tNam = ''
do lx = 1 to m.rd.cc.0 /* loop line of dsnt501i */
cx = pos(' CORRELATION-ID=', m.rd.cc.lx)
if cx > 0 then
tCor = word(substr(m.rd.cc.lx,cx+16),1)
cx = pos(' CONNECTION-ID=', m.rd.cc.lx)
if cx > 0 then
tCon = strip(substr(m.rd.cc.lx,cx+15))
cx = pos(' REASON ', m.rd.cc.lx)
if cx > 0 then
tRea = word(substr(m.rd.cc.lx,cx+8,20),1)
cx = pos(' TYPE ', m.rd.cc.lx)
if cx > 0 then
tTyp = word(substr(m.rd.cc.lx,cx+6,20),1)
cx = pos(' NAME ', m.rd.cc.lx)
if cx > 0 then
tNam = strip(substr(m.rd.cc.lx,cx+6))
end /* loop line of dsnt501i */
/* search preceeding timeOut/deadLock*/
toTx = 'TO.'m.to.0
if tCor = '' | tCon = '' then do
if m.toTx.name \== '' then
toTx = ''
end
else do
/* attention, sometimes we have 1000 s of resource not available
and this search would get really slow
==> use searchLi chain, chaining together TimeOuts
WITHOUT resource name */
mb = m.info.dbMb
tsN = m.info.tst
numeric digits 20
tsB = timeDays2Tst(timestamp2Days(tsN) - 30/86400)
numeric digits 9
do qx=0 while toTx \== ''
if qx > 10000 then do
say 'loopiiiiiiiiiiing' qx toTx
end
else if m.toTx.tst << tsB then do
toTx = ''
leave
end
else if m.toTx.v.corr == tCor & m.toTx.v.conn == tCon ,
& m.toTx.name == '' then
leave
toTx = m.toTx.srchLi
end
if toTx == '' then do
m.ctoNF = m.ctoNF + 1
m.ctoNFX = m.ctoNFx + qx
end
else do
m.ctoFo = m.ctoFo + 1
m.ctoFoX = m.ctoFox + qx
end
end
/* new feature: store these also
evType depending on reason, but some have several */
if toTx == '' then do
if wordPos(tRea, '00C200FA 00C20031 00C900C0 00E70010') >0 then
toTx = newTimeout(info, 'T')
else
toTx = newTimeout(info, '')
m.toTx.v.corr = tCor
m.toTx.v.conn = tCon
end
/* resource an timeout/deadlock anhängen */
m.toTx.type = tTyp
m.toTx.name = space(tNam, 1)
m.toTx.reason = tRea
if tTyp <> '' then
call resourceType info, toTx'.'type, toTx'.'name
return
endProcedure anaResourceNotAvailable
/*____________________________________________________________________
!!
!! give the name of the resourcetype and dbid/obid
!!____________________________________________________________________
*/
resourceType: procedure expose m.
parse arg info, tp, nm
cd = m.tp
if symbol('m.resourceType.cd') <> 'VAR' then do
say '<'cd'>' c2x(cd)
say readNxPos(rd)
call err 'unknown resource type' cd
end
m.tp = m.resourceType.cd
parms = m.resourceTypeParms.cd
names = m.nm
if pos('DI.OI', parms) > 0 then do /* find dbid and obid */
px = 0
nx = 0
do until px = 0
py = pos('.', parms, px + 1)
ny = pos('.', names, nx + 1)
if (py=0) <> (ny=0) then
call err 'resource parms' parms 'mismatch name' names
if py = 0 then do
p1 = substr(parms, px+1)
n1 = substr(names, nx+1)
end
else do
p1 = substr(parms, px+1, py-px-1)
n1 = substr(names, nx+1, ny-nx-1)
end
n.p1 = n1
px = py
nx = ny
end
/* dbid und obid uebersetzen */
m.nm = strip(getDbidObid(m.info.dbSys, n.di, n.oi) names)
end
return cd
endProcedure resourceType
ini: procedure expose m.
parse arg m.modeNew
call resourceTypeIni
call sqlIni
call errAddCleanup "if m.sql_dbSys <> '' then do;" ,
"say 'rollback';call sqlExec Rollback; call sqlDisconnect; end"
if m.modeNew then
f1 = 'TST DBSYS'
else
f1 = 'TST'
call iniFlds ffTimeO, f1 'V.DBMB EVTY V.PLAN V.CORR V.CONN' ,
'H.PLAN H.CORR H.CONN' ,
'REASON TYPE NAME'
call iniFlds ffUow, f1 'DBMB UC PLAN CORR CONN AUTHID LOGRECS'
call iniFlds ffLockE, f1 'DBMB E PLAN PACKAGE COLLID' ,
'CORR CONN RESOURCE LCKST STATEMENT'
call iniFlds ffEOT, f1 'DBMB A CORR JOBNAME CONN AUTHID ASID TCB'
return
endProcedure ini
iniFlds: procedure expose m.
parse arg ff, flds
do fx=1 to words(flds)
m.ff.fx = word(flds, fx)
end
m.ff.0 = words(flds)
return
endProcedure iniFlds
clearFlds: procedure expose m.
parse arg o, ff
do fx=1 to m.ff.0
f1 = m.ff.fx
m.o.f1 = ''
end
return o
endProcedure clearlds
resourceTypeIni: procedure expose m.
/* the old definitions for backward compability */
call rtDef '00000100', 'DB'
call rtDef '00000200', 'TS'
call rtDef '00000201', 'IX-SPACE'
call rtDef '00000202', 'TS'
call rtDef '00000210', 'PARTITION'
call rtDef '00000220', 'DATASET'
call rtDef '00000230', 'TEMP FILE'
call rtDef '00000300', 'TEMP FILE'
call rtDef '00000300', 'PAGE'
call rtDef '00000301', 'IX-MINIPAGE'
call rtDef '00000302', 'TS-PAGE'
call rtDef '00000303', 'IX-PAGE'
call rtDef '00000304', 'TS-RID'
call rtDef '00000D01', 'DBID/OBID'
call rtDef '00000800', 'PLAN'
call rtDef '00000801', 'PACKAGE'
call rtDef '00002000', 'TS CS-CLAIM CLASS'
call rtDef '00002001', 'TS RR-CLAIM CLASS'
call rtDef '00002002', 'TS WRITE-CLAIM CLASS'
call rtDef '00002003', 'IX CS-CLAIM CLASS'
call rtDef '00002004', 'IX RR-CLAIM CLASS'
call rtDef '00002005', 'IX WRITE-CLAIM CLASS'
call rtDef '00002006', 'TS PART CS-CLAIM CLASS'
call rtDef '00002007', 'TS PART RR-CLAIM CLASS'
call rtDef '00002008', 'TS PART WRITE-CLAIM CLASS'
call rtDef '00002009', 'IX PART CS-CLAIM CLASS'
call rtDef '00002010', 'IX PART RR-CLAIM CLASS'
call rtDef '00002011', 'IX PART WRITE-CLAIM CLASS'
/* the complete Db2V10 resource type table */
call rtDef '00000100', 'Database', 'DB'
call rtDef '00000200', 'Table space', 'DB.SP'
call rtDef '00000201', 'Index space', 'DB.SP'
call rtDef '00000202', 'Table space RD.DB.TS'
call rtDef '00000205', 'Compression Dictionary', 'DB.SP'
call rtDef '00000210', 'Partition', 'DB.SP.PT'
call rtDef '00000220', 'Data set', 'DSN'
call rtDef '00000230', 'Temporary file', 'SZ'
call rtDef '00000240', 'Database procedure', 'DBP'
call rtDef '00000300', 'Page', 'DB.SP.PG'
call rtDef '00000301', 'Index minipage', 'DB.SP.PG.MP'
call rtDef '00000302', 'Table space page', 'DB.SP.PG'
call rtDef '00000303', 'Index space page', 'DB.SP.PG'
call rtDef '00000304', 'Table space RID', 'DB.SP.RID'
call rtDef '00000305', 'Index access/table space RID', 'DB.SP.RID'
call rtDef '00000306', 'Index access/table space page', 'DB.SP.PG'
call rtDef '00000307', 'Index space EOF', 'DB.SP.01'
call rtDef '00000400', 'ICF catalog', 'IC'
call rtDef '00000401', 'Authorization function'
call rtDef '00000402', 'Security Server',
, 'SAF/RACF return/reason codes'
call rtDef '00000500', 'Storage group', 'SG'
call rtDef '00000602', 'EDM DBD Space'
call rtDef '00000603', 'EDM DYNAMIC STATEMENT Space'
call rtDef '00000604', 'EDM skeleton storage'
call rtDef '00000605', 'EDM above-the-bar storage'
call rtDef '00000606', 'EDM below-the-bar storage'
call rtDef '00000700', 'Buffer pool space', 'BP'
call rtDef '00000701', 'Group buffer pool', 'GBP'
call rtDef '00000800', 'Plan', 'PL'
call rtDef '00000801', 'Package', 'COLLECTION.PACKAGE.CONTOKEN'
call rtDef '00000802', 'BINDLOCK01 through BINDLOCK20',
, 'BINDLOCK01 through BINDLOCK20'
call rtDef '00000900', '32KB data area'
call rtDef '00000901', 'Sort storage'
call rtDef '00000903', 'Hash anchor', 'DB.SP.PG.AI'
call rtDef '00000904', 'RIDLIST storage'
call rtDef '00000905', 'IRLM storage'
call rtDef '00000906', 'DB2', 'MEMBER'
call rtDef '00000907', 'LOB storage'
call rtDef '00000908', 'Basic Floating Point Extensions Facility'
call rtDef '00000909', 'Extended Time-of-Day (TOD) Clock'
call rtDef '0000090A', 'XML storage'
call rtDef '00000A00', 'Table', 'RD.CR.TB'
call rtDef '00000A10', 'Alias', 'RELDEP.OWNER.ALIAS.RD.CR.AL'
call rtDef '00000A11', 'Distinct type', 'SC.DT'
call rtDef '00000A12', 'User-defined function', 'SC.SN'
call rtDef '00000A13', 'Stored procedure', 'SC.SN'
call rtDef '00000A14', 'Sequence'
call rtDef '00000A16', 'Role'
call rtDef '00000A17', 'Trigger'
call rtDef '00000B00', 'View', 'RD.CR.VW'
call rtDef '00000C00', 'Index', 'RD.CR.IX'
call rtDef '00000C01', 'Index', 'CR.IX'
call rtDef '00000D00', 'DBID/OBID', 'RD.DI.OI'
call rtDef '00000D01', 'DBID/OBID', 'DI.OI'
call rtDef '00000D02', 'OBID', 'OI'
call rtDef '00000E00', 'SU limit exceeded', 'CN'
call rtDef '00000F00', 'Auxiliary column',
,'DI.OI.ROWID.COLN or DI.OI.DOCID.COLN'
call rtDef '00000F01', 'LOB lock', 'DIX.PIX.ROWID.VRSN'
call rtDef '00000F81', 'XML lock', 'DIX.PIX.DOCID'
call rtDef '00001000', 'DDF', 'LOCATION or SUBSYSTEM ID'
call rtDef '00001001', 'System conversation',
, 'LU.MODE.RTNCD.FDBK2.RCPRI.RCSEC.SENSE'
call rtDef '00001002', 'Agent conversation',
, 'LU.MODE.RTNCD.FDBK2.RCPRI.RCSEC.SENSE'
call rtDef '00001003', 'CNOS processing',
, 'LU.MODE.RTNCD.FDBK2.RCPRI.RCSEC.SENSE'
call rtDef '00001004', 'CDB (Communication database)',
, 'LOCATION.AUTHORIZATIONID.PL'
call rtDef '00001005', 'DB access agent', 'LOCATION'
call rtDef '00001007', 'TCP/IP domain name', 'LINKNAME.DOMAIN.ERRNO'
call rtDef '00001008', 'TCP/IP service name', 'LOCATION.SERVICE.ERRNO'
call rtDef '00001080', 'ACCEL', 'SERVER.DOMAIN'
call rtDef '00001102', 'Bootstrap data set (BSDS)', 'MEMBER'
call rtDef '00002000', 'Table space CS-claim class', 'DB.SP'
call rtDef '00002001', 'Table space RR-claim class', 'DB.SP'
call rtDef '00002002', 'Table space write-claim class', 'DB.SP'
call rtDef '00002003', 'Index space CS-claim class', 'DB.SP'
call rtDef '00002004', 'Index space RR-claim class', 'DB.SP'
call rtDef '00002005', 'Index space write-claim class', 'DB.SP'
call rtDef '00002006', 'Table space partition CS-claim class',
, 'DB.SP.PT'
call rtDef '00002007', 'Table space partition RR-claim class',
, 'DB.SP.PT'
call rtDef '00002008', 'Table space partition write-claim class',
, 'DB.SP.PT'
call rtDef '00002009', 'Index space partition CS-claim class',
, 'DB.SP.PT'
call rtDef '00002010', 'Index space partition RR-claim class',
, 'DB.SP.PT'
call rtDef '00002011', 'Index space partition Write-claim class',
, 'DB.SP.PT'
call rtDef '00002100', 'Table space DBET entry', 'DB.SP'
call rtDef '00002101', 'Index space DBET entry', 'DB.SP'
call rtDef '00002102', 'Table space partition DBET entry', 'DB.SP.PT'
call rtDef '00002103', 'Index space partition DBET entry', 'DB.SP.PT'
call rtDef '00002104', 'DBET hash chain lock timeout',
, 'INTERNAL LOCK NN'
call rtDef '00002105', 'Logical partition DBET entry', 'DB.SP.PT'
call rtDef '00002200', 'Routine Parameter Storage', 'DBP'
call rtDef '00002201', 'm.debug Agent Storage', 'DBP'
call rtDef '00002300', 'ICSF encryption and decryption facilities'
call rtDef '00003000', 'Code (release maintenance_level or system' ,
'parameter)', 'REL,APAR,ZPARM'
call rtDef '00003002', 'Number of Stored Procedures'
call rtDef '00003072', 'Index'
call rtDef '00003073', 'Index'
call rtDef '00003328', 'Release dependency'
call rtDef '00003329', 'DBID/OBID', 'DI.OI'
call rtDef '00003330', 'OBID limit exceeded'
call rtDef '00003840', 'LOB column'
call rtDef '00004000', 'Profile exception threshold exceeded',
, 'PID.PTYPE.PNAME'
return
endProcedure resourceTypeIni
rtDef: procedure expose m.
parse arg cd, nm, pa
if symbol('m.resourceType.cd') <> 'VAR' then
m.resourceType.cd = nm
m.resourceTypeParms.cd = pa
return
endProcedure rtDef
getDbidObid: procedure expose m.
parse arg dbSys, dbid, obid
SQL_DBID = STRIP(dbid,L,0)
SQL_OBID = STRIP(obid,L,0)
if symbol('m.id2n.dbSys.dbidObid.dbid.obid') == 'VAR' then
/* haben es schon mal gefunden*/
return m.id2n.dbSys.dbidObid.dbid.obid
/* select from catalog */
/* from sysTables */
if dbSys \== m.sql_dbSys then do
if m.sql_dbSys \== '' then
call sqlDisconnect
if m.tstRZ4 then
if sysvar(sysNode) = 'RZ4' ,
& wordPos(dbSys, 'DP4G DBOL') < 1 then
return ''
call sqlConnect dbSys
end
res = sql2One("SELECT ",
" STRIP(CREATOR,B)!!'.'!!STRIP(NAME,B) ",
" FROm SYSIBM.SYSTABLES ",
" WHERE DBID = " SQL_DBID ,
" AND OBID = " SQL_OBID ,
, , ':m.qq.rNm :SQL_IND', , ' ')
if res == '' then
res = sql2One("SELECT ",
" STRIP(DBNAME,B)!!'.'!!STRIP(NAME,B) ",
" FROM SYSIBM.SYSTABLESPACE ",
" WHERE DBID = " SQL_DBID ,
" AND OBID = " SQL_OBID ,
, , ':m.qq.rNm :SQL_IND', , ' ')
if res == '' then
res = sql2One( "SELECT ",
" STRIP(CREATOR,B)!!'.'!!STRIP(NAME,B) ",
" FROM SYSIBM.SYSINDEXES ",
" WHERE DBID = " SQL_DBID ,
" AND OBID = " SQL_OBID ,
, , ':m.qq.rNm :SQL_IND', , ' ')
m.dbidObid.dbid.obid = res
return m.dbidObid.dbid.obid
endProcedure getDbidObid
/*_________________________________________________________________________
!!
!! INSERT IN DB2 TABELLE TADM60A1
!!_________________________________________________________________________
*/
INSERT_TADM60A1: procedure expose m. dsnRZ4.
IF m.debug THEN SAY "ENTER PROCEDURE INSERT_TADM60A1..."
cIns = 0
cDead = 0
cTime = 0
say ' ' time() 'begin insert into tadm60a1'
call sqlUpdPrep 7,
, "INSERT INTO "m.tadmCreator".TADM60A1 (" ,
"TIMESTAMP, ssid, event_type," ,
"VICTIM_PLAN, VICTIM_CORR_ID, VICTIM_COnn_ID," ,
"SOURCE_PLAN, SOURCE_CORR_ID, SOURCE_COnn_ID," ,
"REASON_CODE, type, name )" ,
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?)"
do tx=1 to m.to.0
/*______________________________________________________________________
row überspringen falls alt
*/
if (m.to.tx.evTy == 'D' & m.to.tx.tst <= m.lastDeadlock ) ,
|(m.to.tx.evTy == 'T' & m.to.tx.tst <= m.lastTimeout ) then
iterate
call sqlUpdArgs 7,
, m.to.tx.tst, m.to.tx.v.dbMb, m.to.tx.evTy,
, m.to.tx.v.plan, m.to.tx.v.corr, m.to.tx.v.conn,
, m.to.tx.h.plan, m.to.tx.h.corr, m.to.tx.h.conn,
, m.to.tx.reason, m.to.tx.type, m.to.tx.name
cIns = cIns + 1
cDead = cDead + (m.to.tx.evTy == 'D')
cTime = cTime + (m.to.tx.evTy == 'T')
end
call sqlCommit
say ' ' time() cIns 'inserted into tadm60a1,' ,
cDead 'deadlocks and' cTime 'timeouts'
IF m.debug THEN SAY "LEAVE PROCEDURE INSERT_TADM60A1..."
RETURN;
/*_________________________________________________________________________
!!
!! INSERT IN DB2 TABELLE TADM63A1
!!_________________________________________________________________________
*/
INSERT_TADM63A1: procedure expose m. dsnRZ4.
IF m.debug THEN SAY "ENTER PROCEDURE INSERT_TADM63A1..."
say ' ' time() 'begin insert into tadm63a1'
call sqlUpdPrep 7,
, "INSERT INTO "m.tadmCreator".TADM63A1 (" ,
"TIMESTAMP," ,
"SSID," ,
"EVENT_TYPE," ,
"PLAN_NAME," ,
"CORRID_ID," ,
"CONN_ID," ,
"AUTHID," ,
"LOGREC)" ,
"VALUES (?,?,?,?,?,?,?,?)"
cIns = 0
cUOW = 0
cCHK = 0
do tx=1 to m.uow.0
ux = 'UOW.'tx
if m.ux.UC == 'U' & m.ux.tst <= m.lastUOW then
iterate
if m.ux.UC == 'C' & m.ux.tst <= m.lastCheckp then
iterate
cIns = cIns + 1
cUOW = cUOW + (m.ux.UC == 'U')
cCHK = cCHK + (m.ux.UC == 'C')
call sqlUpdArgs 7,
,m.ux.tst,
,m.ux.dbMb,
,m.ux.UC,
,m.ux.plan,
,m.ux.corr,
,m.ux.conn,
,m.ux.authid,
,m.ux.logRecs
end
call sqlCommit
say ' ' time() cIns 'inserted into tadm63a1,' ,
cUOW 'uncommitedUOW and' cCHK 'checkpoints'
IF m.debug THEN SAY "LEAVE PROCEDURE INSERT_TADM63A1..."
RETURN;
/*_________________________________________________________________________
!!
!! INSERT IN DB2 TABELLE TADM64A1
!!_________________________________________________________________________
*/
INSERT_TADM64A1: procedure expose m. dsnRZ4.
IF m.debug THEN SAY "ENTER PROCEDURE INSERT_TADM64A1..."
say ' ' time() 'begin insert into tadm64a1'
call sqlUpdPrep 10,
, "INSERT INTO "m.tadmCreator".TADM64A1 (" ,
"TIMESTAMP," ,
"SSID," ,
"EVENT_TYPE," ,
"PLAN_NAME," ,
"PACKAGE_NAME," ,
"COLLECTION_ID," ,
"CORRID_ID," ,
"CONN_ID," ,
"RESOURCE," ,
"LOCK_STATE," ,
"STATEMENT)" ,
"VALUES (?,?,?,?,?,?,?,?,?,?,?)"
cIns=0
do tx=1 to m.LoEs.0
ux = 'LOES.'tx
if m.ux.tst <= m.lastLockesc then
iterate
cIns = cIns + 1
call sqlUpdArgs 10,
,m.ux.tst,
,m.ux.dbMb,
,m.ux.E,
,m.ux.plan,
,m.ux.package,
,m.ux.CollID,
,m.ux.corr,
,m.ux.conn,
,m.ux.resource,
,m.ux.LckSt,
,m.ux.Statement
end
call sqlCommit
say ' ' time() cIns 'inserted into tadm64a1,' ' LOCK ESCALATION'
IF m.debug THEN SAY "LEAVE PROCEDURE INSERT_TADM64A1..."
RETURN;
/*_________________________________________________________________________
!!
!! INSERT IN DB2 TABELLE TADM65A1
!!_________________________________________________________________________
*/
INSERT_TADM65A1: procedure expose m. dsnRZ4.
IF m.debug THEN SAY "ENTER PROCEDURE INSERT_TADM65A1..."
say ' ' time() 'begin insert into tadm65a1'
call sqlUpdPrep 10,
, "INSERT INTO "m.tadmCreator".TADM65A1 (" ,
"TIMESTAMP," ,
"SSID," ,
"EVENT_TYPE," ,
"CORRID_ID," ,
"JOBNAME," ,
"CONN_ID," ,
"AUTHID," ,
"ASID," ,
"TCB)" ,
"VALUES (?,?,?,?,?,?,?,?,?)"
cIns=0
do tx=1 to m.ReEot.0
ux = 'REEOT.'tx
if m.ux.tst <= m.lastReadEot then
iterate
cIns = cIns + 1
call sqlUpdArgs 10,
,m.ux.tst,
,m.ux.dbMb,
,m.ux.A,
,m.ux.corr,
,m.ux.Jobname,
,m.ux.conn,
,m.ux.AuthID,
,m.ux.AsID,
,m.ux.tcb
end
call sqlCommit
say ' ' time() cIns 'inserted into tadm65a1,' ' ABNORMAL EOT'
IF m.debug THEN SAY "LEAVE PROCEDURE INSERT_TADM65A1..."
RETURN;
/*-- quote text t with apostrophs (sql string)
truncate if longer then 18 characters ---------------------------*/
cut18: procedure expose m.
parse arg t
if length(t) <= 18 then
return t
else
return left(space(t, 1), 18)
endProcedur cut18
csv4obj: procedure expose m.
parse arg o, ff, hasNull, oNull
res = ''
do fx=1 to m.ff.0
of1 = o || left('.', m.ff.fx \== '') || m.ff.fx
v1 = m.of1
if hasNull & v1 = oNull then
res = res','
else if v1 = '' then
res = res',""'
else if pos(',', v1) > 0 | pos('"', v1) > 0 then
res = res','quote(v1, '"')
else
res = res','v1
end
return substr(res, 2)
endProcedure csv4obj
/*_____________________________________________________________________
!!
!! DSN erstellen für RZ4
!!_____________________________________________________________________
*/
writeAblfAll: procedure expose m.
parse arg pre
call writeAblf to, fftimeO, pre'.TADM60A1'
call writeAblf uow, ffUow, pre'.TADM63A1'
call writeAblf Loes, ffLockE, pre'.TADM64A1'
call writeAblf ReEot, ffEOT, pre'.TADM65A1'
return 0
endProcedure writeAblfAll
/*______________________________________________________________________
!!
!!new dsn write
!!______________________________________________________________________
*/
writeAblf: procedure expose m.
parse arg st, ff, dsn
do sx=1 to m.st.0
o.sx = csv4obj(st'.'sx, ff, 0)
end
dsn=dsn'.D'date('j')'.T'translate(124578, time(), 12345678)
call writeDsn dsn '::v300', 'O.', m.st.0, 1
return
endProcedure writeAblf
/*--- add one or several arguments to stem m.a -----------------------*/
mAdd: procedure expose m.
parse arg a
ix = m.a.0
do ax = 2 to arg()
ix = ix + 1
m.a.ix = arg(ax)
end
m.a.0 = ix
return a'.'ix
endProcedure mAdd
/* copy eJes begin ***************************************************/
eJesJobExtDD: procedure expose m.
parse arg jMask, dd
call eJesInit jMask
call eJesExec '* st', job_
say 'jMask='jMask':' job_lines 'jobs'
do jx=1 to job_lines
call eJesExec "0 locate" jx, job_
cc = eJesExec("* :s", ds_, 4 8)
if cc <> 0 then do
if ds_msgShort = 'Job no longer found' then
say 'job' job_jobName.jx 'no longer found'
else
call eJesErr ds_, ':s' job_jobName.jx, cc
iterate
end
say 'job' jx job_jobName'('job_jobId'):' ds_lines 'datasets'
cc = eJesExec("0 f" dd" 3 10", ds_, 4 8)
if cc <> 0 | ds_tcdata.ds__ddName <> dd then do
if ds_msgShort = 'String not found' then
say 'dd' dd 'not found' ds_tcdata.ds__ddName
else
call eJesErr ds_, 'dd' ds_tcdata.ds__ddName,
'<> searched' dd, cc
end
else do
say 'dd='dd 'step='ds_tcdata.ds__sName ,
'recs='ds_tcdata.ds__records
call eJesExec "0 :e", ex_
say strip(ex_msg.0 ex_msg.1) '==> dd eJesExt'
end
call eJesExec "0 end", ds_
end
call eJesTerm
return
endProcedure
eJesExec:
parse arg ggS1 ggStmt, ggPrf, ggRet
if ggPrf == '' then
ggCC =eJesRexx("execApi" ggS1" '"ggStmt"'")
else
ggCC =eJesRexx("execApi" ggS1" '"ggStmt"' (prefix" ggPrf)
if ggCC <> 0 & wordPos(ggCC, ggRet) < 1 then
call eJesErr ggPrf, ggStmt, ggCC
return ggCC
endProcedure eJesExec
eJesInit: procedure expose m.
parse arg ggPref
ggCC =eJesRexx('initApi')
if ggCC <> 0 & eJes_MsgShort <> 'SAF security disabled' then
call eJesErr eJes_, 'initApi', ggCC
call eJesExec "0 pReset"
call eJesExec "0 owner=" /* sonst gibts nur meine */
call eJesExec "0 maskChar *%" /* default ist space */
if ggPref \== '' then
call eJesExec "0 jName="ggPref
return
endProcedure eJesInit
eJesTerm: procedure expose m.
cc =eJesRexx('termApi')
if cc <> 0 then
call err 'termApi CC='cc eJes_msgShort
return
endProcedur eJesTerm
eJesErr:
parse arg ggPrf, ggMsg, ggEE
if ggPrf == '' then
ggPrf = 'EJES_'
call eJesScreen ggPrf, 'eJesErr CC='ggEE ggMsg
ggMsg = strip(ggMsg 'cc='ggEE ,
'msg.0='value(ggPrf'Msg.0') 'prefix='ggPrf) ,
|| '\n 'strip(value(ggPrf'MsgShort'))
if datatype(value(ggPrf'Msg.0'), 'n') then
do ggX=1 to value(ggPrf'Msg.0')
ggMsg = ggMsg'\n 'strip(value(ggPrf'Msg.ggX'))
end
call eJesTerm
call err 'eJes' ggMsg
endProcedure eJesErr
eJesMsg:
parse arg ggPrf, ggMsg
say strip(ggMsg value(ggPrf'MsgShort'),
'msg.0='value(ggPrf'Msg.0') 'prefix='ggPrf)
if datatype(value(ggPrf'Msg.0'), 'n') then
do ggX=1 to value(ggPrf'Msg.0')
say 'msg.'ggX'='value(ggPrf'Msg.ggX')
end
return
endProcedure eJesMsg
eJesScreen:
parse arg ggPrf, ggMsg
call eJesMsg ggPrf, ggMsg
say left('eJes screen fun='value(ggPrf'FunName') ,
value(ggPrf'FunType') 'Image.0='value(ggPrf'scrImage.0') ,
, 78, '-')
if datatype(value(ggPrf'scrImage.0'), 'n') then
do ggX=1 to value(ggPrf'scrImage.0')
if value(ggPrf'scrImage.ggX') <> '' then
say strip(value(ggPrf'scrImage.ggX'), 't')
end
return
endProcedure eJesScreen
/* copy eJes end ***************************************************/
/* copy ii begin ********* Installation Info *************************/
iiini: procedure expose m.
if m.ii_ini == 1 then
return
m.ii_ini = 1
m.ii_ds.org = ORG.U0009.B0106.KLEM43
m.ii_ds.db2 = DSN.DB2
m.ii_rz = ''
i = 'RZ0 0 T S0 RZ1 1 A S1' ,
'RZX X X X2 RZY Y Y Y2 RZZ Z Z Z2' ,
'RQ2 Q Q Q2 RR2 R R R2 RZ2 2 B S2 RZ4 4 D S4'
do while i <> ''
parse var i rz m.ii_rz2c.rz m.ii_rz2plex.rz sys i
if rz <> RZ0 & rz <> RZ1 then
m.ii_rz = strip(m.ii_rz rz)
m.ii_rz2Sys.rz = sys
m.ii_sys2rz.sys = rz
end
i = 'DBTF T DTF DVTB V DTB DBOC C DOC DBTC B DBT DBIA A DIA' ,
'DE0G E DE0 DEVG M DEV DPXG I DPX DX0G X DX0' ,
'DPYG Y DPY DPZG N DPZ' ,
'DBOF F DOF DVBP P DBP DP2G Q DP2 DBOL O DOL DP4G U DP4'
do while i <> ''
parse var i db m.ii_db2c.db mbr i
m.ii_mbr2db.mbr = db
m.ii_db2Elar.db = wordPos(db, 'DVTB DVBP DEVG') > 0
end
m.ii_rz2db.rz0 = 'DBTC DBIA'
m.ii_rz2db.rz1 = 'DBTF DVTB DBOC'
m.ii_rz2db.rzx = 'DE0G DEVG DX0G DPXG'
m.ii_rz2db.rzy = 'DE0G DEVG DPYG'
m.ii_rz2db.rzz = 'DE0G DEVG DPZG'
m.ii_rz2db.rq2 = 'DBOF DVBP DP2G'
m.ii_rz2db.rr2 = 'DBOF DVBP DP2G'
m.ii_rz2db.rz2 = 'DBOF DVBP DP2G'
m.ii_rz2db.rz4 = 'DBOL DP4G'
i = ''
do rx=1 to words(m.ii_rz)
rz = word(m.ii_rz, rx)
i = i repAll(' 'space(m.ii_rz2db.rz, 1), ' ', ' 'rz'/')
end
m.ii_rzDb = space(i, 1)
return
endProcedure iiIni
iiDS: procedure expose m.
parse upper arg nm
return iiLazy(ii_ds, nm, 'ds')
iiMbr2DbSys: procedure expose m.
parse upper arg mbr
return iiLazy(ii_mbr2db, left(mbr, 3), 'member')
iiRz2C: procedure expose m.
parse upper arg rz
return iiLazy(ii_rz2c, rz, 'rz')
iiRz2P: procedure expose m.
parse upper arg rz
return iiLazy(ii_rz2plex, rz, 'rz')
iiRz2Dsn: procedure expose m.
parse upper arg rz
return overlay('Z', rz, 2)
iiDBSys2C: procedure expose m.
parse upper arg db
return iiLazy(ii_db2c, db, 'dbSys')
iiSys2RZ: procedure expose m.
parse upper arg sys
return iiLazy(ii_sys2rz, left(sys, 2), 'sys')
iiLazy: procedure expose m.
parse arg st, key, txt
if symbol('m.st.key') == 'VAR' then
return m.st.key
if m.ii_ini == 1 then
return err('no' txt'='key 'in ii' st)
call iiIni
return iiLazy(st, key, txt)
endProcedure iiLazy
iiVPut:procedure expose m.
parse upper arg rz '/' db .
call vPut 'rz', rz
call vPut 'rzC', iiRz2C(rz)
call vPut 'rzP', iiRz2P(rz)
call vPut 'rzD', iiRz2Dsn(rz)
call vPut 'dbSys', db
call vPut 'dbSysC', iidbSys2C(db)
call vPut 'dbSysElar', iiLazy(ii_db2Elar, db)
return 1
endProcedure iiVPut
iiIxVPut:procedure expose m.
parse arg ix
if ix > words(m.ii_rzDb) then
return 0
else
return iiVPut(word(m.ii_rzDb, ix))
endProcedure iiIxVPut
/* copy ii end ********* Installation Info *************************/
/* copy SQL begin ***************************************************
Achtung: inc generiert sql aus sqlRx, Aenderungen nur in sqlRx|
sql interface
***********************************************************************/
/*--- initialize sql -----------------------------------------------*/
sqlIni: procedure expose m.
if m.sql_ini == 1 then
return
m.sql_ini = 1
call utIni
m.sqlNull = '---'
m.sqlInd = 'sqlInd'
m.sql_defCurs= 49
m.sqlCAMsg = 0
m.sqlSuMsg = 2
m.sql_dbSys = ''
m.sql_csmhost = ''
isInProd = wordPos(sysvar(sysNode), 'RZ2') > 0
m.sql_retOk = 'dne' copies('rod', \ isInProd)
return 0
endProcedure sqlIni
/*--- get default db2System ------------------------------------------*/
sqlDefaultSys: procedure expose m.
parse arg
if sysvar(sysnode) == 'RZ1' then
return 'DBAF'
else if sysvar(sysnode) == 'RZ4' then
return 'DP4G'
else if sysvar(sysnode) == 'RZX' then
return 'DX0G'
else
call err 'no default subsys for' sysvar(sysnode)
endProcedure sqlDefaultSys
/*--- connect to the db2 subsystem sys -----------------------------*/
sqlConnect: procedure expose m.
parse upper arg sys, ggRetOk
call sqlIni
address tso "SUBCOM DSNREXX"
if rc <> 0 then do
sRc = RXSUBCOM('ADD','DSNREXX','DSNREXX') /* ADD HOST CMD ENV */
if sRc <> 0 then
call err 'rc' sRc 'from RXSUBCOM(ADD, DSNREXX, DSNREXX)'
end
if sys = '-' then
return 0
if sys == '' then
sys = sqlDefaultSys()
m.sql_dbSys = sys
ggSqlStmt = 'connect' sys
address dsnRexx ggSqlStmt
if rc = 0 then
return 0
interpret sqlErrorHandler(rc, ggRetOk, ggSqlStmt)
return sqlCode
endProcedure sqlConnect
/*--- diconnect from db2 ---------------------------------------------*/
sqlDisconnect: procedure expose m.
parse arg retOk
if m.sql_dbSys == '' then
return 0
ggSqlStmt = 'disconnect'
m.sql_dbSys = ''
m.sql_csmHost = ''
address dsnRexx ggSqlStmt
if rc = 0 then
return 0
interpret sqlErrorHandler(rc, retOk, ggSqlStmt)
return sqlCode
endProcedure sqlDisconnect
/*--- reset sql cursor 'c'cx fields ----------------------------------*/
sqlReset: procedure expose m.
parse arg cx
m.sql.cx.updateCount = ''
m.sql.cx.resultSet = ''
m.sql.cx.d.sqlD = 'noSqlDA'
m.sql.cx.i.sqlD = 'noDescInp'
m.sql.cx.fetchVars = ''
m.sql.cx.fetchFlds = ''
m.sql.cx.fetchClass = ''
m.sql.cx.type = ''
return
endProcedue sqlReset
/*--- execute a query from sql, with one resultset -------------------*/
sqlQuery: procedure expose m.
parse arg cx, src, feVa, retOk
call sqlReset cx
s1 = ''
if pos(left(feVa, 1), '?:') < 1 then
s1 = 'into :M.SQL.'cx'.D'
res = sqlExec('prepare s'cx s1 'from :src', retOk)
if res < 0 then
return res
call sqlFetchVars cx, feVa
call sqlExec 'declare c'cx 'cursor for s'cx
res = sqlExec('open c'cx, retOk)
if res < 0 then
return res
m.sql.cx.updateCount = sqlErrd.3
m.sql.cx.resultSet = cx
return res
endProcedure sqlQuery
/*--- prepare a query from sql, with one resultset -------------------*/
sqlQueryPrepare: procedure expose m.
parse arg cx, src, feVa, retOk
call sqlReset cx
s1 = ''
if feVa == '' | feVa = 'd' then
s1 = 'into :M.SQL.'cx'.D'
res = sqlExec('prepare s'cx s1 'from :src', retOk)
if res < 0 then
return res
call sqlFetchVars cx, feVa
call sqlExec 'declare c'cx 'cursor for s'cx
return sqlExec('describe input s'cx 'into :M.SQL.'cx'.I')
endProcedure sqlQueryPrepare
sqlQueryExecute: procedure expose m.
parse arg cx retOk
do ix=1 to arg()-1
call sqlDASet cx , 'I', ix, arg(ix+1)
end
res = sqlExec('open c'cx 'using descriptor :M.SQL.'cx'.I', retOk)
if res < 0 then
return res
m.sql.cx.updateCount = sqlErrd.3
m.sql.cx.resultSet = cx
return res
endProcedure sqlQueryExecute
/*--- fetch next row to m.dst.* at end return false ------------------*/
sqlFetch: procedure expose m.
parse arg cx, dst, retOk
fetCode = sqlExec('fetch c'cx 'into' m.sql.cx.fetchVars, 100 retOk)
if fetCode == 100 then
return 0
if fetCode < 0 then
return fetCode
interpret m.sql.cx.fetchCode
return 1
endProcedure sqlFetch
/*--- close cursor 'c'cx ---------------------------------------------*/
sqlClose: procedure expose m.
parse arg cx, retOk
return sqlExec('close c'cx, retOk)
endProcedure sqlClose
/*-- execute an sql with no resultset, but possibly outParms ---------*/
sqlUpdate: procedure expose m.
parse arg cx, src, retOk
m.sql.cx.updateCount = ''
m.sql.cx.resultSet = ''
bx = verify(src, '( ')
if bx > 0 then
fun = translate(word(substr(src, bx), 1))
if fun = 'SET' then do
w2 = translate(word(substr(src, bx), 2))
if \ abbrev(w2, ':') then
return sqlExec('execute immediate :src', retOk)
trace ?r
ex = pos('=', w2)
if ex = 0 then
ex = length(w2)+1
var = strip(substr(w2, 2, ex-2))
if var = '' then
call err 'bad hostVar in' src
m.sql.outVar = var
src2 = 'set :M.sql.out.'var substr(w, ex) subword(src, 3)
return sqlExec(src2, retOk)
end
if fun == 'DECLARE' then do
if 'GLOBAL' == translate(word(substr(src, bx), 2)) then
return sqlExec('execute immediate :src', retOk)
end
res = sqlExec(src, retOk)
if wordPos(fun, 'DELETE INSERT UPDATE') > 0 then
m.sql.cx.updateCount = sqlErrd.3
return res
endProcedure sqlUpdate
/*-- prepare an update -----------------------------------------------*/
sqlUpdatePrepare: procedure expose m.
parse arg cx, src, retOk
res = sqlExec('prepare s'cx 'from :src', retOk)
return sqlExec('describe input s'cx 'into :M.SQL.'cx'.I')
endProcedure sqlUpdatePrepare
/*-- execute a prepared update with the given arguments --------------*/
sqlUpdateExecute: procedure expose m.
parse arg cx retOk
do ix=1 to arg()-1
call sqlDASet cx , 'I', ix, arg(ix+1)
end
res = sqlExec('execute s'cx 'using descriptor :M.SQL.'cx'.I',
, retOk)
m.sql.cx.updateCount = sqlErrd.3
return res
endProcedure sqlUpdateExecute
/*-- execute a query, update or call ---------------------------------*/
sqlExecute: procedure expose m.
parse arg cx, src, retOk
f = translate(word(src, 1))
bx = pos('(', f)
if bx > 0 then
f = left(f, max(1, bx-1))
m.sql.cx.fun = f
if f == 'SELECT' | f == 'WITH' | f == '(' then
return sqlQuery(cx, src, , retOk)
else if f == 'CALL' then
call err 'implement sql call for:' src
else
return sqlUpdate(cx, src, retOk)
endProcedure sqlExecute
/*-- fetch all rows to stem and close --------------------------------*/
sqlFetch2St: procedure expose m.
parse arg cx, dst, retOK
do sx=1 while sqlFetch(cx, dst'.'sx)
end
res = sx-1
m.dst.0 = sx-1
call sqlClose cx
return m.dst.0
endProcedure sqlFetch2St
/*-- execute a query, copy result to stem ----------------------------*/
sql2St: procedure expose m.
parse arg src, dst, feVa, retOK
cx = m.sql_defCurs
res = sqlQuery(cx, src, feVa, retOk)
return sqlFetch2St(cx, dst, retOk)
endProcedure sql2St
/*-- return first column of the only row and close -------------------*/
sqlFetch2One: procedure expose m.
parse arg cx, dst, retNone
f1 = sqlFetch(cx, dst)
if f1 then
f2 = sqlFetch(cx, dst'.2')
call sqlClose cx
if \ f1 then
if retNone \== '' then
return substr(retNone, 2)
else
call err 'sqlFetch2One: no row returned'
else if f2 then
call err 'sqlFetch2One: more than 1 row'
if m.sql.cx.fetchFlds == '' then do
c1 = substr(word(m.sql.cx.fetchVars, 1), 2)
res = value(c1)
return res
end
c1 = word(m.sql.cx.fetchFlds, 1)
return m.dst.c1
endProcedure sqlFetch2One
/*-- execute a query and return first column of the only row
if > 1 row fail, if 0 rows return arg(3) or fail ----------*/
sql2One: procedure expose m.
parse arg src, dst, feVa, retOk, retNone
cx = m.sql_defCurs
call sqlQuery cx, src, feVa, retOk
return sqlFetch2One(cx, dst, retNone)
endProcedure sql2One
/*--- describe table and return sqlDA --------------------------------*/
sqlDescribeTable: procedure expose m.
parse upper arg tb, force
if force == 1 | \ datatype(m.sql.table.tb.sqlD, 'n') then
call sqlExec 'describe table :tb into :M.SQL.TABLE.'tb
return 'SQL.TABLE.'tb
endProcedure sqlDescribeTable
/*--- return select column list for table tb
omit Blobs (blobMax < 0) or substr(blob, 1, blobMax) ----------*/
sqlColList: procedure expose m.
parse arg tb al, blobMax
sd = sqlDescribeTable(tb)
bs = ''
lst = ''
if al \== '' & right(al, 1) \== '.' then
al = al'.'
do sx=1 to m.sd.sqld
if wordPos(m.sd.sx.sqlType, '404 405 408 409 412 413') < 1 then
lst = lst',' al || m.sd.sx.sqlName
else do
bs = bs m.sd.sx.sqlName
if blobMax >= 0 then
lst = lst', length('al || m.sd.sx.sqlName')' ,
m.sd.sx.sqlName'Len' ,
|| ', substr('al || m.sd.sx.sqlName ,
|| ', 1,' blobMax')' m.sd.sx.sqlName
end
end
m.sd.colList = substr(lst, 3)
m.sd.blobs = strip(bs)
return substr(lst, 3)
endProcedure sqlColList
/*--- use describe output to generate column names,
'' use names from ca (rexxified)
nms+ use names, check ca for null values
?('?'?nm)+ use names, check for null if preceeded by ?
:... use string as is
fetchVariables and sqlNull names ---------------------*/
sqlFetchVars: procedure expose m.
parse arg cx, src, cd
st = 'SQL.'cx'.COL'
if abbrev(src, ':') then do
m.sql.cx.fetchVars = src
m.sql.cx.fetchCode = cd
m.sql.cx.fetchFlds = ''
return
end
m.sql.cx.fetchVars = ''
if abbrev(src, '?') then do
call err implement + rxFetchVars ?????? /*
r = substr(src, 2)
do wx=1 to words(src)
cn = word(src, wx)
if abbrev(cn, '?') then
call sqlRexxAddVar substr(cn, 2), 0, 1
else
call sqlRexxAddVar cn, 0, 0
end ????????????? */
end
else if src <> '' then do
ff = src
end
else do
ff = ''
do kx=1 to m.sql.cx.d.sqlD
ff = sqlNiceVarsAdd(ff, m.sql.cx.d.kx.sqlName)
end
end
m.sql.cx.fetchFlds = ff
if m.sql.cx.d.sqlD <> words(ff) then
call err 'sqlFetchVars sqlD='m.sql.cx.d.sqlD ,
'<>' words(ff) 'fields of' ff
sNu = ''
sFe = ''
do kx=1 to m.sql.cx.d.sqlD
nm = word(ff, kx)
sFe = sFe', :m.dst.'nm
if m.sql.cx.d.kx.sqlType // 2 then do
sFe = sFe' :m.dst.'nm'.sqlInd'
sNu = sNu 'if m.dst.'nm'.sqlInd < 0 then' ,
'm.dst.'nm '= m.sqlNull;'
end
end
m.sql.cx.fetchVars = substr(sFe, 3)
m.sql.cx.fetchCode = sNu cd
return
endProcedure sqlFetchVars
/* ????????????
sqlRexxAddVar:
parse arg nm, nicify, hasNulls
nm = sqlAddVar(st, nm, nicify)
if \ hasNulls then
vrs = vrs', :m.dst.'nm
else do
vrs = vrs', :m.dst.'nm ':m.dst.'nm'.sqlInd'
sNu = sNu 'if m.dst.'nm'.sqlInd < 0 then' ,
'm.dst.'nm '= m.sqlNull;'
end
return
endSubroutine sqlRexxAddVar ?????? */
sqlNiceVarsAdd: procedure expose m.
parse arg old, nx rest
upper nx
cx = verifId(nx)
if cx > 0 then /* avoid bad characters for classNew| */
nx = left(nx, cx-1)
if nx <> '' & wordPos(nx, old) < 1 0 then
return old nx
else
return old 'COL' || (words(old) + 1)
endProcedure sqlNiceVarsAdd
/*--- set one value in a DA, handle nulls ----------------------------*/
sqlDASet: procedure expose m.
parse arg cx, da, ix, val
m.sql.cx.da.ix.sqlData = val
m.sql.cx.da.ix.sqlInd = - (val == m.sqlNull)
/* data types schienen einmal nicht zu funktionieren .......
if wordPos(m.da.ix.sqlType, '384 385 388 389 392 393') > 0 then
m.da.ix.sqlType = 448 + (m.da.ix.sqlType // 2) */
return
endProcedure sqlDASet
sqlCommit: procedure expose m.
parse arg src
return sqlUpdate(, 'commit')
endProcedure sqlCommit
/*--- execute sql thru the dsnRexx interface -------------------------*/
sqlExec: /* no procedure, to keep variables sql... */
parse arg ggSqlStmt, ggRetOk
m.sql_HaHi = ''
address dsnRexx 'execSql' ggSqlStmt
/* say 'sqlCode' sqlCode 'rc' rc 'for' ggSqlStmt ggNo */
if rc = 0 then
return 0
interpret sqlErrorHandler(rc, ggRetOk, ggSqlStmt)
return sqlCode
endProcedure sqlExec
sqlExecMsg: procedure expose m.
parse arg sql
sc = sqlExec(sql, '*')
return sqlMsgLine(sc, , sql)
sqlErrorHandler: procedure expose m. ,
sqlCode sqlErrMc sqlErrP sqlErrD. sqlWarn. sqlState
parse arg drC, retOk, verb rest
if drC == 0 then
return 'return 0'
if pos('-', retOK) < 1 then
retOK = retOk m.sql_retOk
if wordPos(drC, '1 -1') < 1 then do
eMsg = "'dsnRexx rc="drC"' sqlmsg()"
end
else if pos('*', retOK) > 0 | wordPos(sqlCode, retOK) > 0 then do
if sqlCode < 0 & pos('say', retOK) > 0 then
return "call outNl errMsg(' }'sqlMsg())"
else
return ''
end
else do
upper verb
if verb == 'DROP' then do
if (sqlCode == -204 | sqlCode == -458) ,
& wordPos('dne', retok) > 0 then
return 'return' sqlCode
if sqlCode = -672 & wordPos('rod', retok) > 0 then do
hahi = m.sql_HaHi || sqlMsgLine(sqlCode,
, 'tb='sqlErrMc ,verb rest)'\n'
haHi = haHi || sqlExecMsg('alter table' SqlErrMc ,
'drop restrict on drop')'\n'
call sqlExec verb rest
m.sql_HaHi = hahi
return ''
end
end
if drC < 0 then
eMsg = "sqlmsg()"
else if (sqlCode<>0 | sqlWarn.0 ^==' ') & pos('w',retOK)<1 then
return "call outNl errMsg(' }'sqlMsg()); return" sqlCode
else
return ''
end
if wordPos('rb', retok) > 0 then
eMsg = eMsg " || '\n"sqlExecMsg('rollback')"'"
if wordPos('ret', retok) < 1 then
return "call err" eMsg
m.sql_errRet = 1
return 'call outNl' eMsg
endProcedure sqlErrorHandler
sqlMsgLine: procedure expose m. sqlErrD.
parse arg res, cnt, src, plus
verb = translate(word(src, 1))
if datatype(res, 'n') then
res = 'sqlCode' res
if cnt \== '' then do
res = res',' cnt
vx = wordPos(translate(word(src,1)), 'DELETE INSERT UPDATE')
if datatype(cnt, 'n') then
if vx > 0 then
res = res 'rows' word('deleted inserted updated', vx)
else if cnt <> 0 then
res = res 'rows updated'
end
if plus \== '' then
res = res',' plus
if abbrev(res, ', ') then
res = substr(res, 3)
if src \== '' then do
ll = 75 - length(res)
aa = strip(src)
if length(aa) > ll then
aa = space(aa, 1)
if length(aa) > ll then
aa = left(aa, ll-3)'...'
res = res':' aa
end
return res
endProcedure sqlMsgLine
/*--- send a command to db2 through the TSO dsn processor ------------*/
sqlDsn: procedure expose m.
parse arg st, sys, cmd, rcOk
x = outtrap('M.'st'.')
push 'END'
push cmd
address tso 'DSN SYSTEM('sys')'
rr = rc
x = outtrap(off)
if rr = 0 | rcOk = '*' | wordPos(rr, rcOk) > 0 then
return rr
fl = max(1, m.st.0 - 10)
em = 'rc' rr 'for DSN SYSTEM('sys') cmd' cmd,
'\nOuputlines' fl '-' m.st.0':'
do lx=fl to m.st.0
em = em '\n' strip(m.st.lx, 't')
end
call err em
endProcedure sqlDsn
/*--- issue an sql error message -------------------------------------*/
sqlMsg: /* no procedure, to keep variables sql... */
if \ dataType(sqlCode, 'n') then do
ggRes = 'sqlCode' sqlCode 'not numeric\n'sqlCaMsg()
end
else do
ggRes = sqlDsntiar(sql2CA())
ggWa = sqlMsgWarn()
if ggWa \= '' then
ggRes = ggRes'\nwarnings' ggWa
if m.sqlCAMsg == 1 then
ggRes = ggRes'\n'sqlCaMsg()'\n'sqlCaMsg(sqlCa2Rx(sqlCa))
end
ggSt = 'SQL_HOST'
ggVa = 'SQL_HOST.VAR'
ggBe = 'SQL_HOST.BEF'
call sqlHostVars ggSqlStmt, 12, ggSt
ggFrom = 'ggSqlStmt'
ggW1 = translate(word(ggSqlStmt, 1))
ggW2 = translate(word(ggSqlStmt, 2))
if ggW1 == 'PREPARE' then
ggFrom = sqlHostVarFind(ggSt, 'FROM')
else if ggW1 ggW2 == 'EXECUTE IMMEDIATE' then
ggFrom = sqlHostVarFind(ggSt, 1)
ggPos = 0
if datatype(sqlErrd.5, 'n') & sqlErrd.5 > 0 then do
ggPos = sqlErrd.5
ggRes = ggRes || sqlMsgSrcPos(value(ggFrom), sqlErrd.5)
end
if ggFrom == 'ggSqlStmt' then do
ggRes = ggRes'\nsql =' sqlShorten(ggSqlStmt, 2000, ggPos)
end
else do
ggRes = ggRes'\nsql =' sqlShorten(value(ggFrom), 2000, ggPos)
ggRes = ggRes'\nstmt =' sqlShorten(ggSqlStmt, 2000)
end
ggPref = '\nwith'
do ggXX=1 to m.ggSt.0
if ggFrom = m.ggVa.ggXX then
iterate
ggRes = ggRes || ggPref m.ggBe.ggXX ':'m.ggVa.ggXX ,
'=' sqlShorten(value(m.ggVa.ggXX), 210)
ggPref = '\n '
end
if m.sqlSuMsg == 1 | (m.sqlSuMsg == 2 & m.sql_csmhost \== '') then
ggRes = ggRes'\nsubsys =' m.sql_dbSys ,
|| ', host =' m.sql_csmhost
return ggRes
endSubroutine sqlMsg
sqlShorten: procedure expose m.
parse arg txt, maxL, pos
if length(txt) <= maxL then
return txt
if \ datatype(pos, 'n') | pos < 1 then
pos = 1
ex = pos + min(60, maxL%7)
if ex <= maxL - 4 then
return left(txt, maxL-4) '...'
if ex >= length(txt) then
return left(txt, 67) '...\n'substr(txt, length(txt)-maxL+72)
else
return left(txt, 67) '...\n'substr(txt, ex-maxL+76, maxL-75) ,
'...'
endProcedure sqlShorten
/*--- use dsnTiar to translate sql Info to error text ----------------*/
sqlDsnTiar: procedure expose m.
parse arg ca
if -438 = sqlCa2Rx(ca) then
return '\nSQLCODE = -438:',
'APPLICATION RAISED ERROR WITH sqlState' sqlState ,
'and DIAGNOSTIC TEXT:' sqlErrMc
liLe = 78
msLe = liLe * 10
msg = d2c(msLe,2) || left('', msLe)
len = d2c(liLe, 4)
ADDRESS LINKPGM "DSNTIAR ca msg len"
if rc = 0 then nop
else if rc = 4 then say 'warn linkPgm dsnTiar rc' rc 'sqlCa' ca
else call err 'linkPgm dsnTiar rc' rc 'sqlCa' ca
res = strip(substr(msg, 13, liLe-10))
cx = pos(', ERROR: ', res)
if cx > 0 then
res = left(res, cx-1)':' strip(substr(res, cx+9))
do c=3+liLe by liLe to msLe while substr(msg, c, 10) = ''
res = res'\n 'strip(substr(msg, c+10, liLe-10))
end
return res
endProcedure sqlDsnTiar
/*--- format all rexx sqlCa fields into a message --------------------*/
sqlCaMsg:
return 'sqlCode' sqlCode 'sqlState='sqlState ,
'\n errMC='translate(sqlErrMc, ',', 'ff'x) ,
'\n warnings='sqlWarnCat('+') 'erP='sqlErrP ,
'\n errD.1='sqlErrD.1 '2='sqlErrD.2 '3='sqlErrD.3 ,
'\n errD.4='sqlErrD.4 '5='sqlErrD.5 '6='sqlErrD.6
endProcedure sqlCaMsg
/*--- format the sqlCA into the dsnTiar SQLCA ------------------------*/
sql2Ca: procedure expose m. ,
sqlCode sqlErrMc sqlErrP sqlErrD. sqlWarn. sqlState
if \ (datatype(sqlcode, 'n') & datatype(sqlErrD.1, 'n') ,
& datatype(sqlErrD.3, 'n')) then
return err('sqlCode etc. not numeric\nsqlCa =' sqlCaMsg())
if digits() < 10 then
numeric digits 10
sqlCa = 'SQLCA ' || d2c(136, 4) || d2c(sqlCode, 4) ,
|| d2c(min(70, length(sqlErrMc)), 2)left(sqlErrMc, 70) ,
|| left(sqlErrP, 8) ,
|| d2c(sqlErrD.1, 4)d2c(sqlErrD.2, 4)d2c(sqlErrD.3, 4) ,
|| d2c(sqlErrD.4, 4)d2c(sqlErrD.5, 4)d2c(sqlErrD.6, 4) ,
|| sqlWarnCat() || sqlState
if length(sqlCa) <> 136 then
call err 'sqlCa length' length(sqlCa) 'not 136' ,
'\n'sqlCaMsg() '==>' ca', hex='c2x(ca)
return sqlCa
endProcedure sql2Ca
/*--- extract the fields from the SqlCA and put it to rexx vars ------*/
sqlCA2Rx: procedure expose m. ,
sqlCode sqlErrMc sqlErrP sqlErrD. sqlWarn. sqlState
parse arg ca
numeric digits 10
if length(ca) < 136 | c2d(substr(ca, 9, 4), 4) <> 136 then
call err 'bad sqlCa len' length(ca) 'not 136:' ca', hex='c2x(ca)
sqlCode = c2d(substr(ca, 13 ,4), 4)
sqlErrMC = substr(ca, 19, c2d(substr(ca, 17, 2), 2))
sqlErrP = substr(ca, 89, 8)
do ix=1 to 6
sqlErrD.ix = c2d(substr(ca, 93 + 4 * ix, 4), 4)
end
do ix=0 to 10
sqlWarn.ix = substr(ca, 121 + ix, 1)
end
sqlState = substr(ca, 132, 5)
return sqlCode
endProcedure sqlCA2Rx
/*--- concat the sql warnings with Separator sep --------------------*/
sqlWarnCat: procedure expose m. sqlWarn.
parse arg sep
return sqlWarn.0 || sep,
|| sqlWarn.1||sqlWarn.2||sqlWarn.3||sqlWarn.4||sqlWarn.5||sep ,
|| sqlWarn.6||sqlWarn.7||sqlWarn.8||sqlWarn.9||sqlWarn.10||sep
endProcedure sqlWarnCat
/*--- make the text for sqlWarnings ----------------------------------*/
sqlMsgWarn: procedure expose m. sqlWarn.
r = ''
text =' 1=W var truncated, 1=S scrollable, 1=N nonScrollable,' ,
'2=W nulls in aggregate,' ,
'3=W more cols than vars,' ,
'3=Z more result sets than locators,' ,
'4=W no where, 4=D sensitive dynamic, 4=I insensitive,' ,
'4=S sensitive static,' ,
'5=W not valid sql, 5=1 readOnly, 5=2 readDelete,' ,
'5=3 readDeleteUpdate,' ,
'6=W day changed to month range,' ,
'7=W dec digits truncated,' ,
'8=W char substituted,' ,
'9=W arith excep in count, 9=Z multipe result sets,' ,
'10=W char conversion err in ca,'
do wx = 1 to 10
w = sqlWarn.wx
if w = ' ' then
iterate
t = wx'='w
cx = pos(' 'wx'='w' ', text)
ex = pos(',' , text, cx + 1)
if cx > 0 & ex > cx then
r = r substr(text, cx+1, ex-cx)
else
r = r wx'='w '?,'
end
r = strip(r, 't', ',')
if r = '' & sqlwarn.0 <> '' then
call err 'sqlWarn.0='sqlWarn.0 'but all warns empty'
return r
endProcedure sqlMsgWarn
/*--- show in the source src the point pos (where error occured)
a few lines from src around pos and arrow to pos ----------*/
sqlMsgSrcPos: procedure expose m.
parse arg src, pos
liLe = 68
liCn = 3
afLe = 25
t1 = space(left(src, pos), 1)
t2 = left(' ', substr(src, pos, 1) == ' ' ,
| substr(src, pos+1, 1) == ' ') ,
|| space(substr(src, pos+1), 1)
afLe = min(afLe, length(t2))
if length(t1) + afLe > liLe * liCn then
t1 = '...'right(t1, liLe * liCn - afLe -3)
else if length(t1)+length(t2) > liLe then
t1 = left(' ', (liCn * liLe - length(t1) -afLe) // liLe)||t1
pL = length(t1) // liLe
if length(t2) <= liLe-pL then
tx = t1 || t2
else
tx = t1 || left(t2, liLe-pL-3)'...'
res = '\nsrc' strip(substr(tx, 1, liLe), 't')
do cx=1+liLe by liLe to length(tx)
res = res || '\n +' strip(substr(tx, cx, liLe), 't')
end
loc = 'pos' pos 'of' length(src)
if length(loc)+6 < pL then
return res'\n >' right('>>>'loc'>>>', pL)
else
return res'\n >' left('', pL-1)'<<<'loc'<<<'
endProcdedure sqlMsgSrcPos
/*--- get the hostVars in the sql in src and the word before ---------*/
sqlHostVars: procedure expose m.
parse arg src, cnt, st
cx = 1
sx = 1
do cnt
cx = pos(':', src, cx) + 1
if cx < 2 then
leave
if pos(substr(src, cx, 1), m.ut_rxN1) > 0 then
iterate
ex = verify(src, m.ut_rxDot, 'n', cx)
if ex < 1 then
m.st.var.sx = substr(src, cx)
else
m.st.var.sx = substr(src, cx, ex - cx)
if m.st.var.sx == '' | length(m.st.var.sx) > 100 then
iterate
/* search word before */
do bE = cx-2 by -1 to 1 ,
while substr(src, bE, 1) == ' '
end
do bB = bE by -1 to max(1, bE-20),
while pos(substr(src, bB, 1), m.ut_alfa) > 0
end
if bB < bE & bB >= 0 then
m.st.bef.sx = substr(src, bB+1, bE-bB)
else
m.st.bef.sx = ''
sx = sx + 1
end
m.st.0 = sx-1
return sx
endProcedure sqlHostVars
/*--- find the name of hostvar, by index or by before ----------------*/
sqlHostVarFind: procedure expose m.
parse arg st, fnd
if datatype(fnd, 'n') & fnd <= m.st.0 then
return m.st.var.fnd
do ix=1 to m.st.0
if translate(m.st.bef.ix) = fnd then
return m.st.var.ix
end
return ''
endSubroutine sqlHostVarFind
/* copy SQL end **************************************************/
/* copy time begin ****************************************************
timestamp format yz34-56-78-hi.mn.st.abcdef
11.12.14 wk: added lrsn2uniq
11.05.13 wk: numeric digits transparent: in jeder Procedure drin
time2jul, tst externalisiert
**********************************************************************/
/*--- timestamp to julian --------------------------------------------*/
time2jul: procedure expose m.
parse arg yyyy '-' mm '-' dd '-'
if yyyy < 1100 then
yyyy = 11 || right(yyyy, 2, 0)
/* date function cannot convert to julian, only from julian
use b (days since start of time epoch) instead */
return right(yyyy, 2) ,
|| right(date('b', yyyy || mm || dd, 's') ,
- date('b', yyyy-1 || '1231', 's') , 3, 0)
endProcedure time2jul
/*--- current timestamp ---------------------------------------------*/
timestampNow: procedure expose m.
parse value date('s') time('l') with y 5 m 7 d t
return y'-'m'-'d'-'translate(t, '.', ':')
/*--- timestamp expand: expand to timestamp of 26 chars -------------*/
timestampExp: procedure expose m.
parse arg tst .
if length(tst) < m.timeStamp_Len then
return overlay(tst, m.timeStamp_01)
else
return left(tst, timeStamp_Len)
endProcedure tiemstampExp
/*--- timestamp check return '' if correct or error text ------------*/
timestampcheck: procedure expose m.
parse arg tst
if length(tst) < m.timestamp_d0Len | \ abbrev(m.timestamp_11,
, translate(tst, '111111111', '023456789')) then
return 'bad timestamp' tst
parse arg yyyy '-' mo '-' dd '-' hh '.' mm '.' ss '.' u
if mo < 1 | mo > 12 then
return 'bad month in timestamp' tst
if dd < 1 | dd > 3 || substr('101010110101', mo, 1) then
return 'bad day in timestamp' tst
if mo = 2 then
if dd > date('d', yyyy'0301', 's') - 32 then
return 'bad day in timestamp' tst
if hh > 23 & \ abbrev(24 00 00 000000, hh mm ss u) then
return 'bad hour in timestamp' tst
if mm > 59 then
return 'bad minute in timestamp' tst
if ss > 59 then
return 'bad second in timestamp' tst
return ''
endProcedure timestampCheck
/*--- timestamp to days since 1.1.0001 ------------------------------*/
timestamp2days: procedure expose m.
parse arg yyyy '-' mo '-' dd '-' hh '.' mm '.' ss
numeric digits 20
return date('b', yyyy || mo || dd, 's') ,
+ (((hh * 60) + mm) * 60 + ss) / 86400
/*--- days since 1.1.0001 (with time+microsecs) to timestamp --------*/
timeDays2tst: procedure expose m.
parse arg d
numeric digits 20
r = min(format(d // 1 * 86400, 5, 6), 86399.999999)
s = trunc(r)
t = date('s', trunc(d), 'b')
return left(t, 4)'-'substr(t, 5, 2)'-'right(t, 2) ,
|| '-' || right((s % 3600), 2, 0) ,
|| '.' || right((s // 3600 % 60), 2, 0) ,
|| '.' || right((s // 60), 2, 0) ,
|| substr(r, 6)
endProcedure timeDays2tst
/*--- timestamp diff in days ----------------------------------------*/
timestampDiff: procedure expose m.
parse arg t1, t2
numeric digits 20
return timestamp2days(t1) - timestamp2Days(t2)
/*--- convert 2 digit year to 4 digit -------------------------------*/
timeYear24: procedure expose m.
parse arg s
y = left(date('S'), 4)
s4 = left(y, 2)right(s, 2, 0)
if s4 > y + 30 then
return (left(y, 2) - 1)substr(s4, 3)
else if s4 < y - 69 then
return (left(y, 2) + 1)substr(s4, 3)
else
return s4
endProcedure timeYear24
/*--- convert 2 or 4 digit year Y (A=0...T=19) ----------------------*/
timeYear2Y: procedure expose m.
parse arg y
return substr('ABCDEFGHIJKLMNOPQRST' , ((y + 10) // 20) + 1, 1)
/*--- convert 2 or 4 digit year Y (A=0...T=19) ----------------------*/
timeY2Year: procedure expose m.
parse arg i
j = pos(i, 'ABCDEFGHIJKLMNOPQRST') - 1
if j < 0 then
call err 'timeYearY24 bad input' i
y = left(date('S'), 4)
r = y - (y+10) // 20 + j
if r < y - 15 then
return r + 20
else if r > y + 4 then
return r - 20
else
return r
endProcedure timeY2Year
/*--- convert numeric month to M (Jan=B=1, dec=12=M) ----------------*/
timeMonth2M: procedure expose m.
parse arg m
return substr('BCDEFGHIJKLM', m, 1)
/*--- convert M to numeric Month (Jan=B=1, dec=12=M) ----------------*/
timeM2Month: procedure expose m.
parse arg m
p = pos(m, 'BCDEFGHIJKLM')
if p= 0 then
call err 'bad M month' m
return right(p, 2, 0)
/*--- convert numeric hour 78 to H8 (A=0..D=3) ----------------------*/
timeHour2H: procedure expose m.
parse arg h
h = right(h, 2, 0)
return substr('ABCD', left(h, 1)+1, 1)substr(h, 2)
/*--- convert H8 to numeric Hour 78 (A=0..D=3) ----------------------*/
timeH2Hour: procedure expose m.
parse arg h
p = pos(left(h, 1), 'ABCD') - 1
if p < 0 | length(h) \== 2 then
call err 'bad H hour' h
return p || substr(h, 2)
/*--- read timeZoneOffset and leapSeconds registers
and set variables for uniq ----------------------------------*/
timeIni: procedure expose m.
parse arg debug
numeric digits 25
/* offsets documented in z/OS Data Areas Vol.1 */
cvtOH = '00000010' /* cvt control block Address */
cvtext2O = x2d('00000560') /* offset to extension 2 */
cvtldtoO = x2d('00000038') /* offset to timezone */
cvtlsoO = x2d('00000050') /* offset to leapSeconds */
/* CVT CB address + extention2 */
cvtExt2A = C2D(STORAGE(cvtOH,4)) + cvtext2O
/* cvtLdto timeZone address +offset */
m.time_Zone = c2d(STORAGE(d2x(cvtext2A+cvtldtoO, 8), 8))
m.time_StckUnit = 1e-6 / 256 / 16
/* cvtLso LeapSecs address +offset */
m.time_Leap = c2d(STORAGE(d2x(cvtext2A+cvtlsoO, 8), 8))
m.time_LZT_TAI10_us16 = (m.time_zone - m.time_leap) % 256
m.time_UQDigits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ012345678'
/* find lrsn of day 0, 0 out last 6 bits */
m.time_UQZero = x2d(left(timeTAI102Lrsn( ,
'2004-12-31-00.00.22.000000'), 14)) % 64 * 64
m.timeStamp_01 = '0001-01-01-00.00.00.000000'
m.timeStamp_11 = '1111-11-11-11.11.11.111111'
m.timeStamp_99 = '9999-12-31-23.59.59.999999'
m.timeStamp_len = length(m.timestamp_11)
m.timeStamp_d0Llen = m.timestamp_len - 7
m.time_ini = 1
return
endSubroutine timeIni
/*--- TAI10 timestamp yyyy-mm.... -> stckE value char(16)
BLSUETID is described in z/OS MVS IPCS Customization -------*/
timeTAI102stckE: procedure expose m.
/* timestamp must include microSeconds |||*/
parse arg year'-'mo'-'da'-'hh'.'mm'.'secs
tDate = mo'/'da'/'year hh':'mm'.'secs
ACC=left('', 16, '00'x)
ADDRESS LINKPGM "BLSUETID TDATE ACC"
RETURN acc
endProcedure timeTAI102stckE
timeTAI102lrsn: procedure expose m.
parse arg tst
return c2x(left(timeTAI102StckE(tst), 10))
timeLZT2stckE: procedure expose m.
parse arg tst
numeric digits 23
s =timeTAI102StckE(tst)
return d2c(c2d(left(s, 8)) - m.time_lzt_TAI10_us16,8) || substr(s,9)
endProcedure timeLZT2stckE
timeLZT2lrsn: procedure expose m.
parse arg tst
return c2x(left(timeLZT2StckE(tst), 10))
/*--- expand lrsn to 20 hexDigits -------------------------------*/
timeLrsnExp: procedure expose m.
parse arg s
return left(copies('00', length(s) <= 12 & \abbrev(s, 0))s, 20,0)
/*--- expand stcK(e) to 16 Byte ---------------------------------*/
timeStckExp: procedure expose m.
parse arg s
return left(copies('00'x, length(s) <= 8 & s >> '09'x)s, 16,'00'x)
/*--- conversion from StckE Clock Value to TAI10 Timestamp
BLSUETOD is described in z/OS MVS IPCS Customization --------*/
timeStckE2TAI10: PROCEDURE expose m.
parse arg stck /* must be 16 characters ||||| */
TDATE = left('' , 26)
ADDRESS LINKPGM "BLSUETOD stck TDATE"
/* return format : mo/dd/yyyy hh:mm:ss.uuuuuu */
/* Timestamp format: yyyy-mm-dd-hh.mm.ss.uuuuuu */
parse var TDATE mo '/' dd '/' yyyy hh ':' mm ':' secs
RETURN yyyy'-'mo'-'dd'-'hh'.'mm'.'secs
endProcedure timeStckE2TAI10
/*--- conversion from Lrsn Clock Value to TAI10 Timestamp ----------*/
timeLrsn2TAI10:
return timeStckE2TAI10(x2c(arg(1))'000000000000'x)
/*--- conversion from Lrsn Clock Value to LZT Local Zurich Time -----*/
timeStckE2LZT: procedure expose m.
parse arg s
numeric digits 23
return timeStckE2TAI10(d2c(c2d(left(s, 8)) ,
+ m.time_LZT_TAI10_us16, 8) || substr(s, 9))
endProcedure timeStckE2LZT
/*--- conversion from Lrsn Clock Value to LZT Local Zurich Time -----*/
timeLrsn2LZT: procedure expose m.
parse arg lrsn
return timeStckE2LZT(x2c(lrsn) || '000000000000'x)
/* convert a lrsn to the uniq variable ********************************/
timeLrsn2uniq: procedure expose m.
parse arg lrsn
/* unique are bits 0:41 of the TodClock value
minus 31.12.2004 represented
by base 35 by 'ABC...YZ01..8'
*/
lrsn = left(timeLrsnExp(lrsn), 14)
numeric digits 20
diff = x2d(lrsn) - m.time_UQZero
if diff < 0 then
return'< 2005'
return right(i2q(diff % 64, m.time_UQDigits), 8, 'A')
endProcedure timeLrsn2Uniq
/* convert a uniq variable to lrsn ************************************/
timeUniq2lrsn: procedure expose m.
parse arg uniq
numeric digits 20
u1 = q2i(left(uniq, 8, 'A'), m.time_UQDigits) * 64
lrsn = '00'right(d2x(u1 + m.time_UQZero), 12, 0)'000000'
return lrsn
endProcedure uniq2lrsn
/*--- translate a number in q-system to decimal
arg digits givs the digits corresponding to 012.. in the q sysem
q = length(digits) --------------------------------------------*/
q2i: procedure expose m.
parse arg v, digits
b = length(digits)
i = 0
do x = 1 to length(v)
q = substr(v, x, 1)
r = pos(q, digits)
if r < 1 then
call err 'bad digit' q 'in' v 'valid digits' digits
i = i * b + r - 1
end
return i
endProcedure q2i
/*--- translate a decimal number to q-system - inverse of q2i --------*/
i2q: procedure expose m.
parse arg i, digits
if i = 0 then
return left(digits, 1)
b = length(digits)
v = ''
do while i > 0
v = substr(digits, 1 + (i // b), 1) || v
i = i % b
end
return v
endProcedure i2q
/* copy time end -----------------------------------------------------*/
/* copy adrTso begin *************************************************/
/*--- send ggTsoCmd to tso, fail if rc <> 0 or not listed in ggRet ---*/
adrTso:
parse arg m.tso_stmt, ggRet
call outtrap m.tso_trap.
address tso m.tso_stmt
m.tso_rc = rc
call outtrap off
if m.tso_rc == 0 then
return 0
m.tso_trap = ''
do ggXx=1 to min(7, m.tso_trap.0)
m.tso_trap = m.tso_trap'\n'strip(m.tso_trap.ggXx)
end
if m.tso_trap.0 > 7 then do
if m.tso_trap.0 > 14 then
m.tso_trap = m.tso_trap'\n............'
do ggXx=max(8, m.tso_trap.0-6) to m.tso_trap.0
m.tso_trap = m.tso_trap'\n'strip(m.tso_trap.ggXx)
end
end
if ggRet <> '*' & wordPos(m.tso_rc, ggRet) <= 0 then
call err 'adrTso rc=' m.tso_rc 'stmt='m.tso_stmt m.tso_trap
return m.tso_rc
endSubroutine adrTso
/*--- format dsn from tso format to jcl format -----------------------*/
dsn2jcl: procedure expose m.
parse upper arg dsn ., addPrefix
if left(dsn,1) = "'" then
return strip(dsn, 'b', "'")
sp = sysvar('SYSPREF')
if sp == '' then
sp = userid()
cx = pos('~', dsn)
if cx < 1 & addPrefix == 1 then
return sp'.'dsn
do while cx \== 0
le = left(dsn, cx-1)
ri = substr(dsn, cx+1)
if right(le, 1) == '.' | left(ri, 1) == '.' then
dsn = le || sp || ri
else
dsn = le || left('.', le \== '') || sp ,
|| left('.', ri \== '') || ri
cx = pos('~', spec, cx)
end
return dsn
endProcedure dsn2Jcl
/*--- format dsn from jcl format to tso format -----------------------*/
jcl2dsn: procedure expose m.
parse arg dsn .
return "'"dsn"'"
endProcedure jcl2dsn
dsnSetMbr: procedure expose m.
parse arg lib '(' . , mbr .
bx = pos('(', dsn)
if mbr = '' then
return strip(lib)
else
return strip(lib)'('mbr')'
endProcedure dsnSetMbr
dsnGetMbr: procedure expose m.
parse arg dsn '(' mbr ')'
if mbr = '' then
return arg(2)
else
return strip(mbr)
endProcedure dsnGetMbr
dsnCsmSys: procedure expose m.
parse upper arg dsn, withStar
if pos('/', dsn) < 1 then
sys = '*'
else
parse var dsn sys '/' dsn
if sys <> '' & sys <> '*' & sys <> sysvar(sysnode) then
return sys'/'dsn
else if withStar == 0 then
return dsn
else
return '*/'dsn
endProcedure dsnCsmSys
/**********************************************************************
io: read or write a dataset with the following callsequences:
read: tsoOpen...'R', readDD*, tsoClose
write: tsoOpen...'W', writeDD*, tsoClose
readDD returns true if data read, false at eof
do not forget that open is mandatory to write empty file|
***********************************************************************/
/*--- open dd for read (rw='R') or write (rw='W') --------------------*/
tsoOpen: procedure expose m.
parse upper arg dd, rw
return adrTso('execio' 0 'disk'RW dd '(open)')
return /* end tsoOpen */
/*--- close dd -----------------------------------------------------*/
tsoClose: procedure expose m.
parse upper arg dd
return adrTso('execio 0 diskR' dd '(finis)')
endProcedure tsoClose
/*--- read from DD ggDD into ggSt, return false at eof ---------------*/
readDD:
parse arg ggDD, ggSt, ggCnt
if ggCnt = '' then
ggCnt = 100
call adrTso 'execio' ggCnt 'diskR' ggDD '(stem' ggSt')', 2
return (value(ggSt'0') > 0)
return /* end readDD */
/*--- write to gg ggDD from stem ggSt, ggCnt records -----------------*/
writeDD:
parse arg ggDD, ggSt, ggCnt, ggRet
if ggCnt == '' then
ggCnt = value(ggst'0')
call adrTso 'execio' ggCnt 'diskW' ggDD '(stem' ggSt')', ggRet
return
endSubroutine writeDD
/*--- readNx: read next line, using buffer ---------------------------*/
/*--- begin: allocate dsnSpec and ini ------------------------ -------*/
readNxBegin: procedure expose m.
parse arg m, m.m.dsn, m.m.dd, m.m.Cnt
if m.m.dd = '' then
m.m.dd = 'DDNX*'
if m.m.cnt = '' then
m.m.cnt = 1000
m.m.cx = m.m.cnt + 999
m.m.buf0x = 0
m.m.0 = 0
parse value dsnAlloc('dd('m.m.dd')' m.m.dsn) with m.m.dd m.m.free
call tsoOpen m.m.dd, 'R'
return m
endProcedure readNxBegin
/*--- return the stem of the next line, or '' at end -----------------*/
readNx: procedure expose m.
parse arg m
if m.m.cx < m.m.0 then do
m.m.cx = m.m.cx + 1
return m'.'m.m.cx
end
m.m.buf0x = m.m.buf0x + m.m.0
m.m.cx = 1
if \ readDD(m.m.dd, 'M.'m'.', m.m.cnt) then
return ''
return m'.1'
endProcedure readNx
/*--- return the stem of the curr line, '' at end --------------------*/
readNxCur: procedure expose m.
parse arg m
if m.m.cx <= m.m.0 then
return m'.'m.m.cx
else
return ''
endProcedure readNxCur
/*--- return the position (line number) of reader
plus le characters of the current line (default 50) ------*/
readNxPos: procedure expose m.
parse arg m, le
if m.m.cx > m.m.0 then
return 'line' (m.m.buf0x + m.m.cx)':after EOF'
li = m'.'m.m.cx
li = strip(m.li, 't')
if arg() < 2 then
le = 50
if le < 1 then
li = ''
else if length(li) <= le then
li = ':' li
else
li = ':' left(li, le-3)'...'
return 'line' (m.m.buf0x + m.m.cx)li
endProcedure readnxPos
/*--- close and deallocate ------------------------------------------*/
readNxEnd: procedure expose m.
parse arg m
call tsoClose m.m.dd
call tsoFree m.m.free
return
endProcedure readNxEnd
/*--- standardise a dsn spec
word1 dsName or -
word2 dd or -
word3 disp or -
word4 to first : attributes in tso format
after first : attributes for new allocation
----------------------------------------------------------------------*/
dsnSpec: procedure expose m.
parse upper arg spec
rr = '' /* put leading - in separate words */
do sx=1 while words(rr) < 3 & wx \= ''
wx = word(spec, sx)
do while abbrev(wx, '-') & words(rr) < 3
wx = substr(wx, 2)
rr = rr '-'
end
rr = rr wx
end
spec = rr subWord(spec, sx)
na = ''
dd = ''
di = ''
if left(spec, 1) = '&' then /* external spec is handled ok */
spec = strip(substr(spec, 2))
do wx=1 by 1
w = word(spec, wx)
if w = '' | abbrev(w, '.') | abbrev(w, ':') then
leave
else if wordPos(w, 'OLD SHR MOD NEW')>0 | abbrev(w, 'SYSO') then
di = w
else if w == 'CATALOG' | w == 'CAT' then
di = di 'CAT'
else if abbrev(w, 'DD(') then
dd = substr(w, 4, length(w)-4)
else if abbrev(w, 'DSN(') then
na = strip(substr(w, 5, length(w)-5))
else if na == '' then
na = dsn2jcl(w)
else if pos('(', w) > 0 then
leave
else if dd == '' then
dd = w
else if di == '' then
di = w
else
leave
end
if na == '' then
na = '-'
else if abbrev(na, "'") then
na = substr(na, 2, length(na)-2)
if dd == '' then dd = '-'
if di == '' then di = '-'
re = subword(spec, wx)
if abbrev(re, '.') then
re = substr(re, 2)
return na dd di re
endProcedure dsnSpec
/*--- alloc a dsn with dsnAlloc
if the dsn is inuse wait and retry
until either the allocation is successfull
or the timeout occurs --------------------------------------*/
dsnAllocWait: procedure expose m.
parse upper arg spec, pDi, pDD, timeOut
x = max(1, arg() - 1)
do rt=0
m.tso_trap.1 = ''
m.tso_trap.2 = ''
m.tso_trap.3 = ''
res = dsnAlloc(spec, pDi, pDD, '*')
if \ datatype(res, 'n') then
return res
msg = m.tso_trap.1'\n'm.tso_trap.2'\n'm.tso_trap.3
if rt > timeOut & timeOut \== '' then
return err('timeout allocating' spec time() '\n'msg)
if pos('DATA SET IS ALLOCATED TO ANOTHER', msg) < 1 then
return err('allocating' spec'\n'msg)
say time() 'sleep and retry alloc' spec
call sleep 1, 0
end
endProcedure dsnAllocWait
/*--- alloc a dsn or a dd
spec '-'<ddName>
datasetName? disposition? '.'? attributes? (':' newAtts)?
disp default disposition
dd default dd name
retRc erlaubte ReturnCodes (leer = 0)
returns if ok then ddName <rexx for free> otherwise rc -----*/
dsnAlloc: procedure expose m.
parse upper arg spec, pDi, pDD, retRc
parse value dsnSpec(spec) with na dd di rest
if dd == '-' & pDD \== '' then
dd = pDD
if dd == '-' then
dd = 'DD*'
dd = tsoDD(dd, 'a')
m.tso_dsn.dd = ''
if na == '-' & di == '-' & rest = '' then
return dd
if di = '-' then
if pDi == '' then
di = 'SHR'
else
di = pDi
if pos('(', na) < 1 then
nop
else if di = 'MOD' then
call err 'disp mod for' na
else
di = 'SHR'
if lastPos('/', na, 6) > 0 then
rx = csmAlloc(na dd di rest, retRc)
else
rx = tsoAlloc(na dd di rest, retRc)
if rx = 0 then
return dd dd
else
return rx
endProcedure dsnAlloc
/*--- find a free dd with prefix dd ----------------------------------*/
tsoDD: procedure expose m.
parse upper arg dd, f, noErr
if symbol('m.tso_ddAll') \== 'VAR' then do
call errIni
m.tso_ddAll = ''
end
if f == '-' then do
ax = wordPos(dd, m.tso_ddAll)
if ax > 0 then
m.tso_ddAll = delWord(m.tso_ddAll, ax, 1)
else if noErr \== 1 then
call err 'tsoDD dd' dd 'not used' m.tso_ddAll
end
else if f <> 'A' then
call err 'tsoDD bad fun' f
else do
if right(dd, 1) = '*' then do
d0 = left(dd, length(dd)-1) || m.err.screen
dd = d0
do dx=1 while wordPos(dd, m.tso_ddAll) > 0
dd = d0 || dx
end
end
else if pos('?', dd) > 0 then
dd = repAll(dd, '?', m.err.screen)
if wordPos(dd, m.tso_ddAll) < 1 then
m.tso_ddAll = strip(m.tso_ddAll dd)
m.tso_dsn.dd = ''
m.tso_dsOrg.dd = ''
end
return dd
endProcedure tsoDD
tsoAlloc: procedure expose m.
parse arg na dd disp rest ':' nn, retRc
dd = translate(dd)
c = 'alloc dd('dd')' disp
if na == '-' then
m.tso_dsn.dd = ''
else if na \== 'INTRDR' then do
c = c "DSN('"na"')"
m.tso_dsn.dd = na
end
else do
c = c "sysout(*) writer(intRdr)"
m.tso_dsn.dd = '*intRdr'
end
if wordPos(disp, 'NEW MOD') > 0 & nn \== '' then
c = c dsnCreateAtts(,nn)
if adrTso(c rest, '*') = 0 then
return 0
if pos('IKJ56246I', m.tso_trap) > 0 then
if pos('TED, FILE IN USE', m.tso_trap) > 0 then do
/* IKJ56246I DATA SET A540769.WK.REXX NOT ALLOCATED, FILE IN USE */
say 'tsoAlloc dd' dd 'already in use:' substr(m.tso_trap, 3)
say '.... trying to free'
call tsoFree dd, 1
say '.... retrying to allocate' c rest
if adrTso(c rest, '*') = 0 then
return 0
end
if nn \= '' & wordPos(disp, 'OLD SHR') > 0 ,
& pos('IKJ56228I', m.tso_trap) > 0 ,
& pos(' NOT IN CATALOG ', m.tso_trap) > 0 then do
/* IKJ56228I DATA SET A540769.XY.REXX NOT IN CATALOG OR CATAL*/
say 'tsoAlloc creating' c rest ':'nn
call adrTso 'alloc dd('dd') new catalog' dsnCreateAtts(na, nn)
call adrTso 'free dd('dd')'
return tsoAlloc(na dd disp rest, retRc)
end
if pos('*', retRc) < 1 & wordPos(m.tso_rc, retRc) < 1 then
call err 'tsoAlloc rc='m.tso_rc 'tsoStmt='m.tso_stmt m.tso_trap
call sayNl 'tsoAlloc rc='m.tso_rc 'tsoStmt='m.tso_stmt m.tso_trap
return m.tso_rc
endProcedure tsoAlloc
dsnExists: procedure expose m.
parse upper arg aDsn
parse value dsnCsmSys(aDsn) with rz '/' dsn
if rz == '*' then
return sysDsn("'"dsn"'") == 'OK'
else if dsnGetMbr(dsn) == '' then do
lc = adrCsm('dslist system('rz') dsnMask('dsn') short', 4)
if stemsize = 0 | stemSize = 1 then
return stemSize
call err 'csmExists stemSize='stemsize 'for dsn='aDsn
end
else do
cc = adrCsm("mbrList system("rz") dataset('"dsnSetMbr(dsn)"')",
"member("dsnGetMbr(dsn)") index(' ') short", 8)
if cc <> 0 then do
if pos(' NOT IN CATALOG\', m.tso_trap) > 0 ,
& pos('CSMSV29E DATA SET ', m.tso_trap) > 0 then
return 0
return err('error in csm mbrList' aDsn m.tso_trap)
end
if mbr_name.0 == 0 | mbr_name.0 == 1 then
return mbr_name.0
call err 'csmExists mbr_mem#='mbr_name.0 'for dsn='aDsn
end
endProcedure dsnExists
dsnLikeAtts: procedure expose m.
parse upper arg dsn, isFile
if isFile then do
ddDsn = m.tso_dsn.dsn
if lastPos('/', m.tso_dsn.dsn, 4) < 1 then
return tsoLikeAtts(dsn, 1)
dsn = m.tso_dsn.dsn
end
sx = lastPos('/', dsn, 4)
if sx < 1 then
return tsoLikeAtts(dsn, 0)
else if abbrev(dsn, '*/') | abbrev(dsn, sysVar('SYSNODE')'/') then
return tsoLikeAtts(substr(dsn, sx+1), 0)
else
return csmLikeAtts(dsn)
endProcedure dsnLikeAtts
tsoLikeAtts: procedure expose m.
parse arg dsn, isFile
rc = listDsi("'"dsn"'" copies('FILE', isFile) "SMSINFO")
if rc = 0 then
r = ''
else if rc = 4 & sysReason = 19 then do
r = 'UCOUNT(30)' /* 7.8.13 corrected attribute */
say 'creating' dsn 'with multi volume' mv
end
else if rc ^= 0 then
call err 'listDsi rc' rc 'reason' sysReason,
sysMsgLvl1 sysMsgLvl2
if right(sysDsSms, 7) == 'LIBRARY' | abbrev(sysDsSms, 'PDS') ,
| sysDsOrg = 'PO' then
r = 'dsOrg(PO) DSNTYPE(LIBRARY)' r
else
r = "dsOrg("sysDSorg")" r
if sysUnits = 'TRACK' then
sysUnits = 'TRACKS'
return r "MGMTCLAS("sysMgmtClass")",
"DATACLAS("sysDataClass")" ,
"RECFM("strip(translate('1 2 3', ' 'sysREcFM, '123'))")",
"LRECL("SYSLRECL")",
"SPACE("sysPrimary"," sysSeconds")" ,
sysUnits || left('S', sysUnits == 'TRACK')
/* "blksize("sysBLkSIZE")" removed 3.4.13: let sms do the magic */
endProcedure tsoLikeAtts
tsoFree: procedure expose m.
parse arg ddList, tryClose
do dx=1 to words(ddList)
dd = word(ddList, dx)
if adrTso('free dd('dd')', '*') <> 0 then do
if pos('IKJ56861I', m.tso_trap) > 0 & tryClose == 1 then
if pos('NOT FREED, DATA SET IS OPEN', m.tso_trap),
> 0 then do
/* IKJ56861I FILE A1 NOT FREED, DATA SET IS OPEN */
say 'dataset open:' substr(m.tso_trap, 3)
say '.... trying to close'
if adrTso('execio 0 diskR' dd '(finis)', '*') = 0 then
call adrTso 'free dd('dd')', '*'
end
if m.tso_rc \== 0 then
call sayNl 'rc='m.tso_rc 'tsoStmt='m.tso_stmt m.tso_trap
end
call tsoDD dd, '-', 1
end
return
endProcedure tsoFree
dsnCreateAtts: procedure expose m.
parse arg dsn, atts
res = ''
if dsn \== '' & \ abbrev(dsn, '-') then
res = "dataset('"dsnSetMbr(dsn)"')"
if abbrev(atts, ':') then do
parse var atts a1 atts
rl = substr(a1, 3)
if abbrev(a1, ':F') then do
if rl = '' then
rl = 80
res = res "recfm("space(f b)") lrecl("rl")"
end
else if abbrev(a1, ':V') then do
if rl = '' then
rl = 32756
res = res "recfm("space(v b)") lrecl("rl")"
end
else if abbrev(a1, ':L') then
res = res dsnLikeAtts(rl, 0)
else if abbrev(a1, ':D') then
res = res dsnLikeAtts(rl, 1)
else
call err 'csnCreateAtt bad :' a1
end
aU = ' 'translate(atts)
hasOrg = pos(' DSORG(', aU) > 0 | pos(' DSNTYPE(', aU) > 0
hasMbr = pos('(', dsn) > 0
if hasMbr & \ hasOrg then
atts = atts 'dsorg(po) dsntype(library)'
if hasOrg | hasMbr then do
ww = DSORG DSNTYPE
do wx=1 to words(ww)
do forever
cx = pos(' 'word(ww, wx)'(', ' 'translate(res))
if cx == 0 then
leave
cy = pos(')', res, cx)
res = delstr(res, cx, cy+1-cx)
end
end
end
res = res atts
aU = ' 'translate(res)
if pos(' MGMTCLAS(', aU) < 1 then
res = res 'mgmtclas(COM#A091)'
if pos(' SPACE(', aU) < 1 then
res = res 'space(1, 50) cylinders'
return res
endProcedure dsnCreateAtts
/*--- check if a dataset is archive ------------------------------------
returns 'ok' if dataset on disk
'not' if dataset is not catalogued
'arc' if dataset archived
listDsi errorMsg otherwise ------------------*/
dsnArc: procedure expose m.
parse upper arg dsn
lc = listDsi("'"strip(dsn)"' noRecall")
if lc = 0 then
return 'ok'
else if lc=4 & sysReason = 19 then /* multiple volumes */
return 'ok'
else if lc=16 & sysReason = 5 then
return 'notCat'
else if lc=16 & sysReason = 9 then
return 'arc'
else
return 'listDsi cc='lc', sysReason='sysReason ,
'm2='sysMsgLvl2', m1='sysMsgLvl1
endProcedure dsnArc
/*--- read the dataset specified in ggDsnSpec to stem ggSt -----------*/
readDSN:
parse arg ggDsnSpec, ggSt
ggAlloc = dsnAlloc(ggDsnSpec, 'SHR', 'readDsN')
call adrTso 'execio * diskr' word(ggAlloc, 1) '(stem' ggSt' finis)'
call tsoFree word(ggAlloc, 2)
return
endSubroutine readDsn
/*--- write the dataset specified in ggDsnSpec from stem ggSt
write ggCnt records if not empty otherwise ggst0
if ggSay 1 then say ... records written to ... -------------*/
writeDSN:
parse arg ggDsnSpec, ggSt, ggCnt, ggSay
if ggCnt == '' then
ggCnt = value(ggst'0')
ggAlloc = dsnAlloc(ggDsnSpec, 'OLD', 'readDsN')
call adrTso 'execio' ggCnt 'diskw' word(ggAlloc, 1) ,
'(stem' ggSt 'open finis)'
call tsoFree word(ggAlloc, 2)
if ggSay == 1 | m.debug == 1 then
say ggCnt 'records written to' ggDsnSpec
return
endSubroutine writeDsn
copyDSN: procedure expose m.
parse arg frSpec, toSpec, ggSay
parse value dsnAlloc(frSpec, 'SHR', 'FRDD') with frDD frFr
parse value dsnAlloc(toSpec, 'OLD', 'TODD') with toDD toFr
call tsoOpen frDD, 'R'
call tsoOpen toDD, 'W'
cnt = 0
do while readDD(frDD, r.)
call writeDD toDD, r.
cnt = cnt + r.0
end
call tsoClose frDD
call tsoClose toDD
call tsoFree frFr toFr
if ggSay == 1 | m.debug == 1 then
say cnt 'records copied from' frSpec 'to' to toSpec
return
endSubroutine writeDsn
tsoDsiMaxl:
rc = listDsi(arg(1) 'FILE')
if rc ^= 0 then
call err 'listDsi rc' rc 'reason' sysReason,
sysMsgLvl1 sysMsgLvl2
return SYSLRECL - 4 * abbrev(sysRecFm, 'V')
endSubroutine tsoDsiMaxL
/* copy adrTso end ****************************************************/
/* copy ut begin *****************************************************/
utIni: procedure expose m.
if m.ut_ini == 1 then
return
m.ut_ini = 1
m.ut_digits = '0123456789'
/* 012345678901234567890123456789 */
m.ut_lc = 'abcdefghijklmnopqrstuvwxyz'
m.ut_alfLC = m.ut_lc /* backward compatibility */
m.ut_uc = translate(m.ut_lc)
m.ut_Alfa = m.ut_lc || m.ut_uc
m.ut_alfNum = m.ut_alfa || m.ut_digits
m.ut_alfDot = m.ut_alfNum || '.'
m.ut_alfId = m.ut_alfNum'_' /* avoid rexx allowed @ # $ ¬ . | ? */
m.ut_alfIdN1 = m.ut_digits /* not as first character */
m.ut_rxId = m.ut_AlfNum'@#$?' /* charset puff mit ¬*/
m.ut_rxDot = '.'m.ut_rxId
m.ut_rxN1 = '.0123456789'
m.ut_alfPrint = m.ut_alfNum'+-*/=()[]{}<> .:,;?|''"%&#@$£\_'
m.ut_ucNum = m.ut_uc || m.ut_digits
m.ut_base64 = m.ut_uc || m.ut_lc || m.ut_digits'+-'
m.ut_alfLC = m.ut_lc /* backward compatibility */
m.ut_alfUC = m.ut_uc /* backward compatibility */
return
endProcedure utIni
/*--- if function warning all3 arguments get evaluated|
e.g if(x=0, 'infinity', 1/0) will fail| -----------*/
if: procedure expose m.
parse arg co, ifTrue, ifFalse
if co then
return ifTrue
else
return ifFalse
endProcedure if
/*--- embedded ASSignement:
assign the second argument to the variable with name in first arg
and return the value assigned ----------------------------------*/
ass:
call value arg(1), arg(2)
return arg(2)
/*--- embedded ASSignement return NotNull:
assign the second argument to the variable with name in first arg
and return 1 if value not null, 0 if null ----------------------*/
assNN:
call value arg(1), arg(2)
return arg(2) \== ''
/*--- return current time and cpu usage ------------------------------*/
utTime: procedure expose m.
return time() 'ela='time('E') 'cpu='sysvar('syscpu'),
'su='sysvar('syssrv')
/*--- sleep several seconds ------------------------------------------*/
sleep: procedure expose m.
parse arg secs, sayIt
if sayit <> 0 then
say 'sleeping' secs 'secs' time()
CALL SYSCALLS 'ON'
ADDRESS SYSCALL "sleep" secs
CALL SYSCALLS 'OFF'
if sayit <> 0 then
say 'slept' secs 'secs' time()
return
endProcedure sleep
/*--- left without truncation ----------------------------------------*/
lefPad: procedure expose m
parse arg s, len
if length(strip(s, 't')) >= len then
return strip(s, 't')
return left(s, len)
endProcedure lefPad
/*--- right without truncation ---------------------------------------*/
rigPad: procedure expose m
parse arg s, len
if length(strip(s, 'l')) >= len then
return strip(s, 'l')
return right(s, len)
endProcedure rigPad
/*--- quote string txt using quoteChar qu ("" ==> ") -----------------*/
quote: procedure expose m.
parse arg txt, qu
if qu = '' then
qu = '"'
res = qu
ix = 1
do forever
qx = pos(qu, txt, ix)
if qx = 0 then
return res || substr(txt, ix) || qu
res = res || substr(txt, ix, qx-ix) || qu || qu
ix = qx + length(qu)
end
endProcedure quote
/*--- translate to lowercase -----------------------------------------*/
ut2lc: procedure expose m.
parse arg s
return translate(s, m.ut_lc, m.ut_uc)
/*--- verify an id ---------------------------------------------------*/
verifId: procedure expose m.
parse arg src, extra
if pos(left(src, 1), m.ut_alfIdN1) > 0 then
return 1
else
return verify(src, m.ut_alfId || extra, 'n')
/*--- return the count of occurrences of needle in heyStack ----------*/
posCount: procedure expose m.
parse arg needle, hayStack, start, fin
if start = '' then
start = 1
if fin = '' then
fin = length(hayStack) + 1 - length(needle)
do cnt = 0 by 1
start = pos(needle, haystack, start)
if start < 1 | start > fin then
return cnt
start = start + length(needle)
end
endProcedure posCount
repAll: procedure expose m.
parse arg src
do ax = 2 by 2 to arg()
src = repAl2(src, src, arg(ax), arg(ax+1))
end
return src
endProcedure repAll
repAl2: procedure expose m.
parse arg src, sPos, old, new
res = ''
cx = 1
do forever
nx = pos(old, sPos, cx)
if nx < 1 then
return res || substr(src, cx)
res = res || substr(src, cx, nx-cx) || new
cx = nx + length(old)
end
endProcedure repAl2
repAllWords: procedure expose m.
parse arg src, w, new
res = ''
wx = 0
cx = 1
do forever
wx = wordPos(w, src, wx+1)
if wx < 1 then
return res || substr(src, cx)
nx = wordindex(src, wx)
res = res || substr(src, cx, nx-cx) || new
cx = nx + length(w)
end
endProcedure repAllWords
utc2d: procedure expose m.
parse arg ch
cx = length(ch) // 3
if cx = 0 then
cx = 3
res = c2d(left(ch, cx))
do cx=cx+1 by 3 to length(ch)
res = res * 16777216 + c2d(substr(ch, cx, 3))
end
return res
utInter: procedure expose m.
interpret arg(1)
return
endProcedure utInter
/* copy ut end ********************************************************/ 6
/* copy err begin *** errorhandling, messages, help ****************/
errIni: procedure expose m.
if m.err.ini == 1 then
return
m.err.ini = 1
m.err.handler = ''
m.err.handler.0 = 0
m.err.cleanup = ';'
m.err.opt = ''
parse source m.err.os .
m.err.ispf = 0
m.err.screen = 0
if m.err.os \== 'LINUX' then do
address tso 'profile MsgId' /* brauchen wir in tsoAlloc| */
if sysVar('sysISPF') = 'ACTIVE' then do
m.err.ispf = 1
address ispExec 'vget zScreen shared'
m.err.screen = zScreen
end
end
return
endProcedure errIni
/* configure err -----------------------------------------------------*/
errReset: procedure expose m.
call errIni
parse arg m.err.opt, m.err.handler
m.err.handler.0 = 0
if pos('I', translate(m.err.opt)) > 0 & m.err.ispf then
address ispExec 'control errors return'
return
endSubroutine errReset
/* push error handler ------------------------------------------------*/
errHandlerPush: procedure expose m.
parse arg aH
ex = m.err.handler.0 + 1
m.err.handler.0 = ex
m.err.handler.ex = m.err.handler
m.err.handler = aH
return
endProcedure errHandlerPush
/* push error handler return Constant value --------------------------*/
errHandlerPushRet: procedure expose m.
parse arg rv
call errHandlerPush "return '"rv"'"
return
/* pop error handler ------------------------------------------------*/
errHandlerPop: procedure expose m.
if m.err.handler.0 < 1 then
call err 'errHandlerPop but err.handler.0='m.err.handler.0
ex = m.err.handler.0
m.err.handler = m.err.handler.ex
m.err.handler.0 = ex - 1
return
endProcedure errHandlerPop
/*--- error routine: abend with message ------------------------------*/
err:
parse arg ggTxt, ggOpt
call errIni
drop err handler cleanup opt call return stackHistory
if ggOpt == '' & m.err.handler <> '' then
interpret m.err.handler
call errSay ggTxt
call errCleanup
if ggOpt == '' then
ggOpt = m.err.opt
upper ggOpt
if pos('T', ggOpt) > 0 then do
trace ?r
say 'trace ?r in err'
end
if pos('H', ggOpt) > 0 & m.err_cat == 'f' then
x = show + stackHistory + by + bad + arithmetic + conversion
call errSay ' }errorhandler exiting with exit(12)'
exit errSetRc(12)
endSubroutine err
/*--- set rc for ispf: -------------------------------------------------
if a cmd is run by ispStart, its RC is ignored,
but ISPF passes the value of the shared variable zIspfRc
back as return code
----------------------------------------------------------------------*/
errSetRc: procedure expose m.
parse arg zIspfRc
if m.err.ispf then
address ispExec vput 'zIspfRc' shared
return zIspfRc
endProcedure errSetRc
/*--- error routine: user message cleanup exit -----------------------*/
errAddCleanup: procedure expose m.
parse arg code
call errIni
/* concatenate backwards, then it is executed also backwards */
m.err.cleanup = ';'code || m.err.cleanup
return
endProcedure errAddCleanup
errRmCleanup: procedure expose m.
parse arg code
call errIni
cx = pos(';'code';', m.err.cleanup)
if cx > 0 then
m.err.cleanup = left(m.err.cleanup, cx) ,
|| substr(m.err.cleanup, cx + length(code)+2)
return
endProcedure errRmCleanup
errCleanup: procedure expose m.
call errIni
errCleanup = m.err.cleanup
if errCleanup <> ';' then do
m.err.cleanup = ';'
say 'err cleanup begin' errCleanup
interpret errCleanup
say 'err cleanup end' errCleanup
end
if symbol('m.tso_ddAll') == 'VAR' then
call tsoFree m.tso_ddAll, 1
return
endProcedure errCleanup
/*--- say an errorMessage msg with pref and linesplits --------------*/
errSay: procedure expose m.
parse arg msg
return sayNl(errMsg(msg))
/*--- prefix an errormessage with pref,
split it into lines at \n to stem m.err -------------------*/
errMsg: procedure expose m.
parse arg msg
m.err_cat = 'f'
do while substr(msg, 2, 1) == '}'
parse var msg m.err_cat '}' msg
end
if m.err_cat == ' ' | m.err_cat == 'o' then
return msg
pTxt = ',fatal error,error,input error,scanErr,warning,onlyMsg,'
px = pos(','m.err_cat, pTxt)
if px < 1 then do
px = 1
m.err_cat = 'f'
end
pre = substr(pTxt, px+1, pos(',', pTxt, px+2)-px-1)
if m.err_cat == 's' then
return pre msg
parse source . . s3 . /* current rexx */
return pre 'in' s3':' msg
endProcedure errMsg
splitNL: procedure expose m.
parse arg st, msg
bx = 1
do lx=1 to 20
ex = pos('\n', msg, bx)
if ex < bx then
leave
m.st.lx = substr(msg, bx, ex-bx)
bx = ex+2
end
m.st.lx = substr(msg, bx)
m.st.0 = lx
return st
endProcedure splitNL
outNL: procedure expose m.
parse arg msg
bx = 1
do lx=1 to 20
ex = pos('\n', msg, bx)
if ex < bx then
leave
call out substr(msg, bx, ex-bx)
bx = ex+2
end
call out substr(msg, bx)
return
endProcedure outNL
/*--- say (part of) the lines of a stem ----------------------------*/
sayNl: procedure expose m.
parse arg msg
bx = 1
do lx=1 to 20
ex = pos('\n', msg, bx)
if ex < bx then
leave
say strip(substr(msg, bx, ex-bx), 't')
bx = ex+2
end
say strip(substr(msg, bx), 't')
return 0
endProcedure sayNl
/*--- say a trace message if m.trace is set --------------------------*/
debug: procedure expose m.
parse arg msg
if m.debug == 1 then
say 'debug:' msg
return
endProcedure debug
/*--- output a trace if m.trace is set -------------------------------*/
trc: procedure expose m.
parse arg msg
if m.trace == 1 then
call out 'trc:' msg
return
endProcedure trc
/*--- assert that the passed rexx expression evaluates to true -------*/
assert:
interpret 'if ('arg(1)') == 1 then return 1'
interpret 'call err' quote('assert failed' arg(1) ':' arg(2) '==>'),
arg(2)
endProcedure assert
/*--- abend with Message after displaying help -----------------------*/
errHelp: procedure expose m.
parse arg msg
call errSay 'i}'msg
call help 0
call err 'i}'msg
endProcedure errHelp
/*--- display the first comment block of the source as help ----------*/
help: procedure expose m.
parse arg doClear
if doClear \== 0 then
address tso 'clear'
parse source . . s3 .
say right(' help for rexx' s3, 72, '*')
do lx=1 while pos('/*', sourceLine(lx)) < 1
if lx > 10 then
return err('initial commentblock not found for help')
end
doInc = 1
ho = m.err.helpOpt
do lx=lx+1 to sourceline() while pos('*/', sourceline(lx)) = 0
li = strip(sourceLine(lx), 't')
cx = lastPos('{', li)
if cx > 0 then do
if length(ho) = 1 then
doInc = cx = length(li) | pos(ho, li, cx+1) > 0
li = left(li, cx-1)
end
if doInc then
say li
end
say right(' end help for rexx' s3, 72, '*')
return 4
endProcedure help
/* copy err end *****************************************************/
/* copy out begin ******************************************************
out interface simple with say only
***********************************************************************/
/*--- output a message to the current destination -------------------*/
out: procedure expose m.
parse arg msg
say msg
return
endProcedure out
/* copy out end *****************************************************/