Error Handling

SQL*Plus doesn’t offer too much in the way of error handling. By default, SQL*Plus simply ignores errors and goes on to execute either the next command you type in, or the next command in the script you are running. For interactive use, this is good enough. If an error occurs, you will see the message and take appropriate action. However, the situation is different when you are running a script. Depending on what the script is doing, you may not want SQL*Plus to blindly proceed to the next command when an error occurs. Consider the following script, which creates a new table, copies data to it, then deletes the original table:

CREATE TABLE employees AS 
   SELECT * FROM employee;
DROP TABLE employee;

If the CREATE TABLE command failed, you certainly wouldn’t want the script to continue, because you would lose all your data! To help with this type of situation, SQL*Plus provides the WHENEVER command.

The WHENEVER Command

With the WHENEVER command, you can give SQL*Plus instructions on what to do when an error occurs. Your choices are a bit limited: you can either continue when an error occurs, or you can exit SQL*Plus entirely, possibly returning an error code. Returning an error code is useful if you are calling SQL*Plus from a Unix shell script or a DOS batch file.

There are two types of errors you can handle with WHENEVER. Each has its own variation of the command. WHENEVER SQLERROR is used to handle SQL errors and errors raised from PL/SQL blocks. WHENEVER OSERROR is used to handle operating system errors, such as those you might get when you run out of disk space while spooling a large data extract.

There is one type of error you cannot detect: an error involving a SQL*Plus command. An example would be if you were to misspell a command, such as COLUMN. If your script contained a command like the following:

COLUM employee_name HEADEEN 'Employee Name' FLOORMAT A40

SQL*Plus would generate an error and continue on with the script as if nothing had happened. This isn’t as much of a problem as you might first think. You should test your scripts to be sure your SQL*Plus commands are correct. This is easy to do, and the consequences of a failed SQL*Plus command are usually no worse than some messy formatting of the output. SQL commands, on the other hand, can fail for a variety of reasons that don’t involve simple misspellings. A simple database change can easily cause a SQL command that worked one day to fail the next. Similarly, with operating-system errors, you don’t know in advance when you will run out of disk space.

WHENEVER SQLERROR

The WHENEVER SQLERROR command tells SQL*Plus what to do when a SQL statement or PL/SQL block fails to execute properly. The simplest way to use it is to issue the following command, which tells SQL*Plus to simply abort the script when an error occurs.

WHENEVER SQLERROR EXIT

Now, when a SQL error occurs, SQL*Plus will exit, as the following example shows:

SQL> WHENEVER SQLERROR EXIT 
SQL> SELECT kristi FROM dual; 
SELECT kristi FROM dual 
       * 
ERROR at line 1: 
ORA-00904: invalid column name 


Disconnected from Oracle7 Server Release 7.3.3.0.0 - Production Release 
PL/SQL Release 2.3.3.0.0 - Production 
$

When SQL*Plus exits like this, the default behavior is to commit any transaction that might be open. For a simple SELECT statement like that shown in the previous example, this is not a problem. When you are changing records, it might be. If your script executes several SQL statements that change data, you may not want to commit unless all the changes can be made. In this situation, use the ROLLBACK option to tell SQL*Plus to roll back when an error occurs, like this:

WHENEVER SQLERROR EXIT ROLLBACK

If you’re calling SQL*Plus from a Unix shell script, DOS batch file, VMS command file, or the equivalent, you can have it pass back a return code so your shell script can tell whether your script executed successfully or not. The following command tells SQL*Plus to pass back a standard failure code when an error occurs:

WHENEVER SQLERROR EXIT FAILURE ROLLBACK

The precise code that gets passed back will vary from one operating system to the next. If a simple success/fail indication is not enough, you can have SQL*Plus pass back the specific Oracle error code or any other value you want. The following example shows how to pass back the Oracle error code when a SQL error occurs:

WHENEVER SQLERROR EXIT SQL.SQLCODE

You could also choose to return any arbitrary number, the value of a numeric bind variable, or the value of a substitution variable.

Note

The default behavior of WHENEVER SQLERROR EXIT is to COMMIT any pending transaction. You may want to use the ROLLBACK option to change that behavior.

Using WHENEVER SQLERROR EXIT with the GUI version of SQL*Plus can be really annoying at times. Any error results in SQL*Plus terminating, causing the GUI window to close. Usually this happens before you even realize that an error occurred, making you miss any error message that may have been displayed.

Don’t use THEN. It’s WHENEVER SQLERROR EXIT, not WHENEVER SQLERROR THEN EXIT. I tend to get mixed up on this point often.

PL/SQL errors and WHENEVER

The WHENEVER SQLERROR EXIT command will catch any errors in a PL/SQL block, but only if those errors are raised back to the SQL*Plus level. PL/SQL has its own error-handling mechanism, and using it can prevent SQL*Plus from knowing that an error ever occurred.

