Tratamento de exceções

Em um bloco do Script Snowflake, você pode gerar uma exceção se ocorrer um erro. Você também pode tratar exceções que ocorrem em seu código do Script Snowflake.

Introdução

O Script Snowflake gera uma exceção se um erro ocorrer durante a execução de uma instrução (por exemplo, se uma instrução tentar efetuar um DROP em uma tabela que não existe). Uma exceção impede que as próximas linhas de código sejam executadas.

Em um bloco do Script Snowflake, você pode escrever manipuladores de exceções que capturem tipos específicos de exceções declaradas nesse bloco e em blocos aninhados dentro dele.

Além disso, para erros que podem ocorrer em seu código, você pode definir suas próprias exceções que você pode gerar quando erros ocorrerem.

Quando uma exceção é gerada em um bloco do Script Snowflake (seja por seu código ou por uma instrução cuja execução falhar), o Script Snowflake tenta encontrar um manipulador para essa exceção:

  • Se o bloco no qual a exceção ocorreu tiver um manipulador para essa exceção, a execução será retomada no início desse manipulador de exceções.

  • Se o bloco não tiver seu próprio manipulador de exceções, a exceção poderá ser capturada pelo bloco superior.

    Se a exceção ocorrer a mais de uma camada de profundidade, a exceção será enviada para cima, uma camada de cada vez, até que:

    • Uma camada com um manipulador de exceções adequado trate a exceção.

    • A camada mais externa seja atingida; nesse caso, ocorrerá um erro.

  • Se não houver um manipulador para a exceção no bloco atual ou em qualquer bloco superior, a execução do bloco será interrompida, e o cliente que envia o bloco para execução (por exemplo, a interface da web, o SnowSQL, etc.) relatará isso como um erro do Snowflake.

Um manipulador de exceções pode conter seu próprio manipulador de exceções, caso uma exceção ocorra enquanto ele trata outra exceção.

Como declarar uma exceção

Você pode declarar sua própria exceção na seção DECLARE do bloco. Use a sintaxe descrita em Sintaxe da instrução de exceção. Por exemplo:

DECLARE
  my_exception EXCEPTION (-20002, 'Raised MY_EXCEPTION.');
Copy

Como gerar uma exceção declarada

Para gerar uma exceção, execute o comando RAISE. Por exemplo:

DECLARE
  my_exception EXCEPTION (-20002, 'Raised MY_EXCEPTION.');
BEGIN
  LET counter := 0;
  LET should_raise_exception := true;
  IF (should_raise_exception) THEN
    RAISE my_exception;
  END IF;
  counter := counter + 1;
  RETURN counter;
END;
Copy

Observação: se você estiver usando o método SnowSQL, o Classic Console, execute_stream ou execute_string no código Python Connector, use este exemplo (consulte Uso do Script Snowflake em SnowSQL, Classic Console e conector Python):

EXECUTE IMMEDIATE $$
DECLARE
  my_exception EXCEPTION (-20002, 'Raised MY_EXCEPTION.');
BEGIN
  LET counter := 0;
  LET should_raise_exception := true;
  IF (should_raise_exception) THEN
    RAISE my_exception;
  END IF;
  counter := counter + 1;
  RETURN counter;
END;
$$
;
Copy

A execução para no ponto em que a exceção é gerada. (No exemplo, counter nunca é incrementado e retornado).

O cliente que envia esse bloco para execução (por exemplo, Snowsight) relata um erro e indica que a exceção não foi capturada:

-20002 (P0001): Uncaught exception of type 'MY_EXCEPTION' on line 8 at position 4 : Raised MY_EXCEPTION.
Copy

Se você quiser adicionar código para tratar quaisquer exceções que você gerar (assim como exceções geradas quando as instruções não forem executadas), você pode escrever manipuladores de exceções. Consulte Tratamento de exceções.

Nota

Em um manipulador de exceções, se você precisar levantar a mesma exceção novamente, veja Gerar novamente a mesma exceção em um manipulador de exceções.

Tratamento de exceções

Você pode tratar uma exceção explicitamente capturando-a com uma cláusula EXCEPTION, ou você pode permitir que o bloco passe a exceção para o bloco superior.

