Showing posts with label SQLCODE. Show all posts
Showing posts with label SQLCODE. Show all posts

Saturday, 24 August 2013

Db2 SQL Execution Validation: SQLCA, SQLCODE, Row Counts, and COMMIT Checks


Db2 SQL execution validation flow using SQL statement, SQLCA, SQLCODE, row count, and program action

Validate the SQL result before the next step.

A COBOL program can compile, bind, and start correctly, then still make a bad production decision if it ignores the result of the SQL statement it just ran. A singleton SELECT might return +100, a searched UPDATE might affect zero rows, or a warning flag might show that data was truncated into a host variable.

Db2 SQL execution validation is the application logic that checks those results before the next business step runs. For batch and online programs, that usually means checking SQLCODE, SQLSTATE, selected SQLCA fields, indicator variables, affected-row counts, and commit or rollback decisions.

What SQL execution validation means

Execution validation is not the same as syntax checking. The Db2 precompiler validates embedded SQL structure, and bind processing validates access paths and object references for static SQL. Runtime validation happens after each SQL statement runs inside the program.

At runtime, the program should decide whether the SQL result is expected, unexpected but recoverable, or a hard error. That decision must be close to the statement that caused it. When error handling is pushed to the end of a paragraph, the program often loses the table name, key value, and business context needed for useful diagnosis.

Fields most programs should check

Field or valueWhat to checkTypical program action
SQLCODE0, +100, warnings, and negative values.Continue, branch to no-data logic, log warning, or stop with an error path.
SQLSTATEFive-character class and subclass, useful for grouped error handling.Use with SQLCODE when you need portable or class-based handling.
SQLERRMCMessage tokens returned by Db2.Write it to an error log with the program name, table, and key fields.
SQLERRD(3)Commonly checked for affected-row count after searched UPDATE, DELETE, or INSERT processing. Confirm behavior for your statement and Db2 level.Reject a zero-row update when one row was required, or flag too many rows when a key should be unique.
SQLWARN fieldsWarning flags such as truncation or null assignment cases.Log and decide whether the warning is acceptable for that transaction.
Indicator variablesNull and truncation status for nullable columns.Prevent spaces, zeros, or old working-storage values from being treated as real data.

Validation pattern for singleton SELECT

A singleton SELECT INTO expects either one row or no row. Treating +100 as a normal zero-value result can create bad output files, missing customer records, or incorrect audit rows.

EXEC SQL
   SELECT ACCT_STATUS,
          CURRENT_BAL
     INTO :WS-ACCT-STATUS,
          :WS-CURRENT-BAL
     FROM ACCOUNT
    WHERE ACCT_NO = :WS-ACCT-NO
END-EXEC

EVALUATE SQLCODE
   WHEN 0
      PERFORM VALIDATE-ACCOUNT-DATA
   WHEN +100
      MOVE 'ACCOUNT NOT FOUND' TO WS-ERROR-TEXT
      PERFORM WRITE-APPLICATION-ERROR
   WHEN OTHER
      PERFORM WRITE-DB2-ERROR
      PERFORM ABEND-PROGRAM
END-EVALUATE

If nullable columns are selected, add indicator variables. Without them, the program may accept stale working-storage values after a null column is returned.

Validation pattern for UPDATE, INSERT, and DELETE

For data-change statements, SQLCODE = 0 only says Db2 accepted the statement. The program still needs to check whether the number of affected rows matches the business rule.

EXEC SQL
   UPDATE ACCOUNT
      SET ACCT_STATUS = :WS-NEW-STATUS
    WHERE ACCT_NO     = :WS-ACCT-NO
END-EXEC

EVALUATE SQLCODE
   WHEN 0
      IF SQLERRD(3) = 1
         PERFORM WRITE-AUDIT-ROW
      ELSE
         MOVE 'UNEXPECTED UPDATE COUNT' TO WS-ERROR-TEXT
         PERFORM WRITE-APPLICATION-ERROR
         PERFORM ROLLBACK-WORK
      END-IF
   WHEN +100
      MOVE 'NO ACCOUNT UPDATED' TO WS-ERROR-TEXT
      PERFORM WRITE-APPLICATION-ERROR
   WHEN OTHER
      PERFORM WRITE-DB2-ERROR
      PERFORM ROLLBACK-WORK
END-EVALUATE

