How to set a sum of column to variable in trigger function? - postgresql

I am writing a trigger for a table in PostgreSQL 14 with which I could get the amounts I need, add them up and write them from a row. But I can't write data to variables after SELECT in any way. Here is my code:
CREATE TABLE folder(
Id uuid unique,
Url varchar(255),
Date timestamp,
ParentId uuid,
Size int
);
CREATE TABLE file(
Id uuid unique,
Url varchar(255),
Date timestamp,
ParentId uuid,
Size int,
FOREIGN KEY (ParentId) REFERENCES Folder (Id) ON DELETE CASCADE
);
CREATE OR REPLACE FUNCTION update_dates_and_price_update_folder() RETURNS trigger AS $update_dates_and_price_update_folder$
DECLARE
folders_sum integer;
files_sum integer;
BEGIN
SELECT (sum(size)) FROM file WHERE parentId = NEW.id INTO folders_sum;
SELECT (sum(size)) FROM folder WHERE parentId = NEW.id INTO files_sum;
NEW.size = folders_sum + files_sum;
UPDATE folder set date = NEW.date WHERE id = NEW.parentId OR id = OLD.parentId;
RETURN NEW;
END;
$update_dates_and_price_update_folder$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER update_data_folder AFTER UPDATE
ON folder
FOR ROW
EXECUTE FUNCTION update_dates_and_price_update_folder();
I checked the values of variables via RAISE NOTICE, both variables are always null. If you make such a request:
SELECT (sum(size)) FROM file WHERE parentId = some_id;
It's not null.
select response

Solution
CREATE OR REPLACE FUNCTION update_price_folder() RETURNS trigger AS $update_price_update_folder$
DECLARE
folders_sum integer;
files_sum integer;
file_item file;
folder_item folder;
BEGIN
files_sum := 0;
FOR file_item IN SELECT * FROM file WHERE parentId = NEW.id
LOOP
IF file_item.size IS NOT NULL THEN
files_sum := files_sum + file_item.size;
END IF;
END LOOP;
folders_sum := 0;
FOR folder_item IN SELECT * FROM folder WHERE parentId = NEW.id
LOOP
IF folder_item.size IS NOT NULL THEN
folders_sum := folders_sum + folder_item.size;
END IF;
END LOOP;
NEW.size = folders_sum + files_sum;
RETURN NEW;
END;
$update_price_update_folder$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER update_price_folder_trigger BEFORE UPDATE
ON folder
FOR ROW
EXECUTE FUNCTION update_price_folder();
CREATE OR REPLACE FUNCTION update_dates_folder() RETURNS trigger AS $update_dates_folder$
BEGIN
UPDATE folder set date = NEW.date WHERE id = NEW.parentId OR id = OLD.parentId;
RETURN NEW;
END;
$update_dates_folder$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER update_date_folder AFTER UPDATE
ON folder
FOR ROW
EXECUTE FUNCTION update_dates_folder();

Related

how to get the result of the select query and pass it as a input to delete query in Function Postgresql

CREATE OR REPLACE FUNCTION test(name VARCHAR (255))
RETURNS INTEGER AS
$BODY$
DECLARE
emp_id INTEGER;
BEGIN
SELECT employee.employee_id into emp_id from employee where first_name = name;
DELETE FROM employee WHERE employee_id = emp_id;
return emp_id;
END
$BODY$
LANGUAGE plpgsql;
when i call the function its deleting all the records in my employee table.
First store value in a variable for given input and these execute delete command with this variable and return this variable.
-- PostgreSQL
-- create table and store data
create table employee (emp_id int, name varchar(255));
insert into employee values (1, 'Rahul'), (2, 'Biswas'), (3, 'Shuva');
-- create function
CREATE OR REPLACE FUNCTION test(p_name varchar(255))
RETURNS INTEGER AS
$BODY$
DECLARE v_emp_id INTEGER;
BEGIN
SELECT e.emp_id into v_emp_id from employee e where e.name = p_name;
DELETE FROM employee WHERE emp_id = v_emp_id;
return v_emp_id;
END
$BODY$
LANGUAGE plpgsql;
-- execute function
select * from test('Rahul');
Please check this url https://dbfiddle.uk/?rdbms=postgres_11&fiddle=4c134787dafd8fe58b032d700168318d

PostgreSQL - Trigger on INSERT or UPDATE

