56
1 16 October 2008 • 09:00 – 10:00 Platform: DB2 for z/OS David Churn DST Systems, Inc. Introducing your COBOL programs to DB2 version 8 It is supposed to be easy to change your existing programs from single row to multiple row fetch. There are some difficulties involved in this process especially when it is a COBOL program. This presentation will take you through the issues that have been encountered and how DST solved them. Coding examples and some suggested standards will be included.

Churn0809

Embed Size (px)

Citation preview

Page 1: Churn0809

1

16 October 2008 • 09:00 – 10:00

Platform: DB2 for z/OS

David ChurnDST Systems, Inc.

Introducing your COBOL

programs to DB2 version 8

It is supposed to be easy to change your existing programs from single row to

multiple row fetch. There are some difficulties involved in this process

especially when it is a COBOL program. This presentation will take you

through the issues that have been encountered and how DST solved them.

Coding examples and some suggested standards will be included.

Page 2: Churn0809

2

2

The Five Points

• SQL in COBOL

• Array handling

• Using GET DIAGNOSTICS

• Unit of work

• Mixing in other SQL features

Greetings!

There are other good feature in v8 that will speed performance but do not impact

how COBOL programs are written. Features like scalar full-SELECT and common

table expressions have an impact on the program’s SQL only. They will not be

covered in this presentation.

Page 3: Churn0809

3

3

Disclaimer• The information is presented “as is” without any

warranty either expressed or implied. The use of this information is the client’s responsibility.

• All information in this presentation relates to DB2 Version 8 and COBOL version v3r4.

• COBOL programs were developed on z/OS mainframe using TSO and ISPF.

Page 4: Churn0809

4

4

.

http://www.dstsystems.com

• Leading provider of computer software solutions and services, NYSE listed – “DST”• Revenue $2.24 billion• 110 million+ shareowner accounts

• 24,000 MIPS• 150 TB DASD• 145,000 workstations• 462,000 DB2 objects• Non-mainframe: 600 servers (DB2, Oracle, Sybase) with 2.3 million objects

About DST Systems

If you have ever invested in a mutual fund, have had a prescription filled, or are a

cable or satellite television subscriber, you may have already had dealings with our

company.

DST Systems, Inc. is a publicly traded company (NYSE: DST) with headquarters in

Kansas City, MO. Founded in 1969, it employs about 12,000 associates

domestically and internationally.

The three operating segments - Financial Services, Output Solutions and Customer

Management - are further enhanced by DST’s advanced technology and e-

commerce solutions.

Page 5: Churn0809

5

5

DST associate mix

• Applications Development

• Database Administrators

• Database Consultants

Developers

Project Leaders

Analysts

Programmers

hundreds

DBA

tens

DB Consulting

ones

Our development staff is heavily slanted toward application development. Most

enhancements are coded by the applications area to provide new functionality. Application

Developers code their own SQL and are responsible for checking the EXPLAIN

information.

DBAs assist with the enhancements and handle implementing changes to each production

system’s database. They review new SQL and assist with designing indexes as needed.

Database Consulting assists with SQL development on a project basis and assisting our

trainers. One of our goals as Database Consultants is to pass along best coding practices to

the developers. Database Consultants evaluate new features of DB2 and write the standards

for DB2 and COBOL.

Page 6: Churn0809

6

SQL in COBOL

This section gives an overview of the new features added to SQL in version 8 that

impact how a COBOL program is written. The largest coding change comes from

the new multiple row functions for SELECT and INSERT.

Page 7: Churn0809

7

DB2 v8 gives COBOL programs the ability to define larger fields and data inside the program. COBOL has always given us ways to

full size CURSOR name takes 3 lines using the ISPF editor on TSO. A full size character constant takes 546 lines or 9 printed p

Page 8: Churn0809

8

8

SQL continuation

• Word/literal occupies column 72• Next line has

• Hyphen in column 7• Spaces in 8 through 11• Followed by remainder of word/literal• Character literal requires a quote

7 12 72

| |

EXEC SQL INSERT INTO tbl(column1,colum

- n2, column3, column4)

VALUES ( 0123

- 45, ‘chara

- ‘cter literal’)

END-EXEC.

Even with only 60 characters on a line, it was a rare need to know these rules. With most DB2 object having more characters, it

Page 9: Churn0809

9

9

Binary Numbers – TRUNC(STD)

• COMP, COMP-4 and USAGE BINARY are synonyms for truncating binary.

• Truncation happens when field• Receives data from a MOVE • Arithmetic result

• COMP-5 indicates no truncation.

S9(4) COMP = -9999 to +9999 2 bytesS9(9) COMP = -999,999,999 to +999,999,999 4 bytes

S9(4) COMP-5 = -32768 to +32767 2 bytesS9(9) COMP-5 = -2147483648 to +2147483647 4 bytes

The COBOL standard states that numeric fields must be truncated to their picture

clause. This is sometimes referred to as the ‘odometer effect’. A two byte binary

field can contain a minimum value of -32768 and a maximum of +32767. Because

the field is defined as PIC S9(4) COMP, COBOL will limit them to 4 digits.

DST standard; use COMP-5 for

SMALLINT

INTEGER

VARCHAR lengths

Null indicators

Note: This applies when the COBOL compiler option TRUNC(STD) is set. It may

apply for TRUNC(OPT). It does not apply with TRUNC(BIN). Consult the

COBOL for zOS Programming Guide for more details.

Page 10: Churn0809

10

10

Largest numbers

