Я пытаюсь написать функцию postgres для триггера AFTER UPDATE
, который создает объект JSON с именами столбцов и их обновленными значениями только для измененных столбцов. Я пытаюсь создать обобщенную функцию, которую я могу использовать для любой таблицы, следовательно, используя Record
в качестве аргумента функции. old
и new
происходят из переменных OLD
и NEW
триггера. Конечная цель - сохранить возвращенное значение JSON в поле JSON в таблице аудита.
CREATE OR REPLACE FUNCTION row_updates(old RECORD, new RECORD) RETURNS JSON AS
$$
DECLARE
updates JSON;
BEGIN
WITH columns AS (
SELECT json_object_keys(row_to_json(new)) "column"
)
SELECT
json_object_agg("column", new_value) INTO updates
FROM (
SELECT
"column",
(row_to_json(new)->"column" #>> '{}') as new_value,
(row_to_json(old)->"column" #>> '{}') as old_value
FROM
columns
) changes
WHERE
new_value IS DISTINCT FROM old_value;
RETURN updates;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION audit_change() RETURNS TRIGGER AS
$$
DECLARE
updates JSON;
BEGIN
IF (TG_OP = 'INSERT') THEN
raise NOTICE 'Logging insert on relation (%.%) %', TG_TABLE_SCHEMA, TG_TABLE_NAME;
RETURN NEW;
ELSIF (TG_OP = 'UPDATE') THEN
updates := row_updates(OLD, NEW)
raise NOTICE 'Logging update on relation (%.%) %', TG_TABLE_SCHEMA, TG_TABLE_NAME, updates;
RETURN NEW;
ELSIF (TG_OP = 'DELETE') THEN
raise NOTICE 'Logging delete on relation (%.%) %', TG_TABLE_SCHEMA, TG_TABLE_NAME;
RETURN OLD;
END IF;
END;
$$ LANGUAGE plpgsql;
Я получаю эту ошибку: ERROR: PL/pgSQL functions cannot accept type record
.
Есть ли лучший способ сделать это? Очевидно, что я хочу сделать это в не триггерной функции, чтобы я мог использовать его в разных триггерах для разных таблиц. Также я могу захотеть адаптировать его для триггеров AFTER INSERT
и AFTER DELETE
, если это возможно, поэтому я не хочу отключать TG_OP
в функции триггера и повторять логи c, показанные в SQL выше.
Конечная цель:
=> select * from org_user;
id | org_id | user_id
----+--------+---------
1 | 1 | 1
23 | 1 | 3
=> update org_user set org_id=3, user_id = 4 where id = 23;
-- trigger creates following row in audit table
id | relation | record_id | updates
----+-----------+-----------+--------------
1 | org_user | 23 | {'org_id': 3, 'user_id': 4}