5

Is there a way to get the JSON of the only modified fields?

Now I use the following trigger but the entire line is printed in the changelog.

Example tables:

TABLE tbl_changelog (
  tbl    TEXT,
  op   TEXT,
  new     JSON,
  old     JSON
);

TABLE tbl_items (
  f1    TEXT,
  f2    TEXT
);

Trigger:

CREATE OR REPLACE FUNCTION changelog_procedure() RETURNS trigger AS $$
BEGIN
INSERT INTO tbl_changelog(tbl, op, new, old)
VALUES (TG_TABLE_NAME, TG_OP, row_to_json(NEW), row_to_json(OLD));
RETURN NULL;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

CREATE TRIGGER changelog_items
AFTER INSERT OR UPDATE OR DELETE ON tbl_items
FOR EACH ROW EXECUTE PROCEDURE changelog_procedure();

After inserting and uploaded f2 and f1 the changelog is the following:

tbl_changelog
------------------------------------------------------------------
 tbl      |   op    | new                  |     old
------------------------------------------------------------------
tbl_items | INSERT  | {f1: "aa", f2: "bb"} | 
------------------------------------------------------------------
tbl_items | UPDATE  | {f1: "aa", f2: "cc"} | {f1: "aa", f2: "bb"}
------------------------------------------------------------------
tbl_items | UPDATE  | {f1: "dd", f2: "cc"} | {f1: "aa", f2: "cc"}
------------------------------------------------------------------

I would like to record only the changes, that is:

tbl_changelog
------------------------------------------------------------------
 tbl      |   op    | new                  |     old
------------------------------------------------------------------
tbl_items | INSERT  | {f1: "aa", f2: "bb"} | 
------------------------------------------------------------------
tbl_items | UPDATE  | {f2: "cc"}           | {f2: "bb"}
------------------------------------------------------------------
tbl_items | UPDATE  | {f1: "dd"}           | {f1: "aa"}
------------------------------------------------------------------

1 Answer 1

11

Your trigger function cannot work well, it produces the error while inserting a row:

ERROR: record "old" is not assigned yet

DETAIL: The tuple structure of a not-yet-assigned record is indeterminate.

You should treat the three cases of INSERT, UPDATE and DELETE separately:

create or replace function changelog_procedure() 
returns trigger as $$
declare
    json_new jsonb;
    json_old jsonb;
begin
    if tg_op = 'INSERT' then
        json_new:= to_jsonb(new);
    elsif tg_op = 'DELETE' then
        json_old:= to_jsonb(old);
    else
        select jsonb_object_agg(new_key, new_value), jsonb_object_agg(old_key, old_value)
        into json_new, json_old
        from jsonb_each(to_jsonb(new)) as n(new_key, new_value)
        join jsonb_each(to_jsonb(old)) as o(old_key, old_value) 
        on new_key = old_key and new_value <> old_value;
    end if;
    insert into tbl_changelog(tbl, op, new, old)
    values (tg_table_name, tg_op, json_new, json_old);
    return null;
end;
$$ language plpgsql;
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.