• Largest COBOL numeric picture• S9(31) COMP-3• S9(1)V9(30) COMP-3• z(30)9• z,zzz,zzz,zzz,zzz,zzz,zzz,zzz,zzz,zzz,zz9

• Largest DB2 numeric• DECIMAL(31,0)

• COBOL and DB2 math use 31 digits of precision• Precompiler parm DEC(31)• Compiler parm ARITH(EXTEND)

Without using the parms above, the largest COBOL numeric field can be 18 digits

and the largest DB2 numeric field can be 15 digits. These smaller parms are faster

than the 31 digits parms.

The smaller compiler parms are

Precompiler DEC(15)

Compiler ARITH(COMPAT)

If the program has a multi-operation math statement, then the compiler parms also

specify the number of digits used for the intermediate results.

Page 11: Churn0809

11

11

Definitions

• Array: A group of things arranged in a structured way. In COBOL, a WORKING-STORAGE field with OCCURS.

• Copybook: predefined COBOL instructions that can be added to a program with either the COPY or EXEC SQL INCLUDE instructions.

• Host Variable: COBOL field, in eitherWORKING-STORAGE or LOCAL-STORAGE

• Host Variable Array: An array that is used in SQL as a host variable.

• Restartable: Functionality in a program that lets it start over after an error situation.

• Unit of Work: point where one piece of work is considered done.

These define the terms used in this presentation which can have different meaning

in another context. For this presentation, table means a DB2 table.

Page 12: Churn0809

12

12

Multi-row Fetch/Insert

• Fetch or Insert up to 32,767 rows in one statement• Fewer calls to DB2• Use arrays instead of fields

• Requires more memory• Limit the number returned for CICS

• Request the same or fewer rows than defined in the OCCURS, but not more.

• Increases program complexity• Additional perform loop level• More error handling code

Our current production COBOL compiler v3r4 expanded the maximum size of an

01 level item to 128Mb while leaving the total size of all working-storage at

128Mb.

CICS programs need to manage the amount of memory they are going to use. For

batch programs DST defines all columns in a table with the same number of

occurrences for each column and null indicator. If a CICS program is only going to

use a few columns from a table with hundreds of columns, then we examine how to

reduce the extra unused memory.

Page 13: Churn0809

13

If the program requests 100 rows but DB2 returns only 10 rows, the program performs like

it requested 10 rows. Some knowledge of the data is needed to evaluate whether to use

multi-row FETCH.

recommendation = Use Multi-row fetch when a cursor typically returns 4 or more rows.

Page 14: Churn0809

14

14

Multi-row Fetch SQL syntax

DECLARE MEANINGFUL_NAME CURSOR

WITH ROWSET POSITIONING FOR

SELECT …

FETCH NEXT ROWSET FROM MEANINGFUL_NAME

FOR numeric literal ROWS

INTO :hv-array1, :hv-array2 …

Cursor Definition Example

Fetch Example

FETCH NEXT ROWSET FROM MEANINGFUL_NAME

FOR :hv ROWS

INTO :hv-array1, :hv-array2 …

OR

When the number of rows is not given multi-row FETCH defaults to 1 row.

A program cannot tell DB2 which occurrences to populate in a FETCH. If the program fetches 20 rows in

Page 15: Churn0809

15

15

• More than 4 rows per fetch

• Program is designed for multi-row FETCH

• Driving cursor defined WITH HOLD

• Fetch 100 rows

• Process all rows

• End unit of work (COMMIT)

• Entire cursor process is within a single unit of work.

• Open cursor

• All rows processed completely

• Close cursor

• Start-up array loading cursors

Recommend Multi-row Fetch

Since coding for a multi-row fetch CURSOR is more complex than using a single row CURSOR, we evalu

Page 16: Churn0809

16

16

• Added complexity without clear savings

• Program does not have a clear unit of work

• Burying multi-row functionality inside fetch routine

Avoid Multi-row Fetch

PROCESS(x)

OPENFETCH*

FETCH*

CLOSE

program(1)

UPDATE

Get more rows?MR-FETCH

Give row

*=same routine

Check COMMIT

Fetch time is typically only a small part of the total program time. Even an 55% improvement of 10 seconds is only 5 ½ seconds.

Programs that have no clear Logical Unit of Work are more difficult to change. It may even be impossible to implement restart l

Multi-row SELECT works better when it is designed into the program from the beginning. Burying multi

Page 17: Churn0809

17

If the program requests 100 rows but DB2 returns only 10 rows, the program performs like

it requested 10 rows. Some knowledge of the data is needed to evaluate whether to use

multi-row INSERT.

recommendation = Use Multi-row insert when a unit-of-work typically inserts 15 or more

rows to a table.

Page 18: Churn0809

18

18

Multi-row Insert Syntax

EXEC SQL INSERT INTO table (column list)

VALUES (value list, host variable arrays)

FOR :hv ROWS

ATOMIC

END-EXEC

• Atomic (default)

• Severe error (negative SQLCODE) rolls back entire INSERT

• Stops on first error

• Easier logic to reposition for restart

When a program performs a multi-row INSERT using the ATOMIC option, DB2 behaves according to the

•An error rolls back the entire INSERT but only the INSERT.

•DB2 stops checking for errors after it finds the first failed row. There will be only 1 error returned.

•A COBOL program is easier to code restart and reposition logic using the ATOMIC option.

Page 19: Churn0809

19

19

Multi-row Insert Syntax

EXEC SQL INSERT INTO table (column list)

VALUES (value list, host variable arrays)

