idug na 2005 rich fazio: rexx and db2 udb- the stuff not

58
1 Rexx and DB2- The Stuff NOT in the Manuals Rich Fazio DBA, TransUnion Session: B11 2005-05-25-15-30-00.000000 DB2 for Z/OS: Re Re - - tooled tooled About the Speaker: Rich Fazio has been an IT professional for 18 years and has presented internationally. Special interests include programming techniques, application performance, DB2 recovery strategies and problem resolution techniques. Starting as a Cobol application programmer, his first scripting language was TSO CLISTs. Shortly after, ISPF Macro and REXX programming became a staple. After a year of DB2 programming he then moved on to being a DB2 Database Administrator. The last 15 years as a DB2 DBA have provided many opportunities to "whip-up” various scripts. Most EXECs developed are for aiding repetitive processes or quick data fixes. Rich is currently a Consulting Database Administrator of DB2 for z/OS for TransUnion, Chicago, IL USA. All of the EXECs discussed during this presentation are available and documented. Send an email to [email protected] for a copy of this collection.

Upload: others

Post on 19-Nov-2021

2 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

1

Rexx and DB2-The Stuff NOT in the Manuals

Rich FazioDBA, TransUnion

Session: B112005-05-25-15-30-00.000000

DB2 for Z/OS:

ReRe--tooledtooled

About the Speaker:Rich Fazio has been an IT professional for 18 years and has presented internationally. Special interests include programming techniques, application performance, DB2 recovery strategies and problem resolution techniques.

Starting as a Cobol application programmer, his first scripting language was TSO CLISTs. Shortly after, ISPF Macro and REXX programming became a staple. After a year of DB2 programming he then moved on to being a DB2 Database Administrator. The last 15 years as a DB2 DBA have provided many opportunities to "whip-up” various scripts. Most EXECs developed are for aiding repetitive processes or quick data fixes.

Rich is currently a Consulting Database Administrator of DB2 for z/OS for TransUnion, Chicago, IL USA.

All of the EXECs discussed during this presentation are available and documented.Send an email to [email protected] for a copy of this collection.

Page 2: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

2

2

TransUnionTransUnion

• Founded: 1968• Headquarters: Chicago, Illinois• Size: 3600 employees worldwide• U.S. Reach: Operations in all 50 States• Global Reach: 12 countries on 5 continents • Monthly Process: 2.1 billion items of information

•In 1968, Union Tank Car Company, a railcar leasing operation, created TransUnion as its parent holding company. Using its technical expertise, TransUnion acquired the Credit Bureau of Cook County (CBCC). At the time, the bureau manually maintained 3.6 million card files in 400 seven-drawer cabinets. TransUnion realized it was essential to build a national consumer credit file to facilitate significant company growth in the industry, so in 1972, the company introduced the TransUnion Credit Reporting Online Network Utility System (CRONUS®).

•To support its investment in CRONUS, TransUnion began the process of acquiring regional credit bureaus. In the mid-70s, TransUnion Credit Information Company was established as the holding company for CBCC and other acquired credit bureaus. By the late 70s, TransUnion began to open its first start-up operations in key locations around the country and has had full coverage since 1988.

•In 1981, TransUnion and its credit-reporting subsidiary became part of the Chicago-based Marmon Group, an international association of more than 60 autonomous manufacturing and service companies. Today, TransUnion LLC has more than 250 owned and affiliated credit bureaus nationwide and international operations in 24 countries on five continents.

•CRONUS

•Introduced in 1972, the TransUnion Credit Reporting Online Network Utility System (CRONUS) was the first digital information storage and retrieval data processing solution. CRONUS unified the data, providing credit grantors across the country with a single, fast and accurate source of consumer credit information. Credit grantors were able to receive and update information directly on their personal computers or workstations as a result of CRONUS.

Page 3: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

3

3

EXEC SQL EXEC SQL (The Beginning(The Beginning……))

Agenda--• Who (what) is REXX?• Language elements• Using REXX and DB2• Things to consider• Sample code

Hi! I’m Rexx!

Throughout this presentation REXX is meant to represent REXX for OS/390; DB2 is to represent DB2 UDB for z/OS.

This will be a discussion relating to REXX and techniques to integrate it with DB2 at many levels.

The title of this session is “REXX and DB2….The Stuff not in the Manuals” . What’s missing?What’s missing from IBM documentation is practical examples of REXX code along with techniques for applying each of them. Why are you here?You are here because you want to understand more of this interface into DB2, but don’t have the time to play with building your own infrastructure. You want a fast path introduction into the REXX language as it applies to this interface. You want to know what is important and why.What will you be leaving with?A working knowledge of REXXPractical examples of each major REXX interface into DB2.Sample code of each of these interfaces to utilize in your shop.

Page 4: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

4

4

Rexx Basics Rexx Basics -- CodingCoding

/* REXX – Goldilocks ----------------------*/Call SELECT_PORRIDGE_RTN Exit(0)

SELECT_PORRIDGE_RTN:

EAT_PORRIDGE_FLG = "N"

Do Until EAT_PORRIDGE_FLG = "Y"

Call TASTE_RTN If TASTE_ID > 5 Then Iterate If TASTE_ID < 5 Then Iterate If TASTE_ID = 5 Then EAT_PORRIDGE_FLG = "Y"

EndCall GOBBLE_PORRIDGE_ALL_UP_RTN Return;

Woof!

Start

End

Too Hot?

Too Cold?

Taste Porridge in next bowl

N

Eat PorridgeN

Y

Y

Let’s review some basic REXX coding techniques.

/* REXX */ REXX identifier.CALL Used for performing a subroutine or external routine. Good for

isolating reusable code.

Setting a flag Pretty basic.DO Initiate a loop. This loop will continue until a flag is set to "Y"

IF Used for decision logic

END Denotes the end of the loop (go back to "DO")

ITERATE Stop execution flow, go back to "DO".

RETURN Marks the end of routine (Same as EXIT).

What’s missing? COMMENTS!

Page 5: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

5

5

Rexx Basics Rexx Basics –– CommentsComments

/* ----------------- Select and eat Porridge ------------- */SELECT_PORRIDGE_RTN: EAT_PORRIDGE_FLG = "N" /* Set flag designating that

Goldilocks has not found acceptable porridge */

Do Until EAT_PORRIDGE_FLG = "Y" /* Keep testing porridge */Call TASTE_RTN /* Set TASTE_ID to value range 1-9.

A Value of "1" is very COLD, A Value of "9" is very HOT */

/* Too Hot */ If TASTE_ID > 5 Then Iterate /* Check next Bowl *//* Too Cold */ If TASTE_ID < 5 Then Iterate /* Check next Bowl *//* Just Right */ If TASTE_ID = 5 Then, /* Bowl Selected */

EAT_PORRIDGE_FLG = "Y"End /* End of Porridge test loop, check first bowl */ Call GOBBLE_PORRIDGE_ALL_UP_RTN /* Consume Porridge */Return; /* Porridge has been selected and gobbled – back to main */

Nice Doc!

The difference between good code and great code is comments. Even if your program has the simplest objective, it still needs some kind of comment.

Let’s review the "Goldilocks" solution AFTER comments are added. Now we know what is going on without verbal explanation or a flow chart.

Comments start with /* and end with */

/* This is a valid Comment */

/* This isavalid comment Too */

Page 6: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

6

6

Rexx Basics Rexx Basics –– The StackThe Stack

# Entries: 3

D

QUEUE "D"QUEUE "B" QUEUE "2" SAY "# Entries: " QUEUED()

Stack

# Entries: 3

Terminal

PUSH "D"PUSH "B" PUSH "2" SAY "# Entries: " QUEUED()

Stack

# Entries: 3

Terminal

PUSH "B"QUEUE "2" PUSH "D" SAY "# Entries: " QUEUED()

Stack

Terminal?

DBDB2

DBD2BD

BB2DB2

Rexx has two methods of getting data on a stack. QUEUE adds data from the BOTTOM of the stackPUSH adds data to the TOP of the stack

The first and second example are fairly straight forward.

The third example works like this:

"B" is the first entry in the stack (QUEUE or PUSH would generate the same result)."2" is needed at the end of the stack, so QUEUE is used to do this."D" is needed at the top of the stack, so PUSH will do this.

QUESTION: Why would you mix PUSH and QUEUE in your REXX? ANSWER: Because you like to make your code as complex as possible; pick a technique and stick with it.

Page 7: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

7

7

Rexx Basics Rexx Basics –– The StackThe Stack

# Entries: 4

DB2""

# Entries: 3DB2

Stack Terminal

