почему он работает без PRAGMA AUTONOMOUS_TRANSACTION - PullRequest
1 голос
/ 24 апреля 2019

У меня неправильное понимание относительно

PRAGMA AUTONOMOUS_TRANSACTION

директива.

Насколько я знаю, она используется при ведении журнала или аудитеПроцедура, для запуска независимо от основной программы (автономной, процедуры, функции или триггера).

У меня есть ОБНОВЛЕНИЕ для таблицы, которая сгенерировала DUP_VAL_ON_INDEX.В этом исключении я вызываю процедуру регистрации, которая записывает ошибку в таблицу.В процедуре регистрации я не указал директиву PRAGMA AUTONOMOUS_TRANSACTION, но она все еще делает вставку в моей таблице регистрации.

Вот мой код:

create table TEST_PRAGMA
    ( COL_1 number primary key
    , COL_2 number
    );

--
insert into TEST_PRAGMA values (1, 200);
insert into TEST_PRAGMA values (2, 200);
--
create table T_LOG    
    ( msg_num number primary key
    , MSG_DATE timestamp(6)
    , INFO_MSG varchar2(10)
    , LONG_MSG varchar2(100)
    );
--    
create sequence SEQ_TEST start with 1 increment by 1 nocache nocycle;

Пакет:

create or replace package pkg_logging as

    procedure PRC_LOG ( P_MSG_NUM number 
                      , P_MSG_DATE timestamp
                      , P_INFO_MSG varchar2
                      , p_long_msg varcahr2);
end PKG_LOGGING;
--
create or replace package body pkg_logging as

    procedure PRC_LOG ( P_MSG_NUM number 
                      , P_MSG_DATE timestamp
                      , P_INFO_MSG varchar2
                      , P_LONG_MSG VARCHAR2)
                      as
    begin

        insert into T_LOG
            ( MSG_NUM
            , MSG_DATE
            , INFO_MSG
            , LONG_MSG
            )
        values
            ( P_MSG_NUM 
            , P_MSG_DATE
            , P_INFO_MSG
            , P_LONG_MSG

            );
        commit;
    EXCEPTION
        when OTHERS then
            rollback;
            RAISE_APPLICATION_ERROR(-20000, 'other error has occured: ' || sqlcode || ' - ' || sqlerrm);
    end PRC_LOG;
end PKG_LOGGING;
--
set SERVEROUTPUT on;
begin

    update TEST_PRAGMA set COL_1 = 1 where COL_2 = 200;
    commit;

EXCEPTION
    when DUP_VAL_ON_INDEX then 
    dbms_output.put_line ('DUP_VAL_ON_INDEX error has occured');
        PKG_LOGGING.PRC_LOG(SEQ_TEST.NEXTVAL, systimestamp, 'error', 'test de logging');
        rollback;
end;

Поскольку я не указал директиву PRAGMA, я ожидал не регистрировать ошибку, даже если логика верна.

Может кто-нибудь объяснить мне, почему она все еще регистрирует мою ошибку, и предоставить примергде он не регистрирует код, если я не укажу директиву PRAGMA AUTONOMOUS_TRANSACTION, пожалуйста?

Спасибо,

Ответы [ 2 ]

2 голосов
/ 24 апреля 2019

Может кто-нибудь объяснить мне, почему это все еще регистрирует мою ошибку и предоставить пример, где не регистрируется код, если я не укажу PRAGMA Директива AUTONOMOUS_TRANSACTION, пожалуйста?

Ошибка: Inserted в таблице Log, поскольку вы обрабатываете ее как Exception handling. Вам нужно понимать поведение транзакции AUTONOMOUS как части кода Independent, который выполняется даже в случае сбоя основного вызова proc/pkg. Это не обрабатывается как часть Exception Handling. Как показано в демонстрации ниже, вы можете видеть, что proc, помеченный как AUTONOMOUS, вызывается непосредственно в блоке BEGIN, а не в блоке Exception, чтобы понять поведение.

DECLARE
    l_salary   NUMBER;
