Начало » Использование СУБД » Firebird, HQbird, InterBase » Глюки Firebird4 ? (Firebird 2 и 4 работают по разному) 
	
		
		
			| Глюки Firebird4 ? [сообщение #611] | 
			Tue, 20 September 2022 16:47   | 
		 
		
			
				
				
				
					
						  
						anbsoft
						 Сообщений: 4 Зарегистрирован: September 2022 
						
					 | 
					Junior Member  | 
					 | 
		 
		 
	 | 
 
	
		Давно работал с Firebird 2.0 
Решил попробовать перейти на новую версию (4). 
Некоторые триггеры стали работать некорректно. 
Примерно понимаю почему так, но пока не знаю что с этим делать. 
Привожу код тестовой базы: 
В таблице клиентов ведется долг клиента. 
В таблице затрат проходят затраты по нему. 
Некоторые затраты могут проходить пакетом (При проведении родительской затраты, автоматически проводятся дочернии). 
Вставляем список затрат и подтверждаем их переводом State в 1 
Поля Info и Test1 добавлены для понимания того, что происходит. 
 
CREATE DATABASE 'Base2.fdb' USER 'SYSDBA' PASSWORD 'masterkey'; 
 
CREATE TABLE Klient 
( 
ID              Integer          NOT NULL, 
Name            VarChar(50), 
Dolg            NUMERIC(15,2)   DEFAULT 0, 
PRIMARY KEY     (ID) 
); 
 
CREATE GENERATOR GEN_Klient; 
 
SET TERM ^ ; 
CREATE TRIGGER SET_Klient FOR Klient 
ACTIVE BEFORE INSERT POSITION 0 
AS 
declare variable IDTemp Integer; 
BEGIN 
  IF ((NEW.ID=0) OR (NEW.ID IS NULL)) THEN BEGIN 
    NEW.ID=GEN_ID(GEN_Klient,1); 
  END 
END^ 
SET TERM ; ^ 
 
INSERT INTO Klient (ID, Name, Dolg) VALUES (0,'Test', 0); 
 
CREATE TABLE Zatrat 
( 
ID              Integer          NOT NULL, 
Parent        Integer          default 0,  
IDKlient        Integer, 
State           Integer          default 0, 
Suma            NUMERIC(15,2)    DEFAULT 0, 
Info            VarChar(100), 
Test1           Integer, 
PRIMARY KEY     (ID) 
); 
 
CREATE GENERATOR GEN_Zatrat; 
CREATE GENERATOR GEN_Test; 
 
 
SET TERM ^ ; 
CREATE TRIGGER SET_Zatrat FOR Zatrat 
ACTIVE BEFORE INSERT POSITION 0 
AS 
BEGIN 
  IF ((NEW.ID=0) OR (NEW.ID IS NULL)) THEN BEGIN 
    NEW.ID=GEN_ID(GEN_Klient,1); 
  END 
END^ 
 
CREATE TRIGGER Edit_Zatrat FOR Zatrat 
ACTIVE BEFORE UPDATE POSITION 0 
AS 
declare variable S1 VarChar(20); 
declare variable S2 VarChar(20); 
BEGIN 
  IF ((OLD.State<>1) AND (NEW.State=1)) THEN BEGIN 
    select Dolg from klient where (ID=NEW.IDKlient) into :S1; 
    UPDATE Klient SET Dolg=Dolg-OLD.Suma WHERE (ID=NEW.IDKlient); 
    select Dolg from klient where (ID=NEW.IDKlient) into :S2; 
    NEW.Info=' - ' || coalesce(OLD.Info, '') || ' ' || S1 || ' ' || S2; 
    NEW.Test1=GEN_ID(GEN_Test,1); 
  END 
  IF ((OLD.State=1) AND (NEW.State<>1)) THEN BEGIN 
    UPDATE Klient SET Dolg=Dolg+OLD.Suma WHERE (ID=NEW.IDKlient); 
  END 
END^ 
 
CREATE TRIGGER Edit_Doc FOR Zatrat 
ACTIVE AFTER UPDATE POSITION 0 
AS 
BEGIN 
  IF ((NEW.Parent<>0) AND (NEW.State<>OLD.State)) THEN BEGIN 
    UPDATE Zatrat SET State=NEW.State WHERE (Parent=NEW.Parent) AND (State<>NEW.State) AND (ID<>NEW.ID); 
  END 
END^ 
 
SET TERM ; ^ 
 
INSERT INTO Zatrat (ID, Parent, IDKlient, State, Suma) VALUES (1,1,1,0,10); 
INSERT INTO Zatrat (ID, Parent, IDKlient, State, Suma) VALUES (2,1,1,0,30); 
INSERT INTO Zatrat (ID, Parent, IDKlient, State, Suma) VALUES (3,1,1,0,50); 
INSERT INTO Zatrat (ID, Parent, IDKlient, State, Suma) VALUES (4,1,1,0,200); 
 
UPDATE Zatrat SET State=1 where (ID=1); 
 
SELECT * FROM Klient; 
 
ID NAME         DOLG  
== ======== ======== 
 1 Test     -290.00  
 
Select * FROM Zatrat ORDER BY ID; 
 
ID PARENT IDKLIENT STATE  SUMA  INFO                TEST1  
== ====== ======== ===== ====== =================== =====  
 1      1        1     1  10.00  -  0.00 -10.00         1  
 2      1        1     1  30.00  -  -10.00 -40.00       2  
 3      1        1     1  50.00  -  -40.00 -90.00       3  
 4      1        1     1 200.00  -  -90.00 -290.00      4  
 
COMMIT WORK; 
 
В Firebird 4 результаты совсем друние: 
 
SELECT * FROM Klient; 
 
ID NAME         DOLG  
== ======== ======== 
 1 Test     -740.00  
 
Select * FROM Zatrat ORDER BY ID; 
 
ID PARENT IDKLIENT STATE  SUMA  INFO                TEST1  
== ====== ======== ===== ====== =================== =====  
 1      1        1     1  10.00  -  0.00 -10.00         1  
 2      1        1     1  30.00  -  -10.00 -40.00       2  
 3      1        1     1  50.00  -  -490.00 -540.00     6  
 4      1        1     1 200.00  -  -540.00 -740.00     7  
 
Видно, что между обновлением 2 и 3 строки еще трижды проходит обновление, но никаких следов не оставляет. 
Как побороть эту напасть?
		
		
		
 |  
	| 
		
	 | 
 
 
 |  
	| 
		
 |  
	| 
		
 |  
	| 
		
 |  
	| 
		
 |  
	| 
		
 |  
	| 
		
 |  
	
		
		
			| Re: Глюки Firebird4 ? [сообщение #618 является ответом на сообщение #611] | 
			Wed, 21 September 2022 07:08    | 
		 
		
			
				
				
				
					
						  
						fraks
						 Сообщений: 152 Зарегистрирован: June 2022  Географическое положение: Новосибирск
						
					 | 
					Senior Member  | 
					 | 
		 
		 
	 | 
 
	
		Лог для отслеживания событий в таблице. 
 
CREATE GENERATOR GEN_ZATRAT_LOG_ID;
CREATE TABLE ZATRAT_LOG (
    ID        INTEGER NOT NULL,
    IDZ       INTEGER,
    PARENT    INTEGER,
    IDKLIENT  INTEGER,
    STATE     INTEGER,
    SUMA      NUMERIC(15,2),
    INFO      VARCHAR(100),
    TEST1     INTEGER,
    A         VARCHAR(5)
);
ALTER TABLE ZATRAT_LOG ADD PRIMARY KEY (ID);
SET TERM ^ ;
CREATE OR ALTER TRIGGER ZATRAT_LOG_BI FOR ZATRAT_LOG
ACTIVE BEFORE INSERT POSITION 0
as
begin
  if (new.id is null) then
    new.id = gen_id(gen_zatrat_log_id,1);
end
^
CREATE OR ALTER TRIGGER ZATRAT_AIUD_LOG FOR ZATRAT
ACTIVE AFTER INSERT OR UPDATE OR DELETE POSITION 0
as
declare variable sa varchar(3);
begin
  sa = '';
  if (inserting) then sa = sa || 'I';
  if (updating ) then sa = sa || 'U';
  if (deleting ) then sa = sa || 'D';
  --
  insert into ZATRAT_LOG (idz, parent, idklient, state, suma, info, test1, a)
  values(NEW.id, NEW.parent, NEW.idklient, NEW.state, NEW.suma,  NEW.info,  NEW.test1,  :sa);
end
^
SET TERM ; ^
 
		
		
		
 |  
	| 
		
	 | 
 
 
 |  
	| 
		
 |  
	| 
		
 |  
	
		
		
			| Re: Глюки Firebird4 ? [сообщение #621 является ответом на сообщение #613] | 
			Wed, 21 September 2022 15:13    | 
		 
		
			
				
				
				
					
						  
						SD
						 Сообщений: 452 Зарегистрирован: August 2022 
						
					 | 
					Senior Member  | 
					 | 
		 
		 
	 | 
 
	
		fraks писал(а) Wed, 21 September 2022 03:56 
Для тех кто не в теме - сначала вставляем рыбу затрат, проверяем как оно все выглядит, и потом "подтверждаем" - проводим по долгу клиента. 
 
Вот только обычно это таки делается в разных транзакциях, а здесь разные триггера на одно событие, то есть всё исполняется в рамках одного запроса. 
 
И да, как сказал бы Влад, "нет такой версии". 
Как раз в видимости собственных изменений в пределах PSQL блока был баг, который правился.
		
		
		[Обновления: Wed, 21 September 2022 15:14] Известить модератора  
 |  
	| 
		
	 | 
 
 
 |  
	| 
		
 |  
	
		
		
			| Re: Глюки Firebird4 ? [сообщение #626 является ответом на сообщение #613] | 
			Thu, 22 September 2022 11:21    | 
		 
		
			
				
				
				
					
						  
						sim_84
						 Сообщений: 355 Зарегистрирован: June 2022 
						
					 | 
					Senior Member  | 
					 | 
		 
		 
	 | 
 
	
		fraks писал(а) Wed, 21 September 2022 04:56На FB-2.5.8 работает так же как FB-2.0 
 
Сталкивался с подобной задачей, поэтому бредом не назову, вполне жизненно. 
Правда про корректность решения ничего пока не могу сказать. 
 
Для тех кто не в теме - сначала вставляем рыбу затрат, проверяем как оно все выглядит, и потом "подтверждаем" - проводим по долгу клиента. 
Получается что родительский расход - это типа шапка документа, а все что под ним - это строки документа. 
 
Проблема не в обновление статуса родительских записей, а в том что для них идёт расчёт нарастающих сумм. Нельзя в триггерах использовать логику, которая зависит от порядка срабатывания, например расчёт нарастающих сумм. Мутации оракуля в принципе как раз от этого и защищают, правда заодно режут и кучу других полезных возможностей.
		
		
		
 |  
	| 
		
	 | 
 
 
 |  
	| 
		
 |  
	| 
		
 |  
	| 
		
 |  
	
		
		
			| Re: Глюки Firebird4 ? [сообщение #649 является ответом на сообщение #648] | 
			Mon, 26 September 2022 09:39    | 
		 
		
			
				
				
				
					
						  
						sim_84
						 Сообщений: 355 Зарегистрирован: June 2022 
						
					 | 
					Senior Member  | 
					 | 
		 
		 
	 | 
 
	
		Цитата:Или по дефолту в четверке запускается транзакция с таким уровнем изоляции, которая не видит собственных изменений? 
не бывает таких уровней изолированности. 
 
В 4.0 появился Read Committed Read Consistency, которая может запускать рестарты на конфликтах, а потому один и тот же запрос может выполняться несколько раз, но тут не тот случай. Рестарты в основном вредят в транзакции, если есть не транзакционные операторы (дергание генератора, UDR/UDF отправки почты ...) или автономные транзакции. В остальных случаях они на результат не влияют. 
 
А вот начиная с 3.0 появилась стабильность курсора, которая скорее всего здесь и играет свою роль. 
 
Теперь по коду самого триггера. Ну чего сказать, кто-то его не аккуратно написал так, что одна и та же запись обновляется несколько раз. Ибо по parent в одну и ту же запись из разных вызовов триггеров можно прийти несколько раз.  
Расчёт на то что State изменит первый update и он будет виден остальным не верны. Ибо проверка видимости происходит в разных вызовах триггера, но при этом на одном большом update. Тут как раз срабатывает новшество тройки, а не 4.0. Сам по себе курсор update Zatrat стабилен, то есть пока обновляется одна запись, состояние остальных записей из курсора будет неизменным, даже если они обновляются в вызываемых триггерах. 
		
		
		[Обновления: Mon, 26 September 2022 12:30] Известить модератора  
 |  
	| 
		
	 | 
 
 
 |  
	| 
		
 |  
	
		
		
			| Re: Глюки Firebird4 ? [сообщение #656 является ответом на сообщение #650] | 
			Tue, 27 September 2022 12:37    | 
		 
		
			
				
				
				
					
						  
						anbsoft
						 Сообщений: 4 Зарегистрирован: September 2022 
						
					 | 
					Junior Member  | 
					 | 
		 
		 
	 | 
 
	
		Кто-нибудь может описать алгоритм работы для 4 версии? 
Для 2.0 вроде все логично и понятно: 
Рассмотрю пошагово: 
Изначально у всех четырех строк State=0 (0,0,0,0) Dolg=0 
1) Выполняю UPDATE Zatrat SET State=1 WHERE (ID=1); (1,0,0,0) Dolg=10 
2) Срабатывает триггер для 1 строки, выполняется изменение State для 2 строки (1,1,0,0) Dolg=40 
3) Срабатывает триггер для 2 строки: 
а) 1 строку пропускает, так как у нее State=1 
б) выполняется изменение State для 3 строки (1,1,1,0) Dolg=90 
4) Срабатывает триггер для 3 строки: 
а) 1 и 2 строку пропускает, так как у них State=1 
б) выполняется изменение State для 4 строки (1,1,1,1) Dolg=290 
5) Срабатывает триггер для 4 строки: 
а) 1,2 и 3 строку пропускает, так как у них State=1 
6) заканчивает работу триггер для 4 строки 
7) заканчивает работу триггер для 3 строки 
  продолжает работу триггер для 2 строки и пропускает 4 строку, так как там State=1, заканчивает работу 