FOR :hv ROWS

NOT ATOMIC CONTINUE ON SQLEXCEPTION

END-EXEC

• Not Atomic

• No rollback

• Does not stop

• Restarting can be difficult

• New SQLCODEs and SQLSTATEs

When a program performs a multi-row INSERT using the NOT ATOMIC option, DB2 behaves according to

•DB2 can get multiple errors on the INSERT. The program needs to use GET DIAGNOSTICS to report the errors encountered.

•The code is more complex to restart a program using the NOT ATOMIC option.

•This type of INSERT is ideal for a program where the only response to an error is to save the row, skip over it, and continue pr

DB2 will return one of the four SQLCODEs when a program does a multiple row fetch. To determine the individual errors, the prog

•All successful – SQLCODE = +0, SQLSTATE = 00000

•All successful but warnings – SQLCODE = +252, SQLSTATE = 01659

•At least one (but not all) failed –SQLCODE = -253, SQLSTATE = 22529

•All failed – SQLCODE = -254, SQLSTATE = 22530

Page 20: Churn0809

20

20

• More than 15 rows per insert

• Many Inserts on single table

• Inserting is the only process in the program

• Like load utility without the outage

• Many inserts within a unit of work

Recommend Multi-row Insert

recommendation = Use multi-row insert when the primary purpose of the program is to insert--especially w

Using ATOMIC or NOT ATOMIC options are based on the application logic. To date most have been ATOMIC to support a restart funct

Page 21: Churn0809

21

21

• Multiple tables are changed• If some/all rows fail (ATOMIC), how does the

program back out the other changes related to those rows?

• If some rows fail (NOT ATOMIC), how does the program back out only the changes related to the failed rows?

• Do not delay commits for the purpose of building up a host variable array.

Avoid Multi-row Insert

The program should change other tables after the multi-row insert. So when the program is doing ATOMIC inserts it will

changes only to successful rows. All this logic will add time to the coding and testing of the program.

Artificially delaying commits defeats the purpose of commit checks—reducing rollback time and contention with other programs. If

Page 22: Churn0809

22

Array HandlingMulti-row FETCH and INSERT

It is not about the SQL.It is about the COBOL!

Page 23: Churn0809

23

23

Host Variable Arrays

01 EMPLOYEE-TABLE.

03 DCL-EMPLOYEE-20 OCCURS 20 TIMES.

05 EMPLOYEE-ID PIC S9(5) COMP-3.

05 COMMENT-TXT.

49 COMMENT-TXT-LEN PIC S9(4) COMP-5.

49 COMMENT-TXT-TEXT PIC X(255).

• SQL references arrays of elements

• SQL cannot reference a group item array.

01 DCL-EMPLOYEE-20.

05 EMPLOYEE-ID PIC S9(5)COMP-3

OCCURS 20 TIMES.

05 COMMENT-TXT OCCURS 20 TIMES.

49 COMMENT-TXT-LEN PIC S9(4) COMP-5.

49 COMMENT-TXT-TEXT PIC X(255).

DB2 requires that an array is created for each column used in a SELECT. COBOL has been optimized to process arrays that have mu

instructions than the second example. If many fields are defined in the group item (01) then the performance impact may be noti

DST uses standardized host variable arrays that are created for multi-row functions. If the copybook is used in a CICS module then it

Other OCCURS sizes can be used but need justification.

Page 24: Churn0809

24

24

New Multi-row copybooks

• DCLGEN does not build multi-row copybooks.

• May require two copy books

• WHERE clause references single column definitions

• FETCH INTO references host variable arrays

• Remember group item lists

The standard DCLGEN process included with SPUFI will not generate a table definition

where the COBOL fields have the OCCURS clause. Either someone will have to change

the generated copybook or someone will need to write something produce the fields to be

used in a multi-row function.

There may be a need to have both a single row and a multi-row versions of the copybook.

SQL WHERE clauses can only reference one type of array. SQL cannot reference one

occurrence of an array item.

The only COBOL can pass an array is for an IN list. The array has to be defined as follows.

Each elementary item is required to have a name even if the value is constant.

01 HV-CUST-INITIALS.

05 HV-INIT1-TX PIC X(3) VALUE SPACE.

05 HV-INIT2-TX PIC X(3) VALUE SPACE.

05 HV-INIT3-TX PIC X(3) VALUE SPACE.

05 HV-INIT4-TX PIC X(3) VALUE SPACE.

05 HV-INIT5-TX PIC X(3) VALUE SPACE.

05 HV-INIT6-TX PIC X(3) VALUE SPACE.

PROCEDURE DIVISION.

Page 25: Churn0809

25

25

Array Handling Restrictions

• Use subscripts to access one row from multiple tables

• COMP vs. COMP-5 (COMP is faster)

• Updating many indexes is slower than one subscript• Cannot use variable length arrays

• OCCURS DEPENDING ON rejected by precompiler and coprocessor

• SEARCH and SEARCH ALL will search entire array• Must be visible to the SQL precompiler.

• Cannot use COPY … REPLACING• Initializing groups of arrays is slower than initializing one

array with many fields.

There are many different ways to process COBOL arrays. We found that

using subscripts was the best way to handle the occurrences with many host

variable arrays. We also found that all host variable arrays need to be fixed

length even though it makes the SEARCH or SEARCH ALL statements

process the entire array.

It is faster for COBOL to access an array with an index instead of a subscript.

If there are multiple columns inside host variable arrays, then the faster speed

