Error Handling Using the SQLCA
Error handling with the SQLCA can be done implicitly by using whenever statements or explicitly by checking the contents of the SQLCA fields sqlcode, sqlerrd(2), and sqlwarn0.
Error Handling with the Whenever Statement
The syntax of the whenever statement is as follows:
exec sql whenever condition action
condition
Specifies the error condition. Valid error condidtions are dbevent, sqlwarning, sqlerror, sqlmessage, and not found.
action
Specifies the action to be taken. Valid actions are continue, stop, goto a label or a line number, and call a BASIC subroutine.
For a detailed description of this statement, see the SQL Reference Guide.
The subroutine names qualifying the call action must be legal BASIC identifiers beginning with an alphabetic character or an underscore. If the subroutine name is an Embedded SQL reserved word, specify it in quotes. Note that the label or line number targeted by the goto action must be in the scope of all subsequent Embedded SQL statements until another whenever statement is encountered for the same action. This is necessary because the preprocessor may generate the BASIC statement:
if (condition) then
goto label
end if
after an Embedded SQL statement. If the label is outside the scope of the current Embedded SQL statement, the BASIC compiler will generate an error.
The same scope rules apply to subroutine names used with the call action. However, the reserved subroutine name sqlprint, which prints errors or database procedure messages and then continues, is always in the scope of the program.
When a whenever statement specifies a call as the action, the target subroutine is called and, after its execution, control returns to the statement following the statement that caused the subroutine to be called. Consequently, after handling the whenever condition in the called subroutine, you may want to take some action, instead of merely returning from the BASIC subroutine.
The following example demonstrates use of the whenever statements in the context of printing some values from the Employee table. The comments do not relate to the program but to the use of error handling.
10 ! Main error handling program
exec sql include sqlca
exec sql begin declare section
declare integer eno
declare string ename
declare byte eage
exec sql end declare section
exec sql declare empcsr cursor for &
select idno, name, age &
from employee
! An error when opening the personnel database will
! cause the error to be printed and the program to abort
exec sql whenever sqlerror stop
exec sql connect personnel
! Errors from here on will cause the program to clean up
exec sql whenever sqlerror call clean_up
exec sql open empcsr
print 'Some values from the "employee" table'
! When no more rows are fetched, close the csr
exec sql whenever not found goto Close_Csr
! The last executable Embedded SQL statement was an
! OPEN, so we know that the value of "sqlcode" cannot
! be SQLERROR or NOT FOUND.
while (sqlcode = 0)
exec sql fetch empcsr &
into :eno, :ename, :eage
! This "print" does not execute after the previous
! FETCH returns the NOT FOUND condition.
print eno, ename, eage
next
! From this point in the file onwards, ignore all
! errors. Also turn off the NOT FOUND condition,
! for consistency.
Close_Csr: EXEC SQL CLOSE empcsr
exec sql disconnect
end ! Db_Test
! Clean_Up: Error handling subroutine (print error and disconnect).
20 sub Clean_Up
exec sql include sqlca
exec sql begin declare section
declare string errmsg
exec sql end declare section
exec sql inquire_sql(:errmsg = errortext)
print 'aborting because of error'
print errmsg
exec sql disconnect
! Do not return to Db_Test
stop
end sub ! Clean_Up
The Whenever Goto Action in Embedded SQL Blocks
An Embedded SQL block-structured statement is a statement delimited by the begin and end clauses. For example, the select loop and the unloadtable loop are both block-structured statements. These statements can only be terminated by the methods specified for the particular statement in the SQL Reference Guide. For example, the select loop is terminated either when all the rows in the database result table have been processed or by an endselect statement, and the unloadtable loop is terminated either when all the rows in the forms table field have been processed or by an endloop statement.
Therefore, if you use a whenever statement with the goto action in an SQL block, you must avoid going to a label outside the block. Such a goto would cause the block to be terminated without issuing the runtime calls necessary to clean up the information that controls the loop. (For the same reason, you must not issue a BASIC exit or goto statement that causes control to leave or enter an SQL block.) The target label of the whenever goto statement should be a label in the block. If, however, it is a label for a block of code that cleanly exits the program, the above precautions need not be taken.
The above information does not apply to error handling for database statements issued outside an SQL block nor to explicit hard-coded error handling. For an example of hard-coded error handling, see
The Table Editor Table Field Application in this chapter.
Explicit Error Handling
The program can also handle errors by inspecting values of the SQLCA at various points. For further details, see the SQL Reference Guide.
The following example is functionally the same as the previous example, except that the error handling is hard-coded in BASIC statements.
10 ! Main error handling program
exec sql include sqlca
exec sql begin declare section
declare integer eno
declare string ename
declare byte eage
exec sql end declare section
exec sql declare empcsr cursor for &
select idno, name, age from employee
! Exit if database cannot be opened
exec sql connect personnel
if (sqlcode < 0) then
print 'Cannot access database'
stop
end if
! Error if cannot open cursor
exec sql open empcsr
if (sqlcode < 0) then
call Clean_Up('OPEN "empcsr"')
end if
print 'Some values from the "employee" table'
! The last executable Embedded SQL statement was an OPEN, so we know
! that the value of "sqlcode" cannot be SQLERROR or NOT FOUND
! The following loop is broken by NOT FOUND condition 100) or an error
while (sqlcode = 0)
exec sql fetch empcsr &
into :eno, :ename, :eage
if (sqlcode < 0) then
call Clean_Up('FETCH "empcsr"')
! Do not print the last values twice
else
if (sqlcode <> 100) then
print eno, ename, eage
end if
end if
next
exec sql close empcsr
exec sql disconnect
end
! Clean_Up: Error handling subroutine (print error and disconnect).
20 sub Clean_Up(string reason)
exec sql include sqlca
exec sql begin declare section
declare string errmsg
exec sql end declare section
print 'aborting because of error in', reason
exec sql inquire_sql (:errmsg = errortext)
print errmsg
exec sql disconnect
! Do not return to main program
stop
end sub ! clean_up
How to Determine the Number of Affected Rows
The SQLCA variable sqlerrd(2) indicates how many rows were affected by the last row-affecting statement. Note that this variable is referenced by sqlerrd(2) rather than sqlerrd(3) as in other languages, because BASIC allocates sqlerrd elements 0 through 5.
The following program fragment, which deletes all employees whose employee numbers are greater than a given number, demonstrates how to use sqlerrd:
sub delete_rows(integer lower_bound_num)
exec sql include sqlca
exec sql begin declare section
declare integer low_eno
exec sql end declare section
! Use Embedded SQL variable in DELETE statement
low_eno = lower_bound_num
exec sql delete from employee &
where eno :low_eno
! Print the number of employees deleted
print sqlerrd(2), 'row(s) were deleted.'
end sub ! Delete_Rows