1. Introdução

Na linguagem PL/SQL qualquer tipo de erro é tratado como uma exceção (exception), o que significa uma situação que, a princípio, não deveria ocorrer durante a execução seu programa. Existem dois tipos de exceção:

  • Exceção de sistema (system exception): um erro definido pelo Oracle que é disparado automaticamente pela runtime engine da PL/SQL assim que ela detecta o problema. Exemplos: “duplicate value in index” e “no data found”.
  • Exceção Programada: exceção definida pelo programador e, portanto, específica da aplicação. Este tipo de exceção não é disparada automaticamente, mas apenas para situações indicadas pelo programador em seu código.

Neste artigo, abordaremos o tratamento de exceções de sistema.

2. Tratando uma Exceção de Sistema – Modelo Básico

O primeiro passo requerido para que você possa tratar uma exceção de sistema é inserir um bloco EXCEPTION no final da sua procedure ou função. A Figura 1 indica em destaque este bloco, colocado imediatamente antes da palavra reservada END do bloco principal.

Bloco para Tratamento de Exceções

Figura 1: Bloco para Tratamento de Exceções

Quando um erro ocorre em qualquer linha do programa PL/SQL, uma exceção é disparada. O processamento do programa é imediatamente interrompido e o fluxo é desviado para o bloco de exceções. Dentro deste bloco, a runtime engine do PL/SQL tentará localizar o tratador de exceção adequado para o erro ocorrido. Um exemplo é apresentado na Figura 2.

Tratador de Exceção

Figura 2: Tratador de Exceção

Veja que, na seção de declarações da procedure “p_dummy”, a variável “v1” é especificada como sendo do tipo CHAR(2). Entretanto, no corpo do programa existe uma instrução de atribuição que tenta colocar um valor maior do que a capacidade máxima da variável: v1 := 'ABC';

Durante a execução do programa, quando a runtime engine tentar executar o comando acima, as seguintes ações ocorrerão:

  1. Um erro do tipo VALUE_ERROR será detectado pelo Oracle.
  2. O fluxo normal do programa será interrompido e a execução será desviada para o bloco de exceções (EXCEPTION).
  3. Dentro do bloco de exceções, a runtime engine procurará pelo tratador de exceções adequado para o tipo de erro ocorrido. Um tratador de exceções é simplesmente um bloco de código dentro da seção EXCEPTION que é identificado com o uso da palavra WHEN seguida da descrição do tipo de erro (ex: WHEN VALUE_ERROR).

O programa da Listagem 1 apresenta um exemplo de função com uma rotina para tratamento de exceções programada. O programa cria uma função chamada “f_inverso” que recebe como entrada um número x e retorne como saída: f(x) = 1/x. Esta função realiza o tratamento de exceção quando o usuário passe como entrada o valor 0.

Listagem 1: Função f_inverso


CREATE OR REPLACE FUNCTION f_inverso(x IN NUMBER) RETURN NUMBER IS

/* -----------------------------------------------------------
   FUNÇÃO    : f_inverso
   DESCRIÇÃO : retorna f(x) = 1/x
   ----------------------------------------------------------- */

vFx   NUMBER;

BEGIN

   vFx := 1/x;
   RETURN vFx;

    EXCEPTION
      WHEN ZERO_DIVIDE THEN -- este é o “rótulo” dado pelo Oracle
                            -- para o tipo de erro disparado
                            -- quando ocorre a divisão por zero.
 
         DBMS_OUTPUT.PUT_LINE('----------------------------------');
         DBMS_OUTPUT.PUT_LINE('Erro: divisão por ZERO.');
         DBMS_OUTPUT.PUT_LINE('Não é possível obter o f(x) = 1/x.');
         DBMS_OUTPUT.PUT_LINE('----------------------------------');
          
         RETURN NULL;  -- sempre é preciso retornar um valor
                       -- em uma função!
END f_inverso;
/

Para executar o programa, basta fazer no SQL*Plus o SELECT indicado na Figura 3. Se passarmos o valor 0 como parâmetro, a exceção será tratada, a função retornará NULL e a mensagem de erro programada será exibida (para a mensagem aparecer, não esqueça de habilitar a saída do console com SET SERVEROUT ON).

Execução da Função f_inverso com o parâmetro 0

Figura 3: Execução da Função f_inverso com o parâmetro 0

Ao acompanhar o conteúdo desta seção, você deve ter percebido um inconveniente do modelo básico de tratamento de exceções: para que você possa programar o bloco de exceções, é preciso conhecer a relação de tipos de erro catalogados pelo Oracle, o que parece ser pouco prático. Felizmente é possível contornar este inconveniente, conforme mostraremos a seguir.

3. Aprimorando o Modelo Básico com WHEN OTHERS, SQLERRM e SQLCODE

Na seção anterior chamamos a atenção para um inconveniente: o fato de que é preciso conhecer os “rótulos” dos erros, quando desejamos programar o tratador de exceção. Dois exemplos foram apresentados: ZERO_DIVIDE e VALUE_ERROR.

