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.
Related
I want to update the timestamp attribute 'register_updated' to the current_timestamp whenever any value is updated in the table.
This is my function
CREATE OR REPLACE FUNCTION fn_register_updated()
RETURNS trigger language plpgsql AS $$
BEGIN
UPDATE tb_register
SET register_updated = CURRENT_TIMESTAMP
WHERE (OLD.* IS DISTINCT FROM NEW.*);
RETURN NEW;
END;
$$;
CREATE TRIGGER tg_register_updated
BEFORE UPDATE
ON tb_register
FOR EACH ROW
EXECUTE PROCEDURE fn_register_updated();
But whenever I run an update on a table I receive the following error:
SQL statement "UPDATE tb_register
SET register_updated = CURRENT_TIMESTAMP"
PL/pgSQL function fn_register_updated() line 3 at SQL statement
SQL statement "UPDATE tb_register
SET register_updated = CURRENT_TIMESTAMP"
PL/pgSQL function fn_register_updated() line 3 at SQL statement
SQL statement "UPDATE tb_register
Any ideas on how to solve this?
I am struggling with the use of UPDATE within the body of the function.
Thank you,
Sample code for you:
CREATE OR REPLACE FUNCTION fn_register_updated()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
begin
new.register_updated = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$function$;
create trigger tg_register_updated before
update
on
tb_register for each row execute function fn_register_updated();
I want to update the record of a particular user only. But the update trigger fires for all the users.
CREATE OR REPLACE FUNCTION replace_empty_username_with_null() RETURNS TRIGGER
LANGUAGE plpgsql AS
BEGIN
UPDATE user_temp
SET username=null
WHERE NEW.id = OLD.id and NEW.username = '';
RETURN NEW;
END
CREATE TRIGGER replace_empty_username
AFTER UPDATE OF username ON user_temp
FOR EACH ROW
WHEN (NEW.username = '' AND OLD.id = NEW.id)
EXECUTE PROCEDURE replace_empty_username_with_null();
The expression NEW.id = OLD.id makes sense in the WHEN clause of the trigger ("ID did not change and is not null"). But it makes no sense in the body of the trigger function, where it burns down to TRUE. Also, to only affect the row which is being updated, just use a BEFORE trigger instead:
CREATE OR REPLACE FUNCTION replace_empty_username_with_null()
RETURNS TRIGGER LANGUAGE plpgsql AS
$func$
BEGIN
NEW.username = null; -- !
RETURN NEW;
END
$func$
CREATE TRIGGER replace_empty_username
BEFORE UPDATE OF username ON user_temp -- !
FOR EACH ROW
WHEN (NEW.username = '' AND OLD.id = NEW.id)
EXECUTE PROCEDURE replace_empty_username_with_null();
Related:
PostgreSQL Update trigger
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
i'm coding this trigger in postgreSQL
CREATE OR REPLACE FUNCTION fn_trg_viabilidad_fila()
RETURNS trigger AS
$BODY$
BEGIN
PERFORM S.*
FROM MontoMinimo M, SolicitudPresupuesto S, Cantidad C, Producto P
WHERE P.idProducto=C.idProducto
and C.idPresupuesto=S.idPresupuesto
and M.idMonto=S.idMonto;
IF (C.cantidad < P.canMinExp OR P.exportable = FALSE)
THEN
UPDATE SolicitudPresupuesto
SET viable = FALSE
WHERE idPresupuesto = OLD.idPresupuesto;
RETURN NEW;
END IF;
END
$BODY$
LANGUAGE plpgsql
CREATE TRIGGER trg_viabilidad_fila BEFORE INSERT
OR UPDATE ON SolicitudPresupuesto
FOR EACH ROW EXECUTE PROCEDURE
fn_trg_viabilidad_fila() ;
I can't solve this error..
An error has occurred: ERROR: missing FROM-clause entry for table "c"
LINE 1: SELECT C.cantidad < P.canminexp OR P.exportable = FALSE ^
QUERY: SELECT C.cantidad < P.canminexp OR P.exportable = FALSE
CONTEXT: PL/pgSQL function fn_trg_viabilidad_fila() line 9 at IF
I will be very grateful to any help. Sorry for my bad english
You can't access the columns of a query outside of the query (or the block where you use the query). You need to store the result of the select somewhere. Additionally you shouldn't run an UPDATE on the triggered table, you need to assign the value to the NEW record.
CREATE OR REPLACE FUNCTION fn_trg_viabilidad_fila()
RETURNS trigger AS
$BODY$
DECLARE
l_result boolean;
BEGIN
SELECT (c.cantidad < p.canMinExp OR p.exportable = FALSE)
INTO l_result
FROM MontoMinimo M
JOIN SolicitudPresupuesto s ON m.idMonto = s.idMonto
JOIN Cantidad c ON c.idPresupuesto = s.idPresupuesto
JOIN Producto p ON p.idProducto = c.idProducto;
IF l_result THEN
new.viable := false;
END IF;
RETURN NEW;
END
$BODY$
LANGUAGE plpgsql;
It would be possible to "inline" the query into the IF statement but this way it resembles the structure of your current code better. Also note that I replaced the old, outdated implicit joins by an explicit and more robust JOIN operator.
The assigment new.viable assumes that idpresupuesto is the PK in the table solicitudpresupuesto (because you used that in the WHERE clause of the UPDATE statement)
I have:
CREATE OR REPLACE FUNCTION aktualizujIloscPodan() RETURNS TRIGGER AS
$BODY$
DECLARE
n integer;
sid integer;
BEGIN
sid=0;
IF (TG_OP='INSERT') THEN
sid = NEW."studentID";
ELSIF (TG_OP='DELETE') THEN
sid = OLD."studentID";
END IF;
n = COALESCE ((SELECT count("studentID") as c
FROM "Podania" WHERE "studentID"=sid
GROUP BY "studentID"), 0);
UPDATE "Studenci" SET "licznikpodan" = n WHERE "ID"=sid;
END;
$BODY$
LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS triggenPodan ON "Podania";
CREATE TRIGGER triggenPodan AFTER INSERT OR DELETE
ON "Podania"
EXECUTE PROCEDURE aktualizujIloscPodan();
When I try to execute:
DELETE FROM "Podania"
I get
ERROR: record "old" is not assigned yet
DETAIL: The tuple structure of a not-yet-assigned record is indeterminate.
CONTEXT: PL/pgSQL function "aktualizujiloscpodan" line 11 at assignment
********** Błąd **********
ERROR: record "old" is not assigned yet
Stan SQL:55000
Szczegóły:The tuple structure of a not-yet-assigned record is indeterminate.
Kontekst:PL/pgSQL function "aktualizujiloscpodan" line 11 at assignment
It seems like it doesn't know what is OLD or NEW. How can I fix that?
You need to use FOR EACH ROW
CREATE TRIGGER triggerPodan AFTER INSERT OR DELETE
ON "Podania" FOR EACH ROW
EXECUTE PROCEDURE aktualizujIloscPodan();
For the delete trigger only OLD record is defined and NEW is undefined. So, in the code, check if the trigger is running as DELETE or INSERT (variable TG_OP) and access the appropriate record.
Besides, you can go without counting here at all, like this:
CREATE OR REPLACE FUNCTION aktualizujIloscPodan() RETURNS TRIGGER AS
$BODY$
DECLARE
n integer;
BEGIN
IF TG_OP = 'INSERT' then
UPDATE "Studenci" SET "ilosc_podan" = "ilosc_podan" + 1 WHERE "ID"=NEW."studentID";
ELSIF TG_OP = 'DELETE' then
UPDATE "Studenci" SET "ilosc_podan" = "ilosc_podan" - 1 WHERE "ID"=OLD."studentID";
END IF;
END;
$BODY$
LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS triggenPodan ON "Podania";
CREATE TRIGGER triggenPodan AFTER INSERT OR DELETE
ON "Podania" FOR EACH ROW
EXECUTE PROCEDURE aktualizujIloscPodan();