After every executable SQL statement, DB2 for z/OS writes a status report into a small block of storage called the SQL Communications Area (SQLCA). COBOL programs that ignore that report are guessing. This page shows how to include the SQLCA, how to read SQLCODE, SQLSTATE, SQLERRMC, SQLERRD, and SQLWARN, and how to structure COBOL SQL error handling so a +100 is not treated like a -803.
The SQLCA is a 136-byte structure. Db2 overlays it after each executable SQL statement (SELECT INTO, FETCH, INSERT, OPEN, COMMIT, and so on). Declarative statements such as DECLARE CURSOR do not execute at run time and do not set a new SQLCODE for “running the query.” OPEN does.
You almost never hand-code the layout. You ask the SQL processor to generate it:
1234WORKING-STORAGE SECTION. EXEC SQL INCLUDE SQLCA END-EXEC.
INCLUDE SQLCA is an SQL statement, not a COBOL COPY of a random copybook. The precompiler or the Enterprise COBOL SQL coprocessor expands it into the official fields: SQLCAID, SQLCABC, SQLCODE, SQLERRM (SQLERRML + SQLERRMC), SQLERRP, SQLERRD (six integers), SQLWARN0–SQLWARN7, SQLWARN8–SQLWARNA, and SQLSTATE.
SQLCAID is the eye-catcher SQLCA. If the sixth byte is L, a line number related to a dynamic statement or a native SQL procedure may be present in SQLERRD. SQLCABC is the length, 136. SQLERRP starts with DSN on Db2 for z/OS and can name the detecting module when something fails.
EXEC SQL INCLUDE has three everyday uses in COBOL:
INCLUDE member-name is processed before COBOL COPY in the SQL preparation path. Put DCLGEN members in WORKING-STORAGE with INCLUDE so host variable names match the table the precompiler thinks you are using. Mixing a stale COPYBOOK that drifted from the catalog is a classic -310 / truncation / wrong-column bug.
123EXEC SQL INCLUDE DCLGEN-EMP END-EXEC.
SQLCODE is a signed fullword (PIC S9(9) COMP-5 in the generated COBOL SQLCA). Db2 sets it after every executable SQL statement, whether you provided an SQLCA or stand-alone SQLCODE/SQLSTATE host variables.
| SQLCODE | Meaning |
|---|---|
| 0 | Success. Inspect SQLWARN0 for warnings. |
| +100 | NOT FOUND. SQLSTATE 02000. |
| Other positive | Success with a warning or extra information (see the specific SQLCODE). |
| Negative | Error. Statement did not complete successfully. |
Codes you will memorize in the first week of COBOL + Db2 work:
Never treat “not 0” as a single bucket. +100 in a FETCH loop is the normal end. -803 on INSERT is a business duplicate. -911 means restart the unit of work. Dumping all three to the same ABEND paragraph hides the recovery path.
SQLSTATE is PIC X(5). It is set on every executable statement along with SQLCODE. The coding scheme is shared across IBM relational products, so classes of problems are stable even when the integer SQLCODE is Db2-specific.
You can test classes: IF SQLSTATE(1:2) = '02' is another way to spell not found. Portable middleware prefers SQLSTATE; traditional COBOL batch still branches on SQLCODE. Either is correct if you are consistent and you still look at warnings.
When a SQLCODE message contains substitution variables (table name, constraint name, reason code), Db2 puts those tokens in SQLERRMC. Tokens are separated by X'FF'. SQLERRML is the length, 0 through 70. Zero means SQLERRMC is not pertinent.
Seventy bytes is a hard cap. Long schema-qualified names get truncated. IBM’s Codes book tells you to use GET DIAGNOSTICS with DB2_ORDINAL_TOKEN_n when you need the full token. Calling the IBM sample routine DSNTIAR formats the SQLCA into printable lines for SYSOUT; it still cannot invent bytes that were truncated in SQLERRMC.
12345601 ERR-MSG. 05 ERR-LEN PIC S9(4) COMP VALUE +960. 05 ERR-TEXT PIC X(120) OCCURS 8 TIMES. 01 ERR-TEXT-LEN PIC S9(9) COMP VALUE +120. CALL 'DSNTIAR' USING SQLCA ERR-MSG ERR-TEXT-LEN.
SQLERRD is six integers. COBOL indexes them 1 through 6. C programmers see sqlerrd[0] through sqlerrd[5]. Do not mix the indexing when you read a dump from another language.
| Field | Meaning |
|---|---|
| SQLERRD(1) | Sensitive static cursor: rows in the result when positioned after the last row (+100). Also SQL procedure return status on successful return. Can hold an internal error code. |
| SQLERRD(2) | Same row-count role as (1) for some sensitive static cursor cases. Can hold an internal error code. |
| SQLERRD(3) | Rows that qualified for INSERT/UPDATE/DELETE/MERGE (not trigger/RI extras). Rowset FETCH row count. -1 for some mass DELETE/TRUNCATE. Reason code for -911/-913. Line number when SQLCAID byte 6 is L. |
| SQLERRD(4) | Timerons: a relative cost estimate after PREPARE of a dynamic statement. |
| SQLERRD(5) | Column/position of a syntax error for PREPARE or EXECUTE IMMEDIATE. |
| SQLERRD(6) | Internal error code. |
The field beginners need first is SQLERRD(3): how many rows qualified for a data-change statement, or how many rows a rowset FETCH returned. After -911 or -913 it can hold a timeout/deadlock reason code. After PREPARE, look at SQLERRD(4) (timerons) only as a relative cost hint — it is not elapsed time.
A zero SQLCODE is not “everything is perfect” until you glance at SQLWARN0. If it is W, walk the other flags. Truncation (SQLWARN1) is the one that silently chops last names and then looks like bad data downstream.
| Flag | Meaning |
|---|---|
| SQLWARN0 | Blank if no other warning; W if any other SQLWARN flag is W or Z. |
| SQLWARN1 | W if a string was truncated into a host variable. After OPEN: N non-scrollable, S scrollable. |
| SQLWARN2 | W if nulls were eliminated from a column function argument. |
| SQLWARN3 | W if more result columns than host variables. Z if ASSOCIATE LOCATORS provided too few locators. |
| SQLWARN4 | W if a prepared UPDATE/DELETE has no WHERE. After OPEN of a scroll cursor: D/I/S sensitivity. |
| SQLWARN5 | W if the statement is not valid SQL for this server. After OPEN: 1/2/3 cursor capability. |
| SQLWARN6 | W if date/timestamp plus a month/year duration adjusted the day to the last valid day of the month. |
| SQLWARN7 | W if nonzero fractional digits were dropped in a decimal multiply or divide. |
| SQLWARN8 | W if a character that could not be converted was replaced with a substitute. |
| SQLWARN9 | W if arithmetic exceptions were ignored during COUNT/COUNT_BIG. Z if a procedure returned multiple result sets. |
| SQLWARNA | W if a character conversion error invalidated a character field in the SQLCA or SQLDA names/labels. |
Two styles exist. Prefer explicit tests after each statement for new code.
1234567891011121314151617EXEC SQL SELECT LASTNAME INTO :HV-LASTNAME FROM DSN8C10.EMP WHERE EMPNO = :HV-EMPNO END-EXEC. EVALUATE SQLCODE WHEN 0 IF SQLWARN0 = 'W' PERFORM 8000-SQL-WARNING END-IF PERFORM 2000-USE-ROW WHEN +100 PERFORM 2100-NOT-FOUND WHEN OTHER PERFORM 9000-SQL-ERROR END-EVALUATE.
WHENEVER is a directive to the precompiler: from this point in the source, generated code after SQL statements will CONTINUE or GO TO a label when the condition hits.
123456789EXEC SQL WHENEVER SQLERROR GO TO 9000-SQL-ERROR END-EXEC. EXEC SQL WHENEVER NOT FOUND GO TO 2100-NOT-FOUND END-EXEC. EXEC SQL WHENEVER SQLWARNING CONTINUE END-EXEC.
WHENEVER GOTO fights structured COBOL: a FETCH loop that GO TOs out on +100 is hard to read, and a SQLERROR paragraph that issues more SQL can recurse if you forget WHENEVER SQLERROR CONTINUE at the top of the handler. If you use WHENEVER, reset it in error routines and never GO TO into the middle of a PERFORM.
With STDSQL(YES), declare SQLCODE and SQLSTATE as host variables and skip INCLUDE SQLCA. You still must check them. GET DIAGNOSTICS is the way to retrieve ROW_COUNT, MESSAGE_TEXT, and full tokens when the SQLCA is too small.
Imagine you ask a librarian (Db2) to fetch a book. When they come back, they hand you a sticky note (the SQLCA). The big number on the note (SQLCODE) says “got it,” “no such book,” or “the shelf fell over.” A five-letter code (SQLSTATE) is the same idea in a language other libraries also speak. Extra scribbles (SQLERRMC) name which book caused trouble. Little letter flags (SQLWARN) say things like “I found the book but I had to tear off the last page to fit your tiny backpack” — that is truncation, and you should notice it even when the big number is zero.
1. What does SQLCODE 0 mean after an EXEC SQL statement?
2. SQLCODE +100 typically means:
3. How do you include the SQLCA in a COBOL program?
4. Which SQLCA field holds message tokens substituted into the SQLCODE text?
5. After a multi-row FETCH, where do you find how many rows were returned?