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

Код: 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
06.02.2014, 20:21:16
    #38552491
пуупкин
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Рекурсивный BEFORE UPDATE триггер. Не сохраняется последнее возвращаемое значение
aTz (msk),
ндак это вы сказали в конце RETURN NEW; а не пупкин.
прочитайте в notice, чему у вас NEW.value равен. и всё поймёте.

делайте либо after, как делаете
либо before, но вместо "рекурсивного " вызова через одно место просто скажиет перед RETURN NEW;
NEW.value :=3;
и все, никаких "рекурсивных опдейтов нинада
...
Рейтинг: 0 / 0
06.02.2014, 21:00:29
    #38552513
пуупкин
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Рекурсивный BEFORE UPDATE триггер. Не сохраняется последнее возвращаемое значение
пуупкин,
таки вчитался.
как работает - понятно. на 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
07.02.2014, 17:00:48
    #38553824
Жоао!
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Рекурсивный BEFORE UPDATE триггер. Не сохраняется последнее возвращаемое значение
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
07.02.2014, 17:03:19
    #38553832
Ёш
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Рекурсивный BEFORE UPDATE триггер. Не сохраняется последнее возвращаемое значение
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
10.02.2014, 21:28:56
    #38556328
aTz (msk)
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Рекурсивный BEFORE UPDATE триггер. Не сохраняется последнее возвращаемое значение
Ёш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
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Рекурсивный BEFORE UPDATE триггер. Не сохраняется последнее возвращаемое значение / 6 сообщений из 6, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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