QUEUE "D"QUEUE "B" QUEUE "2" QUEUE ""SAY "# Entries: " QUEUED() EXECIO * DISKW MYDDN (FINIS PULL termdata

Stack File allocated to "MYDDN"

RemoveQUEUE "D"QUEUE "B" QUEUE "2" SAY "# Entries: " QUEUED() DO QUEUED()

PULL stackdata SAY stackdata

ENDPULL termdata

Terminal# Entries: 4_ Awaiting

Terminal Input

1DDDB2

# Entries: 3D

DB2

DDDBBB222""""""

1

2DDDBBB2

# Entries: 3DB

DDDBBB222

# Entries: 3DB2

3

# Entries: 3DB2_

Awaiting Terminal

Input

4 Entries3 Entrie

s

Remove

Rexx has several methods of getting data off of a stack.

PULL picks data off the TOP of the stack. If there is no data on the stack, it requests input from the keyboard.

EXECIO dumps the contents of the stack to a file. Note: There is a null entry on the stack. This signifies the end of the stack. If you leave out that last "QUEUE" statement, you will have a command prompt generated when the EXECIO executes…it’s waiting for more data.

Anyone remember the MS DOS feature "COPY CON"? If you have an empty stack without a "NULL" entry, it works just like COPY CON. You get a command prompt. Everything you enter goes into the file… until you hit enter without putting any data on the line (this creates a "null" entry).

Page 8: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

8

8

Rexx & DB2 Rexx & DB2 –– DB2 V4 and priorDB2 V4 and prior

• No Native "Scripting language"• Third party Rexx/DB2 Interfaces• Homegrown

– Custom Code: Cobol, ASM,…– DSNTEP2– DSNTIAUL

FETCH me some data!

Wasn’t I cute?

Oracle and SQL Server have scripting facilities built in. With DB2, you write a Cobol program…not too "quick and easy".

Before the introduction of the REXX SQL interface (V5ish) you had to make REXX jump through many hoops to handle DB2 data.

These included purchasing third party software, using external programs to do the DB2 work. These external programs would pass back a file or a field of data.

Got a favorite routine to get DB2 data? Try calling it from REXX and use REXX to reformat the data for you!

Through the next few slides I will show you some practical examples of these techniques and some of their drawbacks.

Page 9: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

9

9

DSNTEP2DSNTEP2

SAMPLE DATA via DSNTEP2 This output is generated into DDNAME: SYSPRINT*****************************************************************************1PAGE 1***INPUT STATEMENT:SELECT '~', TB.DBNAME, TB.TSNAME, IX.CREATOR, IX.NAMEFROM SYSIBM.SYSTABLES TB

, SYSIBM.SYSINDEXES IXWHERE TB.NAME = IX.TBNAME AND TB.CREATOR = IX.TBCREATORAND TB.NAME = 'SYSTABLES';+---------------------------------------------------------+ | | DBNAME | TSNAME | CREATOR | NAME |+---------------------------------------------------------+

1_| ~ | DSNDB06 | SYSDBASE | DBUM560 | DBNAME_TSNAME | 2_| ~ | DSNDB06 | SYSDBASE | SYSIBM | DSNDTX01 | 3_| ~ | DSNDB06 | SYSDBASE | SYSIBM | DSNDTX02 | +---------------------------------------------------------+

0SUCCESSFUL RETRIEVAL OF 3 ROW(S)

Consider the DSNTEP2 utility as a generator of data with an SQL Statement as input. A sequential data file is produced (SYSPRINT)

Run DSNTEP2 from a REXX environment; a sequential file (the report) is generated through SYSPRINT. Then, pick out the data from the control syntax and headings. Notice the constant in the query: "~” (tilde). Choose only the lines with a unique character combo in a specific position (in this case the tilde). "Select" data from the report by parsing the record; look for the second WORD to be a tilde or other positional identifier.

ISSUE:What if there is a tilde in my data? How will that affect my REXX….

Page 10: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

10

10

DSNTEP2DSNTEP2

/* Build CNTL/SQL – Code not shown */

Call DSNTEP2 /* Alloc libs - call pgm */

EXECIO * DISKR SYSPRINT (STEM FRED. FINIS

Do I = 1 to FRED.0

If Word(FRED.I,2) = "~" Then,

Do

/* Parse technique #1 */

Parse Var FRED.I "~ |" DATA_A "|" DATA_B

"|" DATA_C "|" DATA_D

Say DATA_A DATA_B DATA_C DATA_D

/* Parse technique #2 */

/* Note: Word(FRED.I,1) is the ###_| */

Say Word(FRED.I,4) Word(FRED.I,6)

Word(FRED.I,8) Word(FRED.I,10)

End

End

Hey, how do I handle long rows?

/* REXX Call to DSNTIAUL Example */

/* Author: Rich Fazio */

/* Published: 2001-10-01 */

/* Supports DB2 for OS/390 V5,V6,V7 */

/*-------------------------------------------------------------------*/

/* Input parms: TABLE_NAME, SSID */

/* TABLE_NAME - Expecting a non-qualified creator */

/* SSID - Expecting a DB2 subsystem ID or SSID Alias */

/*-------------------------------------------------------------------*/

Parse Upper Arg ARGS /* Retrieve arguments from invocation */

TABLE_NAME = WORD(ARGS,1)

SSID = WORD(ARGS,2)

Call SSIDCHKR SSID /* VALIDATE/TRANSLATE Return VERSION */

SSID = WORD(RESULT,1) /* "RESULT" HAS VALIDATED SSID IN IT */

DB2_VERSION = WORD(RESULT,2) /* FYI- here is the version */

MAIN_ROUTINE:

USERID = Sysvar("SYSUID") /* Get userid of caller */

SYSREC00_DSN = USERID||".REXXIAUL.SYSREC00" /* Build DSNAMES */

SYSPUNCH_DSN = USERID||".REXXIAUL.SYSPUNCH" /* Build DSNAMES */

SYSPRINT_DSN = USERID||".REXXIAUL.SYSPRINT" /* Build DSNAMES */

SYSIN_DSN = USERID||".REXXIAUL.SYSIN"

X = LISTDSI(SYSPUNCH 'FILE') /* Check existence */

If SYSDSNAME <> SYSPUNCH_DSN Then Call ALLOC_FILES

X = LISTDSI(SYSREC00 'FILE') /* Check existence */

If SYSDSNAME <> SYSREC00_DSN Then Call ALLOC_FILES

X = LISTDSI(SYSPRINT 'FILE') /* Check existence */

If SYSDSNAME <> SYSPRINT_DSN Then Call ALLOC_FILES

X = LISTDSI(SYSIN 'FILE') /* Check existence */

If SYSDSNAME <> SYSIN_DSN Then Call ALLOC_FILES

/*------------ Build SQL STMT that will be sent into DSNTIAUL --------*/

Queue "SELECT TB.DBNAME, TB.TSNAME, IX.CREATOR, LEFT(IX.NAME,18)"

Queue "FROM SYSIBM.SYSTABLES TB "

Queue " , SYSIBM.SYSINDEXES IX "

Queue "WHERE TB.NAME = IX.TBNAME AND TB.CREATOR = IX.TBCREATOR"

Queue " AND TB.NAME = '"||TABLE_NAME||"'"

Queue ";"

Queue "" /* This is required to signify end of stack */

Address MVS "EXECIO * DISKW SYSIN (FINIS)"

/*------------ Build the DSNTIAUL execution command ------------------*/

Queue "RUN PROGRAM(DSNTIAUL) PLAN(DSNTIAUL) PARM('SQL') ",

"LIB('"||SSID||".DB2.RUNLIB.LOAD')"

Queue "END"

/*---- Execute the contents of the command stack using DSN processor */

Address TSO "DSN SYSTEM ("SSID")"

Address MVS "DELSTACK" /* Clear Stack */

Address MVS "EXECIO * DISKR SYSREC00 (STEM RPT. FINIS)" /* Read file */

Say "DBNAME TSNAME IXCREATOR IXNAME"

Do I = 1 TO RPT.0 /* Loop through data */

Parse Var RPT.I 1 DATA_A 9 DATA_B 17 DATA_C 25 . 27 DATA_D

Say DATA_A DATA_B DATA_C DATA_D

End

Exit

/*-- Delete/Allocate Files ------------------------------------------*/

ALLOC_FILES:

X = MSGSTAT = MSG('OFF') /* Suppress messages */

Address TSO "FREE FI(SYSPRINT,SYSIN)"

Address TSO "DEL '"||SYSPRINT_DSN||"'"

Address TSO "DEL '"||SYSIN_DSN||"'"

X = MSGSTAT = MSG('ON') /* Enable Messaging */

Address TSO

"ALLOC FI(SYSREC00) DA('"SYSREC00_DSN"') NEW CATALOG",

" SP(1,1) TRACKS MGMTCLAS(MCTEMP)"

Address TSO

"ALLOC FI(SYSPUNCH) DA('"SYSPUNCH_DSN"') NEW CATALOG",

" SP(1,1) TRACKS MGMTCLAS(MCTEMP)",

" RECFM(F B) LRECL(80) BLKSIZE(23440)"

Address TSO

"ALLOC FI(SYSPRINT) DA('"SYSPRINT_DSN"') NEW CATALOG",

" SP(1,1) TRACKS MGMTCLAS(MCTEMP)",

" RECFM(F B) LRECL(80) BLKSIZE(23440)"

Address TSO

"ALLOC FI(SYSIN) DA('"||SYSIN_DSN"') NEW CATALOG",

" SP(1,1) TRACKS MGMTCLAS(MCTEMP)",

" RECFM(F B) LRECL(80) BLKSIZE(23440)"

Return;

Use caution with Binary Strings!!

SAMPLE DATA via DSNTEP2 (See previous slide for layout).

TSO REXXTEP2 SYSTABLES T5 <<<< CommandResultDBNAME TSNAME IXCREATOR IXNAME ---- For table: SYSTABLES DSNDB06 SYSDBASE DBUM560 DBNAME_TSNAME DSNDB06 SYSDBASE SYSIBM DSNDTX01 DSNDB06 SYSDBASE SYSIBM DSNDTX02

***

Can do queries and updates…what about big strings, hex data??? Error processing VERY cumbersome!!!! You have to parse all text looking for any unstructured message format.

What if there is binary data being selected (Character for bit data) can cause parsing errors. Quotes, popping up in binary data play havoc with interpretive code. What happens if your "report” spans multiple lines….yikes. There’s got to be a better way.

Sample REXX code available: REXXTEP2

Page 11: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

11

11

DSNTIAULDSNTIAUL

/* REXX Call to DSNTIAUL Example */

/* Author: Rich Fazio */

/* Published: 2001-10-01 */

/* Supports DB2 for OS/390 V5,V6,V7 */

/*-------------------------------------------------------------------*/

/* Input parms: TABLE_NAME, SSID */

/* TABLE_NAME - Expecting a non-qualified creator */

/* SSID - Expecting a DB2 subsystem ID or SSID Alias */

/*-------------------------------------------------------------------*/

Parse Upper Arg ARGS /* Retrieve arguments from invocation */

TABLE_NAME = WORD(ARGS,1)

SSID = WORD(ARGS,2)

Call SSIDCHKR SSID /* VALIDATE/TRANSLATE Return VERSION */

SSID = WORD(RESULT,1) /* "RESULT" HAS VALIDATED SSID IN IT */

DB2_VERSION = WORD(RESULT,2) /* FYI- here is the version */

MAIN_ROUTINE:

USERID = Sysvar("SYSUID") /* Get userid of caller */

SYSREC00_DSN = USERID||".REXXIAUL.SYSREC00" /* Build DSNAMES */

SYSPUNCH_DSN = USERID||".REXXIAUL.SYSPUNCH" /* Build DSNAMES */

SYSPRINT_DSN = USERID||".REXXIAUL.SYSPRINT" /* Build DSNAMES */

SYSIN_DSN = USERID||".REXXIAUL.SYSIN"

X = LISTDSI(SYSPUNCH 'FILE') /* Check existence */

If SYSDSNAME <> SYSPUNCH_DSN Then Call ALLOC_FILES

X = LISTDSI(SYSREC00 'FILE') /* Check existence */

If SYSDSNAME <> SYSREC00_DSN Then Call ALLOC_FILES

X = LISTDSI(SYSPRINT 'FILE') /* Check existence */

If SYSDSNAME <> SYSPRINT_DSN Then Call ALLOC_FILES

X = LISTDSI(SYSIN 'FILE') /* Check existence */

If SYSDSNAME <> SYSIN_DSN Then Call ALLOC_FILES

/*------------ Build SQL STMT that will be sent into DSNTIAUL --------*/

Queue "SELECT TB.DBNAME, TB.TSNAME, IX.CREATOR, LEFT(IX.NAME,18)"

Queue "FROM SYSIBM.SYSTABLES TB "

Queue " , SYSIBM.SYSINDEXES IX "

Queue "WHERE TB.NAME = IX.TBNAME AND TB.CREATOR = IX.TBCREATOR"

Queue " AND TB.NAME = '"||TABLE_NAME||"'"

Queue ";"

Queue "" /* This is required to signify end of stack */

Address MVS "EXECIO * DISKW SYSIN (FINIS)"

/*------------ Build the DSNTIAUL execution command ------------------*/

Queue "RUN PROGRAM(DSNTIAUL) PLAN(DSNTIAUL) PARM('SQL') ",

"LIB('"||SSID||".DB2.RUNLIB.LOAD')"

Queue "END"

/*---- Execute the contents of the command stack using DSN processor */

Address TSO "DSN SYSTEM ("SSID")"

Address MVS "DELSTACK" /* Clear Stack */

Address MVS "EXECIO * DISKR SYSREC00 (STEM RPT. FINIS)" /* Read file */

Say "DBNAME TSNAME IXCREATOR IXNAME"

Do I = 1 TO RPT.0 /* Loop through data */

Parse Var RPT.I 1 DATA_A 9 DATA_B 17 DATA_C 25 . 27 DATA_D

Say DATA_A DATA_B DATA_C DATA_D

End

Exit

/*-- Delete/Allocate Files ------------------------------------------*/

ALLOC_FILES:

X = MSGSTAT = MSG('OFF') /* Suppress messages */

Address TSO "FREE FI(SYSPRINT,SYSIN)"

Address TSO "DEL '"||SYSPRINT_DSN||"'"

Address TSO "DEL '"||SYSIN_DSN||"'"

X = MSGSTAT = MSG('ON') /* Enable Messaging */

Address TSO

"ALLOC FI(SYSREC00) DA('"SYSREC00_DSN"') NEW CATALOG",

" SP(1,1) TRACKS MGMTCLAS(MCTEMP)"

Address TSO

"ALLOC FI(SYSPUNCH) DA('"SYSPUNCH_DSN"') NEW CATALOG",

" SP(1,1) TRACKS MGMTCLAS(MCTEMP)",

" RECFM(F B) LRECL(80) BLKSIZE(23440)"

Address TSO

"ALLOC FI(SYSPRINT) DA('"SYSPRINT_DSN"') NEW CATALOG",

" SP(1,1) TRACKS MGMTCLAS(MCTEMP)",

" RECFM(F B) LRECL(80) BLKSIZE(23440)"

Address TSO

"ALLOC FI(SYSIN) DA('"||SYSIN_DSN"') NEW CATALOG",

" SP(1,1) TRACKS MGMTCLAS(MCTEMP)",

" RECFM(F B) LRECL(80) BLKSIZE(23440)"

Return;

/* Build CNTLCARD (SQLSMT) -

Code to build SQL STMT not shown */

Call DSNTIAUL /* Alloc libs - call pgm */

EXECIO * DISKR SYSREC00 (STEM FRED. FINIS

DO I = 1 to FRED.0

Parse Var FRED.I 1 DATA_A 9 DATA_B

17 DATA_C 25 . 27 DATA_D 44

/* DATA_A is 8 bytes. Starts in POS 1 */

/* DATA_B is 8 bytes. Starts in POS 9 */

/* DATA_C is 8 bytes. Starts in POS 17 */

/* Varchar length – toss bytes 25 and 26 */

/* DATA_D is 18 bytes. Starts in POS 27 */

End

Did it work?

This output is generated by DSNTIAUL into SYSREC00.Sample data generated by DSNTIAUL; viewed in ISPF Browse.****** ***************************** Top of Data **=COLS> ----+----1----+----2----+----3----+----4----000001 DSNDB06 SYSDBASEDBUM560 DBNAME_TSNAME 000002 DSNDB06 SYSDBASESYSIBM DSNDTX01 000003 DSNDB06 SYSDBASESYSIBM DSNDTX02 ****** **************************** Bottom of Data

TSO REXXIAUL SYSTABLES T5 <<<< CommandResultDBNAME TSNAME IXCREATOR IXNAME ---- For table: SYSTABLES DSNDB06 SYSDBASE DBUM560 DBNAME_TSNAME DSNDB06 SYSDBASE SYSIBM DSNDTX01 DSNDB06 SYSDBASE SYSIBM DSNDTX02 ***

Can do queries, difficult mixing updates with selects…which one worked. However, complex data can be retrieved: long strings, hex data.Error processing VERY cumbersome!!!! All text must be parsed, scanning for known msg formats.

This works much better for data gathering. All fields are stored inside variables upon being read. There’s got to be a better more efficient way.

Sample REXX code available: REXXIAUL

Page 12: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

12

12

After DB2 V5 REXX ExtensionAfter DB2 V5 REXX Extension

• What’s Bad about it?No more hoops

for you! FETCH!!!

No Bind Required!

• What’s Good about it?

– Can’t do a SELECT– It’s interpretive: Slower than Cobol

– It’s FREE– Can define up to 200 Cursors– Don’t pre-define host Variables!!!! (except SPs)– Supports Almost all DB2/OS390 SQL Syntax– It’s interpretive: quickly modifiable, easy to rollout

What's Bad: Rexx cannot do a singleton SELECT, however, neither can ANY piece of dynamic code. This is a limitation placed by DB2, not REXX.Rexx cannot also issue "Declare section, Declare Statement, End declare section, Include (for copybooks), and WHENEVER" Rexx has only 1 major drawback…. It’s interpretive, thus slower running than compiled languages. You can compile REXX, however, that takes away some of the flexibility that REXX offers. That’s probably why you code Rexx in the first place.What's Good: It’s FREE! Free is good! No binds as you rollout app.Not having to define host variables is GREAT!!!! Using the SQLDA to process cursors will allow you a 100% dynamic environment.Cursor Names:C1 to C100: By default are defined with the WITH RETURN clauseC51 to C100: By default are defined with the WITH HOLD clauseC101 to C200: These are used with ALLOCATE, to pull result sets from a stored procedure.Dynamic SQL can be a curse and a blessing. Want to start developing apps using dynamic SQL? Try with REXX. You don’t have to worry about defining variables, just code and go. It’s a great place to learn and practice.

Page 13: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

13

13

REXX SQL InterfaceREXX SQL Interface

/* Build SQLSTMT – Not shown */

DECLARE CURSOR

PREPARE STMT

OPEN CURSOR

END_OF_CURSOR = "N"

DO UNTIL END_OF_CURSOR = "Y"

FETCH INTO :DATA_A, :DATA_B, :DATA_C

If SQLCODE = +100 Then END_OF_CURSOR = "Y"

Else Call SQLERR_RTN

End

Exit;

SQLERR_RTN:

xxx

Return;

/* REXX Call to DB2 via SQL Example */

/* Author: Rich Fazio */

/* Published: 2002-03-11 */

/* Supports DB2 for OS/390 V5,V6,V7 */

/*-------------------------------------------------------------------*/

/* Input parms: TABLE_NAME, SSID */

/* TABLE_NAME - Expecting a non-qualified creator */

/* SSID - Expecting a DB2 subsystem ID or SSID Alias */

/*-------------------------------------------------------------------*/

Parse Upper Arg ARGS /* Retrieve arguments from invocation */

TABLE_NAME = WORD(ARGS,1)

SSID = WORD(ARGS,2)

Call SSIDCHKR SSID /* VALIDATE/TRANSLATE Return VERSION */

If RESULT = 8 Then Exit(8) /* Bad return code from SSIDCHKR - Exit */

SSID = WORD(RESULT,1) /* "RESULT" HAS VALIDATED SSID IN IT */

DB2_VERSION = WORD(RESULT,2) /* FYI- here is the version */

MVS_SMFID = WORD(RESULT,3) /* Fyi - MVS LPAR ID for this DB2 */

MAIN_ROUTINE:

/*------------ Build SQL STMT that will be sent into DB2 ------------*/

Say "DBNAME TSNAME IXCREATOR IXNAME"

Call INITIALIZE /* HOUSEKEEPING */

SQLSTMT = "" /* INITIALIZE SQLSTMT TEXT */

SQLSTMT.0 = 5 /* SET NUMBER OF LINES IN SQL STMT */

SQLSTMT.1 = "SELECT TB.DBNAME, TB.TSNAME, IX.CREATOR, IX.NAME "

SQLSTMT.2 = "FROM SYSIBM.SYSTABLES TB "

SQLSTMT.3 = " , SYSIBM.SYSINDEXES IX "

SQLSTMT.4 = "WHERE TB.NAME = IX.TBNAME AND TB.CREATOR = IX.TBCREATOR"

SQLSTMT.5 = " AND TB.NAME = '"||TABLE_NAME||"'"

Do X = 1 TO SQLSTMT.0;SQLSTMT = SQLSTMT||SQLSTMT.X;End /* BUILD STMT */

/* PREPARE THE STATEMENT FOR EXECUTION BY DECLARING THE CURSOR, */

/* DOING A PREPARE OF THE STATEMENT, AND OPENING THE CURSOR. */

/* */

Address DSNREXX

"EXECSQL PREPARE S1 FROM :SQLSTMT" /* PREPARE ABOVE STMT */

If SQLCODE \= "0" Then Do /* ERROR OCCURRED */

ERROR_NOTE = "PREPARE CURSOR FAILED "

ERROR_AID = " NONE "

Call SQLERR_RTN_Exit

End

Address DSNREXX

"EXECSQL DECLARE C1 CURSOR FOR S1" /* DECLARE CURSOR */

If SQLCODE \= "0" Then Do /* ERROR OCCURRED */

ERROR_NOTE = "DECLARE CURSOR FAILED "

ERROR_AID = " NONE "

Call SQLERR_RTN_Exit

End

Address DSNREXX

"EXECSQL OPEN C1" /* OPEN CURSOR */

If SQLCODE \= "0" Then Do /* ERROR OCCURRED */

ERROR_NOTE = "OPEN CURSOR FAILED "

ERROR_AID = " NONE "

Call SQLERR_RTN_Exit

End

END_OF_CURSOR = "N" /* Reset end of cursor flag */

Do Until END_OF_CURSOR = "Y"

Address DSNREXX

"EXECSQL FETCH C1 INTO :DBNAME, :TSNAME, :IXCREATOR, :IXNAME"

If SQLCODE = 0 Then Do

Say DBNAME TSNAME IXCREATOR IXNAME /* Display data */

End

ELSE,

If SQLCODE = +100 Then END_OF_CURSOR = "Y" /* End OF CURSOR */

Else Do

ERROR_NOTE = "FETCH FROM CURSOR FAILED " /* ERROR */

ERROR_AID = " NONE "

Call SQLERR_RTN_Exit

End

End

CLOSE_CURSOR:

Address DSNREXX

"EXECSQL CLOSE C1" /* CLOSE CURSOR */

If SQLCODE /= 0 Then Call SQLERR_RTN_Exit

/*-------------------------------------------------------------------*/

/* End OF TASK PROCESSING */

/*-------------------------------------------------------------------*/

Address DSNREXX "DISCONNECT" /* CUT REXX/ DB2 LINK */

S_RC = RXSUBCOM('DELETE','DSNREXX','DSNREXX')

Exit 0

/*-------------------------------------------------------------------*/

/* START OF TASK PROCESSING */

/*-------------------------------------------------------------------*/

INITIALIZE:

Call TSOCLRXX /* CLEAR SCREEN */

Address MVS /* REXX/DB2 TALKING? CONNECT TO DB2 ENVIRONMENT */

"SUBCOM DSNREXX"

If RC Then

S_RC = RXSUBCOM('ADD','DSNREXX','DSNREXX')

Address DSNREXX "CONNECT" SSID

If RC /= 0 Then Do /* ERROR? */

Say "FAILURE TO CONNECT TO DATABASE"

Exit 8

End

RETURN;

/*-------------------------------------------------------------------*/

/* HARD ERROR- DISPLAY DOC; TERMINATE PROCESSING W /RC=8 */

/*-------------------------------------------------------------------*/

SQLERR_RTN_EXIT:

Call SQLERR_RTN_DISPLAY

EXIT(8)

RETURN;

/*-------------------------------------------------------------------*/

/* SOFT ERROR- DISPLAY DOC; KEEP PROCESSING */

/*-------------------------------------------------------------------*/

SQLERR_RTN_DISPLAY:

Say "SQL STATEMENT RECEIVEING ERROR FOLLOWS"

Say "--------------------------------------------------"

Do X = 1 TO SQLSTMT.0;Say SQLSTMT.X;End

Say "--------------------------------------------------"

Say "APPLICATION DIAGNOSTICS"

Say ERROR_NOTE

Say ERROR_AID

Say "DB2 DIAGNOSTICS FOLLOW:"

Say "--------------------------------------------------"

Say "RESULT OF SQL STATEMENT:"

Say 'SQLCODE ='SQLCODE

Say 'SQLERRM ='SQLERRMC

Say 'SQLERRML='SQLERRML

Say 'SQLERRP ='SQLERRP

Say 'SQLERRD ='SQLERRD.1','|| SQLERRD.2','|| SQLERRD.3',',

||SQLERRD.4','|| SQLERRD.5','|| SQLERRD.6

Say 'SQLWARN ='SQLWARN.0',' || SQLWARN.1',' || SQLWARN.2',' ||,

SQLWARN.3',' || SQLWARN.4',' || SQLWARN.5',' || SQLWARN.6',' ||,

SQLWARN.7',' || SQLWARN.8',' || SQLWARN.9',' || SQLWARN.10

Say 'SQLSTATE='SQLSTATE

RETURN;

Thorough Error

CheckingPseudoCode

TSO REXXSQL SYSTABLES T5 <<<< Command

ResultDBNAME TSNAME IXCREATOR IXNAME ---- For table: SYSTABLES

DSNDB06 SYSDBASE DBUM560 DBNAME_TSNAME

DSNDB06 SYSDBASE SYSIBM DSNDTX01

DSNDB06 SYSDBASE SYSIBM DSNDTX02

***

Sample REXX code available: REXXSQL

Page 14: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

14

14

ParlezParlez--vous DB2?vous DB2?

Create the REXX/DB2 command interface

Remove the REXX/DB2 command interface

Before I can "talk" to DB2, I need to understand the language!

ADDRESS MVS "SUBCOM DSNREXX"

IF RC THEN S_RC = RXSUBCOM(’ADD’,’DSNREXX’,’DSNREXX’)

S_RC = RXSUBCOM(’DELETE’,’DSNREXX’,’DSNREXX’)

Add "DSNREXX" to my Command Environment.

"RXSUBCOM DSNREXX" adds the "DSNREXX" to the REXX command table.If this step is missed, a RC –3 will result when you make a call to DB2. This is more a "REXX thing" than a "DB2 thing", however, you cannot make calls to DB2 without doing this first. Note: this command does not create a DB2 thread. That is done in a CONNECT statement.** The "Address tso "SUBCOM DSNREXX" tests the existence of the REXX/DB2 command interface.

"SUBCOM" is a REXX command.** If the command interface is present, the REXX special register RC is set to zero. In the example above, the

"IF" statement would fail and processing would continue.** If the command interface is not present, the REXX special register RC is set to "1". In the example above,

the "IF" statement would succeed and the RXSUBCOM command is issued.** The string "RXSUBCOM(‘ADD’,’DSNREXX’,’DSNREXX’)", is a command. RXSUBCOM is an alias

for DSNREXX. It’s most logical home is SDSNLOAD or a dedicated REXX/DB2 load library. This program adds "DSNREXX" as a valid REXX command (This allows you to "Address DSNREXX").

----------------------------------------------------------------------------------------------------------------RXSUBCOM has optional parameters (UPDATE, DELETE, QUERY) that are discussed further in:TSO/E REXX Reference: Maintain Entries in the Host Command Environment Table – IRXSUBCM.Return codes of the IRXSUBCM are documented in this section.

The IRXSUBCM command appears to be called from RXSUBCOM, either that, or RXSUBCOM has replicated the function of IRXSUBCM. This information provided for the bit heads.

Page 15: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

15

15

Connect to DB2Connect to DB2 Establish thread between REXX and DB2

The following statements do the same thing.

ADDRESS DSNREXX "CONNECT DB2A" IF SQLCODE /= 0 THEN CALL error_routine

DB2_SSID = "DB2A" ADDRESS DSNREXX "CONNECT" DB2_SSID IF SQLCODE /= 0 THEN CALL error_routine

DB2_SSID = "DB2A" ADDRESS DSNREXX "CONNECT" DB2_SSID IF RC = -3 THEN . . . /* Missed SUBCOM setup */

"ADDRESS DSNREXX" let’s REXX know that a DB2 command is coming. Parsing/syntax checking is passed on to ‘DSNREXX’.

"CONNECT" is a verb identifying DSNREXX to build a thread to the SSID listed.

The DB2 subsystem name can be a literal or variable could be a literal or a variable.

RC –3 indicates that a command environment has not been established.

Page 16: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

16

16

Run SQLRun SQL

Grrrrrrr….Test your

SQLCODE!

Example of Execute Immediate

ADDRESS DSNREXX"EXECSQL UPDATE TEST.MYTABLE SET INQ_IND = ‘Y’"IF SQLCODE \= "0" THEN,DO

ERROR_NOTE = "UPDATE FAILED "ERROR_AID = " NONE "CALL SQLERR_RTN_EXIT

END

Run this SQL Statement. It is one of 10 Update statements in your program….which one received the error?

Wouldn’t it be nice to display the SQL Stmt in the error rtn?

Rather than "UPDATE FAILED" how about:"UPDATE #8" failed“

or“UPDATE of INQ_IND to ‘Y’ failed”

ERROR_NOTE is meant to be a descriptor of the statementERROR_AID is meant to display key info or other diagnostics. Possibly the host variables used by an SQL Statement.

Page 17: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

17

17

Storing SQLStoring SQL

SQLSTMT = "" /* INITIALIZE SQLSTMT TEXT */SQLSTMT.0 = 3 /* SET NUMBER OF LINES IN SQL STMT */SQLSTMT.1 = "SELECT TB.DBNAME, TB.TSNAME, IX.CREATOR, IX.NAME"SQLSTMT.2 = " FROM "||SOURCE_TABLESQLSTMT.3 = "WHERE NAME = ‘"||HOST_VARIABLE||"’"

/* BUILD THE SQLSTMT FOR EXECUTION; EACH LINE APPENDED TO THE PRIOR */

DO X = 1 TO SQLSTMT.0; SQLSTMT = SQLSTMT||SQLSTMT.X ||" " ;END

ADDRESS DSNREXX "EXECSQL " SQLSTMTIF SQLCODE \= "0" THEN,DO

ERROR_NOTE = "QUERY FAILED "ERROR_AID = " NONE "CALL SQLERR_RTN_EXIT

END

Packaging up your SQL code neatly allows for re-usability of routines. It also allows for great program readability and nice error reporting. Leave at least one before each line of SQL text. If there are no embedded blanks between the last byte of line 1 and the first byte of line 2…the two strings will be smashed together – causing an SQL Error. You will note that I concatenate an additional blank in the small DO LOOP. This ensures that there is always a blank between lines.

Note, there are 2 different variables being used in this example:

SQLSTMT is a simple string variable containing the concatenated SQL statement.

SQLSTMT.x is a STEM variable that contains each line of the SQL STMT. (Basically, an array)

SQLSTMT.0 is length. Stem variables use a convention that entry "0" contains a length.Once the STEM variable is fully populated, a simple loop concatenates each line of SQL together on the end of SQLSTMT.

Page 18: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

18

18

Storing SQLStoring SQL

SQLSTMT = "" SQLSTMT.0 = 3 SQLSTMT.1 = "SELECT TB.DBNAME, TB.TSNAME, IX.CREATOR, IX.NAME"SQLSTMT.2 = " FROM "||SOURCE_TABLESQLSTMT.3 = "WHERE NAME = ‘"||HOST_VARIABLE||"’"

Memory area for SQLSTMT

DO X = 1 TO SQLSTMT.0;

SELECT TB.DBNAME, TB.TSNAME, IX.CREATOR, IX.NAME

SQLSTMT = SQLSTMT||SQLSTMT.1 ||" "

DO X = 1 TO SQLSTMT.0; SQLSTMT = SQLSTMT||SQLSTMT.X ||" " ;END

;ENDSQLSTMT = SQLSTMT||SQLSTMT.2 ||" "

SELECT TB.DBNAME, TB.TSNAME, IX.CREATOR, IX.NAME FROM SYSIBM.SYSTABLESSELECT TB.DBNAME, TB.TSNAME, IX.CREATOR, IX.NAME FROM SYSIBM.SYSTABLES WHERE NAME = ‘fred’

SQLSTMT = SQLSTMT||SQLSTMT.3 ||" "

This slide animates the process of loading the SQLSTMT into a variable for DB2 to use.

Notes:

Page 19: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

19

19

Error Routine OutputError Routine Output

Nice Doc!

SQL STATEMENT RECEIVEING ERROR FOLLOWS--------------------------------------------------SELECT TB.DBNAME, TB.TSNAME, IX.CREATOR, IX.NAME

FROM SYSIBM.SYSTABLES

WHERE NAME = ‘fred’--------------------------------------------------APPLICATION DIAGNOSTICS

QUERY FAILED

NONE --------------------------------------------------DB2 DIAGNOSTICS FOLLOW:

RESULT OF SQL STATEMENT:

SQLCODE =-104

SQLERRM =SQLERRM =SQLSTMT:FETCH,INSERT,UPDATE,DELETE,OPEN,CLOSE,SET,CALL

SQLERRML=SQLERRML

SQLERRP =DSN

SQLERRD =0,0,0,0,0,0

SQLWARN = , , , , , , , , , ,

SQLSTATE=42601

Care to call DSNTIAR from a Rexx? IBM does not “officially” support calling it from Rexx. (See sample code available via email) They do, however, recommend calling DSNACCMG. This is an undocumented stored procedure provided by IBM’s Control Center. How can you call a DB2 Stored Procedure if your connection is lost? How do you handle SQL errors in calling DSNACCMG? I feel this was more trouble than it is worth.Here’s the code.SQLERRML = 70 SQLERRD0 = SQLERRD.1 SQLERRD1 = SQLERRD.2 SQLERRD2 = SQLERRD.3 SQLERRD3 = SQLERRD.4 SQLERRD4 = SQLERRD.5 SQLERRD5 = SQLERRD.6 SQLWARN = SQLWARN.1||SQLWARN.2||SQLWARN.3||SQLWARN.4||SQLWARN.5||,

SQLWARN.6||SQLWARN.7||SQLWARN.8||SQLWARN.9||SQLWARN.10 SQLSTATE = "'"SQLSTATE"'" /* Note: The use of QUOTES-this is

important, or it will not work */ MSGTEXT = LEFT(" ",1210," ") SQLSTMT = "" /* INITIALIZE SQLSTMT TEXT */ SQLSTMT.0 = 3 /* SET NUMBER OF LINES IN SQL STMT */ SQLSTMT.1 = "CALL SYSPROC.DSNACCMG (:SQLCODE, :SQLERRML, :SQLERRMC, " SQLSTMT.2 = ":SQLERRP, :SQLERRD0, :SQLERRD1, :SQLERRD2, :SQLERRD3, " SQLSTMT.3 = ":SQLERRD4, :SQLERRD5, :SQLWARN, :SQLSTATE , :MSGTEXT)"

Page 20: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

20

20

Return CodesReturn Codes

RC: Rexx "Return code" field. (value passed from a subcmd that is called by your Rexx)

“ –3” has a special meaning: The command being issued, cannot be found.

SQLCASQLCODE SQLERRMC SQLERRP SQLERRD.1 SQLERRD.2 SQLERRD.3 SQLERRD.4 SQLERRD.5 SQLERRD.6

SQLCASQLWARN.0 SQLWARN.1 SQLWARN.2 SQLWARN.3 SQLWARN.4 SQLWARN.5 SQLWARN.6 SQLWARN.7 SQLWARN.8 SQLWARN.9 SQLWARN.10 SQLSTATE

Note: No eye catchers (SQLAID), no length (SQLABC)

SQLDA

SQLN (not used) stem.SQLD stem.n.SQLTYPE stem.n.SQLLEN stem.n.SQLLEN.SQLPRECISION stem.n.SQLLEN.SQLSCALE stem.n.SQLCCSID stem.n.SQLLOCATOR stem.n.SQLDATA stem.n.SQLIND stem.n.SQLNAME

These descriptions assume you are using the "DSNREXX" connection. If you use SQLDBS Attach, the SQLCA variables are compound variables that begin with the stem SQLCA (DB2 Universal Database for OS/390 and z/OS V7, Application Programming, and SQL Guide).

RC Meaning

0 No SQL warning or error occurred.

-1 An SQL warning occurred.

+1 An SQL Warning occurred

-3 The first token after ADDRESS DSNREXX is in error.

Page 21: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

21

21

Using SQLDAUsing SQLDA

/* The sqlstmt below is stored in parameter marker “:SQLSTMT” */SELECT EMPNO, FIRSTNME, HIREDATE, EDLEVEL, SALARY FROM DSN8710.EMP TB WHERE EMPNO = '000010'

ADDRESS DSNREXX "EXECSQL DECLARE C1 CURSOR FOR S1" ADDRESS DSNREXX "EXECSQL PREPARE S1 INTO :OUTSQLDA FROM :SQLSTMT" ADDRESS DSNREXX "EXECSQL OPEN C1" SAY "TYP TYPNAME NAME LEN PRECISION SCALE DATA IND" DO UNTIL(END_OF_CURSOR_FLG = "Y")

ADDRESS DSNREXX "EXECSQL FETCH C1 USING DESCRIPTOR :OUTSQLDA"…SQLCODE Test…CALL FORMAT_DATA

END

TYP TYPNAME NAME LEN PRECISION SCALE DATA IND 452 CHAR EMPNO 6 0 0 000010 448 VARCHAR FIRSTNME 12 0 0 CHRISTINE 385 DATE HIREDATE 10 0 0 NULL -1501 SMALLINT EDLEVEL 2 0 0 18 485 DECIMAL SALARY 0 9 2 52750.00

This isOne Row!

Column #1

Column #5

/* Format Data Routine */DO I = 1 TO OUTSQLDA.SQLD

TP = OUTSQLDA.I.SQLTYPE /* Length */

LEN = OUTSQLDA.I.SQLLEN IF RIGHT(LEN,6) = "SQLLEN" THEN LEN = 0

/* Precision */ PREC = OUTSQLDA.I.SQLLEN.SQLPRECISION IF RIGHT(PREC,8) = "RECISION" THEN PREC = 0

/* Nulls? Toggle data or ‘null’ for report */ IF OUTSQLDA.I.SQLIND = -1 THEN DATA_VALUE = " NULL" ELSE DATA_VALUE = OUTSQLDA.I.SQLDATA

/* Scale */ SCAL = OUTSQLDA.I.SQLLEN.SQLSCALE IF RIGHT(SCAL,5) = "SCALE" THEN SCAL = 0

/* Report */ SAY OUTSQLDA.I.SQLTYPE||" ",

LEFT(SQLTYPES.TP,10," "), ||LEFT(OUTSQLDA.I.SQLNAME,9," "), ||RIGHT(LEN,3," "), ||RIGHT(PREC,10," "), ||RIGHT(SCAL,8," "), ||RIGHT(DATA_VALUE,15," ")

END I

Page 22: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

22

22

Now what?

This space intentionally left blank.

How you use REXX is up to you…just USE it.

Page 23: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

23

23

SSIDCHKRSSIDCHKR

SSID validation is common. Every REXX that talks with DB2 must have valid DB2 SSID passed in; thus, validation of that SSID is required. Put it in a common routine!

INPUTS

Full SSIDOrSSID Alias -Shortname PROCESS

•Validate alias or full SSID•Translate alias to full SSID•Provide Version of DB2•Provide SMFID of LPAR where DB2 active•Optionally, notify user that they are not logged on proper LPAR

OUTPUTS

Full SSIDDB2 Version SMFID of desired DB2

Do you want to replicate 100

lines of code in every EXEC?

Put subsystem validation in one spot. Make it complete, make it flexible. "SSIDCHKR” is a routine I wrote to validate the DB2 SSID entered from other REXXs. I also have allowed for short names ("aliases”) to be translated inside the routine. Optional validation of the LPAR can be done as well.

Later in the presentation, DISPUTIL ( DB2 –DISPLAY UTILITY command), will be discussed. It reformats DB2 messages. Message formats change with a version or release change of DB2. To make your code flexible enough to handle format changes. Every release messages change… you need to know the version.

Hmmm, something is missing. How about location name? I just added this code recently. What other things can we add at a subsystem level?

Sample REXX code available: SSIDCHKR

Page 24: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

24

24

REXX Calls a DB2 CommandREXX Calls a DB2 Command

/*------------- STUFF DB2 COMMAND STRING INTO THE STACK ----------- */"NEWSTACK" /* Create input stack for TSO CMD processor */X = OUTTRAP(CMD_OUTPUT.) /* Capture to STEM Variable */QUEUE "-DISPLAY UTILITY("UTIL_ID")" /* "UTIL_ID" is a REXX Variable */QUEUE "END"QUEUE ""/* Execute the contents of the command stack using DSN processor */ADDRESS TSO "DSN SYSTEM ("SSID")"X = OUTTRAP(OFF) /* Turn off capture */"DELSTACK" /* Clear the previously created stack */

This process allows you to build a cmd stack (normally the contents of SYSTSIN).Note the creation of a new stack: This insures that you control the contents of the stack (no leftovers). OUTTRAP is used to capture terminal messages from commands. I have indicated to put the results of the "TSO DSN" command into the CMD_OUTPUT stem variable.The stack is built one line at a time, top down (FIFO). If you prefer to build bottom up (LIFO), use "PUSH".The "END" command signifies the "end" of the DB2 command processor task.The QUEUE with a null string is required to signify the end of a stack. Without this statement, a null prompt would appear on your screen. After enter is struck (without any additional data being keyed), control is given back to the REXX. After the stack is built, run it. "ADDRESS TSO….." Runs the DSN Command processor under TSO. In this case, we are passing in the DB2 subsystem name. A static value or system default could be used in stead.Lastly, use DELSTACK to clean up the stack you built. Be a good citizen, don’t litter in your TSO Address space.

Page 25: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

25

25

--DISPLAY UTILITY(*)DISPLAY UTILITY(*)

DSNU100I -DB2A DSNUGDIS - USERID = ROGUE MEMBER = UTILID = ROGUE.ROUGE1 PROCESSING UTILITY STATEMENT 1 UTILITY = LOAD PHASE = RELOAD COUNT = 61197984 NUMBER OF OBJECTS IN LIST = 1 LAST OBJECT STARTED = 1 STATUS = STOPPED

DSNU100I -DB2A DSNUGDIS - USERID = JOEPGMR MEMBER = UTILID = JOEPGMR.JOEPGMR2 PROCESSING UTILITY STATEMENT 1 UTILITY = LOAD PHASE = RELOAD COUNT = 0 NUMBER OF OBJECTS IN LIST = 1 LAST OBJECT STARTED = 1 STATUS = STOPPED

DSNU105I -DB2A DSNUGDIS - USERID = COOLDBA MEMBER = UTILID = COOLDBA.REORG1 PROCESSING UTILITY STATEMENT 1 UTILITY = REORG

Here’s an example of the tools you can develop with REXX and a simple DB2 command.

I present for your enjoyment….the output of the DB2 –DISPLAY UTILITY command.

Screen #1.

Page 26: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

26

26

--DISPLAY UTILITY(*)DISPLAY UTILITY(*)

PHASE = BUILD COUNT = 3260046018 NUMBER OF OBJECTS IN LIST = 1 LAST OBJECT STARTED = 1

DSNU111I -DB2A DSNUGDIS - SUBPHASE = COPY COUNT = 9044775 DSNU111I -DB2A DSNUGDIS - SUBPHASE = SORTOUT COUNT = 2056453640 DSNU111I -DB2A DSNUGDIS - SUBPHASE = RUNSTATS COUNT = 1720416 DSNU105I -DB2A DSNUGDIS - USERID = RFAZIO

MEMBER = UTILID = REORG001 PROCESSING UTILITY STATEMENT 1 UTILITY = REORG PHASE = LOG COUNT = 0 NUMBER OF OBJECTS IN LIST = 1 LAST OBJECT STARTED = 1 STATUS = ACTIVE

DSNU347I -DB2A DSNUGDIS -DEADLINE = NONE

DSNU384I -DB2A DSNUGDIS -MAXRO = DEFER LONGLOG = CONTINUE DELAY = 1200 SECONDS

DSNU383I -DB2A DSNUGDIS - CURRENT ITERATION NUMBER = 207 WRITE ACCESS ALLOWED IN THIS ITERATION = YES

The output of the DB2 –DISPLAY UTILITY command screen #2

Page 27: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

27

27

--DISPLAY UTILITY(*)DISPLAY UTILITY(*)

ITERATION BEFORE PREVIOUS ITERATION: ELAPSED TIME = 01:41:12 NUMBER OF LOG RECORDS PROCESSED = 3746459374

PREVIOUS ITERATION: ELAPSED TIME = 00:23:10 NUMBER OF LOG RECORDS PROCESSED = 84763764 CURRENT ITERATION:

ESTIMATED ELAPSED TIME = 04:10:11 ACTUAL ELAPSED TIME SO FAR = 03:01:03 ACTUAL NUMBER OF LOG RECORDS BEING PROCESSED = 5857634451

CURRENT ESTIMATE FOR NEXT ITERATION: ELAPSED TIME = 01:34:11 NUMBER OF LOG RECORDS TO BE PROCESSED = 36465284634

DSNU111I -DB2A DSNUGDIS - SUBPHASE = COPY COUNT = 5613883 DSN9022I -DB2A DSNUGCCC '-DISPLAY UTILITY' NORMAL COMPLETION

The output of the DB2 –DISPLAY UTILITY command screen #3

Page 28: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

28

28

DISPU RexxDISPU Rexx

DB2 Utility Report for DB2 Subsystem ID DBT4 Display Mode: TERSE MOD5 UTILID Mask(*)

REF STM--LIST NBR USERID -----UTILID------ ----TYPE ---PHASE -----COUNT----- NBR CUR TOT STATUS MEMBER ------------------------------------------------------------------------------------------------1 ROGUE ROGUE.ROUGE1 LOAD RELOAD 61,197,984 1 1 1 STOPPED ------------------------------------------------------------------------------------------------2 JOEPGMR JOEPGMR.JOEPGMR2 LOAD RELOAD 0 1 1 1 STOPPED ------------------------------------------------------------------------------------------------3* COOLDBA COOLDBA.REORG1 REORG BUILD 3,260,046,018 1 1 1 ACTIVE ------------------------------------------------------------------------------------------------4* RFAZIO REORG001 REORG LOG 0 1 1 1 ACTIVE ------------------------------------------------------------------------------------------------* - Additional utility info available for display

<Enter> to loop, <Quit> to exit, <Help> for more options

Commas?!?!

This is my version of –Display UtilityHere is an easier to read, focused display.

Sample REXX code available: DISPU, DISPUTIL

Supports V8 Messages!

Page 29: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

29

29

DISPU DISPU –– Display DetailDisplay Detail

DB2 Utility Report for DB2 Subsystem ID DBT4 Display Mode: VERBOSE MOD5 UTILID Mask(*)

REF STM--LIST NBR USERID -----UTILID------ ----TYPE ---PHASE -----COUNT----- NBR CUR TOT STATUS MEMBER ------------------------------------------------------------------------------------------------1 ROGUE ROGUE.ROUGE1 LOAD RELOAD 61,197,984 1 1 1 STOPPED ------------------------------------------------------------------------------------------------2 JOEPGMR JOEPGMR.JOEPGMR2 LOAD RELOAD 0 1 1 1 STOPPED ------------------------------------------------------------------------------------------------3 COOLDBA COOLDBA.REORG1 REORG BUILD 3,260,046,018 1 1 1 ACTIVE 3 COOLDBA COOLDBA.REORG1 SUBPHASE COPY 9,044,775 3 COOLDBA COOLDBA.REORG1 SUBPHASE SORTOUT 2,056,453,640 3 COOLDBA COOLDBA.REORG1 SUBPHASE RUNSTATS 1,720,416 ------------------------------------------------------------------------------------------------4 RFAZIO REORG001 REORG LOG 0 1 1 1 ACTIVE 4 RFAZIO REORG001 SUBPHASE COPY 5,613,883 ----ITERATION---- Time LOG RECORDS

205 ACTUAL 01:41:12 3,746,459,374 206 ACTUAL 00:23:10 84,763,764 207 ACTUAL 03:01:03 5,857,634,451 WRITE ACCESS ALLOWED 207 ESTIMATE 04:10:11 N/A 208 FORECAST 01:34:11 36,465,284,634

REORG DEADLINE = NONE MAXRO = DEFER, LONGLOG = CONTINUE, DELAY = 1200 SECONDS ------------------------------------------------------------------------------------------------<Enter> to loop, <Quit> to exit, <Help> for more options

Detail anyone? All on one page (or at least fewer pages).

Page 30: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

30

30

IBM IBM ––DISPLAY DATABASE (PreDISPLAY DATABASE (Pre--V8)V8)

DSN SYSTEM (DBP5) -DIS DB(CPCNSD03) SPACE(CINDVS10) DSNT360I -DBP5 *********************************** DSNT361I -DBP5 * DISPLAY DATABASE SUMMARY

* GLOBAL DSNT360I -DBP5 *********************************** DSNT362I -DBP5 DATABASE = CPCNSD03 STATUS = RW

DBD LENGTH = 924692 DSNT397I -DBP5 NAME TYPE PART STATUS PHYERRLO PHYERRHI CATALOG PIECE -------- ---- ---- ------------------ -------- -------- -------- -----CINDVS10 TS 001 RW CINDVS10 TS 002 UT CINDVS10 TS 003 RW CINDVS10 TS 004 RW CINDVS10 TS 005 UT CINDVS10 TS 006 RW CINDVS10 TS 007 RW CINDVS10 TS 008 RW CINDVS10 TS 009 RW CINDVS10 TS 010 RW CINDVS10 TS 011 RW CINDVS10 TS 012 RW CINDVS10 TS 013 RW CINDVS10 TS 014 RW CINDVS10 TS 015 UT ***

Consider IBM –DISPLAY DATABASE output

Interpret output from IBM’s –DISPLAY DATABASE• Lengthy multi-page display of information• Truncation of messages beyond 65k (MSGDSNT306I)• No “summary” display• Miss entries in massive lists• Hit <Enter> one time too many -- Uggg• Does not show corresponding table/ index information

Page 31: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

31

31

IBM IBM ––DISPLAY DATABASE (V8)DISPLAY DATABASE (V8)

DSN SYSTEM (DBP5) -DIS DB(CPCNSD03) SPACE(CINDVS10) DSNT360I -DBP5 *********************************** DSNT361I -DBP5 * DISPLAY DATABASE SUMMARY

* GLOBAL DSNT360I -DBP5 *********************************** DSNT362I -DBP5 DATABASE = CPCNSD03 STATUS = RW

DBD LENGTH = 924692 DSNT397I -DBP5 NAME TYPE PART STATUS PHYERRLO PHYERRHI CATALOG PIECE-------- ---- ----- ----------------- -------- -------- -------- -----CINDVS10 TS 0001 RW CINDVS10 TS 0002 UT CINDVS10 TS 0003 RW

-THRU 0004 CINDVS10 TS 0005 UT CINDVS10 TS 0006 RW

-THRU 0014 CINDVS10 TS 0015 UT

-THRU 0016 CINDVS10 TS 0017 RW CINDVS10 TS 0018 UT CINDVS10 TS 0019 RO CINDVS10 TS 0020 RWCINDVS10 TS 0021 ROCINDVS10 TS 0022 RW***

Consider IBM –DISPLAY DATABASE output (V8)

Better….but….

Page 32: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

32

32

Consolidated Consolidated ––DISPLAY DATABASE ReportDISPLAY DATABASE Report

TSO DISPDB P5 CPCNSD0% EXCL(UTRW)

DB Name Pageset RW UT RO STOP UTRO OTHERCPCNSD01 CCPS5SK0 - - - - - RW,UTRW...254CPCNSD03 CACCTS20 212 30 - - - STOP,RECP....12CPCNSD03 CCPS5SC0 184 70 - - -CPCNSD03 CCPS5SH0 - 50 - - - RO,COPY...204CPCNSD03 CCPS5SK0 184 70 - - -CPCNSD03 CCPS6SB0 181 73 - - -CPCNSD03 CCPS6SC0 204 50 - - -CPCNSD03 CINDVS10 247 5 2 - -CPCNSD03 CINDVS50 196 58 - - -CPCNSD03 CINQYS10 204 50 - - -CPCNSD03 CINQYS20 204 50 - - -CPCNSD03 CPUBRS10 235 19 - - -------------------------------------------------------------Total 3048 | 2000 578 - - - 470Percent 100 | 66 19 - - - 15------------------------------------------------------------Exclusion list provided: UTRWTablespaces Selected: 78 Reported: 12

Output of my Rexx Exec.

Consider maintaining a database with 26 tables, 254 parts a piece. This is part of a 7tb data warehouse and it is loaded every two weeks.

This REXX delivers a consolidated report of tablespaces and their pageset status. Prior to loading, all objects in the database are set to “UT” status. As each partition is loaded, the status is reset to “RW”.

Wildcard a database, add tablespace and/or tablespace pattern. Show indexes and/or table names for each object being shown.

Exclude objects with all parts in a supplied state. This example will ignore any tablespace where all parts are in “RW” mode or “UT” mode.

Sample REXX code available: DISPDB

Supports V8 Messages!

Page 33: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

33

33

REXX REXX –– Run Utility (Prepare)Run Utility (Prepare)

41 Parameters are needed for Stored Procedure DSNUTILS.

UTILITY_ID = "REXXUTIL" RESTART = "NO " UTSTMT = "DIAGNOSE DISPLAY SYSUTIL" RETCODE = 0 UTILITY_NAME = "DIAGNOSE" …..COPYSPACE1 = 0 COPYDSN2 = "" COPYDEVT2 = "" COPYSPACE2 = 0

DATA TYPE of each of the 41 parameters matter!!!!!

Character

Numeric

The advent of Control Center allows for fully remote processing of many features in DB2. This includes running utilities from ANYWHERE via a stored procedure call. Take a look at running a utility from REXX!---------------------Return code of zero is not wishful thinking; I need to define the data type of the field as numeric.

Note: some DB2 maintenance caused the number of parameters to change from 38 to 41 parameters. When calling the DSNUTILS stored procedure, if you get an SQLCODE –440, confirm that the number of parms in your code, match your DB2 maint level.

Sample REXX code available: REXXUTIL

Page 34: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

34

34

REXX REXX –– Run Utility (EXEC)Run Utility (EXEC)

SQLSTMT = "" /* INITIALIZE SQLSTMT TEXT */SQLSTMT.0 = 10 /* SET NUMBER OF LINES IN SQL STMT */SQLSTMT.1 = "CALL SYSPROC.DSNUTILS (:UTILITY_ID,:RESTART, " SQLSTMT.2 = ":UTSTMT,:RETCODE,:UTILITY_NAME," SQLSTMT.3 = ":RECDSN,:RECDEVT,:RECSPACE,:DISCDSN,:DISCDEVT," SQLSTMT.4 = ":DISCSPACE,:PNCHDSN,:PNCHDEVT,:PNCHSPACE," SQLSTMT.5 = ":COPYDSN1,:COPYDEVT1,:COPYSPACE1,:COPYDSN2,:COPYDEVT2," SQLSTMT.6 = ":COPYSPACE2,:RCPYDSN1,:RCPYDEVT1,:RCPYSPACE1,:RCPYDSN2," SQLSTMT.7 = ":RCPYDEVT2,:RCPYSPACE2,:WORKDSN1,:WORKDEVT1,:WORKSPACE1," SQLSTMT.8 = ":WORKDSN2,:WORKDEVT2,:WORKSPACE2,:MAPDSN,:MAPDEVT,"SQLSTMT.9 = ":MAPSPACE,:ERRDSN,:ERRDEVT,:ERRSPACE,:FILTRDSN," SQLSTMT.10= ":FILTRDEVT,:FILTRSPACE) " Do X = 1 TO SQLSTMT.0;SQLSTMT = SQLSTMT||SQLSTMT.X ||" ";End /* BUILD STMT */

Address DSNREXX "EXECSQL "||SQLSTMT /* Run SQL STMT */

You need all parms. The formats must be pre-set. The order, definition of the parms define the signature of the stored procedure.

Leave off a parm… SQLCODE –440Misspell a parm… SQLCODE -440Spell the Stored Procedure name… SQLCODE –440Put in the wrong Schema name…SQLCODE -440Security problems… SQLCODE –440

Page 35: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

35

35

REXX REXX –– RUN Utility (Interpret)RUN Utility (Interpret)

"ASSOCIATE LOCATOR (:LOC1) WITH PROCEDURE SYSPROC.DSNUTILS" - - - - - - - - - - - - -"ALLOCATE C101 CURSOR FOR RESULT SET :LOC1" - - - - - - - - - - - - -Do Until EOF_FLAG = "Y"

"FETCH C101 INTO :SEQNO, :TEXT" (sqlcode test)

EndWhat’s the cursor name inside

the stored procedure?Who Cares!

REXX only supports cursor names of C1-C100 and C101-C200.

DSNUTILS Runs the utility then routes SYSPRINT output to a cursor called SYSPRINT. How do you get the data out???

Define a locator.

"ASSOCIATE LOCATOR" externalizes a locator variable (basically, a pointer to the result set data). One locator is needed for each result set; this is OK for us, DSNUTILS only has one result set.

"ALLOCATE xxx CURSOR" overlays a cursor onto the resultset. Any assembler programmers out there. It’s like laying a DSECT over data identified in a GET Locate.The fetch allows you to assign field names to each column of the result set. As in any CURSOR declaration/FETCH…they order of the columns must stay in sync.

Page 36: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

36

36

REXXUTILREXXUTIL

DSNU000I DSNUGUTC - OUTPUT START FOR UTILITY, UTILID = REXXUTIL DSNU050I DSNUGUTC - DIAGNOSE DISPLAY SYSUTIL DSNU866I -DBP5 DSNUDISS - DISPLAY SYSUTIL JOB INFORMATION USUUID =LOADAA USUJOBNM=LOADJOB1 USUUTNAM=LOAD USUPHASE=RELOAD USUAUID =COOLDBA USUSTATU=A USUTREQ = USURLOK =N USUDBOB ='0115'X USUPSID ='0165'X USUPSDD ='0000'X USUDSNU ='0000'X USUDBNAM=DSN8D71A USUSPNAM=MYTSNAME USUREL =710 USUFORCE= USUNDONE='00000000079B5'X USUPOS ='00000001'X USUCMPOK=Y USUIRQD =N USURDATE='00000000'X USUCKSUM='142074CB'X USUMEMBR= USUOCATR= USUOCATD=N USUOWLNK=N USUOLOG1=N USUOCAT8=N USUDSENV=N USULSIZE='00000000'X USULCUR ='00000000'X

DSNU868I -DBP5 DSNUDISS - DISPLAY SYSUTIL UTILITY DEPENDANT AREA 0000 00000000 00000000 00000000 00000000 00000000 00000000 000000A4 00000000

*....................

You can’t get that in –DISPLAY UTILITY(*)!

Generate Templates or object lists for COPY / MODIFY.

This example uses DIAGNOSE utility. Changing to LOAD utility would be easy.

Why would you want to load from a REXX??

Try copying 200 tables from production to test using IBM Cross Loader and confirming that data was actually moved. Rexx could interpret the output and validate counts. It could also be driven by a list of tables.

Where are these fields documented? SDSNSAMP(DSNWCBDS)

DSNU866I has little doc in messages and codes.

Sample REXX code available: REXXUTIL

Page 37: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

37

37

REXX REXX isis a Stored Procedurea Stored Procedure

Traditional Rexx

Establish REXX/DB2 InterfaceConnect to DB2REXX CodeSQL CodeDisconnect from DB2Remove REXX/DB2 Interface

Rexx Stored Procedure

Establish REXX/DB2 InterfaceConnect to DB2REXX CodeSQL CodeDisconnect from DB2Remove REXX/DB2 Interface

Cobol Program

Attachment to DB2 ssidCALL creator.REXXSPSQL Error checking

DB2 DB2 WLM SPAS

//SYSEXEC DD …

REXXSP

Hey, that looks like Interpretive code!

Rexx run as a stored procedure good for prototyping. Remember…it’s an interpretive language. It will run S-L-O-W-L-Y.

The REXX Stored procedure does NOT NEED to have SQL inside it. However, if you do choose to add SQL inside, connection is not needed. There’s an implicit connection since it’s running inside the WLMSPAS.

How do you reference your REXX as a Stored procedure?-Create a PDS or PDSE -Place your REXX code in a member-Add DDNAME “SYSEXEC” to the WLM SPAS of choice-Create a REXX stored procedure that points to the WLM SPAS library. Set the EXTERNAL NAME to the member name of the REXX PDS.

Anything weird about REXX SP?!?! Sure; you can only have one output parm (OUT or INOUT)…and it must be the LAST parm in the list. Want to pass back more data…pass it back in a cursor.

Sample REXX code available: REXXSP (not much actually)

Page 38: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

38

38

REXX Runs a stand alone utilityREXX Runs a stand alone utility

CALL DSN1COPY from Rexx interactively

Address TSO "ALLOC DDNAME(SYSUT1) DSNAME('"||SYSUT1_DSN||"') SHR REUSE" Address TSO "ALLOC DDNAME(SYSUT2) DSNAME('"||SYSUT2_DSN||"') SHR REUSE"

PARM_DATA = "PAGESIZE(4K),DSSIZE(64G),NUMPARTS(254)" Address LINKMVS "DSN1COPY PARM_DATA" Address TSO "FREE FI(SYSUT1, SYSUT2)"

This technique works with other stand alone utilities as well.

Want to DSN1COPY lots of partitions of data….hmmm, IBM has this little restriction. 254 Steps per MVS JOB. This means, even if you can generate the JCL for the DSN1COPY, you must break up your copy of into chunks of 254 parts each.

Try calling the utility interactively from Rexx. It works well and can do any number of copies from one step.

Sample REXX code available: DSN1XLAT

Page 39: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

39

39

REXX is an SQL ProcessorREXX is an SQL Processor

//REXX EXEC PGM=IRXJCL,PARM=‘SQLPROC DB2A’//SYSEXEC DD DISP=SHR,DSN=MY.REXX.EXEC Your Rexx source lib.//SYSPRINT DD SYSOUT=*//SQLSTMT DD *-- Drop my.index-- If it’s gone already…great; keep goingDROP INDEX MY.INDEX;--sqltest(-204,Dropped already…ok)INSERT INTO MY.TABLE VALUES(….);--sqltest(-803,Dup is ok)DELETE FROM MY.TABLE WHERE X=‘Y’;UPDATE MY.TABLE

SET X=‘Y’WHERE X=‘N’;

Note:This is an input script.

The REXX code is not shown!

DSNTEP2 says +100 on update

is GOOD…is it?

Bad Conditions: Bad Conditions: +100 Update +100 Update +100 Delete +100 Delete Good Conditions: Good Conditions: --803 Insert 803 Insert --204 Drop204 Drop

The above script is input to a REXX SQL processor that works like IBM’s DSNTIAD.

It runs an SQL Statement; however, SQLPROC has an advantage over most SQL Processors. SQLPROC will conditionally allow NON-ZERO SQLCODE values to be treated as Successful.For example: I have an index in a DB2 that I wish to drop. If it is already gone…that’s good. Don’t deal with RC=8 coming out of DSNTEP2 or frustrated operators trying to figure out why this jobstep “failed”. All SQLCODE values provided with an “SQLTEST” phrase will allow the process to continue. Messages indicate the status of each SQLSTMT and indicate why success was delivered. Any unplanned SQLCODE values result in a failure.Since you may want to process creating SQL Stored procedures or triggers, I also allow you to change the SQL Terminator value; I used the same technique as IBM for introducing a new terminator.

-- Create my.trigger--#SQL TERMINATOR !

CREATE TRIGGER(syntax not shown);END!-SQLTEST(0,SUCCESSFUL)

No SELECTs for this baby…it’s not a report writer (not yet…this feature may be coming).Sample REXX code available: SQLPROC

Page 40: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

40

40

““The ENFORCERThe ENFORCER”” -- APPLCHKRAPPLCHKR

Define RulesDefine Rules•• Check the objects against the rules regularlyCheck the objects against the rules regularly•• Report warningsReport warnings•• Correct errorsCorrect errors•• Simplify your daily routineSimplify your daily routine

For every table in a given databaseFor every table in a given database……Verify the CREATOR is Verify the CREATOR is ‘‘xx’’Ensure each table has the 5 required aliases: A, B, C, D, EEnsure each table has the 5 required aliases: A, B, C, D, ESome tables may have optional alias of Q or PSome tables may have optional alias of Q or PAll tables need select granted to All tables need select granted to authid authid U1, update to U2U1, update to U2All views on these tables need different security profileAll views on these tables need different security profile

Sample REXX code available: APPLCHKR

Third party tools perform an outstanding job of migrating objects. However, security profiles and/or alias definitions rarely map 1-to-1, thus, they can get lost. Our third party database change tool could not handle the complex migration rules that our application requires….so, I wrote my own: APPLCHKR. This rexx follows a pre-determined set of rules (some mandatory, some optional). It reports on many anomalies and has the ability to repair object definitions as well.

This Rexx is actually used by several different applications at TU. It has been parameterized in such a way as to make it a usable tool for most databases. Customization of the Rexx would be simple to provide for exceptions or add other rules. It’s a good starting point for use in your shop.

Page 41: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

41

41

Update ParallelismUpdate Parallelism

SQLGENSQLGEN

JCL/SQLJCL/SQL

DB2 CatalogDB2 CatalogPartitionPartitionLimit keysLimit keys

One set of JCL/SQL PER PartitionOne set of JCL/SQL PER Partition

What?!?SQLGEN?

How to invoke “update parallelism”• Change tablespace to LOCKPART YES• Write SQL Stmt that does the update• USE SQLGEN to build one MVS JOB per Part.

I wrote a REXX processor to read in an SQL Stmt (or JCL w/SQL Stmt) and modify a query to allow imbedding of partition limit ranges.

The partition ranges for the system are retrieved from the DB2 catalog and applied to the SQL.

Multiple queries (jobs) are created for parallel execution.

Don’t forget to change the LOCKPART rule to “YES” on the tablespace.

Sample REXX code available: SQLGEN

Page 42: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

42

42

SQLGENSQLGENCool!

SQLGENSQLGENInIn

Single Execution

One file in….One file out

Input JCL/SQL

//U0664### .....JOBCARD.... //* //DSNTEP2 EXEC DSNTEP2,DB2ID='ssid' //SYSIN DD * LOCK TABLE primary.table PART @@@

IN EXCLUSIVE MODE; UPDATE primary.table PT

SET whatever_flg = 'N' WHERE whatever_flg = 'Y'

AND NOT EXISTS (SELECT 1

FROM dependant.table DT WHERE PT.PRIM_KEY = DT.PRIM_KEY

:PRIM_KEY_PHRASE DT ) :PRIM_KEY_PHRASE PT ; COMMIT;

//U0664### .....JOBCARD.... //* //DSNTEP2 EXEC DSNTEP2,DB2ID='ssid' //SYSIN DD * LOCK TABLE primary.table PART @@@

IN EXCLUSIVE MODE; UPDATE primary.table PT

SET whatever_flg = 'N' WHERE whatever_flg = 'Y' AND NOT EXISTS

(SELECT 1 FROM dependant.table DT

WHERE PT.PRIM_KEY = DT.PRIM_KEY AND DT.PRIM_EY BETWEEN 1 AND 50

) AND PT.PRIM_KEY BETWEEN 1 AND 50

; COMMIT;

//U0664### .....JOBCARD.... //* //DSNTEP2 EXEC DSNTEP2,DB2ID='ssid' //SYSIN DD * LOCK TABLE primary.table PART @@@

IN EXCLUSIVE MODE; UPDATE primary.table PT

SET whatever_flg = 'N' WHERE whatever_flg = 'Y' AND NOT EXISTS

(SELECT 1 FROM dependant.table DT

WHERE PT.PRIM_KEY = DT.PRIM_KEY AND DT.PRIM_EY BETWEEN 51 AND 100

) AND PT.PRIM_KEY BETWEEN 51 AND 100

; COMMIT;

//U0664### .....JOBCARD.... //* //DSNTEP2 EXEC DSNTEP2,DB2ID='ssid' //SYSIN DD * LOCK TABLE primary.table PART @@@

IN EXCLUSIVE MODE; UPDATE primary.table PT

SET whatever_flg = 'N' WHERE whatever_flg = 'Y' AND NOT EXISTS

(SELECT 1 FROM dependant.table DT

WHERE PT.PRIM_KEY = DT.PRIM_KEY AND DT.PRIM_EY BETWEEN 101 AND 150

) AND PT.PRIM_KEY BETWEEN 101 AND 150

; COMMIT;

…(251 more)

Part 1Part 1

Part 2Part 2

Part 3Part 3

Part nPart n

Input JCL/SQL

//U0664### .....JOBCARD.... //* //DSNTEP2 EXEC DSNTEP2,DB2ID='ssid' //SYSIN DD * LOCK TABLE primary.table PART @@@

IN EXCLUSIVE MODE; UPDATE primary.table PT

SET whatever_flg = 'N' WHERE whatever_flg = 'Y'

AND NOT EXISTS (SELECT 1

FROM dependant.table DT WHERE PT.PRIM_KEY = DT.PRIM_KEY

:PRIM_KEY_PHRASE DT ) :PRIM_KEY_PHRASE PT ; COMMIT;

Output JCL/SQL

//U0664### .....JOBCARD.... //* //DSNTEP2 EXEC DSNTEP2,DB2ID='ssid' //SYSIN DD * LOCK TABLE primary.table PART @@@

IN EXCLUSIVE MODE; UPDATE primary.table PT

SET whatever_flg = 'N' WHERE whatever_flg = 'Y'

AND NOT EXISTS (SELECT 1

FROM dependant.table DT WHERE PT.PRIM_KEY = DT.PRIM_KEY

AND DT.PRIM_KEY BETWEEN 1 AND 50 )

AND PT.PRIM_KEY BETWEEN 1 AND 50; COMMIT;

//U0664### .....JOBCARD.... //* //DSNTEP2 EXEC DSNTEP2,DB2ID='ssid' //SYSIN DD * LOCK TABLE primary.table PART @@@

IN EXCLUSIVE MODE; UPDATE primary.table PT

SET whatever_flg = 'N' WHERE whatever_flg = 'Y' AND NOT EXISTS

(SELECT 1 FROM dependant.table DT

WHERE PT.PRIM_KEY = DT.PRIM_KEY AND DT.PRIM_EY BETWEEN 1 AND 50

) AND PT.PRIM_KEY BETWEEN 1 AND 50

; COMMIT;

Input Output

How to invoke “update parallelism”

Here’s the inputs and outputs.

The LEFT JCL was read into the REXX. Note the “:PRIM_KEY_PHRASE”. This tells the processor where to insert the partition range and how. The processor defaults to generating an “AND” statement before the limit key range added.

The RIGHT JCL was generated by the REXX. Note: this is just one of 254 jobs created by this one execution. Each JOB has the partition range for a single DB2 Part.

The JOBcard and LOCK TABLE phrases have an unusual symbol in each: ### for the JOBcard and @@@ in the LOCK TABLE statement.

The number of values that could be incremented in the REXX was too unpredictable. I use an edit macro after the fact to increment these values.(this macro is available for the asking).

Page 43: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

43

43

Drop a tablespace w/ TSDROPDrop a tablespace w/ TSDROP

REXX Name: TSDROPControl card Input: DBNAME.TSNAME Masking allowed

JCL Output: DB2 CMD -STOP DATABASE( ) SPACE(Tablespace)DB2 CMD -STOP DATABASE( ) SPACE(Indexspace)IDCAMS DELETE Tablespace Part xIDCAMS DELETE Indexspace Part xSQL DROP TABLESPACE

Drop a tablespace with 254 partsLock DB2 DirectoryLock DB2 CatalogLock DataBase Descriptor (DBD)

Can take 10 min each tablespace. DirectoryDirectory

DB2DB2CatalogCatalog

DBDDBD

LOCKDOWNLOCKDOWN10 Minutes10 Minutes

LOCKDOWNLOCKDOWN1 Second1 Second

The idea is to drop a tablespace with minimal locking to DB2’s shared services: Directory, Catalog, DBD.

Deletion of datasets takes a long time. This Rexx automating the process of deleting the datasets BEFORE you do the drop. It reduces catalog contention to a few seconds. Using this processor, I dropped 500 tablespaces (254 parts each) in 30 seconds. NOTE: The deletion of the datasets prior to the drop took a long time, but the goal was to make the drop a non-issue. I leave folding space/time up to you.

Sample REXX code available: TSDROP

Page 44: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

44

44

User MessagesUser Messages

RFMS0015 – rexxname Failure to connect to database. RC: return codeSSID: ssid

Explanation:Attempt to connect to DB2 Subsystem failed. Return code and DB2 SSID displayed.

Severity:Severe

Application Action:Connection to DB2 could not occur. Message is displayed; processing halts.

User Response:Check to see if the subsystem name is spelled correctly.Check to see if the DB2 subsystem is activeCheck to see if the Rexx is executing on the proper LPAR where the DB2 is supposed to be running.

You will find that universal messaging will help your Rexx code get used. A user of your Rexx code is on an island, away from you. You need to give them all the information needed regarding the execution of the Rexx. Sure, they can always look in the code, but, I think a well scripted message works wonders. Once I came up with a format of a message that I liked, I made sure I used the same format in ALL my Rexx code. It also helps having some character string for the user to find on in case of error. “RFM”(Rich Fazio Message) in this case is a pretty unique character string and should be easy for the user to search output for. It also provides a reminder of what they should do with the messages (Read the Fine Message).

Lastly, if you can provide a simple “Messages and Codes” style reference, it may help the user get past simple errors without much stress. That’s what Rexx is all about: Simplifying processes by scripting them or automating them. My Messages and Codes reference is included with my sample code.

Page 45: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

45

45

REXX Generates DSN1COPYREXX Generates DSN1COPY

Queue "//STEP"||STEP_NO||" EXEC PGM=….." Queue "//SYSUT1 DD DISP=SHR,DSN="||DATA_A…. Queue "//SYSUT2 ….." Queue "//OBIDXLAT DD *" Queue SOURCE_DBID "," TARGET_DBID

Get Source DBID, PSID, OBIDs

Get Target DBID, PSID, OBIDs

Determine number of Partitions, DSSIZE

Processing Tablespace or Indexspace or BOTH?

Process input list of objects to copy

Process control parameters

Build Dataset Names

How would you create a REXX to generate a JCL/CNTL necessary to DSN1COPY an object from one DB2 to another? There’s a lot to consider. Lucky for you, there’s an SQL interface for REXX that makes this type of coding possible.

Page 46: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

46

46

ReferenceReference

DB2????-???? DB2 for OS/390 REXX Language Support Version 5 (1999)*

* SQLDA IS WRONG!!!SC26-9944 DB2 UDB for OS/390 and z/OS SQL Reference V7SC18-7426 DB2 Universal Database for z/OS V8 SQL Reference

SC26-9933 DB2 UDB for OS/390 and z/OS Application Programming and SQL Guide V7SC18-7415 DB2 Universal Database for z/OS V8 Application Programming and SQL Guide

RexxSC28-1975 OS/390 TSO/E REXX Reference (OS390 V2.10)SC28-1974 TSO/E REXX User's Guide (OS390 V2.10)

RedbookSG24-6108 DB2 UDB Server for OS/390 Version 6 Technical UpdateSG24-6418 DB2 for z/OS and OS/390 : Squeezing the Most Out of Dynamic SQL

RTFM!

Page 47: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

47

47

END EXEC (END EXEC (……the end)the end)

Questions?

Thank You!

Page 48: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

48

48

Session Rexx and DB2 –The Stuff NOT in the Manuals (Re-Tooled)

Session: B11

Rich FazioTransUnion

[email protected]

Page 49: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

49

49

Bonus MaterialsThanks so much for attending my session!Thanks so much for attending my session!I first delivered this presentation in 2002. I have since givenI first delivered this presentation in 2002. I have since given it many times across the countryit many times across the countryand once in Europe. I keep coming up with more material for theand once in Europe. I keep coming up with more material for the presentation, but find it hard to presentation, but find it hard to discard prior subdiscard prior sub--topics. Please review this additional material as well. It hastopics. Please review this additional material as well. It has been kept up to been kept up to date and is still very relevant. If you have questions on this date and is still very relevant. If you have questions on this material or anything else in my material or anything else in my presentation, please contact me directly: presentation, please contact me directly: [email protected]@TransUnion.com

ThanksThanksfazfaz

Page 50: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

50

50

The Life and Times of REXXThe Life and Times of REXX

Specs Written

SAA Standard

Available for OS/2

Rexx became the SAA standard in 1987…

Now, can I have my bone?!

Published to VM

1970 … 1979 1983 1987 1989 1990’s * 2004EXEC

REXX

*IBM REXX/DB2 Interface released

I love this story!

Integrated into OS/2AIX, Netware, CICS

Genesis- EXEC – VM Scripting Language (Like DOS .BAT files)1970’s EXEC2 – Structure added, but still clumsyOver time the call came to create a structured, command based languageSpecs created March 1979 – Name: REX (REX "sounded nice")

Based upon ALGOL, Pascal, PL/1 – Added String handling Published REXX into VM 1983 for IBM, by Mike Cowlishaw SAA Standard 1987; Launched MVS/TSO, AS/4001989 Extended Edition of OS/2 1.21990 Rexx part of OS/2 Base product w/multi-media control1990s introduced AIX/6000, Netware, PC Dos, CICS TM1999 IBM REXX/DB2 interface released with DB2 V5Now available for most significant operating systems

Multiple Vendors (Not only IBM)Licensed products, Shareware, FreewareOS/390, AS/400, Unix, Linux, Windows, MS-DOS, et al.Object Oriented REXX introduced late 1990s. Net Rexx (REXX for JAVA) was introduced 1996.

Mike Cowlishaw, A brief History of 'Classic' Rexx, http://www2.hursley.ibm.com/rexx/rexxhist.htm

Page 51: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

51

51

Run donRun don’’t Walkt Walk I want to RUNEVERYWHERE!

IKJEFT1B relays RC!

Functions available: TSO Online IKJEFT1B IRXJCL-------------------------------------------------------------------------------------------REXX Cmds (If, Do, Call, etc) Y Y YDSNREXX Interface Y Y YMVS Cmds (EXECIO, SUBCOM) Y Y YDSNREXX runs SQL: DDL, DML, DCL Y Y YReturn code processing Y Y YTSO Cmds (ALLOC, FREE, etc) Y Y NDB2 Cmds thru "DSN Processor" Y Y NCall DB2 Programs using TSO Attach Y Y NTSO Attach runs SQL: DDL, DML, DCL Y Y NPass in parms via JCL "PARM=" N Y YMany more environments (ISPF Edit Macro, Windows, AS/400, Unix, Java…)

Sample JCL for IRXJCL://REXX EXEC PGM=IRXJCL,PARM=‘REXXNAME PARM1 PARM2 PARM3’//SYSEXEC DD DISP=SHR,DSN=MY.REXX.EXEC Your Rexx source lib.//INPUTDD DD DISP=SHR,DSN=MY.INPUT.DATA Data file for Rexx//OUTPUT DD ……

Sample JCL for IKJEFT1B (TSO BATCH – Similar to IKJEFT01)://TSO EXEC PGM=IKJEFT1B,PARM=‘REXXNAME PARM1 PARM2 PARM3’//SYSEXEC DD DISP=SHR,DSN=MY.REXX.EXEC Your Rexx source lib.//INPUTDD DD DISP=SHR,DSN=MY.INPUT.DATA Data file for Rexx//OUTPUT DD ……

Sample JCL for IKJEFT1B (TSO BATCH – Similar to IKJEFT01)://TSO EXEC PGM=IKJEFT1B//SYSEXEC DD DISP=SHR,DSN=MY.REXX.EXEC Your Rexx source lib.//INPUTDD DD DISP=SHR,DSN=MY.INPUT.DATA Data file for Rexx//OUTPUT DD ……//SYSIN DD DUMMY //SYSTSIN DD *EXEC REXXNAME ‘PARM1 PARM2 PARM3’

Page 52: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

52

52

SSIDLIBSSSIDLIBS

What makes each DB2 subsystem unique? The software levels and parms. Why not have a single place to assign your libraries? SSIDLIBS takes in a full SSID name and presents three libraries to the application: SDSNLOAD, SDSNEXIT, RUNLIB.LOAD, Third Party Libs, etc.

INPUTS

Full SSID

PROCESS

Lookup SSIDAssign libraries to variables

OUTPUTS

SDSNLOADSDSNEXITRUNLIB.LOADOthers???

How many hardcoded dataset names are in your REXX applications. If you call a routine, you have them in one spot. These libraries do not change often, but when they do, it can be difficult.

Also, think of all the code in each REXX.IF SSID = "X", then SDSNLOAD= "y". ELSE IF SSID = "Y", then SDSNLOAD ="q"

Symbolic substitution can make this easier if you have a naming standard that includes SSID, however, if you purchase companies, consolidate datacenters, sometimes you do not have a choice.

Sample REXX code available: SSIDLIBS

Page 53: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

53

53

Formatting StringsFormatting Strings

RPT_LINE = RIGHT(I,3,' ')||RIGHT(RPT_USERID.I,9,' '),

||RIGHT(RPT_UTILID.I,18,' ')||RIGHT(RPT_UTIL_TYPE.I,9,' '),

||RIGHT(RPT_PHASE.I,9,' ')||RIGHT(RPT_PH_CNT.I,17,' '),

||RIGHT(RPT_STMT_NBR.I,4,' ')||RIGHT(RPT_LST_OBJ_STRT.I,4,' '),

||RIGHT(RPT_NBR_OBJ.I,4,' ')||RIGHT(RPT_STATUS.I,9,' '),

||RIGHT(RPT_MEMBER.I,9,' ') /* Build report line */

SAY RPT_LINE /* Write report line to output */

Output:

1 ROGUE ROGUE.ROUGE1 LOAD RELOAD 61,197,984 1 1 1 STOPPED

Formatting the output to give predictable results requires assigning padding to every field. Remember numbers and alphanumeric strings justify differently. Take a default if you like, or be explicit to make it clear to the next person that reviews the code.

Page 54: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

54

54

Building JCL/CNTL StringsBuilding JCL/CNTL Strings

DBNAME = STRIP(DBNAME,B," ") /* Lose blanks– Not good in JCL */TSNAME = STRIP(TSNAME,B," ") /* Lose blanks– Not good in JCL */Queue "//SYSUT2 DD DISP=SHR,",

||"DSN="||SSID||".DSNDBC."||DBNAME||"."||TSNAME||"."||IPREFIX||"0001.A",

||Right(PART_NUM,3,"0")||"," /* Build first line of JCL */

Queue "// AMP=('BUFND=30')" /* Build second line of JCL */

Output:

//SYSUT2 DD DISP=SHR,DSN=DB2A.DSNDBC.MYDBASE.MYTS.J0001.A126, // AMP=('BUFND=30')

When building strings --especially in JCL, note that some strings may have embedded spaces. Be sure to strip out any blanks before using them. This can be done in REXX or DB2, the syntax is the same.

Page 55: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

55

55

REXX is a SCRIPT ProcessorREXX is a SCRIPT Processor

INSERT INTO !!!!@@@@.MY_TABLE(COL_A,COL_B,COL_C)VALUES(1,2,3);SELECT *FROM !!!!@@@@.MY_TABLE MT

, !!!!@@@@.MY_OTHER_TABLE OTWHERE MT.COL_A = OT.COL_B;

INSERT INTO DEV.MY_TABLE(COL_A,COL_B,COL_C)VALUES (1,2,3);SELECT

-------------------------------- RUN SCRIPTIT -----------------------------------PARM(DBT1,DEV) PARM(DBT1,QA) PARM(DBP1,PROD)

INSERT INTO QA.MY_TABLE(COL_A,COL_B,COL_C)VALUES (1,2,3);SELECT

INSERT INTO PROD.MY_TABLE(COL_A,COL_B,COL_C)VALUES (1,2,3);SELECT

•Write your script once

•Execute the same scriptagainst each environment with confidence.

How many times have programmers told you… I need to make this one data change in production “here’s the SQL…just run it in SPUFI”. When you go to run the provided SQL, it does not work due tosyntax, or perhaps the data provided does not fit in the domain of the data type. Surely, this would have been discovered when this data was added in TEST….right?

I support a system with 200 control tables. Many of these tables do not have application managed processes to update them…thus, our hero (the DBA) was stuck making all the changes for them.So, I wrote SCRIPTIT. SCRIPTIT reads in a control file containing SQL. It spits out the modified SQL that is passed on for execution to DSNTEP2. Why not execute the SQL right inside the REXX? Wasn’t that the purpose of this presentation? The REXX environment can handle the SELECT, INSERT, UPDATE,DELETE, etc….but, SELECT has a special issue with it. Creating dynamic reports is more complex than I wanted to get in this REXX. Rather than restrict selects (I actually encourage them to query before/after an update), I let DSNTEP2 format my reports.

A separate set of JCL is set up for each environment. All the programmer does is change the input to the process. They code their SQL once….there’s no chance of fat finger over types or forgetting to run a piece of a script. The whole thing is delivered 100% accurately every time.Sample REXX code available: SCRIPTIT

Page 56: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

56

56

Buffers, Stacks, Stems Buffers, Stacks, Stems –– oh myoh my

./ ADD LIST=ALL,NAME=MEM1 Control card data Control card Data

./ ADD LIST=ALL,NAME=MEM2 Control card data Control card Data

//STEP1 EXEC PGM=A //DD01 DD ….. //SYSIN DD DSN=cntl.lib(MEM1)

//STEP2 EXEC PGM=A //DD01 DD ….. //SYSIN DD DSN=cntl.lib(MEM2)

Queue "//STEP1 …"Queue "//DD01 DD"Queue "//SYSIN DD"

CTL.1 ="./ …MEM1"CTL.2 =" cntlcard "CTL.3 =" cntlcard "

Queue "//STEP2 …"Queue "//DD01 DD"Queue "//SYSIN DD"

CTL.4 ="./ …MEM2"CTL.5 =" cntlcard "CTL.6 =" cntlcard "

Queue ""Execio * diskw File#1Execio * diskw File#2

Section #1

Section #3

Section #2

Section #4 STEM CTL.

Current Stack

#1

#3

#2

#4

Toto’s Cute!

You can make buffers that are added to an existing stack that is used by a calling routine. This is a fairly complex topic; I am referencing it to simply let you know it’s there. I don’t recommend using MAKEBUF/DROPBUF until you have a solid understanding of using data stacks.To get the contents of the current stack to your file: "EXECIO * DISKW FILE1 (FINIS"To get the contents of a STEM Variable to your file: "EXECIO * DISKW FILE2 (STEM varnme. FINIS"Use "QUEUE"s to build JCL (current data stack)Use multiple STEM Variables to create CNTLCARD stacks (1 stack per cntlcard stream).

Use IEBUPTDE to convert a stream of data into individual control cards:// jobcard info

//UPDATE EXEC PGM=IEBUPDTE,PARM=NEW

//SYSPRINT DD SYSOUT=*

//SYSUT2 DD DISP=SHR,DSN=Destination PDS

//SYSIN DD DISP=SHR,DSN=dataset generated by your REXX

BTW- The Wizard of Oz referred to Toto as "He”, however, the "actor” was actually a she. Her name: Terry, a Cairn Terrier.

Page 57: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

57

57

Instant ReplayInstant Replay

./ ADD LIST=ALL,NAME=MEM1 Control card data Control card Data

./ ADD LIST=ALL,NAME=MEM2 Control card data Control card Data

//STEP1 EXEC PGM=A //DD01 DD ….. //SYSIN DD DSN=cntl.lib(MEM1)

//STEP2 EXEC PGM=A //DD01 DD ….. //SYSIN DD DSN=cntl.lib(MEM2)

STEM CTL.

Current Stack

CODE:/* Externalize contents of

data stack to FILE1 */

ADDRESS MVS"EXECIO * DISKW FILE1 (Finis"

/* Externalize contents of stem var to FILE2 */

ADDRESS MVS"EXECIO * DISKW FILE2 (STEM CTL. Finis"

Period

I wanted to focus on the exact command syntax here.

Page 58: IDUG NA 2005 Rich Fazio: Rexx and DB2 UDB- The Stuff NOT

58

58

Single Stack w/Multiple FilesSingle Stack w/Multiple Files

./ ADD LIST=ALL,NAME=MEM1 Control card data Control card Data

./ ADD LIST=ALL,NAME=MEM1 Control card data Control card Data

Section #1//STEP1 EXEC PGM=A //DD01 DD ….. //SYSIN DD DSN=cntl.lib(MEM1)

//STEP2 EXEC PGM=A //DD01 DD ….. //SYSIN DD DSN=cntl.lib(MEM2)

Section #3

Section #2

Section #4

File#2

File#1Queue "//STEP1 …"Queue ""Execio * diskw File#1

Queue "./ …MEM1"Queue ""Execio * diskw File#2

Queue "//STEP2 …"Queue ""Execio * diskw File#1

Queue "./ …MEM1"Queue ""Execio * diskw File#2

Section #1

Section #3

Section #2

Section #4

When using Execio in this fashion: "EXECIO * DISKW FILE1 (FINIS", the entire stack gets pulled off. If you allocate your files with a disposition of MOD you can build mini-stacks and write them out in succession.

It’s a little more overhead, but, if you want to only work with stacks…this technique works well.

It’s also a very appropriate technique if the operating environment has a memory constraint.