postgresql-using trigger to fire - postgresql

I want to create a trigger so that whenever I make a change (Update or Delete) it should copy the old data to a new table (with same template).
I tried this code:
create table restrictions(ID int,name text);
insert into restrictions values(122,'suresh');
select * from restrictions;
create table restrictions_deleted(ID int,name text);// this is my duplicate table for keeping information of all updations.
CREATE OR REPLACE FUNCTION moveDeleted() RETURNS trigger AS $$
BEGIN
INSERT INTO restrictions_deleted VALUES(OLD.ID, OLD.name);
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER moveDeleted
BEFORE DELETE ON restrictions
FOR EACH ROW
EXECUTE PROCEDURE moveDeleted();
delete from restrictions where ID=122;
select * from restrictions_deleted;
This code is capable of recording all the deleted data into duplicate table. But I want to do same for updates also.
Any suggestion, any idea?

First - in a trigger function you need to RETURN NEW; instead of RETURN OLD;.
Second - change the trigger to BEFORE DELETE OR UPDATE.
Last - it is better to have AFTER DELETE OR UPDATE for a logging trigger. This way it wont do useless work, when the change is rolled back.
BTW here is a good example of logging/audit trigger.
UPDATE:
The function will look like:
CREATE OR REPLACE FUNCTION moveDeleted() RETURNS trigger AS $$
BEGIN
IF (TG_OP = 'UPDATE') THEN
INSERT INTO restrictions_deleted VALUES(OLD.ID, OLD.name);
RETURN NEW;
ELSIF (TG_OP = 'DELETE') THEN
INSERT INTO restrictions_deleted VALUES(OLD.ID, OLD.name);
RETURN OLD;
END IF;
END;
$$ LANGUAGE plpgsql;

Related

Postgres Trigger is not firing. Syntax help is appreciated

I'm trying to mark a boolean column on a different table to true upon the insertion of a matching record.
Here's what I've got:
CREATE or replace FUNCTION mark_as_sold() RETURNS trigger AS
$BODY$
BEGIN
UPDATE listing
set listing.sold = true;
WHERE listing.id = NEW.listing_id;
RETURN NEW;
END
$BODY$
LANGUAGE plpgsql;
CREATE TRIGGER update_child_after_update
AFTER INSERT
ON transaction
FOR EACH ROW
EXECUTE PROCEDURE mark_as_sold();
When I create a record on the transaction table, nothing happens. I'm stumped. Any help is greatly appreciated.
I figured it out. I was messing up my relations. Here's the proper code:
CREATE OR REPLACE FUNCTION mark_as_sold()
RETURNS TRIGGER
AS $$
BEGIN
UPDATE listing
set sold = true
WHERE listing.id = NEW.listing_id;
RETURN NEW;
RETURN NEW;
END;
$$
LANGUAGE plpgsql;
CREATE TRIGGER test_trigger BEFORE INSERT OR UPDATE
ON "transaction"
FOR EACH ROW EXECUTE PROCEDURE mark_as_sold();

Performance wise, is it better to create a separate triggers for INSERT, DELETE and UPDATE events or just one for all the events

