Trigger to update a column on update - postgresql

I have written this function and this trigger:
CREATE OR REPLACE FUNCTION test4() RETURNS TRIGGER AS $BODY$
BEGIN
UPDATE "cf"
SET vol=(area*alt);
RETURN null;
END;
$BODY$
LANGUAGE plpgsql
trigger
CREATE TRIGGER trig_upd
AFTER OR UPDATE ON "cf"
FOR EACH ROW
EXECUTE PROCEDURE test4();
I have tested my function and it's ok. So I created the trigger, but I can't insert any value in the table "cf" because the system crashes.

If you want to update the value of vol for each modified row, don't use update, just assign the value. For this to work you also need to define the trigger as a before trigger:
CREATE OR REPLACE FUNCTION test4() RETURNS TRIGGER
AS
$BODY$
BEGIN
new.vol := new.area * new.alt;
RETURN new;
END;
$BODY$
LANGUAGE plpgsql;
CREATE TRIGGER trig_upd
BEFORE UPDATE ON "cf" --<< note the BEFORE!
FOR EACH ROW
EXECUTE PROCEDURE test4()

Related

Build a trigger that avoids to update the values of one column

I have a table in PostgretSQL. I want to build a trigger that avoids to update colb1 column. This column has five alternatives: Rea, Can, Loa, Mul, Alm. So the trigger doesn't let upload values from Rea to Can.
I've built this function but this is fail.
CREATE OR REPLACE FUNCTION example_trigger()
RETURNS trigger AS
$BODY$
BEGIN
new.colb1.tabl1 = 'Rea' := old.colb1.tabl1 = 'Can';
new.colb1.tabl1 = 'Can' := old.colb1.tabl1 = 'Rea';
RETURN new;
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
DROP TRIGGER IF EXISTS trigger_name ON table1;
CREATE TRIGGER trigger_name BEFORE UPDATE ON table1
FOR EACH ROW EXECUTE PROCEDURE example_trigger();
I edited my post with changes:
CREATE OR REPLACE FUNCTION example_trigger()
RETURNS TRIGGER AS
$$
BEGIN
NEW.colb1 := OLD.colb1;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER not_changes
BEFORE UPDATE
ON tabl1
FOR EACH ROW
EXECUTE PROCEDURE example_trigger();
It's not exactly that I want because I want that the values Loa, Mul and Alm can be update.
A slight modification of your function to stop the modification of colb1 from Rea or Can to Can or Rea.
CREATE OR REPLACE FUNCTION example_trigger()
RETURNS TRIGGER AS
$$
BEGIN
IF OLD.colb1 in ('Rea', Can') AND NEW.colb1 IN ('Rea', 'Can') THEN
NEW.colb1 := OLD.colb1;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
This will not stop a user from using an intermediate UPDATE to change to one of Loa/Mul/Alm and then to either Rea or Can

How can I set the date when some row is created? I'm trying to use only Triggers and Functions at PostgreSQL

I have a table called recipes and my goal is for each time that I insert a new row, the column created_at, of this new row, automatically receives the value NOW().
I manage to do something like this on the column updated_at creating this function and trigger, and it's working fine.
CREATE OR REPLACE FUNCTION public.trigger_set_timestamp()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER set_timestamp
BEFORE UPDATE ON recipes
FOR EACH ROW
EXECUTE PROCEDURE trigger_set_timestamp();
But after I changed the code to set the created_at nothing happens and the Postgres doesn't point any errors.
CREATE OR REPLACE FUNCTION public.trigger_set_timestamp_created_at()
RETURNS trigger AS $$
BEGIN
NEW.created_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER set_timestamp_created_at
AFTER INSERT ON recipes
FOR EACH ROW
EXECUTE PROCEDURE trigger_set_timestamp_created_at();

How to avoid recursion in an update trigger that does an update?

I need to update the vat_total_sum of goods in a record using a trigger. But the trigger fires to update the record and falls into recursion. How can I avoid this?
The code:
CREATE OR REPLACE FUNCTION vat_total_sum() RETURNS TRIGGER AS $$
BEGIN
UPDATE goods SET vat_total_sum = NEW.vat_sum / 100 * NEW.vat_percent
WHERE goods_id = new.goods_id;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER vat_total_sum AFTER INSERT OR UPDATE ON goods
FOR EACH ROW EXECUTE PROCEDURE vat_total_sum();
Don't use UPDATE, use a BEFORE triger and assign the new value:
CREATE OR REPLACE FUNCTION vat_total_sum() RETURNS TRIGGER AS $$
BEGIN
new.vat_total_sum := NEW.vat_sum / 100 * NEW.vat_percent;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER vat_total_sum
BEFORE INSERT OR UPDATE ON goods
FOR EACH ROW EXECUTE PROCEDURE vat_total_sum();

Trigger to update table column when new row is insert in postgresql?

I want to write trigger to update table when new row is insert. I am using updatea Query like
UPDATE table SET
geom = ST_SetSRID(ST_MakePoint(longitude, latitude), 4326)
You need to use a BEFORE trigger and then assign the new value:
CREATE OR REPLACE FUNCTION function_update()
RETURNS trigger AS
$BODY$
BEGIN
new.geom := ST_SetSRID(ST_MakePoint(new.longitude, new.latitude), 4326);
RETURN new;
END;
$BODY$
LANGUAGE plpgsql;
This can only be done in a BEFORE trigger:
CREATE TRIGGER triggerinsert
BEFORE INSERT
ON rdpr
FOR EACH ROW
EXECUTE PROCEDURE function_update();
I Solved by writing trigger function like
CREATE OR REPLACE FUNCTION function_update()
RETURNS trigger AS
$BODY$
BEGIN
new.geom := ST_SetSRID(ST_MakePoint(new.longitude, new.latitude), 4326);
RETURN new;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION function_update()
OWNER TO postgres;
and i wrote trigger to update
CREATE TRIGGER triggerinsert
Before INSERT
ON rdpr
FOR EACH ROW
EXECUTE PROCEDURE function_update();

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