powered by simpleCommunicator - 2.0.51     © 2025 Programmizd 02
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Вложенные транзакции
25 сообщений из 37, страница 1 из 2
Вложенные транзакции
    #40027285
НеофитSQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Новая для меня тема частичных откатов, надеюсь я понимаю эти вещи правильно.

Я написал процедуру Outer(), которую кто-то будет вызывать в контексте своей транзакции. О вызывающем предположений мало, поэтому мне надо себя вести вежливо для всех: вызов из джоба, вызов из автономной транзакции, вызов из триггера.

Моя процедура помечает таблицу TBL10, затем вызывает библиотечную процедуру Inner(), которая обновляет другие какие-то таблицы. Библиотечную - значит я не могу ее редактировать, или даже читать. Что именно она обновляет, тоже не знаю. Есть вероятность, что кинет exception без обработки.

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
-- (пример упрощен и стилизован)

procedure Outer is
begin
  -- якорная строчка. Если Inner() потом вылетает, она должна остаться
  -- конечно, вызывающий может всю транзакцию отменить, и эту строчку тоже.
  insert into TBL10 ...;

  --------- начало вложенной транзакции для Inner()
  savepoint BeforeInner;

  begin
    Inner;
  exception 
    when others
      rollback to BeforeInner;
      LogError('exception в Inner(), частичный откат');
      -- без raise, вызывающий не заинтересован в ошибках Inner()
  end;
  --------- завершение вложенной транзакции для Inner()

  -- далее могут быть аналогичные вызовы Inner2(), Inner3()..
end;

procedure Inner
begin
  update TBL21..;
  update TBL22..; -- вылетает по exception, например строка слишком длинная
  update TBL23..;
end Inner;



Код в примере выше при exception в середине Inner() оставит изменения в таблице TBL21, нарушая целостность данных - таблицы 21-22-23 должны изменяться вместе. "Вместе" в житейском смысле, строгой атомарности не требуется. Ранее Inner() всегда вызывалась как одна целая транзакция, поэтому откат по умолчанию устраивал.

Мне нужно чтобы процедура Inner() отработала как вложенная транзакция, т.е. если в середине Inner() происходит exception, чтобы произошел чистый откат только ее действий, но не других ранее происшедших. Процедура Inner не автономная, и не содержит commit, ее чистый откат возможен.

Вызов commit перед вызовом Inner() не устраивает, т.к. это сломает транзакцию тому, кто меня вызывает.
Вложенные транзакции Оракл не поддерживает, поэтому приходится их самому сочинять.

Как я понимаю, у меня есть некоторый выбор:
- поместить SAVEPOINT до вызова Inner(), подавить exception в блоке с откатом до SAVEPOINT, чтобы обеспечить вложенность.
Это самый чистый вариант. Еще не проверил, что "rollback TO" будет позволен из триггера. Если нет, отмечу в документации что функция несовместима с вызовом из триггера.

- поместить мой якорный код в автономную транзакцию и делать полный rollback по exception. Это ограничит откаты для вызывающего, но в моем случае одинокая строчка в TBL10 на целостность данных не влияет.

Бывают еще другие варианты?
...
Рейтинг: 0 / 0
Вложенные транзакции
    #40027298
НеофитSQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Обернул вызываемую чужую процедуру, теперь она "атомарная".
Если вызывающий забудет отказаться от exceptions, получит их как есть.

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
  procedure AtomicInner(eatex in boolean := false) is
  begin
    savepoint BeforeCall;
    Inner;
  exception
    when others then 
      rollback to BeforeCall;
      if not eatex then raise; end if;
  end;



Теперь независимо от ошибок в Inner() или в вызывающем коде, недо-апдейты должны быть исключены.
...
Рейтинг: 0 / 0
Вложенные транзакции
    #40027309
Фотография andrey_anonymous
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
НеофитSQL
Код в примере выше при exception в середине Inner() оставит изменения в таблице TBL21,

Заблуждаетесь.
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
create table dropme_t(s varchar2(100char));
Table created