In order to maintain audit log for the table test_table, I need to create triggers on the base table for INSERT, UPDATE and DELETE events and then insert these records in an audit table.
I can create trigger (and also associated procedure) in the following manner:
Create the procedure as:
CREATE OR REPLACE FUNCTION audit_test_table_function() RETURNS TRIGGER AS $body$
BEGIN
IF (TG_OP = 'DELETE') THEN
INSERT INTO audit_test_table VALUES (OLD.*, now(), user, pg_backend_pid(), 'D', DEFAULT);
RETURN OLD;
ELSIF (TG_OP = 'UPDATE') THEN
INSERT INTO audit_test_table VALUES (NEW.*, now(), user, pg_backend_pid(), 'U', DEFAULT);
RETURN NEW;
ELSIF (TG_OP = 'INSERT') THEN
INSERT INTO audit_test_table VALUES (NEW.*, now(), user, pg_backend_pid(), 'I', DEFAULT);
RETURN NEW;
END IF;
RETURN NULL;
END; $body$ LANGUAGE plpgsql;
And then create the trigger:
CREATE TRIGGER audit_test_table_trigger AFTER INSERT OR UPDATE OR DELETE ON test_table FOR EACH ROW EXECUTE PROCEDURE audit_test_table_function();
Other option would be to create the trigger/function for individual events ie separate one for DELETE event as following:
CREATE OR REPLACE FUNCTION audit_test_table_delete_function() RETURNS TRIGGER AS $body$
BEGIN
INSERT INTO audit_test_table VALUES (OLD.*, now(), user, pg_backend_pid(), 'D', DEFAULT);
RETURN OLD;
END;
$body$ LANGUAGE plpgsql;
CREATE TRIGGER audit_test_table_trigger AFTER DELETE ON test_table FOR EACH ROW EXECUTE PROCEDURE audit_test_table_delete_function();
And similarly for INSERT and UPDATE events.
My question is performance wise which one is recommended. And is there anything else that I should keep in mind?
I have already checked this but it doesn't answer my question.
You'll save a little execution time if you write three simpler functions, but I doubt that it is worth the effort.
If performance is paramount, you might consider writing the trigger functions in C.

Postgres insert or update trigger WHEN condition (old)

