1. Introdução

Este artigo aborda dois temas avançados e de fundamental importância no tratamento de exceções na linguagem PL/SQL:

  • Exceções programadas: exceção que é disparada apenas em situações definidas pelo programador.
  • Blocos internos para tratamento de exceções: recurso que adiciona maior controle ao mecanismo de tratamento de erros.

Como sugestão de leitura deixo o artigo “Tratamento de Exceções de Sistema na linguagem PL/SQL”, onde foram introduzidos os conceitos básicos sobre o tratamento de exceções na linguagem PL/SQL e apresentada a “receita” para realizar o tratamento das exceções de sistema.

2. Exceções Programadas

Caso o programador deseje, poderá definir os seus próprios tipos de exceção na linguagem PL/SQL. Neste caso - ao contrário do que ocorre com as exceções de sistema mostradas artigo citado - a exceção não será disparada automaticamente, mas apenas quando determinadas ações especificadas pelo programador ocorrerem. Para criar exceções programadas, você deve seguir dois passos:

  • Criar uma variável do tipo EXCEPTION na seção de declarações de seu procedure ou função.
  • Inserir um tratador para a sua exceção no bloco de exceções. O comando RAISE é usado para forçar com que a sua exceção seja disparada.

O programa da Listagem 1 exemplifica a utilização das exceções programadas. Trata-se de um procedure que imprime os n primeiros números ímpares, onde n é um parâmetro de entrada especificado pelo usuário. O procedure possui uma exceção programada que é disparada sempre que n for passado com um valor menor igual a zero.

Listagem 1: Procedure p_impares


CREATE OR REPLACE PROCEDURE p_impares(n IN NUMBER) IS

/* -----------------------------------------------------------
   PROCEDURE : p_impares
   DESCRIÇÃO : imprime os “n” primeiros números ímpares
   ----------------------------------------------------------- */

i                    PLS_INTEGER;
j                    PLS_INTEGER := 1;
e_param_invalido     EXCEPTION; -- exceção definida pelo programador

BEGIN

   -- esta linha força com que a exceção programada seja disparada 
   -- caso n seja menor ou igual a zero
   IF n <= 0 THEN RAISE e_param_invalido; END IF; 

   i:=1;

   FOR i IN 1..N LOOP

     DBMS_OUTPUT.PUT_LINE(j);
     j:= j + 2;

   END LOOP;

    EXCEPTION
      WHEN e_param_invalido THEN      -- tratamento da exceção programada
 
         DBMS_OUTPUT.PUT_LINE('----------------------------------');
         DBMS_OUTPUT.PUT_LINE('Erro!!!');
         DBMS_OUTPUT.PUT_LINE('O parametro n deve ser >= 1.');
         DBMS_OUTPUT.PUT_LINE('----------------------------------');

END p_impares;
/

Para executar o programa, basta logar no SQL*Plus e chamar o procedure conforme indicado na Figura 1. Se, como é mostrado no exemplo, passarmos o valor 0 como entrada (ou um número negativo), a exceção programada irá disparar, produzindo uma mensagem de erro no console (para a mensagem aparecer, não esqueça de habilitar a saída do console com SET SERVEROUT ON).

Execução do programa da Listagem 1

Figura 1: Execução do programa da Listagem 1

Dentro de um programa PL/SQL você pode definir quantas exceções programadas desejar. Para disparar cada exceção basta utilizar o processo mostrado na Listagem 1.

3. Blocos Internos para Tratamento de Exceções

A linguagem PL/SQL permite que você possa embutir blocos anônimos dentro do corpo principal do seu programa. Neste caso, eles são chamados de sub-blocos ou blocos internos. Estes blocos são delimitados com o uso das palavras BEGIN e END e têm como característica mais importante o fato de poderem manter uma seção própria para o tratamento de exceções. Isto adiciona um maior grau de controle ao mecanismo de tratamento de erros. A Figura 2 mostra um exemplo de programa com dois blocos internos.

Programa com dois blocos internos