The following PL/SQL block does not contain an error handler, so any SQL errors will be implicitly raised to the calling routine, which in this case is SQL*Plus:

BEGIN
  UPDATE employee 
     SET employee_billing_rate = employee_billing_rate * 1.10;
  COMMIT;
END;
/

However, you could rewrite the block so that it includes an error handler. In that case, the PL/SQL error handler would get the error, and SQL*Plus would not know about it. Here is the same block, but with an error handler:

DECLARE
  success_flag BOOLEAN;
BEGIN
  BEGIN
    UPDATE employee 
       SET employee_billing_rate = employee_billing_rate * 1.10;
    success_flag := TRUE;
  EXCEPTION
    WHEN OTHERS THEN
      success_flag := false;
  END;
  
  IF success_flag THEN
    COMMIT;
  ELSE
    ROLLBACK;
    DBMS_OUTPUT.PUT_LINE('The UPDATE failed.'),
  END IF;
END;
/

In this example, the UPDATE statement is contained in its own PL/SQL block, and any error related to that statement will be trapped by the exception handler for that block. Even if an error occurs, as far as SQL*Plus is concerned, this block will have executed successfully. If you want to handle an error within PL/SQL, but still abort the SQL*Plus script, you can use the RAISE_APPLICATION_ERROR procedure. This procedure is part of a PL/SQL package named DBMS_STANDARD, and should exist in all installations. You call it like this:

RAISE_APPLICATION_ERROR (error_code, error_message);

where:

error_code

Is a negative number. The range from -20000 to -20999 is reserved for user-defined errors.

error_message

Is a text message of up to 2048 characters.

When you call RAISE_APPLICATION_ERROR from a PL/SQL block, control immediately returns to the calling block. You must call the procedure from the outermost PL/SQL block in order to return the error to SQL*Plus. When that happens, SQL*Plus will print the error message and then take whatever action you specified in the most recent WHENEVER SQLERROR command. The next PL/SQL block is the same as the previous one, except for the addition of the RAISE_APPLICATION_ERROR command, which is used to notify SQL*Plus of an error:

DECLARE
  success_flag BOOLEAN;
BEGIN
  BEGIN
    UPDATE employee 
       SET employee_billing_rate = employee_billing_rate * 1.10;
    success_flag := TRUE;
  EXCEPTION
    WHEN OTHERS THEN
      success_flag := false;
  END;
  
  IF success_flag THEN
    COMMIT;
  ELSE
    ROLLBACK;
    DBMS_OUTPUT.PUT_LINE('The UPDATE failed.'),
    RAISE_APPLICATION_ERROR (-20000,
   'The UPDATE of employee billing rates failed.'),
  END IF;
END;
/

Now if an error occurs, SQL*Plus will know about it and can abort the script.

WHENEVER OSERROR

The WHENEVER OSERROR command tells SQL*Plus what to do when an operating system error occurs. Running out of disk space would be a likely operating-system error, one that you might encounter when spooling large amounts of output from a SQL query.

WHENEVER OSERROR works pretty much the same as the WHENEVER SQLERROR command. The simple version, which just causes SQL*Plus to exit when an error occurs, looks like this:

WHENEVER OSERROR EXIT

By default, any changes are committed when SQL*Plus exits. You can change that behavior using the ROLLBACK keyword as follows:

WHENEVER OSERROR EXIT ROLLBACK

As with WHENEVER SQLERROR, you can pass a return code back to a shell script in order to allow it to detect the error. For example:

WHENEVER OSERROR EXIT FAILURE

Unlike the SQLERROR version of the command, there is no equivalent to SQL.SQLCODE for operating-system errors. The other options still apply, however, and you can return an arbitrary value, the value from a bind variable, or the value of a substitution variable.

Tip

Be aware that the range of return codes you can pass back from SQL*Plus varies from one operating system to the next. Under Unix, return codes are limited to one byte, giving you a range of to 255 to work with. VMS, on the other hand, allows much larger values to be returned. Keep this in mind if you are writing a script that needs to be portable across different operating systems.

The following example archives any project_hours data over one year old to a file, then deletes the data that was archived. In order to be sure no data is deleted without first being successfully archived, the WHENEVER commands are used to abort the script in the event of an error.

WHENEVER SQLERROR EXIT FAILURE
WHENEVER OSERROR EXIT FAILURE

--Turn off page headings, column titles, etc.
SET PAGESIZE 0
SET HEADING OFF
SET COLSEP ","

--Archive old records
SPOOL project_hours_archive.txt
SELECT * 
  FROM project_hours
 WHERE TRUNC(time_log_date) <= ADD_MONTHS(TRUNC(SYSDATE),-12);
SPOOL OFF

--Delete the records just archived
DELETE 
  FROM project_hours
 WHERE TRUNC(time_log_date) <= ADD_MONTHS(TRUNC(SYSDATE),-12);
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset