Using the NEW variable in trigger functions - sql

PostgreSQL 9.4
I'm working on the following trigger function:
CREATE OR REPLACE FUNCTION check_inserted_row() RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'INSERT' THEN
--Here I need to transform the NEW trigger variable
--to an array of values or something else that can be iterated over
--and check each value in it
--How can I do that?
RETURN NEW;
END IF;
END $$
LANGUAGE plpgsql;
The issue is I don't know for sure what columns will be contained in NEW as that it might happen that some columns will be added later. To avoid rewriting the trigger any time it happens I'd like to iterate over all columns in NEW and perform required checking.
Is it possible?

Related

String modification trigger is not working

I am trying to write a trigger that executes after the INSERT completes on only newly inserted rows (I do not need to execute on old rows). The trigger should modify inserted string using split_part and replace functions.
The queries are as follows:
DROP TRIGGER IF EXISTS date_extraction ON t32upb
drop function if exists date_extraction();
CREATE FUNCTION date_extraction() RETURNS trigger AS $$
begin
NEW.filename := replace(split_part(old.filename,'_', 2), '.tif', '');
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER
date_extraction
AFTER INSERT on
sentinel_2.t32upb
FOR EACH STATEMENT EXECUTE PROCEDURE
date_extraction();
What is wrong with the query? it is not working

How to register trigger in PostgreSQL?

I have trigger and procedure. I want to be sure that string saved to database will have no '-' characters.
After executing
UPDATE interesant SET interesant_nip = '555-555-5555'
I get error
value is too long for varchar(10).
This suggests that '-' characters were not removed before insert-update.
Why is my trigger not being executed?
CREATE FUNCTION normalize_nip() RETURNS TRIGGER AS $$
BEGIN
-- replace '' to NULL
IF (NEW.interesant_nip LIKE '') THEN
NEW.interesant_nip := NULL;
RETURN NEW;
END IF;
-- remove '-' from string
NEW.interesant_nip := REPLACE(NEW.interesant_nip, '-', '');
RETURN NEW;
END; $$ LANGUAGE plpgsql;"
CREATE TRIGGER interesant_nip_normalize BEFORE INSERT OR UPDATE ON public.interesant FOR EACH ROW EXECUTE PROCEDURE normalize_nip()
The updated or inserted value is always treated as type varchar(10) - before and after the trigger function. So, you cannot handle the value because the input type does not fit the value even if the trigger function converts it into a valid one.
It works if you have an unbounded text type. There you can see that the trigger is executed:
demo:db<>fiddle
So, the only chance to handle this, is, to normalize it before inserting or updating:
demo:db<>fiddle
INSERT INTO interesant VALUES (normalize_nip('555-555-5555'));
UPDATE interesant SET interesant_nip = normalize_nip('555-555-5555')

PostgreSQL trigger after update only on a updated row

I have a small table for news. I want to make a trigger which sets the update date and update time in the row (only for the rows that were updated)
I tried making the following:
CREATE FUNCTION add_update_dates()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
IF (OLD.news_name IS DISTINCT FROM NEW.news_name OR
OLD.news_description IS DISTINCT FROM NEW.news_description OR
OLD.news_text IS DISTINCT FROM NEW.news_text) THEN
UPDATE news SET news_update_date = current_date, news_update_time = current_time;
END IF;
RETURN new;
END
$$;
CREATE TRIGGER update_news_dates
AFTER UPDATE ON news
FOR EACH ROW
EXECUTE PROCEDURE add_update_dates();
But the trigger updates each row in my table (even those that are not updated), when I want only the updated ones. What am I doing wrong?
Your update statement is updating all the rows in the table! It has no where clause.
Just use assignment:
CREATE FUNCTION add_update_dates()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
IF (OLD.news_name IS DISTINCT FROM NEW.news_name OR
OLD.news_description IS DISTINCT FROM NEW.news_description OR
OLD.news_text IS DISTINCT FROM NEW.news_text
) THEN
NEW.news_update_date := current_date;
NEW.news_update_time := current_time;
END IF;
RETURN new;
END;
$$;
As an aside, storing date/time in separate columns makes no sense to me.

