before delete триггер, выполняющий insert строки в "свою" же таблицу: результат удивляет

Таблоид
Дата: 16.06.2012 19:05:47
hi all

Что-то на изврат снова потянуло... :-)
На новой базе ('t2.fdb') делаю следующее:

recreate table tz(id int, f01 int); 
commit;
insert into tz values(1,100);
commit;

set term ^;
execute block as begin
execute statement 'drop sequence g;'; when any do begin end
end^
set term ;^
commit;
create sequence g;
commit;

set term ^;
create or alter trigger tz_bd for tz
active before delete position 0
as
begin
insert into tz(id, f01) values(gen_id(g,1), old.f01);
end^
set term ;^
select * from tz;
commit;
То есть, перед удалением строки в таблице вызывается триггер, вставляющий в эту же таблицу новую строку.
С удивлением обнаружил, что удаление в таком случае может оказаться бесконечным. Что еще более изумляет, так это то, что в результате прерывания этого удаления в базе не оказывается мусора (хотя записи добавлялись - см аттач с трейсом).

Итак:
session #1
C:\1INSTALL\FIREBIRD\Data>isql localhost/3050:C:\1INSTALL\FIREBIRD\Data\T2.FDB -n
Database: localhost/3050:C:\1INSTALL\FIREBIRD\Data\T2.FDB
SQL> delete from tz; -- "ушло в себя", не закончится никогда

session #2
Запускаю трейс:
bin\fbtracemgr -sta -c zaudit.conf -se localhost/3050:service_mgr|mtee trc1.txt

session #3
Вырубаю окно #1 через mon$attachments:
C:\1INSTALL\FIREBIRD\Data>isql localhost/3050:C:\1INSTALL\FIREBIRD\Data\T2.FDB -n
Database: localhost/3050:C:\1INSTALL\FIREBIRD\Data\T2.FDB
SQL> set list on;
SQL> select * from mon$attachments; commit;

+
MON$ATTACHMENT_ID 3
MON$SERVER_PID 1840
MON$STATE 1
MON$ATTACHMENT_NAME C:\1INSTALL\FIREBIRD\DATA\T2.FDB
MON$USER SYSDBA

MON$ROLE NONE

MON$REMOTE_PROTOCOL TCPv4
MON$REMOTE_ADDRESS 127.0.0.1
MON$REMOTE_PID 1472
MON$CHARACTER_SET_ID 0
MON$TIMESTAMP 2012-06-16 18:29:21.4530
MON$GARBAGE_COLLECTION 1
MON$REMOTE_PROCESS C:\1INSTALL\FIREBIRD\FB_2_5\bin\isql.exe
MON$STAT_ID 2

MON$ATTACHMENT_ID 4
MON$SERVER_PID 1840
MON$STATE 1
MON$ATTACHMENT_NAME C:\1INSTALL\FIREBIRD\DATA\T2.FDB
MON$USER SYSDBA

MON$ROLE NONE

MON$REMOTE_PROTOCOL TCPv4
MON$REMOTE_ADDRESS 127.0.0.1
MON$REMOTE_PID 1812
MON$CHARACTER_SET_ID 0
MON$TIMESTAMP 2012-06-16 18:29:23.9680
MON$GARBAGE_COLLECTION 1
MON$REMOTE_PROCESS C:\1INSTALL\FIREBIRD\FB_2_5\bin\isql.exe
MON$STAT_ID 5

SQL> delete from mon$attachments where mon$attachment_id<>current_connection; commit;

После ввода этой команды окно #1 поработало еще около 5...7 сек и затем отвалилось:
Statement failed, SQLSTATE = 08003
connection shutdown
SQL> Statement failed, SQLSTATE = 08003
connection shutdown

Окно с трейсом продолжало молотить еще очень долго, но в итоге там появилось вот что:
+
2012-06-16T18:31:49.6710 (1840:0232B27C) EXECUTE_STATEMENT_FINISH

C:\1INSTALL\FIREBIRD\DATA\T2.FDB (ATT_3, SYSDBA:NONE, NONE, TCPv4:127.0.0.1)