declare
  procedure Inner is
  begin
    insert into dropme_t(s) values('3.1 Inner Success');
    -- вылетает по exception, например строка слишком длинная
    insert into dropme_t(s) values(rpad('3.2 Inner Too Long',1000));
    insert into dropme_t(s) values('3.3 Inner After exception');
  end Inner;
 
  procedure Outer is
  begin
    -- якорная строчка. Если Inner() потом вылетает, она должна остаться
    -- конечно, вызывающий может всю транзакцию отменить, и эту строчку тоже.
    insert into dropme_t(s) values('2.1 Outer before savepoint');
 
    --------- начало вложенной транзакции для Inner()
    savepoint BeforeInner;
    insert into dropme_t(s) values('2.2 Outer after savepoint');
 
    begin
      Inner;
    exception
      when others then
        rollback to BeforeInner;
        insert into dropme_t(s) values('2.3 exception в Inner(), частичный откат');
        -- без raise, вызывающий не заинтересован в ошибках Inner()
    end;
    --------- завершение вложенной транзакции для Inner()
    -- далее могут быть аналогичные вызовы Inner2(), Inner3()..
  end;
begin
  insert into dropme_t(s) values('1. External First transactional data');
  Outer;
  insert into dropme_t(s) values('4. External Last transactional data');
end;
/
PL/SQL procedure successfully completed

select * from dropme_t order by 1;
 
S
--------------------------------------------------------------------------------
1. External First transactional data
2.1 Outer before savepoint
2.3 exception в Inner(), частичный откат
4. External Last transactional data

SQL> 

...
Рейтинг: 0 / 0
Вложенные транзакции
    #40027317
booby
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Если вы совсем ничего не знаете об Inner, значит у вас не может быть и идей о том,
к какому результату приведет вызов
Код: plsql
1.
rollback to BeforeInner;


(Никакого BeforeInner к этому моменту уже может просто не существовать).

PS
опять вы изобретаете собственную терминологию, или притягиваете "похожую" из соседних контекстов.
Никаких "вложенных транзакций" у Oracle нет.
Совсем нет.
Ни точки останова сохранения, ни автономные транзакции не имитирует поведения того, что обычно принято называть "вложенными транзакциями".
...
Рейтинг: 0 / 0
Вложенные транзакции
    #40027321
НеофитSQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
andrey_anonymous,

> Заблуждаетесь.

Долго правил длинное сообщение, отослал с ошибкой в самом начале. Упс.

Поначалу в коде примера не было savepoint/rollback to, и мое утверждение относилось к первоначальному коду, который выглядел примерно так:

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
procedure Outer is
begin
  insert into TBL10 ...;

  begin
    Inner;
  exception 
    when others
      LogError('exception в Inner(), игнорируем..');
      -- без raise, вызывающий не заинтересован в ошибках Inner()
  end;



В этом виде, код оставил бы строку '3.1 Inner Success' в таблице, без соответствующих 3.2 и 3.3, а мне надо было чтобы 3.* ходили хором.

Спасибо за написание примера - у меня написан почти такой же, пока я проверял свое понимание частичного отката с разных сторон.

Других нареканий нет? Так и принято писать, если нужно откатить только вызываемую функцию, а не всю транзакцию?
...
Рейтинг: 0 / 0
Вложенные транзакции
    #40027325
Фотография andrey_anonymous
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
НеофитSQL
Так и принято писать, если нужно откатить только вызываемую функцию, а не всю транзакцию?

Принято подтирать за собой в обработчике исключений, если предполагается подобный исход.
Т.е. в приведенном Вами примере неправильно написана Inner.
...
Рейтинг: 0 / 0
Вложенные транзакции
    #40027327
НеофитSQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
booby
Если вы совсем ничего не знаете об Inner, значит у вас не может быть и идей о том,
к какому результату приведет вызов
Код: plsql
1.
rollback to BeforeInner;


(Никакого BeforeInner к этому моменту уже может просто не существовать).


Вы наверное готовы привести какой-то супер пример, где вызов Inner() подменяет базу данных и перекомпилирует код :)

У меня есть достаточно сведений про процедуру Inner(), чтобы считать ее обыкновенным кодом. В нем есть селекты, апдейты, может даже вставки. Она может дать exception, специально или по ошибке. Там нет явных или скрытых commit/rollback, и там нет DDL или динамического кода. Я надеялся, что это будет понятно из первого сообщения.

Самое главное, процедура Inner считается правильно работающей когда из нее состоит вся транзакция. Этого посыла должно быть достаточно.
...
Рейтинг: 0 / 0
Вложенные транзакции
    #40027329
НеофитSQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
andrey_anonymous
НеофитSQL
Так и принято писать, если нужно откатить только вызываемую функцию, а не всю транзакцию?

