UPDATE ANOTHER COLUMN IN AFTER UPDATE TRIGGER - postgresql

I have a table with three columns: id, date and dateDekete
I try to execute an update on the column dateDelete after an update on another column (column date) using a AFTER UPDATE TRIGGER.
The code that I use to create my trigger is the following:
CREATE OR REPLACE FUNCTION update_delete_date_allocation()
RETURNS trigger LANGUAGE plpgsql AS $body$
BEGIN
NEW."dateDelete" := NEW.date + 1;
RETURN NEW;
END;
$body$;
CREATE TRIGGER delete_date_allocation_trg
AFTER INSERT OR UPDATE ON client.client_portfolio_allocation
FOR EACH ROW
EXECUTE PROCEDURE update_delete_date_allocation();
Although the code executes fine with no error message, the latter column that I try to update does not change.
I was wondering if it's possible to do this. AND if so, what should I change in my code?
I am using Postgres 11.5.

you can't change the new record in an AFTER trigger, you need to declare your trigger as a BEFORE trigger:
CREATE TRIGGER delete_date_allocation_trg
BEFORE INSERT OR UPDATE ON client.client_portfolio_allocation
FOR EACH ROW
EXECUTE PROCEDURE update_delete_date_allocation();

Related

Alter PostreSQL column into a GENERATED ALWAYS column

I have an already made table:
cotizacion(idCot(PK), unit_price,unit_price_taxes)
I need to convert unit_price_taxes into a generated column that is equal to unit_price*1.16. The issue is I can't find the alter table statement which will give me this. Dropping table and creating it again is not an option as this table is already deeply linked with the rest of the database and reinserting all records is not an option at this point.
I tried the following:
ALTER TABLE cotizacion
alter column unit_price_taxes set
GENERATED ALWAYS AS (unit_price*1.16) STORED;
But it's not working. Does anybody know how to get this done or if it's even possible? I would like to avoid creating a new column.
Thanks!
**EDIT:
I also tried the following trigger implementation:
CREATE OR REPLACE FUNCTION calculate_price_taxes()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
declare pu money;
begin
select unit_price from cotizacion into pu
where idCot = new."idCot";
update cotizacion
set unit_price_taxes = pu * (1.16)
where idCot = new."idCot";
return new;
end;
$function$
;
And the trigger delcaration:
Create or replace trigger price_taxes
after update on cotizacion
for each row
execute procedure
calculate_price_taxes()
The most probable reason for your trigger to go into an infinite recursion is that you are running an UPDATE statement inside the trigger - which is the wrong thing to do. Create a before trigger and assign the calculated value to the new record:
create trigger update_tax()
returns trigger
as
$$
begin
new.unit_price_taxes := unit_price * 1.16;
return new;
end;
$$
language plpgsql;
create trigger update_tax_trigger()
before update or insert on cotizacion
for each row execute procedure update_tax();
The only way to "convert" that column to a generated one, is to drop it and add it again:
alter table cotizacion
drop unit_price_taxes;
alter table cotizacion
add unit_price_taxes numeric generated always as (unit_price*1.16) stored;
Note that this will rewrite the entire table which will block access to it. Adding the trigger will be less invasive.

Postgres Function Trigger Sequence Dependency Error