For a key-based update, one affected row may be the only acceptable result. For a batch correction statement, thousands of rows may be expected. Code the expected count instead of assuming any successful SQLCODE is enough.

Cursor FETCH validation

A cursor loop usually has three valid paths: row found, end of cursor, and error. The program should not treat every non-zero SQLCODE as an abend, because +100 is the normal end-of-data signal for a FETCH.

PERFORM UNTIL WS-END-OF-CURSOR = 'Y'
   EXEC SQL
      FETCH C1
       INTO :WS-ACCT-NO,
            :WS-ACCT-STATUS
   END-EXEC

   EVALUATE SQLCODE
      WHEN 0
         PERFORM PROCESS-ACCOUNT
      WHEN +100
         MOVE 'Y' TO WS-END-OF-CURSOR
      WHEN OTHER
         PERFORM WRITE-DB2-ERROR
         PERFORM ROLLBACK-WORK
         MOVE 'Y' TO WS-END-OF-CURSOR
   END-EVALUATE
END-PERFORM

Commit and rollback checks

Transaction control belongs in the validation design. A batch job that updates 50,000 rows should know when to commit, what to do after a failed commit, and how much restart information has been written. An online program should avoid sending a success message before the unit of work is safely committed.

  • Check every SQL statement that changes data before issuing COMMIT.
  • Use ROLLBACK when a related update, insert, or delete fails inside the same unit of work.
  • Log the business key, program name, paragraph, SQLCODE, SQLSTATE, and SQLERRMC.
  • For restartable batch jobs, record the last committed key or checkpoint data.

Common mistakes

Checking only negative SQLCODEs

+100 can be correct for a cursor end, but wrong for a required singleton lookup. Warnings can also matter when host variables receive truncated values.

Ignoring affected-row count

An update that affects zero rows can still return a successful SQL execution path. The business rule decides whether zero rows is acceptable.

Logging only the SQLCODE

A production support team needs more than -803 or -911. Include table name, key values, module name, and message tokens where available.

Related Db2 topics

Use this article with Db2 SQLCODE and SQLSTATE, Db2 Application Environment, Db2 Data Types, Db2 Binding Application, and Db2 Utilities.

FAQ

Should a COBOL Db2 program check SQLCODE after every statement?

Yes. Every embedded SQL statement should have a nearby validation path so the program can handle success, no-data, warning, and error results with the correct business context.

Is SQLCODE +100 always an error?

No. For a cursor fetch, +100 usually means end of cursor. For a required singleton lookup, it may mean the application cannot continue safely.

When should SQLERRD(3) be checked?

Check it when the program must confirm how many rows were affected by a data-change statement. Confirm the exact meaning for your SQL statement and Db2 version.

Db2 GET DIAGNOSTICS Statement Information Items



Db2 GET DIAGNOSTICS Statement Information Items

Db2 GET DIAGNOSTICS statement information items including ROW_COUNT, NUMBER, MORE, cursor attributes, and data types
Db2 statement diagnostics after SQL execution.

GET DIAGNOSTICS is useful when a COBOL + Db2 program needs more detail than a single SQLCODE. After an UPDATE, a multi-row FETCH, an OPEN, a PREPARE, or a stored procedure CALL, statement-information items can tell the program how many rows were affected, how many conditions exist, whether warning detail was discarded, or which cursor attributes Db2 used.

This page focuses only on statement-information items. For the base syntax, read DB2 GET DIAGNOSTICS Statement. For diagnostic details tied to a specific warning or error, use the related condition information page. For connection diagnostics, use the connection information page.

What Statement Information Means

Statement information describes the last SQL statement that ran before GET DIAGNOSTICS. It is not the same as condition information. A statement item answers questions such as:

  • How many rows did the last UPDATE, INSERT, DELETE, MERGE, or FETCH affect?
  • How many warnings or errors are available in the diagnostics area?
  • Did Db2 discard any condition records because the diagnostic area was too small?
  • After an OPEN or ALLOCATE, is the cursor scrollable, held, rowset-positioned, static, dynamic, sensitive, or insensitive?

IBM documents these items in the Db2 for z/OS GET DIAGNOSTICS statement reference. The target host variable must be compatible with the diagnostic item data type.

Statement Information Items and Data Types

Item Data type When to use it
DB2_GET_DIAGNOSTICS_DIAGNOSTICS VARCHAR(32672) Returns text about errors or warnings from the GET DIAGNOSTICS statement itself, such as truncation while assigning a diagnostic value.
DB2_LAST_ROW INTEGER After a multiple-row FETCH, returns +100 when the last row is in the returned rowset; otherwise it returns zero.
DB2_NUMBER_PARAMETER_MARKERS INTEGER After PREPARE, returns the number of parameter markers in the prepared SQL statement.
DB2_NUMBER_RESULT_SETS INTEGER After CALL, returns the number of result sets returned by the stored procedure.
DB2_NUMBER_ROWS DECIMAL(31,0) After OPEN or FETCH, returns the result-table row count when known. After PREPARE, it can return the estimated result count. For sensitive dynamic cursors, treat it as approximate.
DB2_RETURN_STATUS INTEGER After a stored procedure CALL, returns the procedure status when the procedure uses a RETURN statement.
DB2_SQL_ATTR_CURSOR_HOLD CHAR(1) After ALLOCATE or OPEN, returns Y when the cursor can remain open across units of work, or N when it cannot.
DB2_SQL_ATTR_CURSOR_ROWSET CHAR(1) After ALLOCATE or OPEN, returns Y when the cursor supports rowset positioning, or N for row-positioned operation only.
DB2_SQL_ATTR_CURSOR_SCROLLABLE CHAR(1) After ALLOCATE or OPEN, returns Y for a scrollable cursor or N for a forward-only cursor.
DB2_SQL_ATTR_CURSOR_SENSITIVITY CHAR(1) After ALLOCATE or OPEN, returns I for insensitive cursor behavior or S for sensitive cursor behavior.
DB2_SQL_ATTR_CURSOR_TYPE CHAR(1) After ALLOCATE or OPEN, returns F for forward cursor, S for static cursor, or D for dynamic cursor.
MORE CHAR(1) Returns Y when Db2 discarded some warning or error records because the diagnostic area needed too much storage; otherwise returns N.
NUMBER INTEGER Returns the number of condition records stored for the previous SQL statement. Use this before looping through CONDITION 1, CONDITION 2, and so on.
ROW_COUNT DECIMAL(31,0) Returns rows affected by the previous data-change statement or rows fetched by a multiple-row FETCH. After TRUNCATE or some mass-delete cases, Db2 can return -1.
DB2_SQL_NESTING_LEVEL INTEGER Returns the current nesting level for a compiled SQL function, native SQL procedure, or trigger. Outside that nesting context, the value is zero.

COBOL Example: Capture ROW_COUNT After UPDATE

Use ROW_COUNT when the program must log how many rows the last SQL statement changed. In COBOL, define a packed decimal or numeric host variable that can hold DECIMAL(31,0).

01  WS-ROW-COUNT        PIC S9(9) COMP-5.

EXEC SQL
    UPDATE CUSTOMER
       SET STATUS = 'I'
     WHERE LAST_ORDER_DATE < :WS-CUTOFF-DATE
END-EXEC.

IF SQLCODE = 0
   EXEC SQL
       GET DIAGNOSTICS :WS-ROW-COUNT = ROW_COUNT
   END-EXEC
END-IF.

If the update qualifies 250 customer rows, WS-ROW-COUNT receives 250. Do not use this as a substitute for checking SQLCODE; use it after the SQL statement has completed successfully or when your error path expects this item to be available.

COBOL Example: Count Diagnostic Conditions

NUMBER is useful when one SQL statement can return more than one warning or error condition. After reading NUMBER, the program can loop through condition items such as RETURNED_SQLSTATE, DB2_RETURNED_SQLCODE, and MESSAGE_TEXT.

01  WS-DIAG-COUNT       PIC S9(9) COMP-5.
01  WS-DIAG-ID          PIC S9(9) COMP-5.
01  WS-RETURNED-SQLCODE PIC S9(9) COMP-5.
01  WS-MESSAGE-TEXT     PIC X(240).

EXEC SQL
    GET DIAGNOSTICS :WS-DIAG-COUNT = NUMBER
END-EXEC.