is lost to updating multiple indexes instead of one subscript. A program will

compile using the same name for multiple indexes as long as it does not

reference the name. A program will not compile using an INDEXED BY

clause reference another index.

Host variable arrays must be visible to the SQL processor. For programs using the

precomiler, host variable arrays must be defined in the program or included in the

program with EXEC SQL INCLUDE. Programs using the coprocessor can use the

COPY and could change the number of rows using the following statement.

COPY DATYP REPLACING ==OCCURS 20== BY ==OCCURS 40==.

There are fewer Assembler instructions created for initializing one array with

multiple fields instead of initializing many host variable arrays.

Page 26: Churn0809

26

26

• SEARCH WHEN clause is limited to one element

• WORK-DTE-IDX will not be incremented by the SEARCH verb.

• The program compiles successfully, but will produce incorrect results if it requires automatic incrementing.

SEARCH EMPLOYEE-ID [will vary its index]

AT END

(at end code)

WHEN EMPLOYEE-ID (EMPLOYEE-IDX) = ws-employee-id

AND WORK-DTE (WORK-DTE-IDX) = ws-work-dte

(when found code)

END-SEARCH

COBOL Array Handling

When a program has fetched data into host variable arrays and there is a multiple column key, the program cannot use the SEARCH

A custom routine must be coded to find the right row.

Page 27: Churn0809

27

27

1st 8 row FETCHSQLCODE=0SQLSTATE=00000SQLERRD(3) = 8

2nd 8 row FETCHSQLCODE=+100SQLSTATE=02000SQLERRD(3)=4Last 4 rows are carry over from thelast FETCH

Multi-row Fetch data

• When fetched, null columns do not update the column host variable – only the null indicator.

01000Jim Smith8

0234Jean Smith7

076Jean Smith6

025000Henry Jones5

024680Henry Jones4

046Henry Jones3

0200David Churn2

0100David Churn1

NullAccountNameID

0579Kathy Winters12

0357Harry Thompson11

-1John Smith10

0135Jim Smith9

NullAccountNameID

When working with nullable columns it is still important to define and check the

null indicator. It would not be good to mix up John Smith’s money with David

Churn’s account.

Page 28: Churn0809

28

28

MOVE text-field OF DCL-xxx-Mnnn (sub) (1:

text-len OF DCL-XXX-Mnnn (sub) )

TO

MOVE text-field OF DCL-xxx-Mnnn (1:

text-len OF DCL-XXX-Mnnn)

TO

• For VARCHAR columns, move only the bytes that DB2 populated.

• Move source (start,length) to destination

• Becomes move source (sub) (start:length) to …

Moving VARCHAR

Like null indicators, a program should move variable length fields for the length that DB2 returned. Subscripts go at the end o

Page 29: Churn0809

29

29

Process loops

2000-PROCESS.

PERFORM 2100-OPEN-CURSOR

PERFORM 2200-FETCH

PERFORM UNTIL END-OF-CUR

ADD +1 TO FETCH-COUNT

PERFORM 6000-WRITE

PERFORM 2200-FETCH

END-PERFORM

PERFORM 2300-CLOSE-CURSOR

PERFORM 9600-CHECK-UOW

PERFORM 9000-READ

2000-PROCESS.

PERFORM 2100-OPEN-CURSOR

PERFORM UNTIL END-OF-CUR

PERFORM 2200-FETCH

PERFORM 6000-WRITE VARYING

EMPLOYEE-SUB FROM +1 by +1

UNTIL EMPLOYEE-SUB > SQLERRD(3)

ADD SQLERRD(3) TO FETCH-COUNT

END-PERFORM

PERFORM 2300-CLOSE-CURSOR

PERFORM 9600-CHECK-UOW

PERFORM 9000-READ

Old New

There are two changes to the potential program’s logic. 1) The priming FETCH is eliminated. 2) The 6000

This New example is not the best way to actually code it but is a good way to illustrate the change. In actual code, the progra

Page 30: Churn0809

30

30

Multi-row Fetch Caution

• Non-rowset fetch puts data in first occurrence

• Mixing rowset and non-rowset fetchs gives unobvious results

• Fetch more rows than array occurrences gives SQLCODE=+354 SQLSTATE=01668

Thomas6

Suresh5

Mike4

Jim3

Gordon2

David1

NameID

Fetch 3 rows

Fetch 3 rows

Fetch next

Fetch 3 rows

When a program only uses multi-row fetch, rows are returned in a predictable fashion.

The first Fetch 3 rows returns David, Gordon, and Jim.

The second Fetch 3 rows returns Mike, Suresh, and Thomas.

When a program adds a FETCH NEXT in between the Fetch 3 rows it give the following results.

The first Fetch 3 rows returns David, Gordon, and Jim.

The Fetch next returns Gordon.

The second Fetch 3 rows returns Jim, Mike, and Suresh.

This could cause the program to process the rows for Gordon and Jim twice.

Page 31: Churn0809

31

31

• UPDATE/DELETE… WHERE CURRENT...

• Changes behavior based on last FETCH

• UPDATE/DELETE WHERE CURRENT OF..FOR ROW x OF ROWSET

• Update/Deletes a specific row of the rowset

• Restartable cursors must have an ORDER BY

• Cursors with ORDER BY cannot use WHERE CURRENT

Using Where Current

If positioned on a single row (previous fetch was regular), then DB2 updates/deletes

single row

If positioned on a rowset (previous fetch was ROWSET … FOR n ROWS), then

DB2 updates/deletes entire set of rows