Принято подтирать за собой в обработчике исключений, если предполагается подобный исход.
Т.е. в приведенном Вами примере неправильно написана Inner.


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

Теперь из-за автоматизации контекст вызова изменился, и мне нужно подавить/логнуть некритические ошибки, т.к. юзера больше нет.
Мне было проще обернуть ее в "AtomicInner" как я сделал во втором сообщении этой темы, чем лезть в чужой код. Теперь AtomicInner должна подтереть за собой.
...
Рейтинг: 0 / 0
Вложенные транзакции
    #40027332
Фотография andrey_anonymous
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
НеофитSQL
booby
Если вы совсем ничего не знаете об Inner, значит у вас не может быть и идей о том,
к какому результату приведет вызов
Код: plsql
1.
rollback to BeforeInner;



(Никакого BeforeInner к этому моменту уже может просто не существовать).


Вы наверное готовы привести какой-то супер пример

Достаточно изложить Inner чуть иначе:
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
  procedure Inner is
  begin
    savepoint BeforeInner;
    insert into dropme_t(s) values('3.1 Inner Success');
    -- вылетает по exception, например строка слишком длинная
    insert into dropme_t(s) values(rpad('3.2 Inner Too Long',1000));
    insert into dropme_t(s) values('3.3 Inner After exception');
    exception
      when others then
        rollback to BeforeInner;
        raise;
  end Inner;  


и увидеть зачем в примере строчка
Код: plsql
1.
    insert into dropme_t(s) values('2.2 Outer after savepoint');
...
Рейтинг: 0 / 0
Вложенные транзакции
    #40027335
booby
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
НеофитSQL,
В общем, не надейтесь.
Понять вас более чем затруднительно.
Если "из неё состоит вся транзакция", то что в использующем коде делает установка savepoint вообще?
Кроме того, ваш первый пост очевидно показывает, что вся транзакция из Inner точно не состоит.

Более того, если она обязана быть чёрным ящиком, то все свойства ящика должны содержаться именно в ней,
вместе с точкой сохранения и возврата к ней.
Конечно, наличие в ней некоей Most_Inner потребует получения дополнительных уверенностей в способности возврата к объявленной точки.

Вы зря раздражаетесь, кстати.
Это вовсе не редкость и совсем не удивление, когда принципы "красивого программирования", нажитые в "обычных" языках,
не ложатся и диссонируют с практиками, возникающими при работе с БД.

По мере разрастания базы вашего собственного кода ваша уверенность в том, каким требованиям вызываемая процедура
несомненно отвечает, а какие ее особенности окажутся неподходящими в конкретному случаю,
достаточно быстро подвергнется испытаниям.
...
Рейтинг: 0 / 0
Вложенные транзакции
    #40027350
НеофитSQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
andrey_anonymous,


Inner() не редактируется. Она "чужая".
...
Рейтинг: 0 / 0
Вложенные транзакции
    #40027351
НеофитSQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
booby,

вам доводилось использовать savepoint конструкцию в своем коде? Вы звучите, как вроде она вас чем-то обидела.

> Если "из неё состоит вся транзакция", то что в использующем коде делает установка savepoint вообще?

Состояла. Ранее. Теперь inner() - это часть бОльшей транзакции, которую мне удалось поделить на секции с помощью savepoint. Очень полезная штука.

Не дотягивает до вложенных транзакций, но при достаточной дисциплине программиста позволяет их имитировать.
...
Рейтинг: 0 / 0
Вложенные транзакции
    #40027353
booby
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Не важно, кому что доводилось.

Но критически важно вот это:
НеофитSQL
...
Состояла. Ранее. Теперь ...

Похоже, вы это продолжаете недооценивать.

Ладно.
Пусть сейчас ситуация отвечает вашему символу веры.


Просто положите на ум, что вашему роллбеку когда-нибудь может оказаться не к чему откатываться.
Такое понимание снабдит вас новым набором вопросов.
...
Рейтинг: 0 / 0
Вложенные транзакции
    #40027354
НеофитSQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
booby,

Вопрос в начале темы был "так можно? а как лучше?".

Соберитесь с мыслями и блесните экспертизой :)
...
Рейтинг: 0 / 0
Вложенные транзакции
    #40027356
НеофитSQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Попробуем как это все работает.

Первый этап: библиотечная "чужая" функция сама по себе.

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
SQL> create table TEST20 ( num  NUMBER not null,
  2    text VARCHAR2(10) not null -- короткое поле нам даст проблемы
  3  );