9) продолжает работу триггер для 1 строки и пропускает 3 и 4 строку, так как там State=1, заканчивает работу 
10) обновление завершено State (1,1,1,1) Dolg=290 
 
В 4.0 создается впечатление, что рекурсивно запущенные триггеры видят не все уже проведенные изменения.
		
		
		
 |  
	| 
		
	 | 
 
 
 |  
	
		
		
			| Re: Глюки Firebird4 ? [сообщение #657 является ответом на сообщение #656] | 
			Tue, 27 September 2022 13:10    | 
		 
		
			
				
				
				
					
						  
						hvlad
						 Сообщений: 381 Зарегистрирован: August 2022 
						
					 | 
					Senior Member  | 
					 | 
		 
		 
	 | 
 
	
		anbsoft писал(а) Tue, 27 September 2022 12:37Кто-нибудь может описать алгоритм работы для 4 версии? 
Для 2.0 вроде все логично и понятно: 
Рассмотрю пошагово: 
Изначально у всех четырех строк State=0 (0,0,0,0) Dolg=0 
1) Выполняю UPDATE Zatrat SET State=1 WHERE (ID=1); (1,0,0,0) Dolg=10 
2) Срабатывает триггер для 1 строки, выполняется изменение State для 2 строки (1,1,0,0) Dolg=40 
3) Срабатывает триггер для 2 строки: 
а) 1 строку пропускает, так как у нее State=1 
б) выполняется изменение State для 3 строки (1,1,1,0) Dolg=90 
4) Срабатывает триггер для 3 строки: 
а) 1 и 2 строку пропускает, так как у них State=1 
б) выполняется изменение State для 4 строки (1,1,1,1) Dolg=290 
5) Срабатывает триггер для 4 строки: 
а) 1,2 и 3 строку пропускает, так как у них State=1 
6) заканчивает работу триггер для 4 строки 
7) заканчивает работу триггер для 3 строки 
  продолжает работу триггер для 2 строки и пропускает 4 строку, так как там State=1, заканчивает работу Не вникал в DDL, но вот тут, похоже, работает стабильный курсор, не позволяющий апдейту видеть изменения, сделанные "внутренними" триггерами. 
 