If you mix and match row and rowset fetches in your program, the program could

have DB2 accidentally update or delete a rowset instead of a single row!

Page 32: Churn0809

32

32

INSERT INTO TABLE (id, account, name, amount)

VALUES (:hv-id, :hva-account, :hv-name, :hva-amount)

FOR :hv-num-rows ROWS ATOMIC

05 HV-ID PIC S9(7) COMP-3 VALUE ZERO.

05 HVA-ACCOUNT PIC S9(15) COMP-3

OCCURS 100 TIMES VALUE ZERO.

05 HV-NAME PIC X(100) VALUE SPACES.

05 HVA-AMOUNT PIC S9(11)V99 COMP-3

OCCURS 100 TIMES VALUE ZERO.

• A non-array column has the same value for ALL rows

• In the following example, ID and name are simple host variables, not host variable arrays.

Multi-row Insert Feature

Multi-row INSERTs do not have to use host variable arrays for every column. If the value for all rows will be the same, then the program could use a non-array COBOL field to save the one value.

Like the multi-row FETCH, DB2 always starts with the first occurrence of a host variable array. There is no way to tell DB2 to start with any other occurrence. The program should always set the number of rows inserted. If it tells DB2 insert 4 rows from an array of 8 rows, the first 4 occurrences will be used, the remaining 4 occurrences will be ignored.

Page 33: Churn0809

33

33

• Formatting everything

• Null indicators

• VARCHAR lengths

• Number of rows in INSERT

• Error handling in loops

• Group moves to or from the host variable arrays

Multi-row Insert Caution

Like a single INSERT, verify that all the indicators and lengths are set right in the host variable arrays. The program needs t

There will be more logic to process errors from inserting multiple rows. Displaying information from the SQLCA is just a start

If programs usually move a row’s information in one group move, they will have to change to individual moves for the group items

Page 34: Churn0809

34

Using GET DIAGNOSTICS

What happened?!?

Page 35: Churn0809

35

35

GET DIAGNOSTICS

• EXEC SQL GET DIAGNOSTICS identifies• number of messages• each message’s SQLCODE and SQLSTATE• row number in the host variable array

• Calling GET DIAGNOSTICS returns an SQLCODE and refreshes the SQLCA

It is possible to get multiple warnings as well as an error on one row. Whenever a

program uses a multi-row fetch or multi-row insert, it should also use GET

DIAGNOSTICS.

Page 36: Churn0809

36

36

• Success, SQLCODE=0, SQLSTATE=00000• 1 serious error

• “regular” message, SQLCODE=negative• GET DIAGNOSTICS will repeat error

• One to many warnings• Overall warning - SQLCODE=+354,

SQLSTATE=01668 • Multiple warning conditions• GET DIAGNOSTICS needed to expose messages• Check SQLERRD(3) for rows returned

Multi-row Fetch Results

If the program just reports on information in the SQLCA, then it may be hard to determine what happened.

SQLCODE=+354, SQLSTATE=01668

A ROWSET FETCH STATEMENT RETURNED ONE OR MORE ROWS OF DATA, WITH ONE OR MORE BIND CON

Explanation: A rowset FETCH statement returned one or more rows of data, however, one or more bind out processing error conditi

All of the following conditions will cause a multi-row FETCH to return an SQLCODE=+354

•Nulls were bypassed for an aggregate function for row 3, 1 message results

•Date was adjusted occurs for row 9, 1 message results

•rows 3, 9, and 45 have nulls in a column but the program does not have a null indicator. 1 message for each of 3 rows.

•6 errors result! 3 missing null-indicator, 1 nulls bypassed, 1 date adjusted and (1) 01668 (the program had errors)

Page 37: Churn0809

37

37

For Multi-row Insert

• ATOMIC

• Error handling does not change

• NOT ATOMIC CONTINUE ON SQLEXCEPTION

• SQLCODE=+252, SQLSTATE=01659, success with warnings

• SQLCODE=-253, SQLSTATE=22529, one or more rows in error

• SQLCODE=-254, SQLSTATE=22530, all rows failed

• EXEC SQL GET DIAGNOSTICS to see the results for each failed row

Whether the program gets multiple errors depends on the atomicity of the INSERT.

A program can get one error with an ATOMIC multi-row INSERT. A program can

get multiple errors with a NOT ATOMIC multi-row INSERT. The SQLCODE

passed back in the SQLCA is as useful as the one for multi-row FETCH.

Page 38: Churn0809

38

38

GET DIAGNOSTICS

EXEC SQL GET DIAGNOSTICS

:hv1 = NUMBER

,:hv2 = DB2_LAST_ROW

,:hv3 = MORE

END-EXEC.

• How many errors?

• Specific condition information

EXEC SQL GET DIAGNOSTICS CONDITION 1 (or :hv)

:GD-RETURNED-SQLSTATE = RETURNED_SQLSTATE

, :GD-DB2-RETURNED-SQLCODE = DB2_RETURNED_SQLCODE

, :GD-DB2-REASON-CODE = DB2_REASON_CODE

, :GD-DB2-ROW-NUMBER = DB2_ROW_NUMBER

END-EXEC.

GET DIAGNOSTICS can return general information about errors, or information about a specific error. This syntax will provide th

specifics on each error. Unlike DSNTIAR this is another call to DB2 and should use SQLCODE checks after these calls. It is pru

These are the options that are useful when determining how many errors are present.

•NUMBER returns an integer – the number of conditions identified.

