Oracle Exception Handling
Version 11.2.0.3
 
 
Formatting Error Stack Tables And Sequence CREATE TABLE errors (
module       VARCHAR2(50),
seq_number   NUMBER,
error_number NUMBER,
error_mesg   VARCHAR2(100),
error_stack  VARCHAR2(2000),
call_stack   VARCHAR2(2000),
timestamp    DATE);

ALTER TABLE errors
ADD CONSTRAINT pk_errors
PRIMARY KEY (module, seq_number)
USING INDEX
TABLESPACE indx_sml;

CREATE TABLE call_stacks (
module VARCHAR2(50),
seq_number NUMBER,
call_order NUMBER,
object_handle VARCHAR2(10),
line_num NUMBER,
object_name VARCHAR2(80));

ALTER TABLE call_stacks
ADD CONSTRAINT pk_call_stacks
PRIMARY KEY (module, seq_number, call_order)
USING INDEX
TABLESPACE indx_sml;

ALTER TABLE call_stacks
ADD CONSTRAINT fk_cs_errors
FOREIGN KEY (module, seq_number)
REFERENCES errors (module, seq_number)
ON DELETE CASCADE;

CREATE TABLE error_stacks (
module VARCHAR2(50),
seq_number NUMBER,
error_order NUMBER,
facility CHAR(3),
error_number NUMBER(5),
error_mesg VARCHAR2(100));

ALTER TABLE error_stacks
ADD CONSTRAINT pk_error_stacks
PRIMARY KEY (module, seq_number, error_order)
USING INDEX
TABLESPACE indx_sml;

ALTER TABLE error_stacks
ADD CONSTRAINT fk_es_errors
FOREIGN KEY (module, seq_number)
REFERENCES errors (module, seq_number)
ON DELETE CASCADE;

CREATE SEQUENCE error_seq
START WITH 1
INCREMENT BY 1;
Error Handling Package Header CREATE OR REPLACE PACKAGE ErrorPkg AUTHID DEFINER AS

/* Generic error handling package, using DBMS_UTILITY.FORMAT_ERROR_STACK and DBMS_UTILITY.FORMAT_CALL_STACK. This package stores general error information in the errors table, with detailed call stack and error stack information in the call_stacks and error_stacks tables, respectively.

Entry point for handling errors. HandleAll should be called from all exception handlers where you want the error to be logged. p_Top should be TRUE only at the topmost level of procedure nesting. It should be FALSE at other levels. */


PROCEDURE HandleAll(p_Top BOOLEAN);

/* Prints the error and call stacks (using DBMS_OUTPUT) for the given module and sequence number. */

PROCEDURE PrintStacks(p_Module IN errors.module%TYPE,
p_SeqNum IN errors.seq_number%TYPE);

/* Unwinds the call and error stacks, and stores them in the errors and call stacks tables. Returns the sequence number under which the error is stored. If p_CommitFlag is TRUE,
then the inserts are committed. In order to use StoreStacks, an error must have been handled. Thus HandleAll should have been called with p_Top = TRUE. */


PROCEDURE StoreStacks(p_Module IN errors.module%TYPE,
 p_SeqNum OUT errors.seq_number%TYPE,
 p_CommitFlag BOOLEAN DEFAULT FALSE);