Dentro da cláusula EXCEPTION, use uma cláusula WHEN para tratar uma exceção pelo nome. Você pode tratar tanto as exceções que você declara quanto as exceções internas. Atualmente, o Snowflake oferece as seguintes exceções internas:

  • STATEMENT_ERROR: Essa exceção indica um erro durante a execução de uma instrução. Por exemplo, se você tentar remover uma tabela que não existe, essa exceção é gerada.

  • EXPRESSION_ERROR: Essa exceção indica um erro relacionado a uma expressão. Por exemplo, se você criar uma expressão que é avaliada como um VARCHAR e tentar atribuir o valor da expressão a um FLOAT, esse erro será gerado.

Quando ocorre uma exceção, você pode obter informações sobre a exceção lendo as três variáveis internas a seguir:

  • SQLCODE: Esse é um integer assinado de 5 dígitos. Para exceções definidas pelo usuário, essa é a exception_number mostrada na sintaxe para declarar uma exceção.

  • SQLERRM: Essa é uma mensagem de erro. Para exceções definidas pelo usuário, essa é a exception_message mostrada na sintaxe para declarar uma exceção.

  • SQLSTATE: Este é um código de 5 caracteres modelado no padrão ANSI SQL SQLSTATE. O Snowflake usa valores adicionais além daqueles do padrão ANSI SQL.

Para tratar todas as outras exceções que não tenham uma cláusula WHEN, use uma cláusula WHEN OTHER THEN.

Por exemplo:

DECLARE
  my_exception EXCEPTION (-20002, 'Raised MY_EXCEPTION.');
BEGIN
  LET counter := 0;
  LET should_raise_exception := true;
  IF (should_raise_exception) THEN
    RAISE my_exception;
  END IF;
  counter := counter + 1;
  RETURN counter;
EXCEPTION
  WHEN statement_error THEN
    RETURN OBJECT_CONSTRUCT('Error type', 'STATEMENT_ERROR',
                            'SQLCODE', sqlcode,
                            'SQLERRM', sqlerrm,
                            'SQLSTATE', sqlstate);
  WHEN my_exception THEN
    RETURN OBJECT_CONSTRUCT('Error type', 'MY_EXCEPTION',
                            'SQLCODE', sqlcode,
                            'SQLERRM', sqlerrm,
                            'SQLSTATE', sqlstate);
  WHEN OTHER THEN
    RETURN OBJECT_CONSTRUCT('Error type', 'Other error',
                            'SQLCODE', sqlcode,
                            'SQLERRM', sqlerrm,
                            'SQLSTATE', sqlstate);
  END;
Copy

Observação: se você estiver usando o método SnowSQL, o Classic Console, execute_stream ou execute_string no código Python Connector, use este exemplo (consulte Uso do Script Snowflake em SnowSQL, Classic Console e conector Python):

EXECUTE IMMEDIATE $$
DECLARE
  my_exception EXCEPTION (-20002, 'Raised MY_EXCEPTION.');
BEGIN
  LET counter := 0;
  LET should_raise_exception := true;
  IF (should_raise_exception) THEN
    RAISE my_exception;
  END IF;
  counter := counter + 1;
  RETURN counter;
EXCEPTION
  WHEN statement_error THEN
    RETURN OBJECT_CONSTRUCT('Error type', 'STATEMENT_ERROR',
                            'SQLCODE', sqlcode,
                            'SQLERRM', sqlerrm,
                            'SQLSTATE', sqlstate);
  WHEN my_exception THEN
    RETURN OBJECT_CONSTRUCT('Error type', 'MY_EXCEPTION',
                            'SQLCODE', sqlcode,
                            'SQLERRM', sqlerrm,
                            'SQLSTATE', sqlstate);
  WHEN OTHER THEN
    RETURN OBJECT_CONSTRUCT('Error type', 'Other error',
                            'SQLCODE', sqlcode,
                            'SQLERRM', sqlerrm,
                            'SQLSTATE', sqlstate);
END;
$$
;
Copy