•DB2_LAST_ROW returns an integer – it will contain +100 if the last operation was a multi-row fetch that resulted in an end

•MORE returns a Y/N indicator where Y means there was more error information available than could be returned (available memory).

Specifics

•The program should use a perform loop to acquire all of the error information.

•This statement acquires the sqlstate, sqlcode, reason code (sub reason) and row number associated with a specific error.

•The program can hard code a condition number or can use a host variable.

Page 39: Churn0809

39

39

DST Usage

• Copybooks

• WORKING-STORAGE or LOCAL-STORAGE (copy in the notes)

• PROCEDURE DIVISION

• PERFORM 9970-MR-DIAGNOSTICS

01 WS00730 SYNC.

05 GET-DIAGNOSTICS-SQLSTATE PIC X(5) VALUE ZEROS.

05 GD-TOO-MANY-ERRORS PIC X VALUE 'N'.

88 DB2-TOO-MANY-ERRORS VALUE 'Y'.

05 LAST-ROW-SQLCODE PIC S9(9) COMP-5 VALUE ZERO.

88 LAST-ROW-FETCHED VALUE +100.

05 NUM-ERROR-INFO PIC S9(4) COMP VALUE ZERO.

05 ROW-ERROR-INFO OCCURS 500 TIMES.

*500 should be sufficient for 100 fetch/insert (subject

* To change)

10 ROW-SQLCODE PIC S9(9) COMP-5 VALUE ZERO.

10 ROW-REASON-CDE PIC S9(9) COMP-5 VALUE ZERO.

10 ROW-SQLSTATE PIC X(5) VALUE ZEROS.

10 ROW-NUMBER PIC S9(5) COMP-3 VALUE ZERO.

* TEMP WORKING VARIABLES

05 DB2-NUMBER-OF-ERRORS PIC S9(9) COMP-5 VALUE ZERO.

05 DB2-RETURNED-SQLCODE PIC S9(9) COMP-5 VALUE ZERO.

05 DB2-REASON-CODE PIC S9(9) COMP-5 VALUE ZERO.

05 DB2-RETURNED-SQLSTATE PIC X(5) VALUE SPACES.

05 DB2-ROW-NUMBER PIC S9(31) COMP-3 VALUE ZERO.

05 DB2-CONDITION-NUMBER PIC S9(4) COMP-5 VALUE ZERO.

Page 40: Churn0809

40

40

ZC0068 logic

• Issues GET DIAGNOSTICS to obtain• The number of conditions• The previous statement’s sqlstate• Whether more errors occurred than could be

reported (copybook limit Is 500 errors)• Performs a loop to obtain each error’s

• SQLCODE• SQLSTATE• Return code• Row number

• Populates the array of error information within the WS00730C copybook

* THIS MACRO IS USED FOR EXEC SQL GET DIAGNOSTICS ERROR

* PROCESSING.

*

* PROGRAM SHOULD PERFORM 9970-MR-DIAGNOSTICS AFTER ANY

* MULTI-ROW EXEC SQL STATEMENT RETURNS AN ERROR.

*

9970-MR-DIAGNOSTICS.

MOVE ZERO TO NUM-ERROR-INFO OF WS00730.

EXEC SQL

GET DIAGNOSTICS

:WS00730.DB2-NUMBER-OF-ERRORS = NUMBER

, :WS00730.LAST-ROW-INDICATOR = DB2_LAST_ROW

, :WS00730.GD-TOO-MANY-ERRORS = MORE

END-EXEC.

MOVE SQLSTATE TO GET-DIAGNOSTICS-SQLSTATE OF WS00730

IF GET-DIAGNOSTICS-SQLSTATE OF WS00730 = ZEROS

* LIMIT RETURNED ERRORS TO 500.

IF DB2-NUMBER-OF-ERRORS > +500

SET DB2-TOO-MANY-ERRORS OF WS00730 TO TRUE

MOVE +500 TO DB2-NUMBER-OF-ERRORS OF WS00730

END-IF

* DB2 BUILDS THE ERROR/CONDITION INFORMATION IN REVERSE ORDER

* SO WE WILL BUILD OUR LIST OF INFORMATION BACKWARDS

* SO THE ERRORS APPEAR IN THE ORDER THEY OCCURRED.

PERFORM VARYING DB2-CONDITION-NUMBER OF WS00730

FROM DB2-NUMBER-OF-ERRORS OF WS00730 BY -1

UNTIL DB2-CONDITION-NUMBER < 1

OR SQLSTATE NOT = ZEROS

EXEC SQL GET DIAGNOSTICS CONDITION

:WS00730.DB2-CONDITION-NUMBER

:WS00730.DB2-RETURNED-SQLSTATE = RETURNED_SQLSTATE

,:WS00730.DB2-RETURNED-SQLCODE = DB2_RETURNED_SQLCODE

,:WS00730.DB2-REASON-CODE = DB2_REASON_CODE

,:WS00730.DB2-ROW-NUMBER = DB2_ROW_NUMBER

END-EXEC

MOVE SQLSTATE TO GET-DIAGNOSTICS-SQLSTATE OF WS00730

Page 41: Churn0809

41

41

Sample Program Logic

• Design a process to handle the messages.

• Consider

• Can the program continue?

• What has been rolled back?

• Multi-row FETCH

• Multi-row INSERT

• Atomic vs. Not Atomic

• Are other processes dependent on this data?

• Sample code to display gathered messages

This logic will display the errors gathered from the include book code.