Table created

SQL> create or replace procedure Inner(i in integer, s in varchar2) is
  2  begin
  3    insert into test20(num,text) values( i, '1:' || s);
  4    insert into test20(num,text) values( i, '2:' || s || 'extra'); -- тут рванет, если много текста
  5    insert into test20(num,text) values( i, '3:' || s);
  6  end Inner;
  7  /
Procedure created

SQL> exec Inner(1,'one');

PL/SQL procedure successfully completed.

SQL> select * from test20;

       NUM TEXT
---------- ----------
         1 1:one
         1 2:oneextra
         1 3:one

 Ожидаемо, процедура отработала без ошибок

SQL> exec inner(null,null);
BEGIN inner(null,null); END;

*
ERROR at line 1:
ORA-01400: cannot insert NULL into ("EEK"."TEST20"."NUM")
ORA-06512: at "EEK.INNER", line 3
ORA-06512: at line 1
 null запрещен, сломалось сразу

SQL> exec Inner(0,'zero');
BEGIN Inner(0,'zero'); END;

*
ERROR at line 1:
ORA-12899: value too large for column "EEK"."TEST20"."TEXT" (actual: 11,
maximum: 10)
ORA-06512: at "EEK.INNER", line 4
ORA-06512: at line 1
 сломалось на второй строчке, слишком длинная

SQL> select * from test20;

       NUM TEXT
---------- ----------
         1 1:one
         1 2:oneextra
         1 3:one

 а вот тут интересно. Exceptions откатывали не всю транзакцию, а только
 ту часть, которую исполнял exec. Вот такое поведение мне и надо, в PL/SQL.

SQL> rollback; -- действительно ли не было авто-коммита?

Rollback complete.

SQL> select * from test20;

no rows selected  -- действительно не было.

SQL>
...
Рейтинг: 0 / 0
Вложенные транзакции
    #40027357
НеофитSQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Это выглядит так, как если бы exec ставила точку отката перед вызовом функции, и к ней откатывала.

Легко проверить:

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
SQL> truncate table test20;

Table truncated.

SQL> begin
  2    Inner(1,'one');
  3    Inner(0,'zero');
  4  end;
  5  /
begin
*
ERROR at line 1:
ORA-12899: value too large for column "EEK"."TEST20"."TEXT" (actual: 11,
maximum: 10)
ORA-06512: at "EEK.INNER", line 4
ORA-06512: at line 3


SQL> select * from test20;

no rows selected



Получается, вызов процедуры (через exec или через анонимный блок) из SQL+ содержит встроенный savepoint, поэтому если процедура вылетит по exception, только действия этого вызова процедуры будут подвержены откату.
При вызове двух процедур, точка отката одна, поэтому таблица пуста.

Кстати, хороший вопрос для интервью: при каких условиях результат
Код: plsql
1.
2.
3.
4.
5.
SQL> begin
  2    Inner(1,'one');
  3    Inner(0,'zero');
  4  end;
  5  /



отличается от
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
SQL> begin
  2  inner(1,'one');
  3  end;
  4  /

SQL> begin
  2  inner(0,'zero');
  3  end;
  4  /



Теперь кто внимательно читал, знает.
...
Рейтинг: 0 / 0
Вложенные транзакции
    #40027358
booby
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
НеофитSQL,
вы унылы, но вот вам экспертиза:
1) вместо прямых SavePoint Имярек и т.п. используйте арсенал dbms_transaction

2) то, что следовало бы называть "якорной точкой" я бы предложил после объявления сейвпойнта, а не до,
независимо от способа задания точки сохранения.

3) в описанном варианте вызов LogError может оказаться недостижимым кодом, а значит, стоит не на своем месте

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

5) если вы на самом деле имеете дело с чужим кодом,
при получении ошибки можно было бы сравнивать идентификатор транзакции после "якорной точки" и в месте обработки ошибки.
В комбинации с динамическим именем точки сохранения, это позволит выдать дополнительную информацию в лог, как минимум,
и не пытаться делать уже бессмысленный ролбек.
...
Рейтинг: 0 / 0
Вложенные транзакции
    #40027359
НеофитSQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Осталось обернуть Inner. Цель: при exception в Inner() выполнить ограниченый откат, как в SQL+.

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
SQL> create table TEST ( key  INTEGER, text VARCHAR2(100) );

Table created.