END ErrorPkg;
/
Error Handling Package Body CREATE OR REPLACE PACKAGE BODY ErrorPkg AUTHID DEFINER IS
 v_NewLine CONSTANT CHAR(1) := CHR(10);
 v_Handled BOOLEAN := FALSE;
 v_ErrorStack VARCHAR2(2000);
 v_CallStack VARCHAR2(2000);

 PROCEDURE HandleAll(p_Top BOOLEAN) IS
 BEGIN
   IF p_Top THEN
     v_Handled := FALSE;
   ELSIF NOT v_Handled THEN
     v_Handled := TRUE;
     v_ErrorStack := DBMS_UTILITY.FORMAT_ERROR_STACK;
     v_CallStack := DBMS_UTILITY.FORMAT_CALL_STACK;
   END IF;
 END HandleAll;
 --===================================================
 PROCEDURE PrintStacks(p_Module IN errors.module%TYPE,
                       p_SeqNum IN errors.seq_number%TYPE)
 IS
  v_TimeStamp errors.timestamp%TYPE;
  v_ErrorMsg errors.error_mesg%TYPE;

  CURSOR c_CallCur IS
  SELECT object_handle, line_num, object_name
  FROM call_stacks
  WHERE module = p_Module
  AND seq_number = p_SeqNum
  ORDER BY call_order;

  CURSOR c_ErrorCur IS
  SELECT facility, error_number, error_mesg
  FROM error_stacks
  WHERE module = p_Module
  AND seq_number = p_SeqNum
  ORDER BY error_order;
 BEGIN
   SELECT timestamp, error_mesg
   INTO v_TimeStamp, v_ErrorMsg
   FROM errors
   WHERE module = p_Module
   AND seq_number = p_SeqNum;

   -- output general error information
   dbms_output.put_line(TO_CHAR(v_TimeStamp, 'DD-MON-YY HH24:MI:SS'));
   dbms_output.put(' Module: ' || p_Module);
   dbms_output.put(' Error #' || p_SeqNum || ': ');
   dbms_output.put_line(v_ErrorMsg);

   -- output the call stack
   dbms_output.put('Complete Call Stack:');
   dbms_output.put(' Object Handle Line Number Object Name');
   dbms_output.put_line(' ------------- ----------- -----------');

   FOR v_CallRec in c_CallCur LOOP
     dbms_output.put(RPAD(' ' || v_CallRec.object_handle, 15));
     dbms_output.put(RPAD(' ' || TO_CHAR(v_CallRec.line_num), 13));
     dbms_output.put_line(' ' || v_CallRec.object_name);
   END LOOP;

   -- output the error stack
   dbms_output.put_line('Complete Error Stack:');

   FOR v_ErrorRec in c_ErrorCur LOOP
     dbms_output.put(' ' || v_ErrorRec.facility || '-');
     dbms_output.put(TO_CHAR(v_ErrorRec.error_number) || ': ');
     dbms_output.put_line(v_ErrorRec.error_mesg);
   END LOOP;
 END PrintStacks;
 --===================================================
 PROCEDURE StoreStacks(p_Module     IN  errors.module%TYPE,
                       p_SeqNum     OUT errors.seq_number%TYPE,
                       p_CommitFlag IN  BOOLEAN DEFAULT FALSE)
 IS
  v_SeqNum     NUMBER;
  v_Index      NUMBER;
  v_Length     NUMBER;
  v_End        NUMBER;
  v_Call       VARCHAR2(100);
  v_CallOrder  NUMBER := 1;
  v_Error      VARCHAR2(120);
  v_ErrorOrder NUMBER := 1;

  v_Handle      call_stacks.object_handle%TYPE;
  v_LineNum     call_stacks.line_num%TYPE;
  v_ObjectName  call_stacks.object_name%TYPE;
  v_Facility    error_stacks.facility%TYPE;
  v_ErrNum      error_stacks.error_number%TYPE;
  v_ErrMsg      error_stacks.error_mesg%TYPE;
  v_FirstErrNum errors.error_number%TYPE;
  v_FirstErrMsg errors.error_mesg%TYPE;
 BEGIN
   -- get the error sequence number
   SELECT error_seq.nextval
   INTO v_SeqNum
   FROM dual;

   p_SeqNum := v_SeqNum;

   -- insert first part of header info. into the errors table
   INSERT INTO errors
   (module, seq_number, error_stack, call_stack, timestamp)
   VALUES
   (p_Module, v_SeqNum, v_ErrorStack, v_CallStack, SYSDATE);

   /* unwind the error stack to get each error out by scanning the error stack string. Start with the index at the beginning of the string */
   v_Index := 1;

   /* loop through the string, finding each newline. A newline ends each error on the stack */
   WHILE v_Index < LENGTH(v_ErrorStack) LOOP
     -- v_End is the position of the newline
     v_End := INSTR(v_ErrorStack, v_NewLine, v_Index);

     -- the error is between the current index and the newline
     v_Error := SUBSTR(v_ErrorStack, v_Index, v_End - v_Index);

     -- skip over the current error, for the next iteration
     v_Index := v_Index + LENGTH(v_Error) + 1;

     /* an error looks like 'facility-number: mesg'. Get each piece out for insertion. The facility is the first 3 characters of the error. */

     v_Facility := SUBSTR(v_Error, 1, 3);

     -- remove the facility and the dash (always 4 characters)
     v_Error := SUBSTR(v_Error, 5);

     -- next get the error number
     v_ErrNum := TO_NUMBER(SUBSTR(v_Error, 1, INSTR(v_Error, ':') - 1));

     -- remove the error number, colon & space (always 7 chars)
     v_Error := SUBSTR(v_Error, 8);

     -- what's left is the error message
     v_ErrMsg := v_Error;

     /* insert the errors and grab the first error number and message */

     INSERT INTO error_stacks
     (module, seq_number, error_order, facility, error_number, error_mesg)
     VALUES
     (p_Module, p_SeqNum, v_ErrorOrder, v_Facility, v_ErrNum, v_ErrMsg);

     IF v_ErrorOrder = 1 THEN
       v_FirstErrNum := v_ErrNum;
       v_FirstErrMsg := v_Facility || '-' || TO_NUMBER(v_ErrNum) || ': ' || v_ErrMsg;
     END IF;

     v_ErrorOrder := v_ErrorOrder + 1;
   END LOOP;

   -- update the errors table with the message and code
   UPDATE errors
   SET error_number = v_FirstErrNum,
       error_mesg = v_FirstErrMsg
   WHERE module = p_Module
   AND seq_number = v_SeqNum;

   /* unwind the call stack to get each call out by scanning the call stack string.
      Start with the index after the  first call on the stack. This will be after  the first occurrence of 'name' and the newline. */

   v_Index := INSTR(v_CallStack, 'name') + 5;

   /* loop through the string finding each newline. A newline ends a call on the stack */
   WHILE v_Index < LENGTH(v_CallStack) LOOP
     -- v_End is the position of the newline
     v_End := INSTR(v_CallStack, v_NewLine, v_Index);

     -- the call is between the current index and the newline
     v_Call := SUBSTR(v_CallStack, v_Index, v_End - v_Index);

     -- skip over the current call, for the next iteration
     v_Index := v_Index + LENGTH(v_Call) + 1;

     /*  within a call, we have the object handle, then the line number, then the object name, separated by spaces. Separate them out for insertion.
        Trim white space from the call first. */

     v_Call := TRIM(v_Call);

     -- first get the object handle
     v_Handle := SUBSTR(v_Call, 1, INSTR(v_Call, ' '));

     -- remove the object handle,then the white space
     v_Call := SUBSTR(v_Call, LENGTH(v_Handle) + 1);
     v_Call := TRIM(v_Call);

     -- get the line number
     v_LineNum := TO_NUMBER(SUBSTR(v_Call,1,INSTR(v_Call,' ')));

     -- remove the line number, and white space
     v_Call := SUBSTR(v_Call, LENGTH(v_LineNum) + 1);
     v_Call := TRIM(v_Call);

     -- what is left is the object name
     v_ObjectName := v_Call;

     -- insert all calls except the call for ErrorPkg
     IF v_CallOrder > 1 THEN
       INSERT INTO call_stacks
       (module, seq_number, call_order, object_handle, line_num, object_name)
       VALUES
       (p_Module, v_SeqNum, v_CallOrder, v_Handle, v_LineNum, v_ObjectName);
     END IF;

     v_Callorder := v_CallOrder + 1;
   END LOOP;

   IF p_CommitFlag THEN
     COMMIT;
   END IF;
 END StoreStacks;