C:\1INSTALL\FIREBIRD\FB_2_5\bin\isql.exe:1836

(TRA_18, CONCURRENCY | WAIT | READ_WRITE)



Statement 81:

-------------------------------------------------------------------------------

delete from mon$attachments where mon$attachment_id<>current_connection

0 records fetched

135 ms, 1 read(s), 73 fetch(es)



Table Natural Index Update Insert Delete Backout Purge Expunge

***************************************************************************************************************

RDB$RELATIONS 16

RDB$CHARACTER_SETS 1

RDB$COLLATIONS 1



2012-06-16T18:31:49.6710 (1840:0232C0B0) EXECUTE_TRIGGER_FINISH

C:\1INSTALL\FIREBIRD\DATA\T2.FDB (ATT_2, SYSDBA:NONE, NONE, TCPv4:127.0.0.1)

C:\1INSTALL\FIREBIRD\FB_2_5\bin\isql.exe:116

(TRA_17, CONCURRENCY | WAIT | READ_WRITE)

TZ_BD FOR TZ (BEFORE DELETE)

30 ms, 3 write(s), 12 fetch(es), 9 mark(s)



Table Natural Index Update Insert Delete Backout Purge Expunge

***************************************************************************************************************

TZ 1



2012-06-16T18:31:49.6710 (1840:0232C0B0) EXECUTE_TRIGGER_FINISH

C:\1INSTALL\FIREBIRD\DATA\T2.FDB (ATT_2, SYSDBA:NONE, NONE, TCPv4:127.0.0.1)

C:\1INSTALL\FIREBIRD\FB_2_5\bin\isql.exe:116

(TRA_17, CONCURRENCY | WAIT | READ_WRITE)

TZ_BD FOR TZ (BEFORE DELETE)

0 ms, 4 fetch(es), 2 mark(s)



Table Natural Index Update Insert Delete Backout Purge Expunge

***************************************************************************************************************

TZ 1



2012-06-16T18:31:49.6710 (1840:0232C0B0) FAILED EXECUTE_TRIGGER_FINISH

C:\1INSTALL\FIREBIRD\DATA\T2.FDB (ATT_2, SYSDBA:NONE, NONE, TCPv4:127.0.0.1)

C:\1INSTALL\FIREBIRD\FB_2_5\bin\isql.exe:116

(TRA_17, CONCURRENCY | WAIT | READ_WRITE)

TZ_BD FOR TZ (BEFORE DELETE)

0 ms, 1 fetch(es), 1 mark(s)



2012-06-16T18:31:49.7960 (1840:0232B27C) COMMIT_TRANSACTION

C:\1INSTALL\FIREBIRD\DATA\T2.FDB (ATT_3, SYSDBA:NONE, NONE, TCPv4:127.0.0.1)

C:\1INSTALL\FIREBIRD\FB_2_5\bin\isql.exe:1836

(TRA_18, CONCURRENCY | WAIT | READ_WRITE)

131 ms, 1 read(s), 1 write(s), 1 fetch(es), 1 mark(s)



2012-06-16T18:32:00.7650 (1840:0232C0B0) DETACH_DATABASE

C:\1INSTALL\FIREBIRD\DATA\T2.FDB (ATT_2, SYSDBA:NONE, NONE, TCPv4:127.0.0.1)

C:\1INSTALL\FIREBIRD\FB_2_5\bin\isql.exe:116



2012-06-16T18:32:00.7650 (1840:0232C0B0) TRACE_FINI

SESSION_3

В каком статусе окажется транзакция TRA_17, выполнявшая бесконечные DELETE / INSERT'ы ? По ней в трейсе нет ни коммита, ни роллбака ==> она должна быть распознана другими транзакциями как 'dead'.

Но тогда получается, что в базе должны остаться мусорные версии записей, созданные этой транзакцией.
Однако, вот что вижу в gstat -r t2.fdb:
Database "T2.FDB"
Database header page information:
<...>
ODS version 11.2
Oldest transaction 21
Oldest active 22
Oldest snapshot 20
Next transaction 25
Bumped transaction 1
<...>
Database dialect 3
Creation date Jun 16, 2012 18:38:56
Attributes force write

