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 ao tratamento de exceções no Script Snowflake

Snowflake Scripting raises an exception if an error occurs while executing a statement. For example, if a statement attempts to drop a table that doesn’t exist, Snowflake Scripting raises an exception.

In a Snowflake Scripting block, you can write exception handlers that catch specific types of exceptions declared in that block and in blocks nested inside that block. In addition, for errors that can occur in your code, you can define your own exceptions that you can raise when errors occur.

Depois que as instruções no manipulador forem executadas, você poderá optar por sair do bloco ou continuar executando as instruções no bloco. Para obter mais informações, consulte Handling an exception in Snowflake Scripting.

When an exception is raised in a Snowflake Scripting block, either by your code or by a statement that fails to execute, Snowflake Scripting attempts to find a handler for that exception:

  • 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.

  • If the block doesn’t have its own exception handler, then the exception can be caught by the enclosing block.

    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 adjacente, a execução do bloco será interrompida e o cliente que enviar o bloco para execução (por exemplo, Snowsight, SnowSQL e assim por diante) 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.

Declaring an exception in Snowflake Scripting

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

Raising a declared exception in Snowflake Scripting

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ê usar o Snowflake CLI, SnowSQL, o Classic Console, ou o método execute_stream ou execute_string no código Python Connector, use este exemplo (consulte Usar o Snowflake Scripting no Snowflake CLI, SnowSQL, Classic Console e Python Connector):

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

If there is no handler, execution stops at the point when the exception is raised. In the example, counter is never incremented and isn’t returned.

The client that submits this block for execution — for example, Snowsight — reports an error and indicates that the exception was not caught:

-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 Handling an exception in Snowflake Scripting.

Nota

Em um manipulador de exceções, se você precisar levantar a mesma exceção novamente, veja Raising the same exception again in an exception handler in Snowflake Scripting.

Handling an exception in Snowflake Scripting

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.

Cada cláusula WHEN em um bloco de exceção pode ser de um dos seguintes tipos:

  • EXIT: o bloco executa as instruções no manipulador e depois sai do bloco atual. Se o bloco executar uma exceção desse tipo e o bloco contiver instruções após a instrução que causou o erro, essas instruções não serão executadas.

    Se o bloco for um bloco interno e o manipulador de exceção não contiver uma instrução RETURN, a execução sairá do bloco interno e continuará com o código no bloco externo.

    EXIT é o padrão.

  • CONTINUE: o bloco executa as instruções no bloco de exceção e continua com a instrução imediatamente após a que causou o erro.

    Um manipulador CONTINUE pode capturar e manipular exceções sem encerrar o bloco de instruções que gerou a exceção. Com o manipulador padrão EXIT, quando ocorre um erro em um bloco, o fluxo é interrompido e o erro é retornado ao autor da chamada. No entanto, você pode usar um manipulador CONTINUE quando a condição de erro não é grave o suficiente para justificar a interrupção do fluxo.

Uma cláusula EXCEPTION pode ter cláusulas WHEN de ambos os tipos: EXIT e CONTINUE.

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.

Quando você usa uma cláusula WHEN do tipo CONTINUE, essas variáveis internas refletem o erro que causou a exceção na cláusula WHEN. Depois que as instruções na cláusula WHEN forem concluídas e a execução da instrução continuar no bloco, os valores dessas variáveis retornarão os valores que tinham antes da exceção ter sido gerada.

To handle all other exceptions that aren’t built-in or declared, use a WHEN OTHER THEN clause. The WHEN OTHER THEN clause can be of type EXIT or CONTINUE.

Por exemplo, suponha que você tenha a seguinte tabela de log de erros para rastrear suas exceções:

CREATE OR REPLACE TABLE test_error_log(
  error_type VARCHAR,
  error_code VARCHAR,
  error_message VARCHAR,
  error_state VARCHAR,
  error_timestamp TIMESTAMP);
Copy

O bloco anônimo a seguir insere informações sobre as exceções na tabela e retorna informações sobre elas para o usuário:

Dica

O exemplo define uma exceção na seção DECLARE e, em seguida, trata essa exceção. Para um exemplo que lida com uma exceção STATEMENT_ERROR, remova os comentários (--) dessa linha:

-- SELECT 1/0;
Copy

Para um exemplo que lida com outros erros, remova os comentários dessa linha:

-- LET var := 1/0;
Copy
DECLARE
  my_exception EXCEPTION (-20002, 'Raised MY_EXCEPTION.');
BEGIN
  -- SELECT 1/0;
  -- LET var := 1/0;
  LET counter := 0;
  LET should_raise_exception := true;
  IF (should_raise_exception) THEN
    RAISE my_exception;
  END IF;
  counter := counter + 1;
  RETURN 'My counter value: ' || counter;
