Ответ 1
Ветвь ELSE
может быть радикально упрощена. Но еще пара вещей неэффективна/неточна/опасна:
CREATE OR REPLACE FUNCTION sample_trigger_func()
RETURNS TRIGGER AS
$func$
BEGIN
IF TG_OP = 'DELETE' THEN
RAISE INFO 'OLD: %', OLD.name;
EXECUTE format('INSERT INTO %I SELECT ($1).*', TG_TABLE_NAME || '_deletes')
USING OLD #= hstore('{mod_op, mod_datetime}'::text[]
, ARRAY[left(TG_OP, 1), now()::text]);
RETURN OLD;
ELSE -- insert, update
NEW.mod_op := left(TG_OP, 1);
NEW.mod_datetime := now();
RETURN NEW;
END IF;
END
$func$ LANGUAGE plpgsql;
-
В ветке
ELSE
просто назначьтеNEW
напрямую. Нет необходимости в более динамическом SQL - это приведет к тому, что один и тот же триггер снова вызовет бесконечный цикл. Это основная ошибка. -
RETURN NEW;
вне конструкцииIF
будет разбивать вашу триггерную функцию дляDELETE
, так какNEW
не назначается для DELETE. -
Ключевой особенностью является использование
hstore
и оператора hstore#=
для динамического изменения двух выбранных полей известный тип строки - неизвестный во время написания кода. Таким образом, вы не нарушаете оригинальное значениеOLD
, которое может иметь неожиданный побочный эффект, если у вас больше триггеров по цепочке событий.OLD #= hstore('{mod_op, mod_datetime}'::text[] , ARRAY[left(TG_OP, 1), now()::text]);
Должен быть установлен дополнительный модуль
hstore
. Подробности:- Как установить значение составного поля переменных с помощью динамического SQL
- Динамическое переключение имен столбцов для переменной записи в PostgreSQL
Используя
hstore(text[], text[])
здесь, чтобы построить значениеhstore
с несколькими полями "на лету". -
Оператор присваивания в plpgsql равен
:=
: -
Обратите внимание, что я использовал имя столбца
mod_datetime
вместо вводящего в заблуждениеmod_date
, поскольку столбец, очевидно, являетсяtimestamp
, а не adate
.
Я добавил несколько других улучшений, находясь на нем. И сам триггер должен выглядеть так:
CREATE TRIGGER insupdel_bef
BEFORE INSERT OR UPDATE OR DELETE ON table_name
FOR EACH ROW EXECUTE PROCEDURE sample_trigger_func();