set serveroutput on
<<DEMO_AND_TEST_EXCEPTIONS>>
DECLARE
  MYOWNNODATAFOUND EXCEPTION;
--ERROR CAUSED BY  PRAGMA EXCEPTION_INIT (MYOWNNODATAFOUND, -1403);
--  Cannot rename Oracle native exceptions if their numbers are already named
--  in DBMS_STANDARD, e.g., ORA-01403 no data found
  userrange_exc_no constant number := -20202;
  USERRANGE_EXCEPTION EXCEPTION;
  PRAGMA EXCEPTION_INIT (USERRANGE_EXCEPTION, -20202);
  -- not allowed to use the constant to initialize the exception
  UNNUMBERED_USEREXCEPTION EXCEPTION;
  XTEST_NO NUMBER := 0;
  bogo number;
BEGIN
  DBMS_OUTPUT.ENABLE;
  <<XCEP_LOOP>>
  LOOP
    XTEST_NO := XTEST_NO + 1;
    DBMS_OUTPUT.PUT_LINE ('Exception test number ' || XTEST_NO);
    <<INNER_EXCEP_BLK>>
    BEGIN
      <<RAISERS>>
      CASE XTEST_NO
        WHEN 1 THEN
          -- triggers ORA-01403, no data found
          SELECT num_rows INTO BOGO
          FROM USER_TABLES
          WHERE 1 = NULL
            and rownum = 1
          ;
        WHEN 2 THEN
          RAISE USERRANGE_EXCEPTION;
        when 3 then
          RAISE UNNUMBERED_USEREXCEPTION;
        WHEN 4 THEN
          DBMS_STANDARD.RAISE_APPLICATION_ERROR (USERRANGE_EXC_NO, 'this is an intentional exception');
        else
          raise_application_error (-20002, 'ran out of exception cases!');
      END CASE RAISERS;
    EXCEPTION
      WHEN NO_DATA_FOUND THEN
        DBMS_OUTPUT.PUT_LINE ('[INNER] no data found exception: sqlcode ' || SQLCODE ||
          ', message ' || sqlerrm );
      WHEN USERRANGE_EXCEPTION THEN
        DBMS_OUTPUT.PUT_LINE ('[INNER] user-range exception: sqlcode ' || SQLCODE ||
          ', message ' || sqlerrm );
      WHEN UNNUMBERED_USEREXCEPTION THEN
        DBMS_OUTPUT.PUT_LINE ('[INNER] unnumbered exception: sqlcode ' || SQLCODE ||
          ', message ' || SQLERRM );
      WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE ('[INNER] exception: sqlcode ' || SQLCODE ||
          ', message ' || SQLERRM );
        EXIT XCEP_LOOP;   --<<<< THIS IS A PRETTY USEFUL APPLICATION OF PL/SQL BLOCK LABELS
    end inner_excep_blk;
  END LOOP XCEP_LOOP;
  DBMS_OUTPUT.PUT_LINE ('Ending exception test and demo block');
EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE ('EXCEPTION: ' || SQLERRM);
END DEMO_AND_TEST_EXCEPTIONS;
/
 | 
anonymous block completed Exception test number 1 [INNER] no data found exception: sqlcode 100, message ORA-01403: no data found Exception test number 2 [INNER] user-range exception: sqlcode -20202, message ORA-20202: Exception test number 3 [INNER] unnumbered exception: sqlcode 1, message User-Defined Exception Exception test number 4 [INNER] user-range exception: sqlcode -20202, message ORA-20202: this is an intentional exception Exception test number 5 [INNER] exception: sqlcode -20002, message ORA-20002: ran out of exception cases! Ending exception test and demo block  | 
- You can define your own exceptions practically however you like
 - PRAGMA_EXCEPTION_INIT will associate ORA- numbers with your own exception names
- you can use this to throw exceptions with a certain number
 - you can use this to catch exceptions expecting a certain number
 
 - The implicitly-included package DBMS_STANDARD pre-defines a few dozen exception codes, for example here we have used NO_DATA_FOUND
 - If you throw a named exception using RAISE (Oracle predefined or your own), you can't add a message
 - To pass a message along on an exception, you must use RAISE_APPLICATION_ERROR.
 - not many people use <<BLOCK_NAME>> to name blocks at all, let alone as aggressively as I have in this example.  It is completely optional and frankly it rarely helps.  I use the following labels:
- DEMO_AND_TEST_EXCEPTIONS
 - XCEP_LOOP
 - INNER_EXCEP_BLK
 - RAISERS
 
 
