PostgreSQL trigger function update error - postgresql

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

Related

Why is my trigger not functioning correctly in Postgres?

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

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

How to use trigger with 3 different tables

Dear Fellow StackOverFlow-members,
I have 3 tables. bdc(bdc_id, bdc_name, bdc_gc), stt(stt_id, stt_gc), bts(bts_id, bts_pmv).
I want if stt_gc = 'Checked' then set bdc_gc = 'Sent' and bts_pmv = 'To do'
I use Postgresql 11 and beginning with triggers/stored procedures
I tried to check with if condition stt_gc value and matching with the right bdc_gc bts_pmv according to their primary key.
create or replace function before_stt_gc() returns trigger
language plpgsql
as
$$
begin
if new.stt_gc='Checked' then
select bdc_gc from bdc
where new.stt_id = bdc_id;
doe_gc_bts= 'Sent';
select bts_pmv from bts
where new.stt_id = bts_id;
bts_pmv = 'To do'
end if;
end;
$$;
create trigger before_stt_gc_trigger before insert or update on stt
for each row
execute procedure before_stt_gc();
Obviously if I'm here it's because my code is totally wrong...
I want to learn from this, so if possible, explain me what I'm doing wrong here, or if my approach is lacking insight
I presume you are looking for updates within the IF statement
if new.stt_gc='Checked' then
update bdc set bdc_gc = 'Sent'
where new.stt_id = bdc_id;
UPDATE bts SET bts_pmv = 'To do'
where new.stt_id = bts_id;
end if;

Postgresql AFTER UPDATE trigger OLD has no field

I have a trigger, like many other triggers that fire after update and checks a field of the OLD set.
For some reason this trigger throw this error:
ERROR: record "old" has no field "status"
CONTEXT: SQL statement "SELECT NOT (OLD.status = NEW.status) AND NEW.status = 'Success'"
This is the body of the trigger:
CREATE OR REPLACE FUNCTION tr_online_payment_after_update()
RETURNS trigger AS
$BODY$
DECLARE
v_rec RECORD;
BEGIN
-- If state changed to Success
IF NOT (OLD.status = NEW.status) AND NEW.status = 'Success' THEN
-- Find any invoices attached and set them to paid
FOR v_rec IN
SELECT invoice_id_fk
FROM online_payment_invoice
WHERE online_payment_id_fk = NEW.online_payment_id
LOOP
UPDATE invoice
SET paid_date = CURRENT_DATE,
updator_id_fk = -2,
updated = LOCALTIMESTAMP
WHERE invoice_id = v_rec.invoice_id_fk;
END LOOP;
END IF;
RETURN NULL;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
CREATE TRIGGER tr_online_payment_after_update
AFTER UPDATE
ON online_payment
FOR EACH ROW
EXECUTE PROCEDURE tr_online_payment_after_update();
Weird thing is that it actually seems to run the invoice update part of the trigger.
I cannot see what I am missing here. It does not make sense.
This is the full output:
=>UPDATE online_payment SET status = 'Success' WHERE online_payment_id = 18;
ERROR: record "old" has no field "status"
CONTEXT: SQL statement "SELECT NOT (OLD.status = NEW.status) AND NEW.status = 'Success'"
PL/pgSQL function tr_online_payment_after_update() line 7 at IF
SQL statement "UPDATE load_unit
SET load_hinderance = load_hinderance(load_unit_id)
WHERE load_consign_match_id_fk = v_lcm_id"
PL/pgSQL function tr_invoice_after_update() line 50 at SQL statement
SQL statement "UPDATE invoice
SET paid_date = CURRENT_DATE,
updator_id_fk = -2,
updated = LOCALTIMESTAMP
WHERE invoice_id = v_rec.invoice_id_fk"
PL/pgSQL function tr_online_payment_after_update() line 14 at SQL statement
Add the following diagnostic line to the function:
RAISE NOTICE 'Called % % on %',
TG_WHEN, TG_OP, TG_TABLE_NAME;
Then run the SQL statement that invokes the trigger and see if the output is what you expect.

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.