Why is my trigger not functioning correctly in Postgres? - postgresql

I have a trigger that fires when values in another table is updated. Everything seems to be working correctly except breakeven_price is not giving me the value I expect.
Instead of returning the value of map or landed_cost times 100/85, its only returning the larger of map or landed_cost. I'm new to triggers and sql generally, so I don't know if I'm using the trigger or case statements correctly.
BEGIN
IF TG_OP = 'UPDATE' THEN
UPDATE s1.data
SET landed_cost = input.landed_cost, quantity = input.quantity, map = input.map, updated = now(),
breakeven_price =
CASE
WHEN input.map > input.landed_cost THEN (input.map*(100/85))
WHEN input.map <= input.landed_cost THEN (input.landed_cost*(100/85))
END
FROM s1.input
WHERE data.dist_sku = input.dist_sku;
END IF;
RETURN NULL;
END;
EDIT Trigger definition follows:
CREATE TRIGGER pricing_trigger
AFTER UPDATE
FOR EACH ROW
EXECUTE PROCEDURE s1.pricing_function();

Related

Postgresql trigger check if NULL

I have a table where rows described by an unique ID are meant to be updated from an automatic process on a regular bassis.
INSERT INTO mytable(x,y,z) ON CONFLICT(x) DO UPDATE SET y=A...
USE CASE :
Some rows do not have a value yet, the next cycle might set some values.
Other rows already have values, but the next cycle might have NULL values to update. In this case, I don't want the NULL values to replace the current values.
I created this trigger :
CREATE OR REPLACE FUNCTION public.clean_update()
RETURNS trigger
LANGUAGE 'plpgsql'
COST 100
VOLATILE NOT LEAKPROOF
AS $BODY$
BEGIN
NEW.company = COALESCE(NEW.company,OLD.company);
NEW.first_name = COALESCE(NEW.first_name,OLD.first_name);
NEW.last_name = COALESCE(NEW.last_name,OLD.last_name);
NEW.address = COALESCE(NEW.address,OLD.address);
NEW.country = COALESCE(NEW.country,OLD.country);
NEW.phone = COALESCE(NEW.phone,OLD.phone);
NEW.mail = COALESCE(NEW.mail,OLD.mail);
NEW.evaluation = COALESCE(NEW.evaluation,OLD.evaluation);
NEW.positive = COALESCE(NEW.positive,OLD.positive);
NEW.neutral = COALESCE(NEW.neutral,OLD.neutral);
NEW.negative = COALESCE(NEW.negative,OLD.negative);
NEW.nb_products = COALESCE(NEW.nb_products,OLD.nb_products);
NEW.nb_sales = COALESCE(NEW.nb_sales,OLD.nb_sales);
NEW.nb_sale_url = COALESCE(NEW.nb_sale_url,OLD.nb_sale_url);
NEW.unregistered = COALESCE(NEW.unregistered,OLD.unregistered);
RETURN NEW;
END;
$BODY$;
With this trigger, the updated value is not replaced if its NEW.value is NULL, great.
The problem comes when updating only some of the columns :
ERROR: record "new" has no field "nb_sale_url"
Is there a way to check if NEW.x exists event if it's null ?
Or maybe there is a simplier way to do it

How to update same line than insert/update in plpgsql without reaching max_stack_depth

