using rexx to build your own db2 tools using rexx.pdf · 2017-06-02 · what's bad: rexx...

44
Using REXX to Build Your Own DB2 Tools David Simpson Themis Training Session Code: E16 2017-05-04-10.30.00.00.000000 | Platform: DB2 for z/OS

Upload: others

Post on 25-Mar-2020

43 views

Category:

Documents


4 download

TRANSCRIPT

Using REXX to Build Your Own DB2 Tools

David SimpsonThemis TrainingSession Code: E162017-05-04-10.30.00.00.000000 | Platform: DB2 for z/OS

Agenda

• Who (what) is REXX?• Language elements• Using REXX and DB2• Error Handling• Sample applications

What is REXX?

REXX Language

• Scripting Language supplied with z/OS• Can be run using TSO or batch• Can be used to create custom scripts• DB2 interface since DB2 Version 5

DB2 REXX Extension•What’s Bad about it?

No Bind Required!

•What’s Good about it?

–Can’t do a “singleton” 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 for z/OS SQL Syntax–It’s interpretive: quickly modifiable, easy to rollout

Presenter
Presentation Notes
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 clause C51 to C100: By default are defined with the WITH HOLD clause C101 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.

REXX Coding Basics

REXX Basics - Coding

/* 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;

Start

End

Too Hot?

Too Cold?

Taste Porridge in next bowl

N

Eat PorridgeN

Y

Y

Presenter
Presentation Notes
TSO REXXSQL SYSTABLES T5 <<<< Command Result DBNAME TSNAME IXCREATOR IXNAME ---- For table: SYSTABLES DSNDB06 SYSDBASE DBUM560 DBNAME_TSNAME DSNDB06 SYSDBASE SYSIBM DSNDTX01 DSNDB06 SYSDBASE SYSIBM DSNDTX02 ***

REXX Basics - Comments

/* ----------------- 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 */

Presenter
Presentation Notes
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 is a valid comment Too */

REXX Basics – The 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

Presenter
Presentation Notes
Rexx has two methods of getting data on a stack. QUEUE adds data from the BOTTOM of the stack PUSH 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.

REXX Basics – More 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

1DB2

# Entries: 3D

DB2

DB2""

1

2DB2

# Entries: 3DB

DB2

# Entries: 3DB2

3

# Entries: 3DB2_

Awaiting Terminal

Input

Remove

Presenter
Presentation Notes
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).

REXX and DB2

REXX & DB2

• DB2 doesn’t have a native "Scripting language“ for use on ISPF• Third party tools fill the gap• Homegrown

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

FETCH me some data!

Presenter
Presentation Notes
Oracle and SQL Server have scripting facilities built in. With DB2, you write a Cobol program…not too "quick and easy". SQL PL up to this point must be used in stored procedures, triggers, and user-defined functions. 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. Now we can get to it natively.

REXX 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

Presenter
Presentation Notes
TSO REXXSQL SYSTABLES T5 <<<< Command Result DBNAME TSNAME IXCREATOR IXNAME ---- For table: SYSTABLES DSNDB06 SYSDBASE DBUM560 DBNAME_TSNAME DSNDB06 SYSDBASE SYSIBM DSNDTX01 DSNDB06 SYSDBASE SYSIBM DSNDTX02 *** Sample REXX code available: REXXSQL

Parlez-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.

Presenter
Presentation Notes
"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.

Connect to DB2Establish thread between

REXX and DB2The 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 */

Presenter
Presentation Notes
"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.

Run SQL

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

More on this stuff later

Presenter
Presentation Notes
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 statement ERROR_AID is meant to display key info or other diagnostics. Possibly the host variables used by an SQL Statement.

Building an SQL Statement

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

Glue literal intothe statement

Presenter
Presentation Notes
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.

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 ||" "

Building an SQL Statement

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

Cursors

Use parameter marker in place of literal

Cursors

Fill in values for parameter markers

Cursors

Cursors

• Cursor names are pre-defined• C1 thru C100 defined WITH RETURN• C51 thru C100 also have WITH HOLD

• Statement names are also pre-defined• S1 thru S100

That’s NOT like COBOL!

Display Table Definition

/*------------- 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 */

Calling a DB2 Command from REXX

Command goes on the stack with

blank line to complete

Presenter
Presentation Notes
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.

Error Handling

Building an SQL Statement

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

Presenter
Presentation Notes
This statement will produce an error since a singleton select is not supported. The error you get may not be what you expect.

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

Shouldn’t this be an SQLCODE –206?!

Error Handling

Error Handling

Display the offendingStatement

Supplied by the caller

Dump the SQLCA

DSNTIAR?

Presenter
Presentation Notes
Care to call DSNTIAR from a Rexx? IBM does not “officially” support calling it from Rexx. But, we figured out how to make it work. Email for details. Handle with care.

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.

User Messages

Presenter
Presentation Notes
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.

Examples…

Running REXX in Batch

First word of the PARM is the REXX being run.Next come any arguments expected by the script.SYSEXEC is the location of the script.

Running REXX in Batch

This particular script does the following.• Deletes any datasets owned by DBTHMA1• Queries the DB2 catalog and drops any objects owned by DBTHMA1 Tables Views Stored Procedures

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

-DISPLAY UTILITY(*)

Presenter
Presentation Notes
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.

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

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

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

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

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?!?!

Presenter
Presentation Notes
This is our version of –Display Utility Here is an easier to read, focused display. Needs to be reviewed with each release of DB2 for updated messages in –DIS UTIL

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

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

//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: +100 Update +100 Delete Good Conditions: -803 Insert -204 Drop

A REXX SQL Processor

Presenter
Presentation Notes
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

Define Rules• Check the objects against the rules regularly• Report warnings• Correct errors• Simplify your daily routine

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

The “Enforcer” - APPLCHKR

Presenter
Presentation Notes
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, we wrote our 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 a client. 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.

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. Directory

DB2Catalog

DBD

LOCKDOWN10 MinutesLOCKDOWN1 Second

Dropping a tablespace

Presenter
Presentation Notes
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.

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

* SQLDA IS WRONG!!!SC27-8859 DB2 12 for z/OS SQL ReferenceSC27-8845 DB2 12 for z/OS Application Programming and SQL Guide

RexxSA-22-7790 z/OS TSO/E REXX Reference

RedbookSG24-6465 DB2 UDB for z/OS Version 8 Performance Topics SG24-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

IDUG PresentationREXX and DB2 – The Stuff NOT in the Manuals – IDUG North America 2005

RTFM!

Reference

David SimpsonThemis [email protected]

E16Using REXX to Build Your Own DB2 Tools

Please fill out your session evaluation before leaving!