почему он работает без PRAGMA AUTONOMOUS_TRANSACTION

#oracle #exception #plsql #transactions #pragma

#Oracle #исключение #plsql #транзакции #pragma

Вопрос:

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

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, пожалуйста?

Спасибо,

Комментарии:

1. В вашей процедуре вы выполняете вставку с ФИКСАЦИЕЙ — чего еще вы ожидаете, кроме строки, которая будет вставлена?

2. Удалите EXCEPTION when OTHERS then часть из вашей процедуры. Нет никакого смысла перезаписывать конкретную ошибку общей ошибкой -20000. Просто пропустите это, сообщения об ошибках будут почти одинаковыми, и откат будет выполнен в любом случае.

3. Просто чтобы добавить к первому комментарию Вернфрида: вам не нужна прагма AT для фиксации изменений в БД. Вам это нужно, чтобы зафиксировать НЕКОТОРЫЕ изменения в вашем сеансе, не фиксируя другие. В вашем случае произошел сбой обновления, затем вы выполнили вставку, затем вы зафиксировали. Итак, строка журнала была сохранена.

Ответ №1:

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

Ошибка находится Inserted в Log таблице, поскольку вы обрабатываете ее как Exception handling . Вам нужно понимать поведение AUTONOMOUS транзакции как Independent фрагмента кода, который выполняется, даже если основной вызов proc/pkg завершается неудачей. Он не обрабатывается как часть Exception Handling . Как показано в приведенной ниже демонстрации, вы можете видеть, что процедура, помеченная как 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 
  

Ответ №2:

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

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

 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;
  

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

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