9-1. Trapping an Exception

Problem

A procedure in your application has the potential to cause an exception to be raised. Rather than let the program exit and return control to the host machine, you want to perform some cleanup to ensure data integrity, as well as display an informative error message.

Solution

Write an exception handler for your procedure so that the exception can be caught and you can perform tasks that need to be completed and provide a more descriptive message. The following procedure is used to obtain employee information based upon a primary key value or an e-mail address. Beginning with the EXCEPTION keyword in the following example, an exception-handling block has been added to the end of the procedure in order to handle any exceptions that may occur when no matching record is found.

CREATE OR REPLACE PROCEDURE obtain_emp_detail(emp_info IN VARCHAR2) IS
  emp_qry         VARCHAR2(500);
  emp_first       employees.first_name%TYPE;
  emp_last        employees.last_name%TYPE;
  email              employees.email%TYPE;

  valid_id_count    NUMBER := 0;
  valid_flag        BOOLEAN := TRUE;
  temp_emp_info        VARCHAR2(50);

BEGIN
  emp_qry := 'SELECT FIRST_NAME, LAST_NAME, EMAIL FROM EMPLOYEES ';
  IF emp_info LIKE '%@%' THEN
    temp_emp_info := substr(emp_info,0,instr(emp_info,'@')-1);
    emp_qry := emp_qry || 'WHERE EMAIL = :emp_info';
  ELSE
    SELECT COUNT(*)
    INTO valid_id_count
    FROM employees
    WHERE employee_id = emp_info;

    IF valid_id_count > 0 THEN
        temp_emp_info := emp_info;
        emp_qry := emp_qry || 'WHERE EMPLOYEE_ID = :id';
    ELSE
        valid_flag := FALSE;
    END IF;
  END IF;

  IF valid_flag = TRUE THEN
    EXECUTE IMMEDIATE emp_qry
    INTO emp_first, emp_last, email
    USING temp_emp_info;

    DBMS_OUTPUT.PUT_LINE(emp_first || ' ' || emp_last || ' - ' || email);
  ELSE
    DBMS_OUTPUT.PUT_LINE('THE INFORMATION YOU HAVE USED DOES ' ||
                         'NOT MATCH ANY EMPLOYEE RECORD'),
  END IF;

  EXCEPTION
    WHEN NO_DATA_FOUND THEN
      DBMS_OUTPUT.PUT_LINE('THE INFORMATION YOU HAVE USED DOES ' ||
                         'NOT MATCH ANY EMPLOYEE RECORD'),
    WHEN INVALID_NUMBER THEN
      DBMS_OUTPUT.PUT_LINE('YOU MUST ENTER AN EMAIL ADDRESS INCLUDING ' ||
                         'THE @ OR A POSITIVE INTEGER VALUE FOR THE ' ||
                         'EMPLOYEE ID.'),
END;

Here are the results of calling the procedure with various arguments:

SQL> EXEC OBTAIN_EMP_DETAIL(000);
THE INFORMATION YOU HAVE USED DOES NOT MATCH ANY EMPLOYEE RECORD

PL/SQL procedure successfully completed.

SQL> EXEC OBTAIN_EMP_DETAIL('TEST'),
YOU MUST ENTER AN EMAIL ADDRESS INCLUDING THE @ OR A POSITIVE INTEGER VALUE FOR
THE EMPLOYEE ID.

PL/SQL procedure successfully completed.

SQL> EXEC OBTAIN_EMP_DETAIL(200);
Jennifer Whalen - JWHALEN

PL/SQL procedure successfully completed.

This procedure is essentially the same as the one demonstrated in Recipe 8-1. The difference is that when an exception is raised, the control will go into the exception block. At that time, the code you place within the exception block will determine the next step to take as opposed to simply raising an Oracle error and returning control to the calling procedure, calling function, or host environment.

How It Works

To perform remedial actions when an exception is raised, you should always make sure to code an exception handler if there is any possibility that an exception may be thrown. The sole purpose of an exception handler is to catch exceptions when they are raised and handle the outcome in a controlled fashion. There are two different types of exceptions that can be raised by a PL/SQL application: internally defined and user defined. Oracle Database has a defined set of internal exceptions that can be thrown by a PL/SQL application. Those exceptions are known as internally defined. It is also possible to define your own exceptions, which are known as user defined.

An exception-handling block is structured like a CASE statement in that a series of exceptions is listed followed by a separate set of statements to be executed for each outcome. The standard format for an exception-handling block is as follows:

EXCEPTION
  WHEN name_of_exception THEN
    -- One or more statements

Exception blocks begin with the EXCEPTION keyword, followed by a series of WHEN..THEN clauses that describe different possible exceptions along with the set of statements that should be executed if the exception is caught. The exception name can be one of the Oracle internally defined exceptions, or it can be the name of an exception that has been declared within your code. To learn more about declaring exceptions, please see Recipe 9-3 in this chapter. In the solution to this recipe, the internally defined NO_DATA_FOUND exception is raised if an unknown e-mail address is entered into the procedure because there will be no rows returned from the query. When the exception block encounters the WHEN clause that corresponds with NO_DATA_FOUND, the statements immediately following the THEN keyword are executed. In this case, an error message is printed using the DBMS_OUTPUT package. However, in a real-world application, this is where you will place any cleanup or error handling that should be done to help maintain the integrity of the data accessed by your application.

