Postgres insert or update trigger WHEN condition (old) - postgresql

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');
}
}
}

Related

Trigger function can't update table

Here is my trigger function
CREATE OR REPLACE FUNCTION test_table_insert()
RETURNS TRIGGER
AS $$
BEGIN
IF NEW.id IS NULL THEN
RAISE EXCEPTION 'id is null';
END IF;
UPDATE e_sub_agreement SET ro_id = NEW.id WHERE id = NEW.id;
RETURN NEW;
END;
$$
LANGUAGE plpgsql;
CREATE TRIGGER test_table_insert AFTER INSERT ON e_sub_agreement FOR EACH ROW EXECUTE PROCEDURE test_table_insert();
The problem is that it doesn't update table e_sub_agreement. I checked NEW value and everything is good. It returns with the new id. If I change where statement id = "some existing id in table", then it works. It changes ro_id to the new id. How is it possible? My guess is that data has not been inserted into table and trigger function can't find row with the given id. But it's not how trigger's after insert function works. What's the magic?
An AFTER trigger can not change anything. Running an additional UPDATE is also quite inefficient. Change this to a BEFORE trigger and assign the value you want:
CREATE OR REPLACE FUNCTION test_table_insert()
RETURNS TRIGGER
AS $$
BEGIN
IF NEW.id IS NULL THEN
RAISE EXCEPTION 'id is null';
END IF;
NEW.ro_id := NEW.id;
RETURN NEW;
END;
$$
LANGUAGE plpgsql;
CREATE TRIGGER test_table_insert
BEFORE INSERT ON e_sub_agreement
FOR EACH ROW EXECUTE PROCEDURE test_table_insert();
Note that the NOT NULL check is better done by defining the column as NOT NULL.

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();

How to create a trigger that checks values?

I need help with my trigger. For example i have Query like this
INSERT INTO test(id_users,id_type,id_color) VALUES(1,3,4);
in my data base i have table with name: test
ghost, id, id_users, id_type, id_color
and i need before insert or update to check
Create trigger
Begin
select id from test where ghost = false and id_users = 26 and id_type = 3
if NO execute INSERT
if YEST exit with no action
END
How can i creat this trigger ?
There are two ways, depending on how you want to manage the problem.
If you wish to silence it, use a before trigger and return null:
create function ignore_invalid_row() returns trigger as $$
begin
if not exists(
select 1
from test
where not ghost
and id_users = new.id_users
and id_type = new.id_type
)
then
return null;
end if;
return new;
end;
$$ language plpgsql;
create trigger ignore_invalid_row_tg before insert on test
for each row execute procedure ignore_invalid_row();
If you wish to raise it, use a constraint trigger and raise an exception:
create function reject_invalid_row() returns trigger as $$
begin
if not exists(
select 1
from test
where not ghost
and id_users = new.id_users
and id_type = new.id_type
)
then
raise exception 'message';
end if;
return null;
end;
$$ language plpgsql;
create constraint trigger reject_invalid_row_tg after insert on test
for each row execute procedure reject_invalid_row();
http://www.postgresql.org/docs/current/static/sql-createtrigger.html
First, you need to create a trigger function and then the trigger, based on it:
CREATE OR REPLACE FUNCTION my_trigger_function() RETURNS TRIGGER AS $BODY$ BEGIN
IF EXISTS (SELECT id FROM test WHERE ghost = false AND id_users = 26 AND id_type = 3) THEN
return NEW;
ELSE
return NULL;
END IF; END; $BODY$ LANGUAGE 'plpgsql';
Then, you create trigger based on this function:
CREATE TRIGGER t_my_trigger BEFORE INSERT ON test FOR EACH ROW EXECUTE PROCEDURE my_trigger_function();
For more on triggers, see postgres docs.

postgresql-using trigger to fire

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;