powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Рекурсивный BEFORE UPDATE триггер. Не сохраняется последнее возвращаемое значение
6 сообщений из 6, страница 1 из 1
Рекурсивный BEFORE UPDATE триггер. Не сохраняется последнее возвращаемое значение
    #38552416
aTz (msk)
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Помогите разобраться, никак не могу понять как работает рекурсивный триггер. Проблема более понятна на примере:

Код: 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.
BEGIN;

SET client_min_messages = 'notice';

CREATE TABLE test (
  id INTEGER, 
  status INTEGER, 
  value numeric
);

CREATE SEQUENCE test_seq;

CREATE OR REPLACE FUNCTION test_tr() RETURNS trigger AS $body$
DECLARE
	seq integer;
BEGIN
	seq := nextval('test_seq');
    RAISE NOTICE 'Started: %', seq;
    IF new.status != old.status THEN
    	UPDATE test SET value = 3 WHERE id = new.id;
    END IF;
	RAISE NOTICE 'Finished: %, RETURNED NEW: %', seq, new;
    RETURN new;
END
$body$ LANGUAGE 'plpgsql';

CREATE TRIGGER test_tr BEFORE UPDATE ON test FOR EACH ROW EXECUTE PROCEDURE test_tr();

INSERT INTO test (id, status, value) VALUES (1, 1, 1);

UPDATE test SET status = 2, value = 2 WHERE id = 1;

SELECT * FROM test;

ROLLBACK;



В результате видим следующее:
Код: powershell
1.
2.
3.
4.
5.
6.
7.
8.
NOTICE:  Started: 1
NOTICE:  Started: 2
NOTICE:  Finished: 2, RETURNED NEW: (1,1,3)
NOTICE:  Finished: 1, RETURNED NEW: (1,2,2)
UPDATE 0
 id | status | value 
----+--------+-------
  1 |      1 |     3



Почему в результате поле "value" обновилось и стало "3", а поле "status" так и осталось со значением "1" при том что самый первый вызванный триггер вернул строку (1, 1, 1)
...
Рейтинг: 0 / 0
Рекурсивный BEFORE UPDATE триггер. Не сохраняется последнее возвращаемое значение
    #38552491
пуупкин
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
aTz (msk),
ндак это вы сказали в конце RETURN NEW; а не пупкин.
прочитайте в notice, чему у вас NEW.value равен. и всё поймёте.

делайте либо after, как делаете
либо before, но вместо "рекурсивного " вызова через одно место просто скажиет перед RETURN NEW;
NEW.value :=3;
и все, никаких "рекурсивных опдейтов нинада
...
Рейтинг: 0 / 0
Рекурсивный BEFORE UPDATE триггер. Не сохраняется последнее возвращаемое значение
    #38552513
пуупкин
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
пуупкин,
таки вчитался.
как работает - понятно. на 1-й записи отрабатывает 2 триггера (точнее 2 раза - один). тот который стартовал последним - того и тапки - тот и возвращает последнее слово через return new;
независимо от того, кто якобы последний вдоль по "нити"
Код: sql
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.
BEGIN;
DROP TABLE  IF EXISTS test;
DROP SEQUENCE IF EXISTS  test_seq;
--SET client_min_messages = 'notice';

CREATE TABLE test (
  id INTEGER, 
  status INTEGER, 
  value numeric
);

CREATE SEQUENCE test_seq;

CREATE OR REPLACE FUNCTION test_tr() RETURNS trigger AS $body$
DECLARE
	seq integer;
BEGIN
	seq := nextval('test_seq');
	RAISE NOTICE 'Started: %', seq;
	RAISE NOTICE 'SO: %,  OLD: %', seq, OLD;
	IF new.status != old.status THEN		
		UPDATE test SET value = 3 WHERE id = new.id;
	END IF;
	RAISE NOTICE ' % , %' , seq, (SELECT (test.*)::text FROM test WHERE id = new.id);
	RAISE NOTICE 'FN: %, RETURNED NEW: %', seq, new;
	RETURN new;
END
$body$ LANGUAGE 'plpgsql';

CREATE TRIGGER test_tr BEFORE UPDATE ON test FOR EACH ROW EXECUTE PROCEDURE test_tr();

INSERT INTO test (id, status, value) VALUES (1, 1, 1);

UPDATE test SET status = 2, value = 2 WHERE id = 1;

SELECT * FROM test;

ROLLBACK;
---------------------------------
NOTICE:  Started: 1
NOTICE:  SO: 1,  OLD: (1,1,1)
NOTICE:  Started: 2
CONTEXT:  SQL statement "UPDATE test SET value = 3 WHERE id = new.id"
PL/pgSQL function "test_tr" line 9 at SQL statement
NOTICE:  SO: 2,  OLD: (1,1,1)
CONTEXT:  SQL statement "UPDATE test SET value = 3 WHERE id = new.id"
PL/pgSQL function "test_tr" line 9 at SQL statement
NOTICE:   2 , (1,1,1)
CONTEXT:  SQL statement "UPDATE test SET value = 3 WHERE id = new.id"
PL/pgSQL function "test_tr" line 9 at SQL statement
NOTICE:  FN: 2, RETURNED NEW: (1,1,3)
CONTEXT:  SQL statement "UPDATE test SET value = 3 WHERE id = new.id"
PL/pgSQL function "test_tr" line 9 at SQL statement
NOTICE:   1 , (1,1,3)
NOTICE:  FN: 1, RETURNED NEW: (1,2,2)