Sample DISPLAY loopPERFORM VARYING DB2-CONDITION-NUMBER OF WS00730

FROM +1 BY +1

UNTIL DB2-CONDITION-NUMBER OF WS00730

> NUM-ERROR-INFO OF WS00730

DISPLAY 'ROW NUMBER : ' ROW-NUMBER OF WS00730

(DB2-CONDITION-NUMBER OF WS00730)

DISPLAY 'SQLSTATE : ' ROW-SQLSTATE OF WS00730

(DB2-CONDITION-NUMBER OF WS00730)

DISPLAY 'SQLCODE : ' ROW-SQLCODE OF WS00730

(DB2-CONDITION-NUMBER OF WS00730)

DISPLAY 'REASON-CDE : ' ROW-REASON-CDE OF WS00730

(DB2-CONDITION-NUMBER OF WS00730)

DISPLAY 'ROW CONTENTS : ' UPON CONSOLE

(put DISPLAY OF HOST VARIABLE ARRAY CONTENT here)

END-PERFORM

Page 42: Churn0809

42

Unit of work

Page 43: Churn0809

43

43

• Why restart? DB2 rollback costs twice as much as the actual change (insert, update, or delete)

• Restart concept

• Where does the unit of work begin and end

• Determine what triggers an end to a unit of work.

• Next row from driving cursor

• Change in control break field value

• End of file condition

• Must be designed into the program

• Do other changes COMMIT before multi-row INSERT?

• How does the program reposition during restart?

Restartability

Maintainable units of work do not just happen. The associate writing the program has to figure out where they should go and put

When the program does not have an apparent unit of work, it is difficult to enhance the program. It may be impossible to enhanc

If you cannot identify the beginning and ending of the Unit Of Work, you cannot “sneak” in a multi-row operation.

If a Unit of Work ends before the program completes a multi-row insert, the insert will become part of the next unit of work

If a Unit of Work ends before the program completely processes all the rows fetched from a multi-

Page 44: Churn0809

44

44

Instead of Try{Priming Read}

PERFORM UNTIL EOF

MOVE fields

INSERT

CHECK COMMIT

READ

END-PERFORM.

{Priming READ}

PERFORM UNTIL EOF

MOVE ZERO TO WS-NUMBER-TO-INSERT

PERFORM VARYING x FROM +1 BY +1

UNTIL x > +100 OR EOF

MOVE fields TO hv array (x)

ADD +1 TO WS-NUMBER-TO-INSERT

READ

END-PERFORM

IF WS-NUMBER-TO-INSERT > ZERO

MR-INSERT

CHECK COMMIT

END-IF

END-PERFORM.

Mass Inserts

If the program’s process is to insert rows into a table, then the program can COMMIT after filling a multiple row buffer. This

Page 45: Churn0809

45

45

Multi-row FETCH within a UOW

PERFORM UNTIL EOC

FETCH

Perform Process-

row

END-PERFORM.

CHECK COMMIT

PERFORM UNTIL EOC

MR-FETCH

MOVE SQLERRD(3)

TO rows-fetched

PERFORM process-row

VARYING x FROM +1 BY +1

UNTIL x > rows-fetched

END-PERFORM

CHECK COMMIT

Instead of Try

The program should not process more data than DB2 returned. Refer to new example.

Page 46: Churn0809

46

Mixing SQL features

•INSERT within SELECT

•SELECT INTO … ORDER BY … FETCH FIRST

•SEQUENCE objects

•XML

Page 47: Churn0809

47

47

Select from Insert

• Fewer DB2 calls

• INSERT and SELECT errors are possible

• Can be used with multi-row FETCH and INSERT

• Does not work when an AFTER TRIGGER modifies the changed table. (SQLCODE=-989, SQLSTATE=560C3) Proceed with caution.

SELECT col_id, insert_tsp, i_amt