SQL> create or replace procedure Outer(n in integer) is
  2    -- обертка для Inner(). Если Inner() сбойнет, откатывает только ее.
  3    procedure AtomicInner(i in integer, s in varchar2) is
  4    begin
  5      savepoint PrepForUndo;
  6      Inner(i,s);
  7    exception
  8      when others then
  9        rollback to PrepForUndo;
 10    end AtomicInner;
 11
 12  begin
 13    insert into TEST(key,text)  values( n, 'anchor key');
 14    AtomicInner( n, rpad('A',n) ); -- сломается в середине апдейтов
 15  end Outer;
 16  /

Procedure created.

SQL> exec outer(5); -- параметры >3 вызывают сбой

PL/SQL procedure successfully completed.

SQL> select * from test;

KEY  TEXT
----   -----
 5  anchor key

SQL> select * from test20;

no rows selected



Якорная строка на месте, Inner() чисто откатился в ноль.

Еще разок:

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
SQL> truncate table test;
Table truncated

SQL> begin
  2  Outer(5);
  3  Outer(1);
  4  Outer(8);
  5  end;
  6  /

PL/SQL procedure successfully completed.

SQL> select * from test left join test20 on key=num order by key, test20.text;
KEY TEXT             NUM TEXT
---- --------------- ----- ----------
   1 anchor key          1 1:A
   1 anchor key          1 2:Aextra
   1 anchor key          1 3:A
   5 anchor key                                                                                  
   8 anchor key                                                                                  


Ура! первый и третий вызовы сбойнули, чисто откатив неполные измения в TEST20.
При этом якорная строчка осталась в таблице, как я и хотел.

Успех?
...
Рейтинг: 0 / 0
Вложенные транзакции
    #40027360
НеофитSQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
booby
НеофитSQL,
вы унылы, но вот вам экспертиза:
1) вместо прямых SavePoint Имярек и т.п. используйте арсенал dbms_transaction

2) то, что следовало бы называть "якорной точкой" я бы предложил после объявления сейвпойнта, а не до,
независимо от способа задания точки сохранения.

3) в описанном варианте вызов LogError может оказаться недостижимым кодом, а значит, стоит не на своем месте

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

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


Спасибо, по делу. Я почитаю про dbms_transaction, и узнаю что еще Оракл предлагает в этой области.
2) Якорная строчка, не точка. Это я так назвал строчку, которую я хочу сохранить даже если код после нее бросать exceptions.
3) Тут вы меня удивили. rollback to BeforeInner - это не goto BeforeInner; после него выполняется следующая строка.
Вы считаете, что возможна ситуация, когда rollback исполнится, а LogError - нет? Я с нетерпением жду ответа.
4) отложим на минутку, я как раз собирался это протестировать.
5) дитто
...
Рейтинг: 0 / 0
Вложенные транзакции
    #40027362
НеофитSQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
"Если вы думаете, что все идет хорошо, вы что-то не замечаете" - Закон Мерфи.

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

А вдруг? Booby сказал, что будет некуда возвращаться.

Подложим моему коду свинью, изменив inner следующим образом:
Код: plsql
1.
2.
3.
4.
5.
6.
7.
create or replace procedure Inner(i in integer, s in varchar2) is
begin
  if i in (1,11) then rollback; end if; -- строка подвоха!
  insert into test20(num,text) values( i, '1:' || s);
  insert into test20(num,text) values( i, '2:' || s || 'extra');
  insert into test20(num,text) values( i, '3:' || s);
end Inner;



И повторим эксперимент:
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
SQL> truncate table test;

Table truncated.

SQL> truncate table test20;

Table truncated.

SQL> begin
  2  Outer(0); -- ожидаем успех
  3  Outer(1); -- ожидаем подвох, результаты предыдущей строки наверное пропали
  4  Outer(5); -- ожидаем фэйл, но якорь будет?
  5  Outer(11); -- еще подвох, плюс фэйл!
  6  end;
  7  /
begin
*
ERROR at line 1:
ORA-01086: savepoint 'PREPFORUNDO' never established in this session or is
invalid
ORA-06512: at "EEK.OUTER", line 9
ORA-12899: value too large for column "EEK"."TEST20"."TEXT" (actual: 13,
maximum: 10)
ORA-06512: at "EEK.OUTER", line 14
ORA-06512: at line 5



Вот она, коварная ошибка при попытке частичного rollback на несуществующий savepoint!