EXCEPTION
  WHEN STATEMENT_ERROR THEN
    INSERT INTO test_error_log VALUES(
      'STATEMENT_ERROR', :sqlcode, :sqlerrm, :sqlstate, CURRENT_TIMESTAMP());
    RETURN OBJECT_CONSTRUCT('Error type', 'STATEMENT_ERROR',
                            'SQLCODE', sqlcode,
                            'SQLERRM', sqlerrm,
                            'SQLSTATE', sqlstate);
  WHEN my_exception THEN
    INSERT INTO test_error_log VALUES(
      'MY_EXCEPTION', :sqlcode, :sqlerrm, :sqlstate, CURRENT_TIMESTAMP());
    RETURN OBJECT_CONSTRUCT('Error type', 'MY_EXCEPTION',
                            'SQLCODE', sqlcode,
                            'SQLERRM', sqlerrm,
                            'SQLSTATE', sqlstate);
  WHEN OTHER THEN
    INSERT INTO test_error_log VALUES(
      'OTHER', :sqlcode, :sqlerrm, :sqlstate, CURRENT_TIMESTAMP());
    RETURN OBJECT_CONSTRUCT('Error type', 'Other error',
                            'SQLCODE', sqlcode,
                            'SQLERRM', sqlerrm,
                            'SQLSTATE', sqlstate);
END;
Copy

Observação: se você usar o Snowflake CLI, SnowSQL, o Classic Console, ou o método execute_stream ou execute_string no código Python Connector, use este exemplo (consulte Usar o Snowflake Scripting no Snowflake CLI, SnowSQL, Classic Console e Python Connector):

EXECUTE IMMEDIATE $$
DECLARE
  my_exception EXCEPTION (-20002, 'Raised MY_EXCEPTION.');
BEGIN
  -- SELECT 1/0;
  -- LET var := 1/0;
  LET counter := 0;
  LET should_raise_exception := true;
  IF (should_raise_exception) THEN
    RAISE my_exception;
  END IF;
  counter := counter + 1;
  RETURN 'My counter value: ' || counter;
EXCEPTION
  WHEN STATEMENT_ERROR THEN
    INSERT INTO test_error_log VALUES(
      'STATEMENT_ERROR', :sqlcode, :sqlerrm, :sqlstate, CURRENT_TIMESTAMP());
    RETURN OBJECT_CONSTRUCT('Error type', 'STATEMENT_ERROR',
                            'SQLCODE', sqlcode,
                            'SQLERRM', sqlerrm,
                            'SQLSTATE', sqlstate);
  WHEN my_exception THEN
    INSERT INTO test_error_log VALUES(
      'MY_EXCEPTION', :sqlcode, :sqlerrm, :sqlstate, CURRENT_TIMESTAMP());
    RETURN OBJECT_CONSTRUCT('Error type', 'MY_EXCEPTION',
                            'SQLCODE', sqlcode,
                            'SQLERRM', sqlerrm,
                            'SQLSTATE', sqlstate);
  WHEN OTHER THEN
    INSERT INTO test_error_log VALUES(
      'OTHER', :sqlcode, :sqlerrm, :sqlstate, CURRENT_TIMESTAMP());
    RETURN OBJECT_CONSTRUCT('Error type', 'Other error',
                            'SQLCODE', sqlcode,
                            'SQLERRM', sqlerrm,
                            'SQLSTATE', sqlstate);
END;
$$
;
Copy

Para o valor retornado, este exemplo trata cada tipo de exceção chamando OBJECT_CONSTRUCT para construir e retornar um objeto que contém 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"                |
| }                                    |
+--------------------------------------+

Você pode consultar o test_error_log tabela para confirmar que o erro foi registrado:

SELECT * FROM test_error_log;
Copy
+--------------+------------+----------------------+-------------+-------------------------+
| ERROR_TYPE   | ERROR_CODE | ERROR_MESSAGE        | ERROR_STATE | ERROR_TIMESTAMP         |
|--------------+------------+----------------------+-------------+-------------------------|
| MY_EXCEPTION | -20002     | Raised MY_EXCEPTION. | P0001       | 2025-09-05 12:15:00.068 |
+--------------+------------+----------------------+-------------+-------------------------+

O exemplo anterior usou cláusulas WHEN do tipo padrão (EXIT). Se uma das cláusulas WHEN capturar uma exceção, ela executará as instruções na cláusula WHEN e depois sairá. Portanto, o código a seguir não é executado:

counter := counter + 1;
RETURN 'My counter value: ' || counter;
Copy