I wan't to add a trigger in a PostgreSQL database. This trigger is used to concatenate values of 2 columns to update a 3rd one. I wan't to run it when a row is inserted or updated in the table.
Table
CREATE TABLE IF NOT EXISTS FILE(ID INT NOT NULL PRIMARY KEY, FOLDER TEXT, NAME TEXT, URL TEXT);
Function
CREATE OR REPLACE FUNCTION LINK() RETURNS trigger AS
$$
BEGIN
IF (TG_OP = 'UPDATE') THEN
UPDATE FILE
SET URL = CONCAT(FOLDER, NAME)
WHERE ID = OLD.ID;
ELSIF (TG_OP = 'INSERT') THEN
UPDATE FILE
SET URL = CONCAT(FOLDER, NAME)
WHERE ID = NEW.ID;
END IF;
RETURN NULL;
END
$$
LANGUAGE PLPGSQL;
Trigger
CREATE TRIGGER TRIGGER_LINK
BEFORE INSERT OR UPDATE
ON FILE
FOR EACH ROW
EXECUTE PROCEDURE LINK();
When I insert a value in table like
INSERT INTO FILE VALUES (1, 'C:\', 'doc.pdf');
I have an error message list index out of range because ID number is not yet created and UPDATE query on INSERT can't execute. But if I make an AFTER UPDATE it will run infinitely.
How to run a trigger function on INSERT or UPDATE with a WHERE clause on ID to target only inserted or updated row ? I'm using PostgreSQL 10.14.
Don't use UPDATE to do that. Just assign the value. Also, a BEFORE trigger should not return null because that will abort the operation.
CREATE OR REPLACE FUNCTION LINK() RETURNS trigger AS
$$
BEGIN
new.url := CONCAT(new.FOLDER, new.NAME);
RETURN new; --<< important!
END
$$
LANGUAGE PLPGSQL;

Postgres update trigger

I've problem with a trigger function in postgresql.
Here my simple code.
CREATE TABLE specie
(specie_id INT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
nome_comune TEXT UNIQUE,
nome_scientifico TEXT UNIQUE);
CREATE TABLE rilevatore
(rilevatore_id INT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
nome_cognome TEXT);
CREATE TABLE evento_investimento
(evento_id INT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
data DATE,
ora TIME WITHOUT TIME ZONE,
rilevatore_id INT REFERENCES rilevatore (rilevatore_id),
specie_id INT REFERENCES specie(specie_id));
CREATE VIEW inserimento_dati_vista AS
SELECT row_number() OVER ()::integer AS gid,
evento_investimento.ora,
evento_investimento.data,
rilevatore.nome_cognome,
specie.nome_comune,
specie.nome_scientifico
FROM evento_investimento
JOIN specie ON evento_investimento.specie_id = specie.specie_id
JOIN rilevatore ON evento_investimento.rilevatore_id = rilevatore.rilevatore_id;
CREATE OR REPLACE FUNCTION inserimento_dati_fun_2() RETURNS trigger AS $$
BEGIN
if not exists(select * from rilevatore where rilevatore.nome_cognome=new.nome_cognome) then
INSERT INTO rilevatore (nome_cognome)
VALUES (NEW.nome_cognome);
end if;
if not exists(select * from specie where specie.nome_comune=new.nome_comune) then
INSERT INTO specie (nome_comune, nome_scientifico)
VALUES (NEW.nome_comune, NEW.nome_scientifico);
end if;
INSERT INTO evento_investimento (data, ora, rilevatore_id, specie_id)
VALUES (NEW.data,NEW.ora,
(SELECT rilevatore_id FROM rilevatore WHERE rilevatore.nome_cognome = NEW.nome_cognome),
(SELECT specie_id FROM specie WHERE specie.nome_comune = NEW.nome_comune));
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
create trigger inserimento_dati_fun_trg
instead of insert on inserimento_dati_vista for each row EXECUTE procedure inserimento_dati_fun_2();
Now, I want to add a function that allow to update all the tables by using the view inserimento_dati_vista.
I've tried with a simple code to update only the data column
CREATE OR REPLACE FUNCTION update_dati_fun_2() RETURNS TRIGGER AS $$
BEGIN
IF (TG_OP = 'UPDATE') THEN
IF old.data is distinct from new.data then
UPDATE evento_investimento
SET data = new.data;
END IF;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
create trigger update_dati_fun_2_trg
instead of update on inserimento_dati_vista for each row EXECUTE procedure update_dati_fun_2();
However when I perfomr the query in order to update only a row, the trigger update all the rows in the table. Here some code to fill data.
INSERT INTO inserimento_dati_vista
(data, ora, nome_cognome, nome_comune, nome_scientifico)
VALUES
('2020-01-01', '16:54:00','mario', 'lupo', 'Canis lupus'),
('2020-01-02', '13:54:00','luca', 'lontra', 'Lutra lutra');
UPDATE inserimento_dati_vista
SET data = '2021-01-02' where nome_cognome = 'luca'
Update function is:
CREATE OR REPLACE FUNCTION update_dati_fun_2() RETURNS TRIGGER AS $$
BEGIN
IF (TG_OP = 'UPDATE') THEN
IF old.data is distinct from new.data then
UPDATE evento_investimento e
SET data = new.data
FROM rilevatore r
WHERE nome_cognome = new.nome_cognome AND r.rilevatore_id = e.rilevatore_id;
END IF;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Postgresql: UPDATE before INSERT function

I have problem when create function for trigger. I want to UPDATE inserted value BEFORE INSERT data to DB.
My code look like this:
CREATE OR REPLACE FUNCTION test_func()
RETURNS TRIGGER AS
$$
DECLARE cnt INTEGER;
BEGIN
cnt := COUNT(*) FROM sample_tbl WHERE id = NEW.id AND created_date = NEW.created_date;
NEW.current_order := cnt + 1; // I want to set value of sample_tbl.current_order automatically
END
$$ LANGUAGE plpgsql;
CREATE TRIGGER test_trigger
BEFORE INSERT
ON test_tbl
FOR EACH ROW
EXECUTE PROCEDURE test_func();
I inserted data then IDE said:
control reached end of trigger procedure without RETURN
Where: PL/pgSQL function test_func()
The error says that you must return something from the Trigger ( either NEW or NULL )
There's no Trigger needed for this. A simple View using this select query will give you the required result
--create or replace view sample_view as
select t.id, t.created_date,
row_number() OVER ( partition by id,created_date order by id ) as current_order
FROM sample_tbl t;
This will exactly match the records if updated using a Trigger
CREATE OR REPLACE FUNCTION test_func()
RETURNS TRIGGER AS
$$
DECLARE cnt INTEGER;
BEGIN
select COUNT(*) INTO cnt FROM sample_tbl WHERE id = NEW.id
AND created_date = NEW.created_date;
NEW.current_order := cnt + 1;
RETURN NEW; --required
END
$$ LANGUAGE plpgsql;
Demo
Your trigger function is just missing RETURN NEW; statement:
CREATE OR REPLACE FUNCTION test_func()
RETURNS TRIGGER AS
$$
DECLARE cnt INTEGER;
BEGIN
cnt := COUNT(*) FROM sample_tbl WHERE id = NEW.id AND created_date = NEW.created_date;
NEW.current_order := cnt + 1;
RETURN NEW;
END
$$ LANGUAGE plpgsql;

Aftre update deletion of row

I have the following table called 'second'
TABLE public.second
(
userid bigint,
companyid bigint
)
and table called visibility_matrix
CREATE TABLE public.visibility_matrix
(
name character varying,
companyid bigint
)
I created a trigger after I update the 'second' table I want to delete the row in visibility matrix ,I tried to achieve it via deleting the row with the companyid in visibility_matrix but it didn't work,any suggestions? here is the trigger:
CREATE OR REPLACE FUNCTION pos_org_rel_refresh()
RETURNS trigger AS
$$
DECLARE
r Integer ;
BEGIN
IF TG_OP='UPDATE' THEN
DELETE FROM visibility_matrix where companyid=NEW.companyid;
END IF;
END;
$$
LANGUAGE 'plpgsql';
CREATE TRIGGER test_trigger
AFTER UPDATE OR DELETE
ON second
FOR EACH ROW
EXECUTE PROCEDURE pos_org_rel_refresh();
you are missing a return statement, this should fix it:
CREATE OR REPLACE FUNCTION pos_org_rel_refresh() RETURNS trigger AS
$$
DECLARE
r Integer ;
BEGIN
IF TG_OP='UPDATE' THEN
DELETE FROM visibility_matrix where companyid=NEW.companyid;
RETURN NEW;
END IF;
RETURN NULL;
END;
$$
LANGUAGE 'plpgsql';