PL/SQL by Example / Exceptions

As already indicated in the introduction, the part of the block introduced by the keyword EXCEPTION is used for catching program runtime errors. We distinguish between system errors, which are raised automatically when an error occurs, and user-defined errors, which must be raised explicitly using the RAISE command. The following examples describe several of the most common system errors.

System

VALUE_ERROR

DECLARE
  l_Number NUMBER;
BEGIN
  l_Number:='Francis';
EXCEPTION
  WHEN VALUE_ERROR THEN
    dbms_output.put_line('Implicit conversion failed!');
END;

ZERO_DIVIDE

DECLARE
  l_Number NUMBER;
BEGIN
  l_Number:=20/0;
EXCEPTION
  WHEN ZERO_DIVIDE THEN
    dbms_output.put_line('Division by zero!');
END;

NO_DATA_FOUND

DECLARE
  l_Number NUMBER;
BEGIN
  SELECT employee_id
    INTO l_Number
    FROM employees
    WHERE (last_name='Sauce')
  ;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    dbms_output.put_line('Employee Sauce does not exist!');
END;

TOO_MANY_ROWS

DECLARE
  l_Number NUMBER;
BEGIN
  SELECT employee_id
    INTO l_Number
    FROM employees
    WHERE (salary>3000)
  ;
EXCEPTION
  WHEN TOO_MANY_ROWS THEN
    dbms_output.put_line('More than one row!');
END;

COLLECTION_IS_NULL

CREATE OR REPLACE TYPE Varchar_TT IS TABLE OF VARCHAR2(4000);
/
DECLARE
  lt_Array Varchar_TT;
BEGIN
  --lt_Array:=Varchar_TT();
  --lt_Array.EXTEND(1);
  lt_Array(1):='test';
EXCEPTION
  WHEN COLLECTION_IS_NULL THEN
    dbms_output.put_line('Uninitialized object!');
END;

SUBSCRIPT_BEYOND_COUNT

DECLARE
  lt_Array Varchar_TT;
  l_Cnt NUMBER;
BEGIN
  lt_Array:=Varchar_TT();
  lt_Array.EXTEND(1);
  lt_Array(2):='test';
EXCEPTION
  WHEN SUBSCRIPT_BEYOND_COUNT THEN
    dbms_output.put_line('Array index out of bounds!');
END;

OTHERS

It is possible to catch multiple types of errors in one block, however, if an error does not have its own handler, the program will terminate with the general exception OTHERS. The error message is returned by the SQLERRM function.

DECLARE
  lt_Array Varchar_TT;
BEGIN
  lt_Array:=Varchar_TT();
  lt_Array.EXTEND(1);
  lt_Array(2):='test';
EXCEPTION
  WHEN COLLECTION_IS_NULL THEN
    dbms_output.put_line('Uninitialized object!');
  WHEN OTHERS THEN
    dbms_output.put_line(SQLERRM); -- ORA-06533: Subscript beyond count
END;

In situations where we know in advance that an operation will be potentially dangerous, it is good practice to prevent errors rather than just catch them. In very large systems, this simplifies subsequent problem finding.

DECLARE
  l_DangerousNumber NUMBER:=0;
  l_Count PLS_INTEGER;
  l_Result NUMBER;
BEGIN
  -- handling NO_DATA_FOUND and TOO_MANY_ROWS
  SELECT COUNT(1)
    INTO l_Count
    FROM employees
    WHERE (last_name='Russell')
  ;
  IF    (l_Count>0)
    AND (l_Count<2)
  THEN
    -- exactly one exists
    SELECT CASE -- handling ZERO_DIVIDE and NULL values
             WHEN (NVL(l_DangerousNumber, 0)=0)
               THEN 0
             ELSE (salary/l_DangerousNumber)
           END
      INTO l_Result
      FROM employees
      WHERE (last_name='Russell')
    ;
    dbms_output.put_line(l_Result);
  ELSE
    -- does not exist or there are more
    dbms_output.put_line('Number of employees: '||l_Count);
  END IF;
EXCEPTION
  WHEN OTHERS THEN
    dbms_output.put_line('Unknown error!');
END;

User-defined

If the predefined Oracle exceptions are not sufficient for us, the next step is to define our own in the declarative part of an anonymous block or package. The RAISE command is used to raise a user-defined exception.

DECLARE
  MY_EXCEPTION EXCEPTION;
BEGIN
  -- potentially
  -- dangerous
  -- operations
  RAISE MY_EXCEPTION;
EXCEPTION
  WHEN MY_EXCEPTION THEN
    dbms_output.put_line('My exception');
END;

Exception Propagation

The next example explains the system of catching and propagating exceptions through several levels of code blocks. In this case, the exceptions are declared in a separate package.

CREATE OR REPLACE PACKAGE ExceptionsPackage
AS
  OUTER_EXCEPTION EXCEPTION;
  INNER_EXCEPTION EXCEPTION;
END;
/
BEGIN
  /* START OF INNER BLOCK */
  BEGIN
    -- potentially
    -- dangerous
    -- operations
    RAISE ExceptionsPackage.INNER_EXCEPTION;
  EXCEPTION
    WHEN ExceptionsPackage.INNER_EXCEPTION THEN
      dbms_output.put_line('Inner exception');
  END;
  /* END OF INNER BLOCK */
  -- inner exception was caught, continuing
  -- another
  -- dangerous
  -- operation
  RAISE ExceptionsPackage.OUTER_EXCEPTION;
EXCEPTION
  -- OUTER_EXCEPTION has no handler, program ends in the general one
  WHEN OTHERS THEN
    dbms_output.put_line('Another unexpected error');
END;

Leave a reply

Your email address will not be published. Required fields are marked *