Is there a way to iterate through individual fields of OLD records in a Postgres plpgsql trigger?
I want to write a trigger function that logs all the changes that were made to the particular fields of a record. For example:
-- My Data Table
CREATE TABLE data (
id integer,
value integer
);
-- My Changelog table
CREATE TABLE changelog (
id integer,
changes text,
user text,
changed_on timestamp
);
CREATE OR REPLACE FUNCTION data_history() RETURNS TRIGGER AS
$BODY$
DECLARE
changes text;
BEGIN
changes := '''';
IF old.value <> new.value THEN
changes := CONCAT(changes, old.value, ':', new.value)
INSERT INTO
changelog(changes, user, changed_on)
VALUES(changes, current_user, NOW());
END IF
RETURN NEW;
END;
$BODY$
language plpgsql;
CREATE OR REPLACE TRIGGER change_trigger AFTER UPDATE ON data FOR EACH ROW EXECUTE PROCEDURE data_history();
While this works as expected the function is completely table-dependent. I would like to make it more generic by comparing all individual fields of OLD and NEW in a loop. Is there any way to do that?
2
Answers
You could add a
tracked_table
column to yourchangelog
table and set up a generic trigger function you can attach to any table you wish to track.OLD
andNEW
records follow the structure of the table the trigger operates on so you can useTG_TABLE_NAME
to find the list of column names ininformation_schema.columns
then loop over them to run your comparisons in a dynamic SQL block. Demo:Depending on what and how closely you’re trying to track, you could consider pgaudit extension. If you want to record changes to move back and forth between versions, there’s Continuous Archiving and Point-in-Time Recovery.
Today, the most simple solution can be based on transformation to json type, and then iteration over json:
Iteration over jsonb type will be probably little bit more effective
The
hstore
type (from extensionhstore
) has very nice functionality for detection difference between two different valuessee http://okbob.blogspot.com/2015/01/most-simply-implementation-of-history.html