An exception block can contain any number of WHEN..THEN clauses, and therefore, any number of exceptions can each contain their own set of handler statements. Even if a simple message was to be displayed, as is the case with the solution to this recipe, a different and more descriptive error message can be coded for each different exception that may possibly be raised. This situation is reflected in the second exception handler contained within the solution because it returns a different error message than the first.

As mentioned previously, Oracle contains a number of internally defined exceptions. Table 9-1 provides a list of the internally defined exceptions, along with a description of their usage.

Table 9-1. Oracle Internal Exceptions

Exception Code Description
ACCESS_INTO_NULL -6530 Values are assigned to an uninitialized object.
CASE_NOT_FOUND -6592 No matching choice is available within CASE statement, and no ELSE clause has been defined.
COLLECTION_IS_NULL -6531 Program attempts to apply collection methods other than EXISTS to varray or a nested table that has not yet been initialized.
CURSOR_ALREADY_OPEN -6511 Program attempts to open a cursor that is already open.
DUP_VAL_ON_INDEX -1 Program attempts to store duplicate values in a unique index column.
INVALID_CURSOR -1001 Program attempts to use a cursor operation that is allowed.
INVALID_NUMBER -1722 Conversion of string into number is incorrect because of the string not being a number.
LOGIN_DEINIED -1017 Program attempts to log in to the database using an incorrect user name and/or password.
NO_DATA_FOUND +100 SELECT statement returns no rows.
NOT_LOGGED_ON -1012 Program attempts to issue a database call without being connected to the database.
PROGRAM_ERROR -6501 Internal problem exists.
ROWTYPE_MISMATCH -6504 Cursor variables are incompatible. A host cursor variable must have a compatible return type that matches a PL/SQL cursor variable.
SELF_IS_NULL -30625 Instance of object type is not initialized.
STORAGE_ERROR -6500 PL/SQL ran out of memory or was corrupted.
SUBSCRIPT_BEYOND_COUNT -6533 Program references nested table or varray element using an index number that goes beyond the number of elements within the object.
SYS_INVALID_ROWID -1410 Conversion of character string into ROWID fails because character string does not represent a valid row ID.
TIMEOUT_ON_RESOURCE -51 Oracle Database is waiting for resource, and timeout occurs.
TOO_MANY_ROWS -1422 Attempts to select more than one row using a SELECT INTO statement.
VALUE_ERROR -6502 Program attempts to perform an invalid arithmetic, conversion, or truncation operation.
ZERO_DIVIDE -1476 Program attempts to divide a number by zero.

An exception handler's scope corresponds to its enclosing code block. They have the same scope as a variable would have within a code block. If your code contains a nested code block, an exception handler that is contained within the nested code block can only handle exceptions raised within that code block. The outer code block can contain an exception handler that will handle exceptions for both the outer code block and the nested code block. If an exception is raised within the nested code block and there is no corresponding handler for an exception that has been raised within the nested code block, then the exception is propagated to the outer code block to look for a corresponding handler there. If no handler is found, then runtime will be passed to the procedure or function that called it or the host system, which is what you do not want to have occur. The following code demonstrates an example of using an exception handler within a nested code block:

DECLARE
  CURSOR emp_cur IS
  SELECT *
  FROM EMPLOYEES;

  emp_rec emp_cur%ROWTYPE;
BEGIN
  FOR emp_rec IN emp_cur LOOP
    DBMS_OUTPUT.PUT_LINE(emp_rec.first_name || ' ' ||
         emp_rec.last_name);
    DECLARE
      emp_dept  departments.department_name%TYPE;
    BEGIN
      SELECT department_name
      INTO emp_dept
      FROM departments
      WHERE department_id = emp_rec.department_id;
      DBMS_OUTPUT.PUT_LINE('Department: ' || emp_dept);
    EXCEPTION
      WHEN NO_DATA_FOUND THEN
        DBMS_OUTPUT.PUT_LINE('EXCEPTION IN INNER BLOCK'),
    END;
  END LOOP;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
   DBMS_OUTPUT.PUT_LINE('EXCEPTION IN OUTER BLOCK'),
END;

Multiple exceptions can be listed within the same exception handler if you want to execute the same set of statements when either of them is raised. You can do this within the WHEN clause by including two or more exception names and placing the OR keyword between them. Using this technique, if either of the exceptions that are contained within the clause is raised, then the set of statements that follows will be executed. Let's take a look at an exception handler that contains two exceptions within the same handler:

EXCEPTION
  WHEN NO_DATA_FOUND OR INVALID_EMAIL_ADDRESS THEN
    --  statements to execute
  WHEN OTHERS THEN
    --  statements to execute
END;

Image Note You cannot place the AND keyword in between exceptions because no two exceptions can be raised at the same time.

It is easy to include basic exception handling in your application. Code an exception-handling block at the end of each code block that may raise an exception. It is pertinent that you test your application under various conditions to try to predict which possible exceptions may be raised; each of those possibilities should be accounted for within the exception-handling block of your code.

..................Content has been hidden....................

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