PERFORM VARYING WS-DIAG-ID FROM 1 BY 1
        UNTIL WS-DIAG-ID > WS-DIAG-COUNT
   EXEC SQL
       GET DIAGNOSTICS CONDITION :WS-DIAG-ID
           :WS-RETURNED-SQLCODE = DB2_RETURNED_SQLCODE,
           :WS-MESSAGE-TEXT     = MESSAGE_TEXT
   END-EXEC
END-PERFORM.

For basic SQLCODE and SQLSTATE handling, see the SQLCA guide. GET DIAGNOSTICS adds detail, but the program still needs a clear SQL error-handling rule.

Cursor Attribute Items

The cursor attribute items are useful after OPEN when a program needs to confirm cursor behavior. For example, a Db2 browse program might record whether a cursor is scrollable, static or dynamic, rowset-positioned, and sensitive or insensitive.

01  WS-CURSOR-SCROLLABLE PIC X.
01  WS-CURSOR-TYPE       PIC X.
01  WS-CURSOR-SENSITIVE  PIC X.

EXEC SQL
    OPEN C1
END-EXEC.

IF SQLCODE = 0
   EXEC SQL
       GET DIAGNOSTICS
           :WS-CURSOR-SCROLLABLE = DB2_SQL_ATTR_CURSOR_SCROLLABLE,
           :WS-CURSOR-TYPE       = DB2_SQL_ATTR_CURSOR_TYPE,
           :WS-CURSOR-SENSITIVE  = DB2_SQL_ATTR_CURSOR_SENSITIVITY
   END-EXEC
END-IF.

For scrollable cursor design rules, see Db2 Scrollable Cursor Guidelines for COBOL Programs.

Common Mistakes

  • Reading ROW_COUNT after another SQL statement has already run. The diagnostics area belongs to the previous eligible SQL statement.
  • Moving straight to CONDITION 2 without checking NUMBER.
  • Using a short character host variable for DB2_GET_DIAGNOSTICS_DIAGNOSTICS and then ignoring truncation.
  • Assuming DB2_NUMBER_ROWS is exact for every cursor. Sensitive dynamic cursors can make this value approximate.
  • Defining all targets as PIC X. Numeric items such as ROW_COUNT, NUMBER, and DB2_NUMBER_ROWS need compatible numeric host variables.

FAQ

Is ROW_COUNT the same as SQLERRD(3)?

They can overlap for common row-count cases, but GET DIAGNOSTICS ROW_COUNT is the direct statement-information item. Use your shop standard consistently and test the specific SQL statement type.

When should I use NUMBER?

Use NUMBER before reading condition information. It tells the program how many diagnostic condition records are available for the previous SQL statement.

Can GET DIAGNOSTICS be dynamically prepared?

No. In Db2 for z/OS, GET DIAGNOSTICS is an executable statement for embedded applications, and it cannot be dynamically prepared.

Keep the call close to the SQL statement you are diagnosing. If another SQL statement runs first, the diagnostic area may no longer describe the statement you meant to inspect.

Saturday, 17 August 2013

Db2 Binding an Application: COBOL DBRM, Package, Plan, and Run JCL


Db2 application bind checklist from COBOL source to DBRM package plan and run JCL
Bind the DBRM before the program runs.

A COBOL program with embedded SQL does not run against Db2 just because the load module was created. The SQL has to be precompiled into a DBRM, the program has to be compiled and link-edited, and Db2 must have a package or plan that matches what the program calls at run time.

That is the point of application binding. It connects the program's static SQL to Db2 access paths before the batch job, CICS transaction, or IMS program reaches production.

Where binding fits in the COBOL build

A typical static SQL build has four moving parts. If one of them is missing or from the wrong compile, the job can fail even when the COBOL source looks correct.

Build part What it creates Why it matters
Db2 precompile Modified COBOL source and a DBRM member The DBRM contains the static SQL that Db2 will bind.
COBOL compile Object module The embedded SQL calls have already been replaced with host-language calls.
Link-edit Executable load module The load module calls the Db2 language interface at run time.
Bind Package and plan entries in Db2 Db2 stores executable forms of the SQL statements and the selected access paths.

Precompile creates the DBRM

The Db2 precompiler reads the COBOL source and finds each EXEC SQL block. It checks SQL syntax, handles host variable references, includes members such as SQLCA or DCLGEN copybooks when requested, and writes a DBRM member to a partitioned data set.

The DBRM is not the same as the COBOL object module. It is the Db2-side input for binding. In many shops the member name matches the program name, such as PAYRPT01, because that makes the bind JCL and promotion controls easier to audit.

