Showing posts with label SQLSTATE. Show all posts
Showing posts with label SQLSTATE. 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 SQLCODE and SQLSTATE: SQLCA Return Codes for COBOL Programs


Db2 SQLCODE SQLSTATE and SQLCA return code checks for COBOL programs
Check SQLCODE after each Db2 statement.

SQLCODE +100 is not the same as a failed SELECT. It means no row was found, and a COBOL program often has to handle that path cleanly. A negative SQLCODE is different: it means the SQL statement failed and the program should not continue as if the data is valid.

Db2 returns SQL status information through the SQLCA, including SQLCODE, SQLSTATE, warning flags, row counts, and diagnostic text. Good programs check those fields immediately after each SQL statement that matters.

SQLCODE quick meaning

SQLCODE valueMeaningProgram action
0Statement completed successfullyContinue normal processing.
> 0Statement completed with a warning or special conditionHandle the specific code. +100 is common for no row found.
< 0Statement failedLog SQLCA details and take the error path.

SQLSTATE quick meaning

SQLSTATE is a five-character return code. The first two characters identify a class, and the last three identify a subclass. It is useful when code needs a standard status check across database products, while SQLCODE often gives Db2-specific detail.

SQLSTATE classGeneral meaning
00Successful completion
01Warning
02No data
Other classesError or condition that must be checked against Db2 message documentation

Important SQLCA fields

FieldUse
SQLCODEPrimary numeric return code checked by many COBOL programs.
SQLSTATEFive-character status code useful for standard classes.
SQLERRMCMessage tokens that can identify object names or values related to an error.
SQLERRDDiagnostic integers; one common use is row count after some SQL operations.
SQLWARNWarning indicators, such as truncation or null-related conditions.

COBOL SQLCODE handling pattern

EXEC SQL
    SELECT CUSTOMER_NAME
      INTO :WS-CUSTOMER-NAME
      FROM CUSTOMER
     WHERE CUSTOMER_ID = :WS-CUSTOMER-ID
END-EXEC.

EVALUATE SQLCODE
    WHEN 0
        CONTINUE
    WHEN +100
        MOVE 'N' TO WS-CUSTOMER-FOUND
    WHEN OTHER
        DISPLAY 'DB2 ERROR SQLCODE=' SQLCODE
        DISPLAY 'SQLSTATE=' SQLSTATE
        DISPLAY 'SQLERRMC=' SQLERRMC
        PERFORM ABEND-ROUTINE
END-EVALUATE.

Do not bury this check far away from the SQL statement. When a production issue happens, the fastest clue is often the exact statement, SQLCODE, SQLSTATE, and message tokens written together.

Common mistakes

  • Treating +100 as a fatal error when it is a normal no-row path for the program.
  • Ignoring positive warning codes because only negative codes stop the job.
  • Checking SQLCODE after several statements instead of immediately after the statement that set it.
  • Logging only SQLCODE and losing SQLSTATE or SQLERRMC details.
  • Continuing after a negative SQLCODE and writing output records from invalid host variable values.

Related DB2 topics

SQLCODE handling connects to Db2 SQL Execution Validation, Db2 Application Environment, Db2 Data Types, Db2 Triggers, and Db2 DSN Command Reference.

FAQ

Is SQLCODE +100 an error?

No. +100 means no row was found or no more rows are available. It is often a normal branch in SELECT and cursor logic.

Should COBOL check SQLCODE or SQLSTATE?

Many Db2 COBOL programs check SQLCODE first because it gives Db2-specific detail. SQLSTATE is useful for standard status classes and cross-product logic.

Where is SQLCODE stored?

When the SQLCA is included, Db2 places the return code in the SQLCA field SQLCODE. Some precompiler options can also use standalone SQLCODE and SQLSTATE host variables.

The rule is simple enough to save a batch night: check the SQL return status before trusting the host variables.

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.

New In-feed ads