My problem is I reach the limit of the stack. And the message error says “You should increase max_stack_depth” and shows me the line that I use to update another column.
I encounter this error after an update request (code below).
I know my problem may look like others questions but none of them explain why I reach this error.
What I want to do is simple and I've done it many times, but here I'm missing something.
I want: if there is an update on the table support_fh pull a trigger. I expect this trigger to do:
if the new values of the update request are section= 'DISTRIBUTION' and modulo= '6' and fabricant = 'NEXANS' and capacite = 12 then set diametre = '12.5' (code below).
Of course it is the line of diametre from the same line than update request.
Futhermore I know I should use the character varying type instead of the integer type, but I was asked to so it like that.
My trigger function:
create or replace function maj_diam() returns trigger
as
$$
Declare fab_loc character varying;
Declare section_loc character varying;
Declare capa_loc character varying;
Declare modulo_loc character varying;
BEGIN
Select fabricant into fab_loc from support_fh where id = new.id;
Select section into section_loc from support_fh where id = new.id;
Select capcite into capa_loc from support_fh where id = new.id;
Select modulo into modulo_loc from support_fh where id = new.id;
if fab_loc = 'NEXANS' and section_loc = 'DISTRIBUTION'
and capa_loc = '12' and modulo_loc = '6' then
update support_fh set diametre = '12.2' where id = new.id;
endif;
return new;
end;
$$;
My trigger :
create trigger maj_diam
After update on support_fh
for each row
execute procedure maj_diam();
My update request to test my trigger :
update support_fh set fabricant = 'NEXANS', section = 'DISTRIBUTION', capacite = '12', modulo = '6'
where id = 11827;
I want to learn from this, so, if possible, explain to me what I'm doing wrong here, or if my approach is lacking insight.
You get that problem because the update in the trigger will launch the trigger again, causing an infinite loop. No value of max_stack_depth is big enough for that (and increasing that value too much is dangerous anyway).
Instead of what you are doing, you should create a BEFORE trigger and modify the NEW value that are about to be inserted:
IF NEW.fab_loc = 'NEXANS' AND NEW.section_loc = 'DISTRIBUTION'
AND NEW.capa_loc = '12' AND NEW.modulo_loc = '6'
THEN
NEW.diametre := '12.2';
END IF;
If you want to change columns in a row that is updated (or inserted), don't use UPDATE in the trigger function. Declare the trigger as BEFORE UPDATE, then simply assign the new values.
You also don't need four select statements to read four columns from the same table.
But as you are only accessing columns from the same row that was updated, you don't even need a SELECT at all.
So your trigger function can be simplified to:
create or replace function maj_diam() returns trigger
as
$$
BEGIN
if new.fabricant = 'NEXANS'
and new.section = 'DISTRIBUTION'
and new.capcite = '12'
and new.modulo = '6'
then
new.diametre := '12.2';
end if;
return new;
end;
$$;
Assuming that capcite, modulo and diametre are actually numbers, you shouldn't compare them with varchar values. So the above code should probably be: new.diametre := 12.2; or new.capcite = 12.
And the trigger definition needs to be changed to:
create trigger maj_diam
BEFORE update on support_fh
for each row
execute procedure maj_diam();

PostgreSQL trigger function update error

