Postgresql Trigger example - postgresql

I created a trigger when a row updated trigger will change that row. When I update a column I get an error. How can I solve?
ERROR: stack depth limit exceeded
HINT: Increase the configuration parameter "max_stack_depth" (currently 2048kB), after ensuring the platform's stack depth limit is adequate.
CREATE OR REPLACE FUNCTION ynt_call()
RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'UPDATE' THEN
UPDATE ynt.a_test SET date_time = now();
END IF;
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE TRIGGER update_call AFTER UPDATE ON ynt.a_test FOR EACH ROW EXECUTE PROCEDURE ynt_call();

Of course you're looping until the end of time, because you are updating row when updating row. You should set the field date_time using the special variable new as following:
CREATE OR REPLACE FUNCTION ynt_call()
RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'UPDATE' THEN
new.date_time = now();
END IF;
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE TRIGGER update_call AFTER UPDATE ON ynt.a_test FOR EACH ROW EXECUTE PROCEDURE ynt_call();

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.

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

postgresql - trigger function with condition

I'm trying to create a trigger with a condition. Based on the geom length I want the attribute "nom" (= name) to be written in upper case or lower case.
here's what I have:
CREATE OR REPLACE FUNCTION public.test_upper_lower()
RETURNS trigger AS
$BODY$
BEGIN
NEW.dummy:= (ST_Length(new.geom));
if (SELECT dummy FROM ligne_ligne)>100
then NEW.nom:= LOWER(nom) FROM ligne_ligne;
else NEW.nom:= UPPER(nom) FROM ligne_ligne;
end if;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
DROP trigger IF EXISTS test_upper_lower on public.ligne_ligne;
CREATE trigger test_upper_lower BEFORE INSERT OR UPDATE on public.ligne_ligne
FOR EACH ROW
EXECUTE PROCEDURE public.test_upper_lower();
With this I have a "more than one row returned by a subquery" error
Based on other questions on this forum I tried it using case instead of if and using when in the trigger itself not the function but neither are working
Any ideas ?
Thanks
You don't need (or can actually) use SELECT statements to access data from the inserted row.
The part SELECT dummy FROM ligne_ligne returns all rows from that table - not just from the one relevant to the trigger.
As you just want to check the value you just calculated, simply use new.dummy at that point:
CREATE OR REPLACE FUNCTION public.test_upper_lower()
RETURNS trigger AS
$BODY$
BEGIN
NEW.dummy:= ST_Length(new.geom);
if new.dummy > 100 then --<< no SELECT necessary
NEW.nom:= LOWER(new.nom); --<< no "FROM", just access the value
else
NEW.nom:= UPPER(new.nom);
end if;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;