This is not very complicated to do. If a BEFORE trigger returns a NULL value, this prevents the UPDATE from taking place.
So in the trigger function you can check the conditions if an UPDATE is allowed. If not, Run an UPDATE
statement that marks the row as inactive and do an INSERT.
Something along the lines (not tested!):
create or replace function prevent_update()
returns trigger
as
$$
begin
-- adjust this check to whatever you want to prevent
-- but make sure to only enter this part if the status is not inactive.
-- you might want to check pg_trigger_depth() as well to make sure you never create an endless loop
if old is distinct from new and new.status <> 'inactive' then
-- The row was changed, but status column is the same, so we want to mark
-- the row as inactive
-- Because the new status is 'inactive' the trigger that is now fired for this UPDATE statement
-- will not do anything because we only do this for status other than 'inactive'
UPDATE the_table
SET status = 'inactive'
WHERE id = new.id; --<< the primary key column
INSERT INTO the_table (id, some_column, status)
VALUES (default, new.some_column, 'active');
-- do NOT proceed with original UPDATE
return null;
end if;
-- all OK, proceed with the update
return new;
end;
$$
language plpgsql;
And the trigger definition:
create trigger prevent_update_trigger
before update on the_table
for each row execute procedure prevent_update();
I would be very careful to deploy something like this. This kind of "magic" in the background might be the cause of a lot of confusion and trouble, so use with care!
status
is modified.instead of triggers
can only be defined on VIEWs, not regular tables