anbsoft9) продолжает работу триггер для 1 строки и пропускает 3 и 4 строку, так как там State=1, заканчивает работу 
10) обновление завершено State (1,1,1,1) Dolg=290 
 
В 4.0 создается впечатление, что рекурсивно запущенные триггеры видят не все уже проведенные изменения. 
Всё это есть мина замедленного действия, независимо от версии FB. 
Оно зависит от физического порядка записей в таблице, это раз. 
Для нескольких сотен записей тут будет переполнение стека, это два. 
Бизнес логика в триггерах была, есть и будет - ЗЛО, так делать НЕЛЬЗЯ.
		
		
		
 |  
	| 
		
	 | 
 
 
 |  
	| 
		
 |  
	
		
		
			| Re: Глюки Firebird4 ? [сообщение #659 является ответом на сообщение #656] | 
			Tue, 27 September 2022 15:06    | 
		 
		
			
				
				
				
					
						  
						sim_84
						 Сообщений: 355 Зарегистрирован: June 2022 
						
					 | 
					Senior Member  | 
					 | 
		 
		 
	 | 
 
	
		ты не учитываешь тот факт, что update это тоже курсор. И обновляет он у тебя отнюдь не одну запись. 
 
Подсказываю 
 
UPDATE Zatrat SET State=NEW.State WHERE (Parent=NEW.Parent) AND (State<>NEW.State) AND (ID<>NEW.ID);  
Эквивалентно 
 
FOR 
  SELECT *
  FROM Zatrat
  WHERE (Parent=NEW.Parent) AND (State<>NEW.State) AND (ID<>NEW.ID) 
  AS CURSOR C
DO
  UPDATE SET State=NEW.State WHERE CURRENT OF C;  
Не зависимо чего там наменяли триггеры при апдейте первой и последующих записей, сам SELECT выдаст точно такое количество строк и их результаты, как будто UPDATE записей в триггерах вообще не было.
		
		
		[Обновления: Tue, 27 September 2022 15:12] Известить модератора  
 |  
	| 
		
	 | 
 
 
 |  
	
		
		
			| Re: Глюки Firebird4 ? [сообщение #660 является ответом на сообщение #659] | 
			Tue, 27 September 2022 15:49   | 
		 
		
			
				
				
				
					
						  
						anbsoft
						 Сообщений: 4 Зарегистрирован: September 2022 
						
					 | 
					Junior Member  | 
					 | 
		 
		 
	 | 
 
	
		sim_84 писал(а) Tue, 27 September 2022 15:06Не зависимо чего там наменяли триггеры при апдейте первой и последующих записей, сам SELECT выдаст точно такое количество строк и их результаты, как будто UPDATE записей в триггерах вообще не было. 
Спасибо, теперь понял.
		
		
		
 |  
	| 
		
	 | 
 
 
 |   
Переход к форуму:
 
 Текущее время: Tue Nov 04 13:12:26 GMT+3 2025 
 Общее время, затраченное на создание страницы: 0.02000 секунд 
 |