| Начало » Использование СУБД » 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 версии?Не вникал в DDL, но вот тут, похоже, работает стабильный курсор, не позволяющий апдейту видеть изменения, сделанные "внутренними" триггерами.Для 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, заканчивает работу 
 anbsoft
 9) продолжает работу триггер для 1 строки и пропускает 3 и 4 строку, так как там State=1, заканчивает работуВсё это есть мина замедленного действия, независимо от версии FB.10) обновление завершено State (1,1,1,1) Dolg=290
 
 В 4.0 создается впечатление, что рекурсивно запущенные триггеры видят не все уже проведенные изменения.
 Оно зависит от физического порядка записей в таблице, это раз.
 Для нескольких сотен записей тут будет переполнение стека, это два.
 Бизнес логика в триггерах была, есть и будет - ЗЛО, так делать НЕЛЬЗЯ.
 |  
	|  |  |  
	|  |  
	| 
		
			| 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);
 
 Не зависимо чего там наменяли триггеры при апдейте первой и последующих записей, сам SELECT выдаст точно такое количество строк и их результаты, как будто UPDATE записей в триггерах вообще не было.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;[Обновления: 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 записей в триггерах вообще не было.Спасибо, теперь понял. |  
	|  |  | 
 
 
 Текущее время: Fri Oct 31 08:10:25 GMT+3 2025 
 Общее время, затраченное на создание страницы: 0.01465 секунд |