//PC.SYSIN    DD  DSN=APP.SOURCE(PAYRPT01),DISP=SHR
//PC.SYSCIN   DD  DSN=APP.WORK.COBOL(PAYRPT01),DISP=SHR
//PC.DBRMLIB  DD  DSN=APP.DBRMLIB(PAYRPT01),DISP=SHR

Bind the package from the DBRM

For most production applications, bind the DBRM as a package and include the package in a plan. A package keeps the bind unit close to one program module, so a change to one COBOL program does not force every related DBRM in a large plan to be rebound.

BIND PACKAGE(APP01)
  MEMBER(PAYRPT01)
  ACTION(REPLACE)
  ISOLATION(CS)
  CURRENTDATA(NO)
  QUALIFIER(PROD)

The exact bind options depend on the shop standard and workload. For example, a read-only reporting program may use different lock and isolation choices from an update program that posts end-of-day financial rows. Do not copy a bind card from another application without checking the program's SQL behavior.

Bind the plan used by run JCL

The application plan tells the run command which packages can be used. A common pattern is to bind packages into a collection and then bind a plan with a package list.

BIND PLAN(PAYPLAN)
  PKLIST(APP01.PAYRPT01)
  ACTION(REPLACE)

Some sites use a wildcard package list such as APP01.* for a controlled collection. That can reduce plan maintenance, but it should still be managed by promotion rules so test packages do not accidentally become callable from production jobs.

Run JCL must point to the right plan

After compile, link-edit, package bind, and plan bind, the batch job still has to call the expected plan. A typical DSN run step names the Db2 subsystem, program, plan, and application load library.

//RUNSQL  EXEC PGM=IKJEFT01
//STEPLIB DD  DSN=DSN.V12.SDSNLOAD,DISP=SHR
//SYSTSIN DD  *
  DSN SYSTEM(DSN1)
  RUN PROGRAM(PAYRPT01) PLAN(PAYPLAN) -
      LIB('APP.PROD.LOADLIB')
  END
/*
//SYSPRINT DD SYSOUT=*

If the run step names an old plan, a test collection, or the wrong load library, the program may call a package that does not match the current DBRM. That is why a release checklist should compare the load module, DBRM member, package bind, plan bind, and run JCL before the job is released.

Common bind failures and runtime clues

Binding problems usually show up in the bind output, Db2 messages, or SQLCODE returned to the program. The exact message text matters, so always check the Db2 output from the failing job before changing bind options.

Symptom Likely area to check
Bind fails because a table or view cannot be found Check the qualifier, owner, current environment, and whether the object exists in that subsystem.
Bind fails because the user is not authorized Check package, plan, table, view, and execute privileges for the bind owner.
Runtime SQLCODE points to package not found Check package collection, plan package list, subsystem, and whether the package was promoted.
Runtime SQLCODE points to timestamp or consistency mismatch Check whether the load module and DBRM/package came from the same precompile.

Application bind checklist

  • Use the DBRM created from the same source version that produced the load module.
  • Keep DBRM library, load library, package collection, and plan name visible in promotion records.
  • Bind changed programs as packages instead of rebinding a large plan directly from many DBRMs.
  • Review QUALIFIER, OWNER, VALIDATE, isolation, and current data options against the application type.
  • Confirm the run JCL names the intended plan and load library before moving the job to production.

Related DB2 topics

Application binding sits close to several other Db2 topics. Review Db2 Binding and Rebinding for package and plan maintenance, Db2 Packages for package structure, Db2 Objects for database object context, and Db2 SQL Optimization Tips for COBOL Programs for access path review.

FAQ

Is a DBRM the same as a package?

No. The DBRM is produced by the precompile step. A package is created when Db2 binds that DBRM into the catalog and directory.

Can a COBOL Db2 program run without a bind?

A static SQL COBOL program needs a valid package or plan before it can run successfully against Db2. Dynamic SQL follows a different prepare path at run time.

Why does a program fail after a successful compile?

The compile only proves the host language build completed. The run can still fail if the DBRM was not bound, the plan does not include the package, or the load module and package do not match.

For production, treat bind output as part of the build evidence. A clean compile without the matching DBRM, package, plan, and run JCL is not enough.

New In-feed ads