Was this helpful?
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, 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 conditions are dbevent, sqlwarning, sqlerror, sqlmessage, and not found.
action
Specifies the action to be taken. Valid actions are continue, stop, goto a label and call a Pascal procedure.
For a detailed description of this statement, see the SQL Reference Guide.
In Embedded SQL/Pascal, all labels and procedure names must be legal Pascal label identifiers, beginning with a digit, an alphabetic character, or an underscore. If the label is an Embedded SQL reserved word, it should be specified in quotes. Note that the label 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 can generate the Pascal statement:
if (condition) then goto label;
after an Embedded SQL statement. If the scope of the label is invalid, the Pascal compiler will generate an error.
The same scope rules apply to procedure names used with the call action. Note that the reserved procedure 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 procedure is called, and after its execution, control returns to the statement following the statement that caused the procedure to be called. Consequently, after handling the whenever condition in the called procedure, you may want to take some action, instead of merely returning from the Pascal procedure. Returning from the Pascal procedure will cause the program to continue execution with the statement following the Embedded SQL statement that generated the error.
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.
program Db_Test( input, output );
label
        Close_Csr,
        Exit_Label;

exec sql begin declare section;
var
     eno:         [word] -32768 .. 32767;
     ename:         varying[20] of Char;
     age:         [byte] -128 .. 127;
exec sql end declare section;
     exec sql include sqlca;

     exec sql declare empcsr cursor for
        select eno, ename, age
        from employee;

    {
    | Clean_Up: Error handling procedure (print error and disconnect).
    }
    procedure Clean_Up;
    exec sql begin declare section;
    var
             errmsg: varying[200] of Char;
    exec sql end declare section;
    begin         {Clean_Up}
         exec sql whenever sqlerror stop;
         inquire_sql (:errmsg = errortext) ;
         writeln( 'Aborting because of error: ' );
         writeln( errmsg );
         exec sql disconnect;
         goto Exit_Label;
    end; {Clean_Up}

begin             {Db_Test}
    {
    | 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;

    writeln( 'Some values from the "employee" table.' );

    {When no more rows are fetched, close the cursor.}
    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 (sqlca.sqlcode = 0) do
    {Loop is broken by NOT FOUND}
    begin
        exec sql fetch empcsr
             into :eno, :ename, :age;

        {
        | This writeln statement does not execute
        |    after the previous FETCH returns the
        |    NOT FOUND condition.
        }
        writeln( eno, ', ', ename, ', ', age );
    end; {while}
    {
    | From this point in the file onwards, ignore
    |    all errors. Also turn off the NOT FOUND
    | condition, for consistency.
    }
    exec sql whenever sqlerror continue;
    exec sql whenever not found continue;
Close_Csr:
    exec sql close empcsr;
    exec sql disconnect;

Exit_Label:;
end; {Db_Test}
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 loops are both block-structured statements. These statements can be terminated only 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 Pascal goto statement that causes control to leave or enter the middle of 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 precaution 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 in the SQLCA record 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 Pascal statements.
program Db_Test( input, output );
label
    Exit_Label;
exec sql begin declare section;
const
    not_found = 100;
var
    eno:     [word] -32768 .. 32767;
    ename:     varying[20] of Char;
    age:     [byte] -128 .. 127;
exec sql end declare section;
    exec sql include sqlca;

    exec sql declare empcsr cursor for
         select eno, ename, age
         from employee;

    {
    | Clean_Up: Error handling procedure (print error and disconnect).
    }
    procedure Clean_Up( str : varying[ub] of Char );
    exec sql begin declare section;
    var
        errmsg: varying[200] of Char;
        err_stmt: varying[40] of Char;
    exec sql end declare section;
    begin {Clean_Up}
        err_stmt := str;
        exec sql inquire_sql (:errmsg = ERRORTEXT);
        writeln('Aborting because of error in ', err_stmt, ': ');
        writeln( errmsg );
        exec sql disconnect;

        goto Exit_Label;
    end; {Clean_Up}

begin                 {Db_Test}
        {Exit if the database cannot be opened.}
        exec sql connect personnel;
        if (sqlca.sqlcode < 0) then
        begin
            writeln( 'Cannot access database.' );
            goto Exit_Label;
        end;

        {Errors if cannot open cursor.}
        exec sql open empcsr;
        if (sqlca.sqlcode < 0) then
                Clean_Up( 'OPEN "empcsr"' ); {No return}
        writeln( '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.
        }
            while (sqlca.sqlcode = 0) do    {
                            | Loop is broken by NOT FOUND
                            }
            begin
                exec sql fetch empcsr
                    into :eno, :ename, :age;

            {Do not print the last values twice.}
            if (sqlca.sqlcode < 0) then
                    Clean_Up( 'FETCH "empcsr"' )
            else if (sqlca.sqlcode <> NOT_FOUND) then
                writeln( eno, ', ', ename, ', ', age );
        end; {while}

{
| From this point in the file onwards, ignore all errors.
}
    exec sql close empcsr;
    exec sql disconnect;

Exit_Label:;
end; {Db_Test}
How to Determine the Number of Affected Rows
The third element of the SQLCA array sqlerrd indicates how many rows were affected by the last row-affecting statement. The following program fragment, which deletes all employees whose employee numbers are greater than a given number, demonstrates how to use sqlerrd:
procedure Delete_Rows( lower_bound: Integer );
exec sql begin declare section;
var
     lower_bound_num: Integer;
exec sql end declare section;
begin
    lower_bound_num := lower_bound;
    exec sql delete from employee
         where eno > :lower_bound_num;

    {Print the number of employees deleted.}
    writeln( sqlca.sqlerrd[3], ' (rows) were deleted.' );
end; {Delete_Rows}
Last modified date: 04/03/2024