i cant seem to logically place this error being produced unlike most gives little to no information as to why (usually seems theirs very good pre-processors or you can dig into the logic, yet here i get no error.
also i have another function working well, that adds those keys, so its not that..
other then the transaction seems to fail and i get in the terminal when i enter
update guest_list set coatcheck = true where ticket_number = 3;
"PL/pgSQL function coatcheck_gen() line 8 at SQL statement
SQL statement "update guest_list set coatcheck_num = coat_num where ticket_number = old.ticket_number"
PL/pgSQL function coatcheck_gen() line 8 at SQL statement
SQL statement "update guest_list set coatcheck_num = coat_num where ticket_number = old.ticket_number"
this goes on for pages, then ends.
i've tried uses new. old. or just numbers to see. and nothing. same error.
all of the tables are fine. all updates work when just done on command.
it appears in examples elsewhere seemly correct...
the function is
create or replace function coatcheck_gen() returns trigger as $gencoatcheck$
declare
coat_num bigint;
begin
IF (TG_OP = 'UPDATE') then
if ( new.coatcheck = true ) then
coat_num := (old.frkey_id_event + old.frkey_id_guest);
update guest_list set coatcheck_num = coat_num where ticket_number = old.ticket_number;
return new;
END IF;
return new;
end if;
return new;
end;
$gencoatcheck$ LANGUAGE plpgsql;
trigger
create trigger trg_coatchek_gen after update on guest_list for each row when (new.coatcheck = true) execute Procedure coatcheck_gen();
You are making an infinite loop by updating the table inside the trigger.
You call it first and set the coatcheck = true, then the trigger update the table again but since coatcheck = true it will be again processed by the trigger (and this loop will never end).
You sould replace the entire line
update guest_list set coatcheck_num = coat_num where ticket_number = old.ticket_number;
by
new.coatcheck_num = coat_num;
and make the trigger before update

What should return trigger BEFORE DELETE

I am trying to make simple trigger function (Postgresql) but I am getting same error 'function did not return any row' in all these cases (just simple examples):
New:
UPDATE somewhere SET something = something - 1;
RETURN NEW;
Old:
UPDATE somewhere SET something = something - 1;
RETURN OLD;
What should I return when I call this function "before delete"? ("after insert/update" works well)
Tyvm for tips!
Full code as requested:
Function:
CREATE OR REPLACE FUNCTION pictogram_frequency_on_delete()
RETURNS trigger AS
$BODY$
DECLARE
new_frequency RECORD;
target_unit RECORD;
current_row RECORD;
units_with_same_type RECORD;
what RECORD;
BEGIN
SET search_path TO 'myScheme';
CASE TG_OP
WHEN 'DELETED' THEN what := OLD;
ELSE what:= OLD;
END CASE;
SELECT unit_type_uid INTO STRICT target_unit
FROM unit
WHERE unit_uid = what.unit_uid;
SELECT count(*) AS exists INTO STRICT current_row
FROM unit_type_pictogram utp
WHERE utp.pictogram_uid = what.pictogram_uid
AND utp.unit_type_uid = target_unit.unit_type_uid;
IF (current_row.exists = 0) THEN
RETURN what; /* return new/old doesnt work too */
END IF;
UPDATE unit_type_pictogram utp
SET frequency = frequency - 1
WHERE utp.pictogram_uid = what.pictogram_uid
AND utp.unit_type_uid = target_unit.unit_type_uid;
RETURN what; /* return new/old doesnt work too */
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
Trigger:
CREATE TRIGGER on_delete_frequency
BEFORE DELETE
ON unit_pictogram
FOR EACH ROW
EXECUTE PROCEDURE pictogram_frequency_on_delete();
From documentation:
Trigger functions invoked by per-statement triggers should always
return NULL. Trigger functions invoked by per-row triggers can return
a table row (a value of type HeapTuple) to the calling executor, if
they choose. A row-level trigger fired before an operation has the
following choices:
It can return NULL to skip the operation for the current row. This instructs the executor to not perform the row-level operation that
invoked the trigger (the insertion, modification, or deletion of a
particular table row).
For row-level INSERT and UPDATE triggers only, the returned row becomes the row that will be inserted or will replace the row being
updated. This allows the trigger function to modify the row being
inserted or updated.
A row-level BEFORE trigger that does not intend to cause either of
these behaviors must be careful to return as its result the same row
that was passed in (that is, the NEW row for INSERT and UPDATE
triggers, the OLD row for DELETE triggers).
EDIT
Try something like this:
CREATE OR REPLACE FUNCTION pictogram_frequency_on_delete()
RETURNS trigger AS
$BODY$
BEGIN
UPDATE unit_type_pictogram AS utp
SET frequency = frequency - 1
FROM unit
WHERE utp.pictogram_uid = OLD.pictogram_uid
AND unit_uid = OLD.unit_uid
AND utp.unit_type_uid = unit.unit_type_uid;
RETURN OLD;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
You should RETURN OLD;.
Your function must be defined as RETURNS trigger.

PostgreSQL Trigger and rows updated

I am trying to update a table according to this trigger:
CREATE TRIGGER alert
AFTER UPDATE ON cars
FOR EACH ROW
EXECUTE PROCEDURE update_cars();
Trigger Function :
CREATE FUNCTION update_cars()
RETURNS 'TRIGGER'
AS $BODY$
BEGIN
IF (TG_OP = 'UPDATE') THEN
UPDATE hello_cars SET status = new.status
WHERE OLD.ID = NEW.ID;
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
The trigger works fine. When the cars table is updated, the hello_cars table is updated but the status column in each row is updated and contains same new status! It must be updated according to a car ID.
I think my problem is in condition: WHERE OLD.ID = NEW.ID; but I can't tell what's wrong.
Thanks in advance.
OLD and NEW are aliases to the rows which fired the trigger. So when you execute a statement like
UPDATE cars SET status='xyz' WHERE cars.id = 42;
then the trigger function will execute
UPDATE hello_cars SET status='xyz' WHERE 42 = 42
The part 42=42 is always true. So each row in hello_cars is updated.
You really want something like
[...]WHERE hello_cars.id = OLD.ID
or a little shorter
[...]WHERE id = OLD.ID
But you also need to think about what happens, if the initial update changes cars.id. In this case OLD.ID is not equal NEW.ID. What should happen in the table hello_cars in this case? But that's another question.
OLD.ID and NEW.ID are referencing values in the updated row of the table cars and thus (unless you change the ID in cars) will always evaluate to true and therefor all rows in hello_cars are updated.
I think you probably want:
UPDATE hello_cars
SET status = new.status
WHERE id = new.id;
This assumes that there is a column id in the table hello_cars that matches the id in cars.