Figura 2: Programa com dois blocos internos

O esquema da Figura 2 mostra um programa estruturado para funcionar da seguinte forma. Quando começar a executar, entrará no BLOCO INTERNO 1. Se alguma exceção ocorrer nos comandos pertencentes a este bloco, a seção para tratamento de exceções que será executada é a do BLOCO INTERNO 1 e não a seção do corpo principal.

Após o BLOCO INTERNO 1 ser inteiramente processado, o programa passará a executar os comandos do BLOCO INTERNO 2. Isto ocorrerá independentemente do fato de ter ou não ocorrido um erro durante a execução do bloco anterior. Da mesma forma que ocorreu no processamento do BLOCO 1, se algum erro ocorrer durante o processamento do BLOCO INTERNO 2 é a sua própria seção de exceções que será usada para tratá-lo.

O programa da Listagem 2 exemplifica a utilização de blocos internos. Trata-se de um procedure que recebe dois parâmetros numéricos x e y. Inicialmente, o programa realiza a divisão de x por y dentro de um bloco delimitado por BEGIN e END (BLOCO 1). Caso y tenha valor zero, será disparada a exceção associada a este bloco (obs: é uma exceção do tipo ZERO_DIVIDE. Consulte o artigo citado no início do texto se você não ainda não conhece os tipos de exceção da PL/SQL). Em seguida, o programa realiza a divisão de y por x dentro de outro bloco (BLOCO 2). Desta vez, caso x tenha o valor zero, será disparada a exceção associada ao BLOCO 2 (mais uma vez uma exceção do tipo ZERO_DIVIDE).

Listagem 2: Procedure p_duas_divisoes


CREATE OR REPLACE PROCEDURE p_duas_divisoes(x IN NUMBER, y IN NUMBER) IS

/* -----------------------------------------------------------
   PROCEDURE : p_duas divisões
   DESCRIÇÃO : calcula e imprime x/y e y/x
   ----------------------------------------------------------- */

resultado1 NUMBER;
resultado2 NUMBER;

BEGIN

   -- BLOCO INTERNO 1: Tratamento de x/y
   BEGIN

      resultado1 := x/y;     
      DBMS_OUTPUT.PUT_LINE('x/y = ' || TO_CHAR(resultado1));

    EXCEPTION
      WHEN ZERO_DIVIDE THEN      -- tratamento da exceção disparada se y=0 
 
         DBMS_OUTPUT.PUT_LINE('----------------------------------');
         DBMS_OUTPUT.PUT_LINE('Erro!!!');
         DBMS_OUTPUT.PUT_LINE('O parametro y deve ser diferente de 0.');
         DBMS_OUTPUT.PUT_LINE('----------------------------------');


   END;   
	
		
   -- BLOCO INTERNO 2: Tratamento de y/x
   BEGIN

      resultado2 := y/x;     
      DBMS_OUTPUT.PUT_LINE('y/x = ' || TO_CHAR(resultado2));

    EXCEPTION
      WHEN ZERO_DIVIDE THEN      -- tratamento da exceção disparada se x=0 
 
         DBMS_OUTPUT.PUT_LINE('----------------------------------');
         DBMS_OUTPUT.PUT_LINE('Erro!!!');
         DBMS_OUTPUT.PUT_LINE('O parametro x deve ser diferente de 0.');
         DBMS_OUTPUT.PUT_LINE('----------------------------------');


   END;   

END p_duas_divisoes;
/ 

A Figura 3 apresenta exemplos de três chamadas do programa. Na primeira chamada, passamos y com valor zero, disparando a exceção definida no BLOCO 1. Na segunda chamada, x foi passado com valor 0, fazendo com que fosse disparada a exceção associada ao BLOCO 2. Na terceira chamada, x = 1 e y = 2 e assim o programa roda normalmente, sem que nenhuma exceção seja disparada.

Execução do programa da Listagem 2

Figura 3: Execução do programa da Listagem 2

Assim concluímos este artigo abordando o tratamento de exceções na linguagem PL/SQL.