FROM FINAL TABLE (

INSERT INTO TRIGGER_TBL

(insert_tsp, i_amt)

VALUES(current timestamp,

:i-amt)

Programs have the ability to get the assigned values of columns in the same

statement where they are created. Some of the DB2 assigned columns that can be

brought back to the program are identity column values, assigned sequence object

values, and special register values (like CURRENT TIMESTAMP).

When using this type of statement both INSERT errors and SELECT errors are

possible, like the following examples.

•Duplicate key from INSERT (SQLSTATE=23505)

•Null without a null indicator from the SELECT (SQLSTATE=22002)

Unfortunately, this statement is incompatible with some AFTER triggers. When an

AFTER trigger, triggered Stored Procedure, or triggered UDF modifies the table

that called it, the program gets an SQLCODE +100. Calling GET DIAGNOSTICS

reveals the following error.

The following text is from DB2 V8 Messages and Codes, pages 132-133

-989 AFTER TRIGGER trigger-name ATTEMPTED TO MODIFY A ROW IN

TABLE table-name THAT WAS INSERTED BY AN INSERT STATEMENT

WITHIN A SELECT STATEMENT

Explanation: An INSERT statement was specified in the FROM clause of a

SELECT statement of a subselect or a SELECT INTO statement, but the underlying

target base table of the INSERT has an AFTER trigger defined which modifies the

table being inserted. This is disallowed.

System Action: The statement cannot be processed.

Page 48: Churn0809

48

48

Open cursor

Fetch x rows

Close cursor

Audit rows(x)

Build details(x)

Add rows(y)

The number of rows and iterations should all be the same!

Insert rows(1)

Multi-row Select from Insert

Whenever a program is going to do many inserts and it needs some of

the assigned values, it can be written to execute a multi-row INSERT

within a multi-row SELECT. To keep the logic simpler, keep the

number of rows in the FETCH the same as the number of rows in the

INSERT. An ORDER BY INPUT SEQUENCE clause should be used

on the CURSOR definition to ensure the host variable array rows

match. GET DIAGNOSTICS should also be used to look for INSERT

errors with OPEN statement and SELECT errors on the FETCH.

Page 49: Churn0809

49

49

• Build all detail columns into host variable arrays

• 3 calls to DB2

becomes

OPEN

FETCH

CLOSE5

6

4

3

2

1

occ ID lmdt lmid col1 col2 col3 seq col4

Build

IJ

KL

GH

EF

CD

AB

lmid

Y

Z

X

W

V

U

col1

55

66

44

33

22

11

col2

222

333

111

99

88

77

col3

UV

WX

ST

QR

OP

MN

col4

500

600

400

300

200

100

ID

...03

...03

...02

...02

...01

...01

lmdt

10

12

8

6

4

2

seq

Audit

col4seqcol3col2col1lmidlmdtID

MN27711UAB…01100

col4seqcol3col2col1lmidlmdtID

COBOL arrays example

When to program needs to create a single image of a row it cannot use a group

move from the host variable arrays. Instead individual moves from the host

variable arrays need to move each column into a one row image. The color show

when the individual host variable arrays are populated.

Note; ID is an identity column, lmdt is populated with a CURRENT TIMESTAMP,

and seq is given a value from a sequence object.

Page 50: Churn0809

50

50

DECLARE NEW_EMPLOYEES CURSOR

WITH ROWSET POSITIONING FOR

SELECT EMPLOYEE_ID

, WORK_DTE

FROM FINAL TABLE

( INSERT INTO HOURS

( WORK_DTE, WORK_HOURS

, APPROVAL_DTE )

VALUES ( CURRENT DATE

, :DCLHOURS.WORK-HOURS, NULL )

FOR :WS-INSERT-QTY ROWS ATOMIC )

ORDER BY INPUT SEQUENCE

CURSOR definition

The slide show the actual SQL for populating an example row.

Below is an example of the FETCH

EXEC SQL

FETCH NEXT ROWSET

FROM NEW_EMPLOYEES

FOR :WS-INSERT-QTY ROWS

INTO :DCLHOURS.EMPLOYEE-ID

, :DCLHOURS.WORK-DTE

END-EXEC

.

EVALUATE SQLCODE

WHEN ...

WHEN OTHER

GET DIAGNOSTICS needed

END-EVALUATE

Page 51: Churn0809

51

51

Use thisSELECT A.COL1

, A.COL2

INTO :WS-COL1

, :WS-COL2

FROM TABLEA AS A

WHERE …

ORDER BY A.COL1

FETCH FIRST 1 ROW ONLY

SELECT INTO with ORDER BY

• Replace CURSOR with single SELECT

• Get first or last row from results

• Continuing after “more than one” error gives unpredictable results (SQLCODE -811, SQLSTATE=21000)

Instead of thisOPENFETCH one rowCLOSE

Another handy feature introduced in version 8 is allowing an ORDER BY clause on

a non-CURSOR SELECT. When used with the FETCH FIRST 1 ROW ONLY, a

program does not need a CURSOR process to SELECT a specific row.

Using “more-than-one-row” logic to return first row has always been unpredictable.

In test results, we found that DB2 v7 would return the first row found—usually in

the clustering sequence. In DB2 v8, the second row is returned.

Page 52: Churn0809

52

Sequence Objects work just like Identity columns but are not part of a single table. They can be used like an identity column b

those numbers. The syntax above shows how to get values from a sequence object without a SELECT, INSERT, or UPDATE.

A program can also return the just assigned value of an identity column by using the IDENTITY_VAL_LOCAL() function. The functio

Page 53: Churn0809

53

53

XML

• COBOL verbs

• XML PARSE to read a document

• XML GENERATE to write a document

• DB2 processes and functions

• XML extender• Save document in one column

• Use document access definition file (DAD)• Parse into a relational table

• Build XML document

• Functions to build elements from columns

• Better functionality in DB2 9

There are several choices for building and reading an XML document that relates to

DB2 data. COBOL has its own built in function to parse an XML document and

build an XML document from an array. DB2 has an automatic way to parse and

build XML documents from table items. Programs can also call XML scalar

functions on DB2 columns to return the data in XML format.

It would take another presentation to cover these options adequately. There is also

better functionality in DB2 9 to handle XML data.

Page 54: Churn0809

54

54

Conclusions

• Longer names yield better descriptions

• More COBOL complexity

• Multi-row INSERT

• Multi-row FETCH

• Less COBOL complexity

• SELECT from INSERT

• SELECT INTO … ORDER BY … FETCH FIRST

• New features

• Sequence Objects

• XML

Our COBOL programs have access to new features that improve performance. The

multi-row functions improve performance while adding some complexity to our

programs. The other features improve performance and make the program less

complex. Most of the improvements make fewer trips between the program and

DB2.

DB2 version 8 provides new functionality that makes handling XML data easier and

DB2 9 will make it even easier.

Page 55: Churn0809

55

55

Acknowledgements

• Special thanks to Mike Todd

Special thanks to Mike Todd for providing access to his notes and permission to use

the information.

Page 56: Churn0809

56

56

David Churn

DST Systems, Inc.

[email protected]

Session F14

Introducing your COBOL programs to DB2 version 8