2016-07-23

Loopback links, ORA-04091 и dirty read в Oracle

При чтении статьи встретилась знакомая по триггерам ошибка mutating table error в неожиданном месте, интересный способ ее обхода и совершенно неожиданные результаты.

CREATE TABLE a(n NUMBER);
Table created
INSERT INTO a VALUES(100);
1 row inserted
INSERT INTO a VALUES(200);
1 row inserted
INSERT INTO a VALUES(300);
1 row inserted
COMMIT;
Commit complete
CREATE OR REPLACE FUNCTION a_avg RETURN NUMBER AS
  l_avg NUMBER;
BEGIN
  SELECT AVG(n) INTO l_avg FROM a;
  dbms_output.put_line('avg=' || l_avg);
  RETURN l_avg;
END a_avg;
/
Function created
SHOW ERRORS
No errors for FUNCTION SYSTEM.A_AVG
UPDATE a SET n = a_avg();
UPDATE a SET n = a_avg()
ORA-04091: table SYSTEM.A is mutating, trigger/function may not see it
ORA-06512: at "SYSTEM.A_AVG", line 4

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

Далее в статье приводится способ обхода через loopback database link. Модифицируем немного функцию и вставляем @loopback

CREATE OR REPLACE FUNCTION a_avg_loopback RETURN NUMBER AS
  l_avg NUMBER;
BEGIN
  SELECT AVG(n) INTO l_avg FROM a@loopback;
  dbms_output.put_line('avg=' || l_avg);
  RETURN l_avg;
END a_avg_loopback;
/
Function created
SHOW ERRORS
No errors for FUNCTION SYSTEM.A_AVG_LOOPBACK
UPDATE a SET n = a_avg_loopback();
avg=200
avg=233.333333333333333333333333333333333333
avg=244.444444444444444444444444444444444444
3 rows updated
ROLLBACK;
Rollback complete

Ошибка пропала, но… Через loopback мы смогли увидеть данные, которые еще не закоммичены. Этакий dirty read, но скорее всего мы просто присоединяемся к той же транзакции. Причем, как видно, результат для каждой строчки считается с учетом обновленных строк.

UPD: дальнейшее исследование показало, что loopback link вообще не создал отдельной сессии.

UPD2: сессия создается, но ровно 1 раз за сессию. Исследование тут

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