Transaction Autonomous – O Que, Quando, Onde e Por Que
Um subprograma normalmente tem suas operações salvas ou não no banco de dados de acordo com o que acontece com o programa principal onde ele está inserido. Isso quer dizer que, se uma procedure chama uma função e a procedure falha, nem as alterações feitas pela procedure nem as alterações feitas pela função são salvas na base, afinal trata-se da mesma transação, a qual é atômica (indivisível, ou seja, ou ela toda é commitada ou ela toda sofre rollback). No entanto, se a função contiver o pragma autonomous_transaction, ela se comporta como uma segunda transação, que é isolada e independente, e suas alterações na base podem ser salvas ou não independentemente da transação master que a originou.
A diretiva AUTONOMOUS_TRANSACTION altera a forma com que a transação trata a um subprograma. Um pragma na verdade é uma diretiva de compilação e os subprogramas marcados com este pragma são processados em tempo de compilação e não em tempo de execução, e passam informações diretamente ao compilador.
O termo AUTONOMOUS_TRANSACTION se refere à habilidade do PL/SQL temporariamente suspender a transação corrente e iniciar uma nova transação, totalmente independente, que funciona de forma autônoma com relação à transação original.
Imagine a seguinte situação: Para fins de auditoria, criamos uma tabela de log que contém os dados referentes a todas as alterações feitas nos dados de 5 tabelas críticas do banco de dados da empresa, armazenando o IP da máquina que originou a transação, qual tabela sofreu alteração, o que foi feito, etc, e um trigger é responsável por inserir tais dados nessa tabela. Pois bem, vamos supor então que por um motivo ou outro essa transação tenha falhado. Não desejamos que os logs de auditoria também sofram rollback, pois estaremos perdendo dados preciosos de tentativas frustradas de acesso não autorizado.
Neste caso, o trigger deve disparar um subprograma autônomo, cujo sucesso da transação não dependa do resultado da transação principal que o originou.
A utilização em tratativas de logs de auditoria são comuns e não causam nenhum tipo de problema ao banco de dados, uma vez que não está lidando com tabelas de negócio, ou seja, que guardam dados essenciais para o negócio. Por este motivo a transação autônoma é segura, pois a integridade do banco de dados está resguardada.
Digo isso porque já vi muitos códigos resolvendo regras de negócio com transações autônomas, o que pode gerar um problema grande para o banco de dados. Imagine que a alteração de um valor em uma tabela deve causar alterações em outras tabelas. Se a primeira alteração falha, o ideal é que as demais alterações não ocorram… neste caso o procedimento que faria as demais alterações não poderia jamais ser autônomo!
Qualquer subprograma, como procedures, funções ou até mesmo blocos anônimos PL/SQL podem conter este pragma. No entanto, se for utilizado dentro de pacotes, o pragma deve ser declarado para as funções e procedures que fazem parte do pacote, e não para o pacote em si.
Exemplo de Utilização
Como um exemplo de utilização da transação autônoma, vamos assumir que precisamos gravar logs de erro em uma tabela do banco de dados. Precisamos fazer rollback da transação principal porque ela resultaria em um erro, mas não queremos perder o log do que aconteceu nessa transação. A tabela que conterá os logs de erro possui a seguinte estrutura:
CREATE TABLE tb_log_erros(
codigo integer,
msg varchar2(2000),
data date,
usuario varchar2(50),
nm_mach varchar2(100),
prog varchar2(100)
);
O procedimento que deve ser invocado para inserir o log do erro na tabela é:
CREATE OR REPLACE PROCEDURE grava_log_erros(
log_codigo IN INTEGER,
log_msg IN VARCHAR2) IS
--
PRAGMA AUTONOMOUS_TRANSACTION;
--
CURSOR cur_erro IS
SELECT machine, program
FROM v$session
WHERE audsid = USERENV('SESSIONID');
--
PT = Parent Transaction;
CT = Child Autonomous Transaction;
--
rec cur_erro%ROWTYPE;
--
BEGIN
--
OPEN cur_erro;
FETCH cur_erro INTO rec;
CLOSE cur_erro;
--
INSERT INTO tb_log_erros values (
log_codigo,
log_msg,
SYSDATE,
USER,
rec.machine,
rec.program
);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBAACK;
END;
/
Para testar o código acima, podemos executar o seguinte bloco anônimo PL/SQL:
BEGIN
INSERT INTO HR.EMPLOYEES (first_name) VALUES (‘Maria’);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
grava_log_erros(SQLCODE,SQLERRM);
ROLLBACK;
RAISE;
END;
Ao executar o código acima, basta verificar nas tabelas EMPLOYEES e TB_LOG_ERROS as linhas inseridas, como segue:
SQL> select * from employees where first_name = ‘Maria’;
no rows selected.
SQL> select codigo, msg from tb_log_erros;
CODIGOMSG
--------------------------------------------------------------------------------------------------------
-1400ORA-01400: cannot insert NULL into (“HR”.”EMPLOYEES”.”EMPLOYEE_ID”
Referências
- BURLESON CONSULTING. PL/SQL Autonomous Transaction Tips. Burleson Consulting, 2015. Disponivel em: http://www.dba-oracle.com/t_autonomous_transaction.htm
- ORACLE HELP CENTER. Autonomous_transaction Pragma. Database PL/SQL User’s Guide and Reference, 2017. Disponivel em: https://docs.oracle.com/cd/B19306_01/appdev.102/b14261/autonotransaction_pragma.htm