--Private Proc marking as Autonomous transaction 
procedure nested_block ‬ 
   as 
   pragma AUTONOMOUS_TRANSACTION;
   BEGIN
     UPDATE emp
     SET salary=salary+15000
     WHERE emp_no=1002;
    COMMIT;
    END;‭
--Main Block    ‬
BEGIN
SELECT salary 
INTO l_salary 
FROM emp 
WHERE emp_no=1001; 

Dbms_output.put_line('Before Salary of 1001 is'||l_salary); 

SELECT salary 
INTO l_salary 
FROM emp WHERE emp_no=1002;

Dbms_output.put_line('Before Salary of 1002 is '|| 1_salary);

UPDATE emp
    SET
        salary = salary + 5000
WHERE emp_no = 1001;

--Calling Autonomous transaction
nested_block;

--And rolling back previous updates.
ROLLBACK;

SELECT salary INTO
    l_salary
FROM emp
WHERE emp_no = 1001;

dbms_output.put_line('After Salary of 1001 is'|| l_salary);

SELECT salary 
INTO  l_salary
FROM emp
WHERE emp_no = 1002;

dbms_output.put_line('After Salary of 1002 is ' || l_salary);

end;

Выход:

На выходе будет Update выполнено в Autonomous транзакции. Обновления, сделанные в блоке main, будут rolledback, но не те, которые сделаны в private proc, помеченном как Autonomous

Before Salary of 1001 is 15000 
Before Salary of 1002 is 10000 
After  Salary of 1001 is 15000 
After  Salary of 1002 is 25000 
0 голосов
/ 24 апреля 2019

PKG_LOGGING.PRC_LOG () имеет оператор commit, поэтому он будет фиксироваться.

Предположим, ваш код выглядел так:

set SERVEROUTPUT on;
begin

    insert into TEST_PRAGMA values (3, 300);
    PKG_LOGGING.PRC_LOG(SEQ_TEST.NEXTVAL, systimestamp, 'info', 'inserted a record');

    update TEST_PRAGMA set COL_1 = 1 where COL_2 = 200;
    commit;

EXCEPTION
    when DUP_VAL_ON_INDEX then 
    dbms_output.put_line ('DUP_VAL_ON_INDEX error has occured');
        PKG_LOGGING.PRC_LOG(SEQ_TEST.NEXTVAL, systimestamp, 'error', 'test de logging');
        rollback;
end;

Сколько записей вы бы имели в TEST_PRAGMA? Три .Потому что вставка была зафиксирована, когда мы вызвали PKG_LOGGING.PRC_LOG (), и, следовательно, откат в обработчике исключений не имел никакого эффекта.И именно поэтому мы должны использовать PRAGMA AUTONOMOUS_TRANSACTION в процедурах аудита и регистрации: чтобы мы могли успешно сохранять наши сообщения регистрации, не влияя на более широкую транзакцию.

Таким образом, вы должны добавить PRAGMA AUTONOMOUS_TRANSACTION к PKG_LOGGING.PRC_LOG ().


Кстати, я думаю, вы должны быть осторожны с обработчиком ошибок, подобным этому, в пакете регистрации:

EXCEPTION
    when OTHERS then
        rollback;
        RAISE_APPLICATION_ERROR(-20000, 'other error has occured: ' || sqlcode || ' - ' || sqlerrm);
end PRC_LOG;

В некоторых ситуациях мы определенно хотели бы остановить наш процесс, если мы не можем записать важную информацию.Но в других случаях мы хотим, чтобы регистрация закончилась неудачей.Например, мне нужен пакетный запуск в течение ночи, чтобы завершить работу, если он не может записать ошибки, потому что этот журнал - мой единственный способ узнать, что - если что-то - пошло не так, и лучше всего не запускать его, чтобы он выполнялсяне полностью, и я не знаю, что некоторые вещи не удалось.Но если я просто пишу некоторые сообщения трассировки в Test, я мог бы предпочесть, чтобы длительный процесс завершался без полного набора трасс, а не для завершения, потому что в таблице журналов не хватило места.

Также, использование raise_application_error() не обязательно.Просто выполните raise; после отката и покончите с этим.

...