Trigger Not Executing Yet It's Created

I have a trigger function I'm trying to have execute in Postgres.
It compiles and adds the trigger, however it does not insert the value into the table as I had hoped.
The function it uses looks like this:
CREATE OR REPLACE FUNCTION
calc_gnpDifference(n integer, o integer)
RETURNS NUMERIC AS $$
SELECT $1 ::numeric - $2::numeric AS gnpDifference;
$$ LANGUAGE SQL;
And the Trigger part:
CREATE OR REPLACE FUNCTION autoCalculate() RETURNS TRIGGER AS $$
BEGIN
IF NEW.gnp_old > NEW.gnp_old THEN
NEW.gnpDifference := calc_gnpDifference(NEW.gnp_old, NEW.gnp);
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER insertDifference ON country;
CREATE TRIGGER insertDifference BEFORE INSERT ON country
FOR EACH ROW EXECUTE PROCEDURE autoCalculate();
However, when I insert data, the trigger does not update the gnpDifference field as I had hoped. Thoughts on why this might be happening?
Obviously this condition: IF NEW.gnp_old > NEW.gnp_old will never be true so the trigger will never have any effect.

SQL, update column in a specific row, instead of all rows

The task is to update the specific row in the column klienta_nr, which is located in the table klientu_ieteikumi. If a specific row is deleted in klienti table. This code updates the whole column not the specific row. What is the problem?
CREATE FUNCTION "funkc"() RETURNS "opaque" AS '
DECLARE
BEGIN
IF (TG_OP = ''DELETE'') THEN
UPDATE klientu_ieteikumi SET klienta_nr = NULL ;
END IF;
RETURN NEW;
END;
' LANGUAGE 'plpgsql';
CREATE TRIGGER "triger"
AFTER DELETE ON "klienti"
FOR EACH ROW EXECUTE PROCEDURE funkc();
This one did what I wanted, thanks anyway everyone :)
CREATE FUNCTION "funkcija1"() RETURNS TRIGGER AS $$
BEGIN
UPDATE klientu_ieteikumi SET klienta_nr = NULL
FROM klienti WHERE old.klienta_nr = klientu_ieteikumi.klienta_nr;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER "trigeris"
AFTER DELETE ON "klienti"
FOR EACH ROW EXECUTE PROCEDURE funkcija1();
you need a WHERE clause to restrict the number of rows affected by the statement.
edit:
UPDATE klientu_ieteikumi
SET klienta_nr = NULL
WHERE klienta_ieteikumi.klienta_nr = klienti.klienta_nr
You presented "solution" cannot work and is wrong in a sneaky way.
CREATE FUNCTION funkcija1()
RETURNS TRIGGER AS
$func$
BEGIN
CREATE FUNCTION funkcija1()
RETURNS TRIGGER AS
$func$
BEGIN
UPDATE klientu_ieteikumi
SET klienta_nr = NULL
FROM klienti -- !!
WHERE klientu_ieteikumi.klienta_nr = OLD.klienta_nr;
RETURN NEW;
RETURN NULL;
END
$func$ LANGUAGE plpgsql;
CREATE TRIGGER trigeris
AFTER DELETE ON klienti
FOR EACH ROW EXECUTE PROCEDURE funkcija1();
There is no NEW in an AFTER trigger. This wouldn't work at all and raise an exception immediately.
The unbound (and utterly pointless) table klienti in the FROM clause leads to a CROSS JOIN. I.e., instead of just one time, the UPDATE is executed as many times as there are rows in klienti. This would be a major drag on performance, and you might never find out, since there is no error message. Just a lot of wasted cycles and table bloat on your server.