END ErrorPkg;
/
Format Error Stack Demo Table And Trigger CREATE TABLE ttt (f1 NUMBEr);

CREATE OR REPLACE TRIGGER ttt_insert
BEFORE INSERT ON ttt
BEGIN
  RAISE ZERO_DIVIDE;
END ttt_insert;
/
Error Producing Procedures (A, B, And C) CREATE OR REPLACE PROCEDURE C AUTHID DEFINER AS
BEGIN
  INSERT INTO ttt VALUES (7);
EXCEPTION
  WHEN OTHERS THEN
    ErrorPkg.HandleAll(FALSE);
    RAISE;
END C;
/
--===================================================
CREATE OR REPLACE PROCEDURE B AUTHID DEFINER AS
BEGIN
  C;
EXCEPTION
  WHEN OTHERS THEN
    ErrorPkg.HandleAll(FALSE);
RAISE;

END B;
/
--===================================================
CREATE OR REPLACE PROCEDURE A AUTHID DEFINER AS
 v_ErrorSeq NUMBER;
BEGIN
 B;
EXCEPTION
  WHEN OTHERS THEN
    ErrorPkg.HandleAll(TRUE);
    ErrorPkg.StoreStacks('Scott', v_ErrorSeq, TRUE);
    ErrorPkg.PrintStacks('Scott', v_ErrorSeq);
END A;
/
Run Format Error Stack Demo exec a;

-- Examine the tables errors, call_stack, and error_stack
 
Morgan's Library Page Footer
This site is maintained by Dan Morgan. Last Updated: This site is protected by copyright and trademark laws under U.S. and International law. © 1998-2013 Daniel A. Morgan All Rights Reserved