Reputation: 1564
I have the following tables.
tbl_groups contains all the current data, and I'm storing the name in a history table when it changes.
CREATE TABLE tbl_groups (
id integer NOT NULL,
name varchar NOT NULL,
active boolean NOT NULL
);
CREATE TABLE tbl_groups_history (
id integer NOT NULL,
group_id integer NOT NULL,
name varchar NOT NULL
);
When the active field is false an update may not occur. So I created a trigger for this to throw an exception when this happens:
CREATE OR REPLACE FUNCTION fn_group_deny_update_when_inactive() RETURNS TRIGGER AS
$BODY$
BEGIN
IF
OLD.active = false
THEN
RAISE EXCEPTION 'Record is inactive';
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
CREATE TRIGGER 01_group_can_update
BEFORE UPDATE ON tbl_groups
FOR EACH ROW
EXECUTE PROCEDURE fn_group_deny_update_when_inactive();
I also have a trigger that writes a record to the history table
CREATE OR REPLACE FUNCTION fn_group_log_history() RETURNS trigger as
$BODY$
BEGIN
IF
(NEW.name <> OLD.name)
THEN
INSERT INTO tbl_groups_history (group_id, name)
VALUES (OLD.id, OLD.name);
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
CREATE TRIGGER 02_group_write_history_log
BEFORE UPDATE ON tbl_groups
FOR EACH ROW
EXECUTE PROCEDURE fn_group_log_history();
I have named the triggers with 01 and 02 as a prefix, so I know in what order they will be executed as PostgreSQL fires them in alphabetical order.
I have some questions about this approach as I'm not sure if it's good practice:
Everything in one function:
CREATE OR REPLACE FUNCTION fn_group_before_update() RETURNS trigger as
$BODY$
BEGIN
IF
OLD.active = false
THEN
RAISE EXCEPTION 'Record is inactive';
END IF;
IF
(NEW.name <> OLD.name)
THEN
INSERT INTO tbl_groups_history (group_id, name)
VALUES (OLD.id, OLD.name);
END IF;
RETURN NEW;
END;
$BODY$
Any thoughts about this considering performance, maintenance of code and stuff like that?
Upvotes: 6
Views: 11536
Reputation: 121494
Two problems. 01_group_can_update
is not a proper name, so change this for e.g.:
CREATE TRIGGER trg_01_group_can_update
BEFORE UPDATE ON tbl_groups
FOR EACH ROW
EXECUTE PROCEDURE fn_group_deny_update_when_inactive();
Next, the id
in tbl_groups_history
should be serial:
CREATE TABLE tbl_groups_history (
id serial NOT NULL,
group_id integer NOT NULL,
name varchar NOT NULL
);
There is no reason to have more than one trigger in your case, so the second solution seems better. However, the first variant should work too. Per the documentation:
If more than one trigger is defined for the same event on the same relation, the triggers will be fired in alphabetical order by trigger name. In the case of BEFORE and INSTEAD OF triggers, the possibly-modified row returned by each trigger becomes the input to the next trigger. If any BEFORE or INSTEAD OF trigger returns NULL, the operation is abandoned for that row and subsequent triggers are not fired (for that row).
Upvotes: 7