Вывод: если не знаете кто вас вызывает, не используйте rollback или commit - вы необратимо сломаете транзакцию, о которой не имеете представления (и не должны иметь, т.к. ваш код ниже в иерархии и может вызываться из разных мест).

Подозреваю, что от такого лома нет приема. Если вы вызвали чужую процедуру, которая сделала commit(или rollback) в неожиданный для вас момент, никакие бережные построения транзакций вас не уберегут.
...
Рейтинг: 0 / 0
Вложенные транзакции
    #40027363
НеофитSQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
НеофитSQL
booby
НеофитSQL,
вы унылы, но вот вам экспертиза:
1) вместо прямых SavePoint Имярек и т.п. используйте арсенал dbms_transaction

2) то, что следовало бы называть "якорной точкой" я бы предложил после объявления сейвпойнта, а не до,
независимо от способа задания точки сохранения.

3) в описанном варианте вызов LogError может оказаться недостижимым кодом, а значит, стоит не на своем месте

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

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


Спасибо, по делу. Я почитаю про dbms_transaction, и узнаю что еще Оракл предлагает в этой области.
2) Якорная строчка, не точка. Это я так назвал строчку, которую я хочу сохранить даже если код после нее бросать exceptions.
3) Тут вы меня удивили. rollback to BeforeInner - это не goto BeforeInner; после него выполняется следующая строка.
Вы считаете, что возможна ситуация, когда rollback исполнится, а LogError - нет? Я с нетерпением жду ответа.
4) отложим на минутку, я как раз собирался это протестировать.
5) дитто


Извиняюсь за двойную цитату, пишу дополнение.

Пункт 3 прояснился, вместе с пунктом 4 когда я обозрел ошибку ORA-01086. rollback/TO может вызвать свой exception, теперь понятно.
По поводу 5 я подумаю, спасибо за идею. Подавить все exception не самоцель, в этом конкретном случае это вызвано грубой ошибкой программирования.
Если при этом остановится импорт данных, рухнет джоб или у кого-то запищит пейджер, в моих условиях это приемлемо.
Мне нужно подавить exceptions, которыми процедура сигнализировала (ранее пользователям) о дефектах в данных.
Теперь я такие exceptions ловлю и записываю, одновременно заботясь о частичном откате, т.к процедура этого не делала.
...
Рейтинг: 0 / 0
Вложенные транзакции
    #40027370
andreymx
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
а так ошибки нет, но результат какой?
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
CREATE TABLE T(ID NUMBER);

DECLARE

    v NUMBER := 0;

    PROCEDURE print
    IS
    BEGIN
        SELECT COUNT(*) INTO v FROM T;
        dbms_output.put_line('count='||v);
    END;

    PROCEDURE T
    IS
    BEGIN
        DELETE T;
        SAVEPOINT a1;
    END;

BEGIN

    DELETE T;
    INSERT INTO T VALUES(1);
    COMMIT;

    SAVEPOINT a1;
    print;
    T();
    ROLLBACK TO a1;
    print;

END;

ЗЫ: глобальные переменные - зло
...
Рейтинг: 0 / 0
Вложенные транзакции
    #40027396
Фотография andrey_anonymous
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreymx
а так ошибки нет, но результат какой?

Ну может хоть сейчас поймет, если в 22247097 не догнал :)
...
Рейтинг: 0 / 0
Вложенные транзакции
    #40027457
НеофитSQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
andreymx,


Думаю, что удалит строки, и переопределит savepoint.

Вряд ли у оракла эти savepoints уважают scope, скорее всего это глобальный список name:SCN в сессии.

Могло бы кинуть ошибку при переопределении savepoint, но тогда пришлось бы добавить "cancel savepoint", которого сейчас нет.
...
Рейтинг: 0 / 0
Вложенные транзакции
    #40027461
НеофитSQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Ещё inner() может удалить все данные и сделать commit.
Против этого у меня тоже лекарства нет.

Функция не содержала transaction management потому что вызывалась "между двумя коммитами", тем самым являясь самостоятельной транзакцией.

Теперь это потребовалось, потому что она стала участвовать в бОльшей транзакции неопределенного размера.

SQL+ это хорошо понимает, поэтому там сделано как у меня - каждая DML команда изменения данных является "вложенной" транзакцией, чтоб результаты предыдущих команд не пропали.

Да и сервер перед каждым DDL использует savepoint, согласно документации.
...
Рейтинг: 0 / 0
25 сообщений из 37, страница 1 из 2
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Вложенные транзакции
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]