Retenção de erros
Este tópico descreve como o Amazon Redshift lida com erros.
Quando uma consulta ou comando em um procedimento armazenado gera um erro, as consultas subsequentes não são executadas e a transação é revertida. Mas você pode tratar os erros usando um bloco EXCEPTION.
nota
O comportamento padrão é que um erro fará com que as consultas subsequentes não sejam executadas, mesmo quando não há outras condições de geração de erros no procedimento armazenado.
[ <<label>> ] [ DECLARE declarations ] BEGIN statements EXCEPTION WHEN OTHERS THEN statements END;
Quando ocorre uma exceção e você adiciona um bloco de tratamento de exceções, é possível gravar instruções RAISE e a maioria das outras instruções PL/PGSQL. Por exemplo, é possível levantar uma exceção com uma mensagem personalizada ou inserir um registro em uma tabela de registro.
Ao inserir o bloco de tratamento de exceções, a transação atual é revertida, e cria-se uma nova transação para executar as instruções no bloco. Se as instruções no bloco forem executadas sem erros, a transação será confirmada, e a exceção será lançada novamente. Por fim, o procedimento armazenado é encerrado.
A única condição compatível em um bloco de exceção é OTHERS, que corresponde a todos os tipos de erros, exceto o cancelamento de consulta. Além disso, se ocorrer um erro em um bloco de tratamento de exceções, ele poderá ser detectado por um bloco de tratamento de exceções externo.
Quando ocorre um erro dentro do procedimento NONATOMIC, o erro não será relançado se for tratado por um bloco de exceções. Consulte a instrução PL/pgSQL RAISE
para lançar uma exceção capturada pelo bloco de tratamento de exceções. Essa instrução só é válida em blocos de tratamento de exceções. Para ter mais informações, consulte RAISE.
Controle do que acontecerá depois de um erro em um procedimento armazenado, com o manipulador CONTINUE
O manipulador CONTINUE
é um tipo de manipulador de exceções que controla o fluxo de execuções em um procedimento armazenado NONATOMIC. Ao usá-lo, você pode capturar e processar exceções sem encerrar o bloco de instruções existente. Normalmente, quando ocorre um erro em um procedimento armazenado, o fluxo é interrompido, e o erro é retornado ao chamador. No entanto, em alguns casos de uso, a condição de erro não é grave o suficiente para justificar a interrupção do fluxo. Convém processar o erro normalmente, usando a lógica de tratamento de erros de sua preferência em uma transação à parte e, em seguida, continuar executando as instruções que seguem o erro. Esta é a sintaxe.
[ DECLARE declarations ] BEGIN statements EXCEPTION [ CONTINUE_HANDLER | EXIT_HANDLER ] WHEN OTHERS THEN handler_statements END;
Há várias tabelas do sistema disponíveis para ajudar você a coletar informações sobre vários tipos de erros. Para mais informações, consulte STL_LOAD_ERRORS, STL_ERROR e SYS_STREAM_SCAN_ERRORS. Também há tabelas de sistema adicionais que você pode usar para solucionar erros. Mais informações sobre elas podem ser encontradas em Referência de visualizações e tabelas do sistema.
Exemplo
O exemplo a seguir mostra como gravar instruções no bloco de tratamento de exceções. O procedimento armazenado está usando o comportamento padrão de gerenciamento de transações.
CREATE TABLE employee (firstname varchar, lastname varchar); INSERT INTO employee VALUES ('Tomas','Smith'); CREATE TABLE employee_error_log (message varchar); CREATE OR REPLACE PROCEDURE update_employee_sp() AS $$ BEGIN UPDATE employee SET firstname = 'Adam' WHERE lastname = 'Smith'; EXECUTE 'select invalid'; EXCEPTION WHEN OTHERS THEN RAISE INFO 'An exception occurred.'; INSERT INTO employee_error_log VALUES ('Error message: ' || SQLERRM); END; $$ LANGUAGE plpgsql; CALL update_employee_sp(); INFO: An exception occurred. ERROR: column "invalid" does not exist CONTEXT: SQL statement "select invalid" PL/pgSQL function "update_employee_sp" line 3 at execute statement
Neste exemplo, se chamarmos update_employee_sp
, é gerada a mensagem informativa An exception occurred. (Ocorreu uma exceção), e a mensagem de erro é inserida no log employee_error_log
da tabela de logs. A exceção original é lançada novamente antes que o procedimento armazenado seja encerrado. As consultas a seguir mostram registros resultantes da execução do exemplo.
SELECT * from employee; firstname | lastname -----------+---------- Tomas | Smith SELECT * from employee_error_log; message ------------------------------------------------ Error message: column "invalid" does not exist
Para obter mais informações sobre RAISE, inclusive ajuda para formatação e uma lista dos níveis adicionais, consulte Instruções da PL/pgSQL compatíveis.
O exemplo a seguir mostra como gravar instruções no bloco de tratamento de exceções. O procedimento armazenado está usando o comportamento NONATOMIC de gerenciamento de transações. Neste exemplo, nenhum erro é revertido ao chamador depois da conclusão da chamada do procedimento. A instrução UPDATE não é revertida devido ao erro na próxima instrução. A mensagem informativa é gerada e a mensagem de erro é inserida na tabela de log.
CREATE TABLE employee (firstname varchar, lastname varchar); INSERT INTO employee VALUES ('Tomas','Smith'); CREATE TABLE employee_error_log (message varchar); -- Create the SP in NONATOMIC mode CREATE OR REPLACE PROCEDURE update_employee_sp_2() NONATOMIC AS $$ BEGIN UPDATE employee SET firstname = 'Adam' WHERE lastname = 'Smith'; EXECUTE 'select invalid'; EXCEPTION WHEN OTHERS THEN RAISE INFO 'An exception occurred.'; INSERT INTO employee_error_log VALUES ('Error message: ' || SQLERRM); END; $$ LANGUAGE plpgsql; CALL update_employee_sp_2(); INFO: An exception occurred. CALL SELECT * from employee; firstname | lastname -----------+---------- Adam | Smith (1 row) SELECT * from employee_error_log; message ------------------------------------------------ Error message: column "invalid" does not exist (1 row)
Este exemplo mostra como criar um procedimento armazenado em dois sub-blocos. Quando o procedimento armazenado é chamado, o erro do primeiro sub-bloco é tratado por seu bloco de tratamento de exceções. Após a conclusão do primeiro sub-bloco, o procedimento continua executando o segundo sub-bloco. Pelo resultado, podemos ver que nenhum erro é gerado quando a chamada do procedimento é concluída. As operações UPDATE e INSERT na tabela “employee” são confirmadas. As mensagens de erro dos dois blocos de exceção são inseridas na tabela de log.
CREATE TABLE employee (firstname varchar, lastname varchar); INSERT INTO employee VALUES ('Tomas','Smith'); CREATE TABLE employee_error_log (message varchar); CREATE OR REPLACE PROCEDURE update_employee_sp_3() NONATOMIC AS $$ BEGIN BEGIN UPDATE employee SET firstname = 'Adam' WHERE lastname = 'Smith'; EXECUTE 'select invalid1'; EXCEPTION WHEN OTHERS THEN RAISE INFO 'An exception occurred in the first block.'; INSERT INTO employee_error_log VALUES ('Error message: ' || SQLERRM); END; BEGIN INSERT INTO employee VALUES ('Edie','Robertson'); EXECUTE 'select invalid2'; EXCEPTION WHEN OTHERS THEN RAISE INFO 'An exception occurred in the second block.'; INSERT INTO employee_error_log VALUES ('Error message: ' || SQLERRM); END; END; $$ LANGUAGE plpgsql; CALL update_employee_sp_3(); INFO: An exception occurred in the first block. INFO: An exception occurred in the second block. CALL SELECT * from employee; firstname | lastname -----------+----------- Adam | Smith Edie | Robertson (2 rows) SELECT * from employee_error_log; message ------------------------------------------------- Error message: column "invalid1" does not exist Error message: column "invalid2" does not exist (2 rows)
O exemplo a seguir mostra como usar o manipulador de exceções CONTINUE. Esse exemplo cria duas tabelas e as usa em um procedimento armazenado. O manipulador CONTINUE controla o fluxo de execução em um procedimento armazenado com comportamento de gerenciamento de transações NONATOMIC.
CREATE TABLE tbl_1 (a int); CREATE TABLE tbl_error_logging(info varchar, err_state varchar, err_msg varchar); CREATE OR REPLACE PROCEDURE sp_exc_handling_1() NONATOMIC AS $$ BEGIN INSERT INTO tbl_1 VALUES (1); -- Expect an error for the insert statement following, because of the invalid value INSERT INTO tbl_1 VALUES ("val"); INSERT INTO tbl_1 VALUES (2); EXCEPTION CONTINUE_HANDLER WHEN OTHERS THEN INSERT INTO tbl_error_logging VALUES ('Encountered error', SQLSTATE, SQLERRM); END; $$ LANGUAGE plpgsql;
Chame o procedimento armazenado:
CALL sp_exc_handling_1();
O fluxo avança assim:
Ocorre um erro porque é feita uma tentativa de inserir um tipo de dados incompatível em uma coluna. O controle passa para o bloco EXCEPTION. Quando o bloco de tratamento de exceções é acessado, a transação atual é revertida e uma nova transação implícita é criada para executar as instruções nela.
Se as instruções em CONTINUE_HANDLER forem executadas sem erros, o controle passará imediatamente para a instrução depois da instrução que estiver causando a exceção. (Se uma instrução em CONTINUE_HANDLER gerar uma nova exceção, você poderá tratá-la com um manipulador de exceções dentro do bloco EXCEPTION.)
Depois de você chamar o procedimento armazenado de amostra, as tabelas conterão os seguintes registros:
Se você executar
SELECT * FROM tbl_1;
, ele retornará dois registros. Eles contêm os valores1
e2
.Se você executar
SELECT * FROM tbl_error_logging;
, ele retornará um registro com estes valores: Erro encontrado, 42703 e A coluna "val" não existe em tbl_1.
O exemplo adicional do tratamento de erros a seguir usa um manipulador EXIT e um manipulador CONTINUE. Ele cria duas tabelas: uma tabela de dados e uma tabela de logs. Ele também cria um procedimento armazenado que demonstra o tratamento de erros:
CREATE TABLE tbl_1 (a int); CREATE TABLE tbl_error_logging(info varchar, err_state varchar, err_msg varchar); CREATE OR REPLACE PROCEDURE sp_exc_handling_2() NONATOMIC AS $$ BEGIN INSERT INTO tbl_1 VALUES (1); BEGIN INSERT INTO tbl_1 VALUES (100); -- Expect an error for the insert statement following, because of the invalid value INSERT INTO tbl_1 VALUES ("val"); INSERT INTO tbl_1 VALUES (101); EXCEPTION EXIT_HANDLER WHEN OTHERS THEN INSERT INTO tbl_error_logging VALUES ('Encountered error', SQLSTATE, SQLERRM); END; INSERT INTO tbl_1 VALUES (2); -- Expect an error for the insert statement following, because of the invalid value INSERT INTO tbl_1 VALUES ("val"); INSERT INTO tbl_1 VALUES (3); EXCEPTION CONTINUE_HANDLER WHEN OTHERS THEN INSERT INTO tbl_error_logging VALUES ('Encountered error', SQLSTATE, SQLERRM); END; $$ LANGUAGE plpgsql;
Depois de criar o procedimento armazenado, chame-o com o seguinte:
CALL sp_exc_handling_2();
Quando ocorre um erro no bloco de exceção interno, delimitado pelo conjunto interno de BEGIN e END, ele é tratado pelo manipulador EXIT. Qualquer erro ocorrido no bloco externo é tratado pelo manipulador CONTINUE.
Depois de você chamar o procedimento armazenado de amostra, as tabelas conterão os seguintes registros:
Se você executar
SELECT * FROM tbl_1;
, ele retornará quatro registros, com os valores 1, 2, 3 e 100.Se você executar
SELECT * FROM tbl_error_logging;
, ele retornará dois registros. Eles têm estes valores: Erro encontrado, 42703 e A coluna "val" não existe em tbl_1.
Se a tabela tbl_error_logging não existir, ela vai gerar uma exceção.
O exemplo a seguir mostra como usar o manipulador de exceções CONTINUE com o loop FOR. Esse exemplo cria três tabelas e as usa em um loop FOR dentro de um procedimento armazenado. O loop FOR é uma variante do conjunto de resultados, o que significa que itera nos resultados de uma consulta:
CREATE TABLE tbl_1 (a int); INSERT INTO tbl_1 VALUES (1), (2), (3); CREATE TABLE tbl_2 (a int); CREATE TABLE tbl_error_logging(info varchar, err_state varchar, err_msg varchar); CREATE OR REPLACE PROCEDURE sp_exc_handling_loop() NONATOMIC AS $$ DECLARE rec RECORD; BEGIN FOR rec IN SELECT a FROM tbl_1 LOOP IF rec.a = 2 THEN -- Expect an error for the insert statement following, because of the invalid value INSERT INTO tbl_2 VALUES("val"); ELSE INSERT INTO tbl_2 VALUES (rec.a); END IF; END LOOP; EXCEPTION CONTINUE_HANDLER WHEN OTHERS THEN INSERT INTO tbl_error_logging VALUES ('Encountered error', SQLSTATE, SQLERRM); END; $$ LANGUAGE plpgsql;
Chame o procedimento armazenado:
CALL sp_exc_handling_loop();
Depois de você chamar o procedimento armazenado de amostra, as tabelas conterão os seguintes registros:
Se você executar
SELECT * FROM tbl_2;
, ele retornará dois registros. Eles contêm os valores 1 e 3.Se você executar
SELECT * FROM tbl_error_logging;
, ele retornará um registro com estes valores: Erro encontrado, 42703 e A coluna "val" não existe em tbl_2.
Observações sobre o uso referente ao manipulador CONTINUE:
As palavras-chave CONTINUE_HANDLER e EXIT_HANDLER só podem ser usadas em procedimentos armazenados NONATOMIC.
As palavras-chave CONTINUE_HANDLER e EXIT_HANDLER são opcionais. EXIT_HANDLER é o padrão.