Variable header data:
*END*


Database file sequence:
File T2.FDB is the only file

Analyzing database pages ...
TZ (129)
Primary pointer page: 171, Index root page: 172
Average record length: 12.00, total records: 1
Average version length: 0.00, total versions: 0, max versions: 0
Data pages: 1, data page slots: 1, average fill: 1%
Fill distribution:
0 - 19% = 1
20 - 39% = 0
40 - 59% = 0
60 - 79% = 0
80 - 99% = 0

Решил подключиться к базе снова и выполнить GC:
SQL> select count(*) from tz;
- выполнилось мгновенно (вернуло значение = 1), а трейс также показывает, что никакого мусора в базе нет:
select count(*) from tz
1 records fetched
0 ms, 6 fetch(es)

Table Natural Index Update Insert Delete Backout Purge Expunge
***************************************************************************************************************
TZ 1

ВОПРОСЫ.
1. Триггер before delete выполняет добавление ОДНОЙ строки. Дальше он должен завершиться и за ним должен выполниться уже сам delete-стейтмент. Этот delete может и видит "новую строку", добавленную триггером, но это ОДНА строка! Почему выполнение ушло в какой-то цикл ?

2. Как так могло случиться, что мусора в базе нет ? (хотя размер .fdb растёт, да еще как!)
Таблоид
Дата: 16.06.2012 19:18:45
По второму вопросу кое-что прояснилось: обруб коннекта, выполняющего DML-стейтмент, через mon$attach, судя по всему вообще никогда не меняет кол-во версий, которое было ДО начала этого DML-стейтмента. Только что проверил, срубив простой инсерт 1 млн строк: база "вспухла", а версий по gstat'у нет.
Гаджимурадов Рустам
Дата: 16.06.2012 19:35:23

Таблоид> То есть, перед удалением строки в таблице вызывается
Таблоид> триггер, вставляющий в эту же таблицу новую строку.
Таблоид> С удивлением обнаружил, что удаление в таком случае
Таблоид> может оказаться бесконечным.

Код не смотрел, скорее всего у тебя строки пересекаются (типичная рекурсия).

Posted via ActualForum NNTP Server 1.5

Гаджимурадов Рустам
Дата: 16.06.2012 19:36:19

Таблоид> Только что проверил, срубив простой инсерт 1 млн строк: база "вспухла", а версий по gstat'у нет.

Если не врешь - похоже на багу. Хотя мне непонятно, как такое
может быть даже теоретически. А просто select count(*) из этой
таблицы после этого не тормозит?

Posted via ActualForum NNTP Server 1.5

hvlad
Дата: 16.06.2012 19:42:03
Не, ну это же надо - одни и теже грабли столькими способами мучать...

"Бесконечный" делит - это нестабильный курсор. Пробуй 3-ку.

Отменённый (прерванный) запрос убирает за собой - ты опять удивляешься.

Роллбека в трейсе нет, т.к. ты его не вызывал.
Purge\expunge на уровне аттача\БД должен показать работу по откату delete и его внутренностей
Таблоид
Дата: 17.06.2012 23:00:50
hvlad
Отменённый (прерванный) запрос убирает за собой - ты опять удивляешься.

Роллбека в трейсе нет, т.к. ты его не вызывал.
Purge\expunge на уровне аттача\БД должен показать работу по откату delete и его внутренностей
1) Как можно убедиться в том, что отменённый запрос убирает за собой, если в трейсе нету ничего на эту тему ? Вот прямо на вышеприведенном примере. Или просто оборвать цикл вставки 10 лямов строк (он прервётся ГОРАЗДО БЫСТРЕЕ, чем обычно делается роллбак).
2) "Purge\expunge на уровне аттача\БД должен показать работу по откату delete" - мне не понятны эти слова. Можно как-то попроще эту "механику" объяснить ?