Oracle PL / SQL: genere una excepción definida por el usuario con SQLERRM personalizado

¿Es posible crear excepciones definidas por el usuario y poder cambiar SQLERRM?

Por ejemplo:

DECLARE
    ex_custom       EXCEPTION;
BEGIN
    RAISE ex_custom;
EXCEPTION
    WHEN ex_custom THEN
        DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
/

El resultado es "Excepción definida por el usuario". ¿Es posible cambiar ese mensaje?

EDITAR: Aquí hay algunos detalles más.

Espero que este ilustre mejor lo que intento hacer.

DECLARE
    l_table_status      VARCHAR2(8);
    l_index_status      VARCHAR2(8);
    l_table_name        VARCHAR2(30) := 'TEST';
    l_index_name        VARCHAR2(30) := 'IDX_TEST';
    ex_no_metadata      EXCEPTION;
BEGIN

    BEGIN
        SELECT  STATUS
        INTO    l_table_status
        FROM    USER_TABLES
        WHERE   TABLE_NAME      = l_table_name;
    EXCEPTION
        WHEN NO_DATA_FOUND THEN
            -- raise exception here with message saying
            -- "Table metadata does not exist."
            RAISE ex_no_metadata;
    END;

    BEGIN
        SELECT  STATUS
        INTO    l_index_status
        FROM    USER_INDEXES
        WHERE   INDEX_NAME      = l_index_name;
    EXCEPTION
        WHEN NO_DATA_FOUND THEN
            -- raise exception here with message saying
            -- "Index metadata does not exist."
            RAISE ex_no_metadata;
    END;

EXCEPTION
    WHEN ex_no_metadata THEN
        DBMS_OUTPUT.PUT_LINE('Exception will be handled by handle_no_metadata_exception(SQLERRM) procedure here.');
        DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
/

En realidad, hay docenas de esos sub-bloques. Me pregunto si hay una manera de tener una única excepción definida por el usuario para que se genere cada uno de esos sub-bloques, pero que dé un mensaje diferente, en lugar de crear una excepción definida por el usuario separada para cada sub-bloque.

En .NET, sería como tener una excepción personalizada como esta:

    public class ColorException : Exception
    {
        public ColorException(string message)
            : base(message)
        {
        }
    }

Y luego, un método tendría algo como esto:

        if (isRed)
        {
            throw new ColorException("Red is not allowed!");
        }

        if (isBlack)
        {
            throw new ColorException("Black is not allowed!");
        }

        if (isBlue)
        {
            throw new ColorException("Blue is not allowed!");
        }

preguntado el 16 de mayo de 11 a las 16:05

5 Respuestas

Si. Solo tienes que usar el RAISE_APPLICATION_ERROR función. Si también desea nombrar su excepción, deberá usar el EXCEPTION_INIT pragma para asociar el número de error a la excepción nombrada. Algo como

SQL> ed
Wrote file afiedt.buf

  1  declare
  2    ex_custom EXCEPTION;
  3    PRAGMA EXCEPTION_INIT( ex_custom, -20001 );
  4  begin
  5    raise_application_error( -20001, 'This is a custom error' );
  6  exception
  7    when ex_custom
  8    then
  9      dbms_output.put_line( sqlerrm );
 10* end;
SQL> /
ORA-20001: This is a custom error

PL/SQL procedure successfully completed.

contestado el 16 de mayo de 11 a las 20:05

¡Precisamente lo que necesito! Supongo que hice mi edición cuando ya había respondido a mi pregunta. Muchas gracias. - tgxiii

Podrías usar RAISE_APPLICATION_ERROR así:

DECLARE
    ex_custom       EXCEPTION;
BEGIN
    RAISE ex_custom;
EXCEPTION
    WHEN ex_custom THEN
        RAISE_APPLICATION_ERROR(-20001,'My exception was raised');
END;
/

Eso generará una excepción que se parece a:

ORA-20001: My exception was raised

El número de error puede estar comprendido entre -20001 y -20999.

contestado el 16 de mayo de 11 a las 20:05

Normalmente pierdo la pista de todos mis -20001-escribe códigos de error, así que trato de consolidar todos los errores de mi aplicación en un paquete agradable como este:

SET SERVEROUTPUT ON

CREATE OR REPLACE PACKAGE errors AS
  invalid_foo_err EXCEPTION;
  invalid_foo_num NUMBER := -20123;
  invalid_foo_msg VARCHAR2(32767) := 'Invalid Foo!';
  PRAGMA EXCEPTION_INIT(invalid_foo_err, -20123);  -- can't use var >:O

  illegal_bar_err EXCEPTION;
  illegal_bar_num NUMBER := -20156;
  illegal_bar_msg VARCHAR2(32767) := 'Illegal Bar!';
  PRAGMA EXCEPTION_INIT(illegal_bar_err, -20156);  -- can't use var >:O

  PROCEDURE raise_err(p_err NUMBER, p_msg VARCHAR2 DEFAULT NULL);