I need write insert or update trigger, but with WHEN condition with compare OLD and NEW rows.
According documentation OLD is null for insert operation. How i can use OLD in WHEN condition for INSERT AND UPDATE triggers?
Example trigger:
CREATE TRIGGER mytrigger
BEFORE INSERT OR UPDATE ON "mytable"
FOR EACH ROW
WHEN (NEW.score > 0 AND OLD.score <> NEW.score)
EXECUTE PROCEDURE mytrigger();
but for insert OLD is null.
Option A:
You can change the code so that conditions will be in the trigger function rather than the trigger itself. With this approach OLD will be used only in the UPDATE.
Trigger:
CREATE TRIGGER mytrigger
BEFORE INSERT OR UPDATE ON "mytable"
FOR EACH ROW
EXECUTE PROCEDURE mytrigger();
Trigger function:
CREATE OR REPLACE FUNCTION mytrigger()
RETURNS trigger AS
$BODY$
begin
if NEW.score > 0 then
--code for Insert
if (TG_OP = 'INSERT') then
YOUR CODE
end if;
--code for update
if (TG_OP = 'UPDATE') then
if OLD.score <> NEW.score then -- (if score can be null see #voytech comment to this post)
YOUR CODE
end if;
end if;
end if;
return new;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
Option B:
As Thilo suggested write two triggers that share the same trigger function.
Triggers:
CREATE TRIGGER mytrigger1
BEFORE INSERT ON "mytable"
FOR EACH ROW
WHEN NEW.score > 0
EXECUTE PROCEDURE mytrigger();
CREATE TRIGGER mytrigger2
BEFORE UPDATE ON "mytable"
FOR EACH ROW
WHEN (NEW.score > 0 AND OLD.score <> NEW.score)
EXECUTE PROCEDURE mytrigger();
Trigger function:
CREATE OR REPLACE FUNCTION mytrigger()
RETURNS trigger AS
$BODY$
begin
YOUR CODE
return new;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
Trigger.oldMap.keySet(); will give the Id's present in the Trigger.oldMap.
It is a set type collection of all the Id's.
have a look at the following example, change the DML events in the trigger events every time and see the debug logs.
you will understand which trigger context variable is available for which DML event.
CREATE TRIGGER Email_Check_On_Contact
before update{
oldMap<ID,Contact>=new Map<ID,Contact>();
o = trigger.oldMap;
for(Contact newcont: trigger.new)
{
if(newcont.Email != o.get(newcont.Id).Email)
{
newcont.Email.addError('Email cannot be changed');
}
}
}

Postgres - ´after update´ trigger doesn't fire

I have this table for which one of the columns is to be filled with values from other columns of the same table (these other columns represent a taxonomic hierarchy whose lowest level I wish to store in this other column).
To achieve this I implemented the following trigger:
CREATE OR REPLACE FUNCTION get_taxon()
RETURNS TRIGGER LANGUAGE plpgsql AS
$BODY$
BEGIN
UPDATE taxon SET taxon = coalesce(subespecie, especie, genero, subfamilia, familia, infraordem, subordem, ordem, superordem, subclasse, classe, subphylum, phylum, reino )
WHERE taxon IS NULL;
RETURN NEW;
END
$BODY$
VOLATILE;
CREATE TRIGGER update_taxon
AFTER INSERT OR UPDATE ON taxon
FOR EACH STATEMENT
WHEN (pg_trigger_depth() = 0) -- Prevent recursive trigger calls
EXECUTE PROCEDURE get_taxon();
When I insert a new record the trigger works as expected, but if I update an existing record, nothing happens - the trigger just ignores UPDATE operations and I don't know why.
Can anyone shed some light on this please?
After your statement trigger first fired all records in the table should have their taxon field updated with the best available information. When you update a record you may want to update the taxon value, but you are better off with a BEFORE INSERT OR UPDATE FOR EACH ROW trigger. The only new data is contained in the row for which the trigger fires, of course. So try this:
CREATE OR REPLACE FUNCTION get_taxon()
RETURNS TRIGGER LANGUAGE plpgsql AS
$BODY$
BEGIN
NEW.taxon := coalesce(NEW.subespecie, NEW.especie, NEW.genero, NEW.subfamilia,
NEW.familia, NEW.infraordem, NEW.subordem, NEW.ordem, NEW.superordem,
NEW.subclasse, NEW.classe, NEW.subphylum, NEW.phylum, NEW.reino);
RETURN NEW;
END;
$BODY$ VOLATILE;
CREATE TRIGGER update_taxon
BEFORE INSERT OR UPDATE ON taxon
FOR EACH ROW EXECUTE PROCEDURE get_taxon();

How to apply a update after an inser or update POSTGRESQL Trigger

How to apply an update after an insert or update in POSTGRESQL; I have got a table which has a field lastupdate; I want that field to be set up whenever the row is updated or when it was inserted.
I tried this trigger, but It is not working! HELP!!
CREATE OR REPLACE FUNCTION fn_update_profile()
RETURNS TRIGGER AS $update_profile$
BEGIN
IF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE' ) THEN
UPDATE profile SET lastupdate=now() where oid=OLD.oid;
RETURN NULL;
ELSEIF (TG_OP = 'DELETE') THEN
RETURN NULL;
END IF;
RETURN NULL; -- result is ignored since this is an AFTER trigger
END;
$update_profile$ LANGUAGE plpgsql;
Your trigger function can be a lot easier than you had. Keep in mind that PG will do the update or the insert on the original table, you only have to deal with keeping the profile table up-to-date:
CREATE OR REPLACE FUNCTION fn_update_profile()
RETURNS TRIGGER AS $update_profile$
BEGIN
UPDATE profile SET lastupdate = now() WHERE oid = NEW.oid;
RETURN NEW;
END;
$update_profile$ LANGUAGE plpgsql;
The INSERT and UPDATE trigger functions both use the NEW parameter; the INSERT trigger function does not have the OLD parameter. You should always return NEW from the trigger function if successful (or OLD from a DELETE trigger), even if it is an AFTER INSERT OR UPDATE trigger; the whole operation will be rolled back if NULL is returned. If you then define the actual trigger to fire after the insert or update, you should be good:
CREATE TRIGGER tr_update_profile
AFTER INSERT OR UPDATE ON my_table
FOR EACH ROW EXECUTE PROCEDURE fn_update_profile();