Se você quiser tratar uma exceção e depois continuar executando o código no bloco, especifique cláusulas WHEN do tipo CONTINUE. O exemplo a seguir é o mesmo que o exemplo anterior, mas especifica cláusulas WHEN do tipo CONTINUE e remove a instrução RETURN de cada cláusula WHEN:

DECLARE
  my_exception EXCEPTION (-20002, 'Raised MY_EXCEPTION.');
BEGIN
  -- SELECT 1/0;
  -- LET var := 1/0;
  LET counter := 0;
  LET should_raise_exception := true;
  IF (should_raise_exception) THEN
    RAISE my_exception;
  END IF;
  counter := counter + 1;
  RETURN 'My counter value: ' || counter;
EXCEPTION
  WHEN STATEMENT_ERROR CONTINUE THEN
    INSERT INTO test_error_log VALUES(
      'STATEMENT_ERROR', :sqlcode, :sqlerrm, :sqlstate, CURRENT_TIMESTAMP());
  WHEN my_exception CONTINUE THEN
    INSERT INTO test_error_log VALUES(
      'MY_EXCEPTION', :sqlcode, :sqlerrm, :sqlstate, CURRENT_TIMESTAMP());
  WHEN OTHER CONTINUE THEN
    INSERT INTO test_error_log VALUES(
      'OTHER', :sqlcode, :sqlerrm, :sqlstate, CURRENT_TIMESTAMP());
END;
Copy

Observação: se você usar o Snowflake CLI, SnowSQL, o Classic Console, ou o método execute_stream ou execute_string no código Python Connector, use este exemplo (consulte Usar o Snowflake Scripting no Snowflake CLI, SnowSQL, Classic Console e Python Connector):

EXECUTE IMMEDIATE $$
DECLARE
  my_exception EXCEPTION (-20002, 'Raised MY_EXCEPTION.');
BEGIN
  -- SELECT 1/0;
  -- LET var := 1/0;
  LET counter := 0;
  LET should_raise_exception := true;
  IF (should_raise_exception) THEN
    RAISE my_exception;
  END IF;
  counter := counter + 1;
  RETURN 'My counter value: ' || counter;
EXCEPTION
  WHEN STATEMENT_ERROR CONTINUE THEN
    INSERT INTO test_error_log VALUES(
      'STATEMENT_ERROR', :sqlcode, :sqlerrm, :sqlstate, CURRENT_TIMESTAMP());
  WHEN my_exception CONTINUE THEN
    INSERT INTO test_error_log VALUES(
      'MY_EXCEPTION', :sqlcode, :sqlerrm, :sqlstate, CURRENT_TIMESTAMP());
  WHEN OTHER CONTINUE THEN
    INSERT INTO test_error_log VALUES(
      'OTHER', :sqlcode, :sqlerrm, :sqlstate, CURRENT_TIMESTAMP());
END;
$$
;
Copy
+---------------------+
| anonymous block     |
|---------------------|
| My counter value: 1 |
+---------------------+

A saída mostra que o exemplo continuava executando o seguinte código depois que a exceção foi gerada:

counter := counter + 1;
RETURN counter;
Copy

For more information about CONTINUE handlers, see EXCEPTION (Script Snowflake).

In rare cases, you might want to explicitly handle an exception by doing nothing. This enables you to continue, rather than terminate, when the exception occurs. For more information, see the NULL command.

Nota

Se você precisar gerar a mesma exceção novamente, veja Raising the same exception again in an exception handler in Snowflake Scripting.

If you don’t set up a handler for an exception, the client that submits the block for execution; for example, Snowsight reports an error as explained in Raising a declared exception in Snowflake Scripting.

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

Raising the same exception again in an exception handler in Snowflake Scripting

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ê usar o Snowflake CLI, SnowSQL, o Classic Console, ou o método execute_stream ou execute_string no código Python Connector, use este exemplo (consulte Usar o Snowflake Scripting no Snowflake CLI, SnowSQL, Classic Console e Python Connector):

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

Passing variables to an exception handler in Snowflake Scripting

É 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ê usar o Snowflake CLI, SnowSQL, o Classic Console, ou o método execute_stream ou execute_string no código Python Connector, use este exemplo (consulte Usar o Snowflake Scripting no Snowflake CLI, SnowSQL, Classic Console e Python Connector):

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ê usar o Snowflake CLI, SnowSQL, o Classic Console, ou o método execute_stream ou execute_string no código Python Connector, use este exemplo (consulte Usar o Snowflake Scripting no Snowflake CLI, SnowSQL, Classic Console e Python Connector):

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). |
+----------------------------------------------------------------------+