Na realidade, a Oracle catalogou (ou seja, colocou “rótulos”) apenas em um conjunto relativamente pequeno de erros, aqueles mais comuns. Alguns destes rótulos são apresentados na Figura 4.

Alguns erros catalogados na PL/SQL

Figura 4: Alguns erros catalogados na PL/SQL

Imagine um programa que contém apenas o tratador para o erro do tipo NO_DATA_FOUND. Se durante a execução do mesmo ocorrer uma exceção do tipo ZERO_DIVIDE, elq não será tratada. O que acontecerá é que a exceção vai “estourar”, o que significa que programa seria interrompido e nenhuma ação de tratamento seria realizada. Para confirmar que isto realmente ocorre, modificamos o exemplo da função f_inverso, conforme indicado na Listagem 2. O ZERO_DIVIDE foi trocado por NO_DATA_FOUND no código.

Listagem 2: Função f_inverso (com ZERO_DIVIDE trocado por NO_DATA_FOUND)


CREATE OR REPLACE FUNCTION f_inverso(x IN NUMBER) RETURN NUMBER IS

/* -----------------------------------------------------------
   FUNÇÃO    : f_inverso
   DESCRIÇÃO : retorna f(x) = 1/x
   ----------------------------------------------------------- */

vFx   NUMBER;

BEGIN

   vFx := 1/x;
   RETURN vFx;

    EXCEPTION
      WHEN NO_DATA_FOUND THEN -- linha alterada

         DBMS_OUTPUT.PUT_LINE('----------------------------------');
         DBMS_OUTPUT.PUT_LINE('Erro: divisão por ZERO.');
         DBMS_OUTPUT.PUT_LINE('Não é possível obter o f(x) = 1/x.');
         DBMS_OUTPUT.PUT_LINE('----------------------------------');
          
         RETURN NULL;  -- sempre é preciso retornar um valor
                       -- em uma função!
END f_inverso;
/

Execute a função alterada passando o parâmetro 0 (Figura 5). Observe que dessa vez, as mensagens que você tinha programado para serem exibidas se ocorresse um erro não estão mais aparecendo. Em lugar disso o “erro estourou” e o Oracle exibiu a sua própria mensagem:

Execução da função f_inverso com o parâmetro 0 (NO_DATA_FOUND)

Figura 5: Execução da função f_inverso com o parâmetro 0 (NO_DATA_FOUND)

Para que você não precise se preocupar em conhecer exatamente o rótulo de cada erro e nem tenha o enorme trabalho de criar muitos tratadores dentro de seu código, a PL/SQL disponibiliza um tratador chamado OTHERS. Você pode entendê-lo como um tratador de exceções “coringa” ou “genérico”. Quando um bloco de exceções possui uma cláusula WHEN OTHERS, ela é automaticamente usada para tratar qualquer tipo de erro que não esteja sendo tratado dentro do bloco de exceções (que não possua um WHEN associado).

Um recurso muito comumente utilizado nos programas PL/SQL é utilizar a cláusula WHEN OTHERS junto com as funções SQLCODE e SQLERRM. Estas funções retornam os seguintes valores:

  • SQLCODE: retorna o código do último erro ocorrido na execução do programa.
  • SQLERRM: retorna a mensagem associada ao erro indicado em SQLCODE.

O exemplo da Listagem 3 demonstra a utilização da cláusula WHEN OTHERS e das funções SQLCODE e SQLERRM.

Listagem 3: Função f_inverso (com WHEN OTHERS)


CREATE OR REPLACE FUNCTION f_inverso(x IN NUMBER) RETURN NUMBER IS

/* -----------------------------------------------------------
   FUNÇÃO    : f_inverso
   DESCRIÇÃO : retorna f(x) = 1/x
   ----------------------------------------------------------- */

vFx   NUMBER;

BEGIN

   vFx := 1/x;
   RETURN vFx;

    EXCEPTION
      WHEN OTHERS THEN      -- se qualquer erro ocorrer...
 
         DBMS_OUTPUT.PUT_LINE('----------------------------------');
         DBMS_OUTPUT.PUT_LINE('Erro na execução da função.');
         DBMS_OUTPUT.PUT_LINE('Entre em contato com o administrador.');
         DBMS_OUTPUT.PUT_LINE('Código Oracle: ' || SQLCODE);
         DBMS_OUTPUT.PUT_LINE('Mensagem Oracle: ' || SQLERRM);
         DBMS_OUTPUT.PUT_LINE('----------------------------------');
         RETURN NULL;  

END f_inverso;
/ 

Desta vez, o resultado da execução é o apresentado na Figura 6.

Execução da função f_inverso com o parâmetro 0 (WHEN OTHERS)

Figura 6: Execução da função f_inverso com o parâmetro 0 (WHEN OTHERS)

Assim concluímos o artigo sobre tratamento de exceções de sistema no PL/SQL.