Este exemplo trata cada tipo de exceção chamando OBJECT_CONSTRUCT para construir e retornar um objeto que contenha os detalhes sobre a exceção. O exemplo produz a seguinte saída:

+--------------------------------------+
| anonymous block                      |
|--------------------------------------|
| {                                    |
|   "Error type": "MY_EXCEPTION",      |
|   "SQLCODE": -20002,                 |
|   "SQLERRM": "Raised MY_EXCEPTION.", |
|   "SQLSTATE": "P0001"                |
| }                                    |
+--------------------------------------+

Em casos raros, você pode querer lidar explicitamente com uma exceção, não fazendo nada. Isto permite que você continue, em vez de abortar, quando a exceção ocorre. Para obter mais informações, consulte o comando NULL.

Se você não configurar um manipulador para uma exceção, o cliente que envia o bloco para execução (por exemplo, a interface da web) relatará um erro (como explicado em Como gerar uma exceção declarada).

-20002 (P0001): Uncaught exception of type 'MY_EXCEPTION' on line 8 at position 4 : Raised MY_EXCEPTION.
Copy

Nota

Se você precisar gerar a mesma exceção novamente, veja Gerar novamente a mesma exceção em um manipulador de exceções.

Gerar novamente a mesma exceção em um manipulador de exceções

Em alguns casos, você pode precisar gerar a mesma exceção que você capturou em seu manipulador de exceções. Nesses casos, execute o comando RAISE sem especificar nenhum argumento.

Por exemplo, suponha que durante o tratamento de exceções, você precise capturar alguns detalhes sobre a exceção antes de gerar a mesma exceção novamente. Depois de capturar os detalhes, execute o comando RAISE:

BEGIN
  SELECT * FROM non_existent_table;
EXCEPTION
  WHEN OTHER THEN
    LET LINE := SQLCODE || ': ' || SQLERRM;
    INSERT INTO myexceptions VALUES (:line);
    RAISE; -- Raise the same exception that you are handling.
END;
Copy

Observação: se você estiver usando o método SnowSQL, o Classic Console, execute_stream ou execute_string no código Python Connector, use este exemplo (consulte Uso do Script Snowflake em SnowSQL, Classic Console e conector Python):

EXECUTE IMMEDIATE $$
BEGIN
  SELECT * FROM non_existent_table;
EXCEPTION
  WHEN OTHER THEN
    LET LINE := SQLCODE || ': ' || SQLERRM;
    INSERT INTO myexceptions VALUES (:line);
    RAISE; -- Raise the same exception that you are handling.
END;
$$;
Copy

Como passar variáveis para um manipulador de exceção

É possível passar variáveis para um manipulador de exceção. O manipulador de exceção pode executar código com base no valor da variável, e o valor da variável pode ser retornado em mensagens de erro.

Para que uma variável seja passada para um manipulador na seção EXCEPTION, a variável deve ser declarada na seção DECLARE. Se uma variável for declarada na seção BEGIN … END do bloco, ela não poderá ser acessada na seção EXCEPTION.

Além disso, se você estiver escrevendo um procedimento armazenado do Snowflake Scripting que aceita argumentos, é possível usar esses argumentos em um manipulador de exceção.

Por exemplo, o seguinte bloco anônimo passa o valor da variável counter_val para o manipulador de exceção:

DECLARE
  counter_val INTEGER DEFAULT 0;
  my_exception EXCEPTION (-20002, 'My exception text');
BEGIN
  WHILE (counter_val < 12) DO
    counter_val := counter_val + 1;
    IF (counter_val > 10) THEN
      RAISE my_exception;
    END IF;
  END WHILE;
  RETURN counter_val;
EXCEPTION
  WHEN my_exception THEN
    RETURN 'Error ' || sqlcode || ': Counter value ' || counter_val || ' exceeds the limit of 10.';
END;
Copy

Observação: se você estiver usando o método SnowSQL, o Classic Console, execute_stream ou execute_string no código Python Connector, use este exemplo (consulte Uso do Script Snowflake em SnowSQL, Classic Console e conector Python):