END;
/

CREATE OR REPLACE PACKAGE BODY errors AS
  unknown_err EXCEPTION;
  unknown_num NUMBER := -20001;
  unknown_msg VARCHAR2(32767) := 'Unknown Error Specified!';

  PROCEDURE raise_err(p_err NUMBER, p_msg VARCHAR2 DEFAULT NULL) AS
    v_msg VARCHAR2(32767);
  BEGIN
    IF p_err = unknown_num THEN
      v_msg := unknown_msg;
    ELSIF p_err = invalid_foo_num THEN
      v_msg := invalid_foo_msg;
    ELSIF p_err = illegal_bar_num THEN
      v_msg := illegal_bar_msg;
    ELSE
      raise_err(unknown_num, 'USR' || p_err || ': ' || p_msg);
    END IF;

    IF p_msg IS NOT NULL THEN
      v_msg := v_msg || ' - '||p_msg;
    END IF;

    RAISE_APPLICATION_ERROR(p_err, v_msg);
  END;
END;
/

Luego llame errors.raise_err(errors.invalid_foo_num, 'optional extra text') para usarlo, como tal:

BEGIN
  BEGIN
    errors.raise_err(errors.invalid_foo_num, 'Insufficient Foo-age!');
  EXCEPTION
    WHEN errors.invalid_foo_err THEN
      dbms_output.put_line(SQLERRM);
  END;

  BEGIN
    errors.raise_err(errors.illegal_bar_num, 'Insufficient Bar-age!');
  EXCEPTION
    WHEN errors.illegal_bar_err THEN
      dbms_output.put_line(SQLERRM);
  END;

  BEGIN
    errors.raise_err(-10000, 'This Doesn''t Exist!!');
  EXCEPTION
    WHEN OTHERS THEN
      dbms_output.put_line(SQLERRM);
  END;
END;
/

produce esta salida:

ORA-20123: Invalid Foo! - Insufficient Foo-age!
ORA-20156: Illegal Bar! - Insufficient Bar-age!
ORA-20001: Unknown Error Specified! - USR-10000: This Doesn't Exist!!

Respondido 17 Jul 13, 22:07

Buen consejo! ¡Fue muy útil para mi proyecto! - Serpiente Hoja

Ésta es una buena práctica. Dos cuestiones menores sobre raise_application_error pensamiento: 1) el tamaño del segundo parámetro está limitado a 2 bytes y 2048) Preferiría que el tercer parámetro sea true (en lugar de predeterminado false) para obtener un seguimiento de pila completo. - user272735

declare
   z exception;

begin
   if to_char(sysdate,'day')='sunday' then
     raise z;
   end if;

   exception 
     when z then
        dbms_output.put_line('to day is sunday');
end;

respondido 11 nov., 12:17

create or replace PROCEDURE PROC_USER_EXP 
AS
duplicate_exp EXCEPTION;
PRAGMA EXCEPTION_INIT( duplicate_exp, -20001 );
LVCOUNT NUMBER;
BEGIN
  SELECT COUNT(*) INTO LVCOUNT FROM JOBS WHERE JOB_TITLE='President';
  IF LVCOUNT >1 THEN 
   raise_application_error( -20001, 'Duplicate president customer excetpion' );
  END IF;

  EXCEPTION 
  WHEN duplicate_exp THEN 
  DBMS_OUTPUT.PUT_LINE(sqlerrm);
END PROC_USER_EXP;

La salida de ORACLE 11g será así:

Connecting to the database HR. 
ORA-20001: Duplicate president customer excetpion 
Process exited. 
Disconnecting from the database HR

Respondido el 24 de diciembre de 18 a las 13:12

La salida de ORACLE 11g será así: -Conectando a la base de datos HR. ORA-20001: Excedencia de cliente presidente duplicada. Proceso finalizado. Desconectarse de la base de datos HR. - Raj sharma

Esto es bueno excepto (irónicamente) por el bloque EXCEPCIÓN. En casi todas las circunstancias, las excepciones manejadas en una cláusula WHEN deben volver a aparecer en el programa que realiza la llamada. El programa que llama necesita saber que la rutina llamada falló. El simple uso de DBMS_OUTPUT no es lo suficientemente bueno, porque el programa de llamada podría no ser capaz de procesar el búfer de salida del servidor y / o porque no fuerza al programa de llamada a reconocer el error. - APC

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.