почему так - надо код пж смотреть.
...
Рейтинг: 0 / 0
Рекурсивный BEFORE UPDATE триггер. Не сохраняется последнее возвращаемое значение
    #38553824
Жоао!
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
aTz (msk)
Код: powershell
1.
2.
3.
4.
5.
6.
7.
8.
NOTICE:  Started: 1
NOTICE:  Started: 2
NOTICE:  Finished: 2, RETURNED NEW: (1,1,3)
NOTICE:  Finished: 1, RETURNED NEW: (1,2,2)
UPDATE 0
 id | status | value 
----+--------+-------
  1 |      1 |     3


Почему в результате поле "value" обновилось и стало "3", а поле "status" так и осталось со значением "1" при том что самый первый вызванный триггер вернул строку (1, 1, 1)

Потому, что именно это вы и заложили в свой триггер.

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
UPDATE test SET status = 2, value = 2 WHERE id = 1;
-- срабатывает ВНЕШНИЙ триггер: NEW = (1,2,2), OLD = (1,1,1)
  -- new.status != old.status
  UPDATE test SET value = 3 WHERE id = new.id;
  -- срабатывает ВНУТРЕННИЙ триггер: NEW = (1,1,3), OLD = (1,1,1), т.к. он BEFORE,
  -- т.е. ваш ручной UPDATE еще не отработал
    -- new.status = old.status -- делать нечего
  -- ВНУТРЕННИЙ триггер отработал, вернул NEW = (1,1,3), т.е. status = 1
  -- во ВНЕШНЕМ триггере отрабатывает UPDATE test SET value = 3 WHERE id = 1;
  -- ВНЕШНИЙ триггер возвращает NEW = (1,2,2), как заказано
-- доходит очередь до изначального UPDATE…
-- и тут вам PG пишет: ОШИБКА:  кортеж, который должен быть изменён, уже модифицирован в операции, вызванной текущей командой.
-- …и игнорирует дальнейшее изменение.
-- ручной UPDATE не происходит.
...
Рейтинг: 0 / 0
Рекурсивный BEFORE UPDATE триггер. Не сохраняется последнее возвращаемое значение
    #38553832
Фотография Ёш
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
aTz (msk)
Код: powershell
1.
2.
3.
4.
5.
6.
7.
8.
NOTICE:  Started: 1
NOTICE:  Started: 2
NOTICE:  Finished: 2, RETURNED NEW: (1,1,3)
NOTICE:  Finished: 1, RETURNED NEW: (1,2,2)
UPDATE 0
 id | status | value 
----+--------+-------
  1 |      1 |     3



Почему в результате поле "value" обновилось и стало "3", а поле "status" так и осталось со значением "1" при том что самый первый вызванный триггер вернул строку (1, 1, 1)postgres же Вам подсказывает: ОБНОВЛЕНО СТРОК 0 (UPDATE 0) :) У Вас триггер before, а внутри него Вы изменили строку к которой он должен был применяться. То есть когда из триггера возвращается RETURNED NEW: (1,2,2) его уже не к чему применять, исходная обновляемая строка уже изменена и PG ничего не делает (UPDATE 0). Если бы он её применил, то просто бы затёр новую строку (1,1,3).

PS: в 9.3 у Вас, кстати, было бы сообщение об ошибке:
Код: plaintext
1.
2.
ОШИБКА:  кортеж, который должен быть изменён, уже модифицирован в операции, вызванной текущей командой
ПОДСКАЗКА:  Возможно, для распространения изменений в другие строки следует использовать триггер AFTER вместо BEFORE.
...
Рейтинг: 0 / 0
Рекурсивный BEFORE UPDATE триггер. Не сохраняется последнее возвращаемое значение
    #38556328
aTz (msk)
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Ёшpostgres же Вам подсказывает: ОБНОВЛЕНО СТРОК 0 (UPDATE 0) :) У Вас триггер before, а внутри него Вы изменили строку к которой он должен был применяться. То есть когда из триггера возвращается RETURNED NEW: (1,2,2) его уже не к чему применять, исходная обновляемая строка уже изменена и PG ничего не делает (UPDATE 0). Если бы он её применил, то просто бы затёр новую строку (1,1,3).

PS: в 9.3 у Вас, кстати, было бы сообщение об ошибке:
Код: plaintext
1.
2.
ОШИБКА:  кортеж, который должен быть изменён, уже модифицирован в операции, вызванной текущей командой
ПОДСКАЗКА:  Возможно, для распространения изменений в другие строки следует использовать триггер AFTER вместо BEFORE.


Да, спасибо всем за ответы!
Собственно говоря я уже и пришёл к этому умозаключению сам, а ваши ответы это подтвердили.
Жаль что у меня не 9.3, вопрос бы даже и не возник :)

Я подошёл к этому вопросу с точки зрения рекурсивного вызова, когда обычно итоговый результат возвращает корневой вызов, забыв что это БД.
...
Рейтинг: 0 / 0
6 сообщений из 6, страница 1 из 1
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Рекурсивный BEFORE UPDATE триггер. Не сохраняется последнее возвращаемое значение
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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