EXECUTE IMMEDIATE $$
DECLARE
  counter_val INTEGER DEFAULT 0;
  my_exception EXCEPTION (-20002, 'My exception text');
BEGIN
  WHILE (counter_val < 12) DO
    counter_val := counter_val + 1;
    IF (counter_val > 10) THEN
      RAISE my_exception;
    END IF;
  END WHILE;
  RETURN counter_val;
EXCEPTION
  WHEN my_exception THEN
    RETURN 'Error ' || sqlcode || ': Counter value ' || counter_val || ' exceeds the limit of 10.';
END;
$$
;
Copy

O bloco retorna a seguinte mensagem de erro:

+---------------------------------------------------------+
| anonymous block                                         |
|---------------------------------------------------------|
| Error -20002: Counter value 11 exceeds the limit of 10. |
+---------------------------------------------------------+

A seguir está um exemplo de um procedimento armazenado do Snowflake Scripting que passa um argumento. O exemplo demonstra como você pode usar o argumento em um manipulador de exceção:

CREATE OR REPLACE PROCEDURE exception_test_vars(amount INT)
  RETURNS TEXT
  LANGUAGE SQL
AS
DECLARE
  my_exception_1 EXCEPTION (-20002, 'Value too low');
  my_exception_2 EXCEPTION (-20003, 'Value too high');
BEGIN
  CREATE OR REPLACE TABLE test_order_insert(units INT);
  IF (amount < 1) THEN
    RAISE my_exception_1;
  ELSEIF (amount > 10) THEN
    RAISE my_exception_2;
  ELSE
    INSERT INTO test_order_insert VALUES (:amount);
  END IF;
  RETURN 'Order inserted successfully.';
EXCEPTION
  WHEN my_exception_1 THEN
    RETURN 'Error ' || sqlcode || ': Submitted amount ' || amount || ' is too low (1 or greater required).';
  WHEN my_exception_2 THEN
    RETURN 'Error ' || sqlcode || ': Submitted amount ' || amount || ' is too high (exceeds limit of 10).';
END;
Copy

Observação: se você estiver usando o método SnowSQL, o Classic Console, execute_stream ou execute_string no código Python Connector, use este exemplo (consulte Uso do Script Snowflake em SnowSQL, Classic Console e conector Python):

CREATE OR REPLACE PROCEDURE exception_test_vars(amount INT)
  RETURNS TEXT
  LANGUAGE SQL
AS
$$
DECLARE
  my_exception_1 EXCEPTION (-20002, 'Value too low');
  my_exception_2 EXCEPTION (-20003, 'Value too high');
BEGIN
  CREATE OR REPLACE TABLE test_order_insert(units INT);
  IF (amount < 1) THEN
    RAISE my_exception_1;
  ELSEIF (amount > 10) THEN
    RAISE my_exception_2;
  ELSE
    INSERT INTO test_order_insert VALUES (:amount);
  END IF;
  RETURN 'Order inserted successfully.';
EXCEPTION
  WHEN my_exception_1 THEN
    RETURN 'Error ' || sqlcode || ': Submitted amount ' || amount || ' is too low (1 or greater required).';
  WHEN my_exception_2 THEN
    RETURN 'Error ' || sqlcode || ': Submitted amount ' || amount || ' is too high (exceeds limit of 10).';
END;
$$
;
Copy

As seguintes chamadas para o procedimento armazenado mostram a saída esperada:

CALL exception_test_vars(7);
Copy
+------------------------------+
| EXCEPTION_TEST_VARS          |
|------------------------------|
| Order inserted successfully. |
+------------------------------+
CALL exception_test_vars(-3);
Copy
+-----------------------------------------------------------------------+
| EXCEPTION_TEST_VARS                                                   |
|-----------------------------------------------------------------------|
| Error -20002: Submitted amount -3 is too low (1 or greater required). |
+-----------------------------------------------------------------------+
CALL exception_test_vars(20);
Copy
+----------------------------------------------------------------------+
| EXCEPTION_TEST_VARS                                                  |
|----------------------------------------------------------------------|
| Error -20003: Submitted amount 20 is too high (exceeds limit of 10). |
+----------------------------------------------------------------------+