I have the following functions and trigger with sequence setup:
I want to create a function and trigger that anytime I add a new row to STRATEGY_SITES table, the 'SITE_NUM' field will have the new sequential number from SITENUM_SEQ. Schema name is gismgr.
I am getting the following error:Underlying DBMS error[Error:control reached end of trigger procedure without return Context: PL/pgSQL function process_sites_edit()(gismgr.strategy_sites)::SQLSTATE=2F005][gismgr.startegy_sites]
CREATE OR REPLACE FUNCTION process_sites_edit() RETURNS TRIGGER AS $SITE_EDIT_TRIGGER$
begin
new.SITE_NUM := nextval('gismgr.SITENUM_SEQ');
end;
$SITE_EDIT_TRIGGER$ LANGUAGE 'plpgsql';
create TRIGGER SITE_EDIT_TRIGGER
before insert or update on STRATEGY_SITES for each row
EXECUTE PROCEDURE process_strategy_sites_edit();
CREATE SEQUENCE gismgr."SITENUM_SEQ" owned by gismgr.strategy_Sites.site_num
INCREMENT 1
START 19080
MINVALUE 19080
MAXVALUE 9999999999999999
CACHE 20;
This seems to be an ORACLEism which is unnecessary in Postgres. Assuming your table already exsists then just
alter table *table_name* alter column site_num default nextval('gismgr.SITENUM_SEQ')
Also make sure the insert does not mention the site_num column. If you feel you must continue with the trigger approach the your trigger function needs to specify the return value.
CREATE OR REPLACE FUNCTION process_sites_edit()
RETURNS TRIGGER AS $SITE_EDIT_TRIGGER$
begin
new.SITE_NUM := nextval('gismgr.SITENUM_SEQ');
return new;
end;
$SITE_EDIT_TRIGGER$ LANGUAGE 'plpgsql';
I would also suggest you do not want to fire the trigger on updates. That will change the site number on any/every update of a given row Are there FK referencing it - they will not be updated the update would fail. Further the procedure executed must match the function name:
create TRIGGER SITE_EDIT_TRIGGER
before insert on STRATEGY_SITES for each row
EXECUTE PROCEDURE process_sites_edit();

postgresSQL How to apply trigger only on updated row?

I'm trying to create a function + trigger that will update my "modif" attribut to current date when there is an update or insert on my table called "nada".
the code work well but all the rows are affected.I only want the current date on the rows that were updated.
Any idea ?
This is my code so far:
CREATE OR REPLACE FUNCTION public.maj_modif()
RETURNS "trigger" AS
$BODY$
BEGIN
NEW.modif:= (SELECT current_date);
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS maj_modif ON public.nada;
CREATE TRIGGER maj_modif BEFORE INSERT OR UPDATE ON public.nada
FOR EACH ROW
EXECUTE PROCEDURE public.maj_modif();
If I try the same code without "FOR EACH ROW" in the trigger I get this erreur: « new » is not affected yet (...) The structure of the registration line is not yet determined.
I assume that you only want the trigger to fire if any columns were actually changed.
That can be done with
CREATE TRIGGER maj_modif BEFORE UPDATE ON public.nada
FOR EACH ROW
WHEN OLD <> NEW
EXECUTE PROCEDURE public.maj_modif();
That only works for UPDATE, because on INSERT OLD is not defined. Define the INSERT trigger without the WHEN clause.

Postgres triggers and producers (column "new" of relation does not exist)

I am trying to create a trigger and procedure to update a last_changed_timestamp column upon UPDATE and INSERT.
I can register the function and trigger just fine, but when I try to update a record I receive the error.
CREATE OR REPLACE FUNCTION update_my_table_last_changed_timestamp()
RETURNS trigger AS
$BODY$
BEGIN
UPDATE my_table SET NEW.last_changed_timestamp = NOW();
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
CREATE TRIGGER trigger_update_my_table_last_changed_timestamp
BEFORE UPDATE
ON my_table
FOR EACH ROW
EXECUTE PROCEDURE update_my_table_last_changed_timestamp();
column "new" of relation "my_table" does not exist
I also do not fully understand how update_my_table_last_changed_timestamp knows which row it's suppose to update, nor if there were parameters passed to it, how the I would get those variables from the trigger to the procedure.
Modify the NEW record, there is no need to update.
BEGIN
NEW.last_changed_timestamp = NOW();
RETURN NEW;
END;
Read in the documentation: Overview of Trigger Behavior
If you still want to access a (other )table in the update trigger.
You can add to beginning of your trigger body the following:
EXECUTE format('SET search_path TO %I', TG_TABLE_SCHEMA);
For some reason with the update trigger it can happen that you're not on the correct search_path (i believe some old psql version have this)

PostgreSql Trigger does not work when called 'insert'

The code below is my code to create trigger function to change column "pass".
create or replace function change_pass()
returns trigger as
$$
begin
NEW.pass := 'XXXXXXXXX';
return NEW;
end
$$
language plpgsql;
create trigger change_pass
AFTER insert or update on "D_ACCOUNT"
for each row execute procedure change_pass();
When i called insert, i did not see any changes in my data.
Can anyone explain to me where i was wrong?
You need a BEFORE trigger to change values in the NEW record:
create trigger change_pass
BEFORE insert or update on "D_ACCOUNT"
for each row execute procedure change_pass();