create a procedure based on column data change in postgrersql - postgresql

Fire trigger on update of columnA or ColumnB or ColumnC
Set up the scene
begin;
create temp table account_details(email text primary key, username text, password text);
insert into account_details(email, username, password) values('a.com','b','c'),('b.com','d','e');
commit;
create function:
CREATE OR REPLACE FUNCTION notify_insert_account_details()
RETURNS trigger
LANGUAGE plpgsql AS
$$
BEGIN
RAISE NOTICE 'hello world';
END
$$;
create trigger:
CREATE TRIGGER trigger_update_account_details
AFTER UPDATE ON account_details
FOR EACH ROW
WHEN (OLD.email IS DISTINCT FROM NEW.email
OR OLD.username IS DISTINCT FROM NEW.username
OR OLD.password IS DISTINCT FROM NEW.password)
EXECUTE PROCEDURE notify_insert_account_details();
Trying to update:
update account_details set username = 'username1' where email = 'a.com';
The result: notify_insert_account_details() fired. but didn't execute the update clause.
NOTICE: hello world
ERROR: control reached end of trigger procedure without RETURN
CONTEXT: PL/pgSQL function notify_insert_account_details()
My question: how sophisticated this procedure/function notify_insert_account_details() Can become, let's say linked within 3 table. Can anyone showcase an example/demo?

Related

How to get changed data from FOR EACH STATEMENT without transition tables

I would like to write a trigger with FOR EACH STATEMENT. This trigger will listen multiple events and I don't use transition tables in my trigger.
So, can I get the change data with the code below?
CREATE OR REPLACE FUNCTION on_delete_update_employee_ref()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
DELETE FROM employee_ref
WHERE id in (SELECT ins_upd_del.id
FROM TG_TABLE_NAME ins_upd_del
WHERE ins_upd_del.employee_type = 'new');
RETURN NULL;
END;
$$;
DROP TRIGGER IF EXISTS on_delete_update_employee_ref ON employee;
CREATE TRIGGER on_delete_update_employee_ref
AFTER INSERT OR UPDATE OR DELETE ON employee
FOR EACH STATEMENT
EXECUTE FUNCTION on_delete_update_employee_ref();

postresql trigger to join lastname and firstname with pgadmin

I've tried this trigger using PgAdmin4; (this GUI i think have some bugs)
my trigger is:
CREATE TRIGGER name_update BEFORE UPDATE ON customer
FOR EACH ROW
BEGIN
SET NEW.name = CONCAT_WS(', ', NEW.lastname, NEW.firstname);
END;
CREATE TRIGGER name_insert BEFORE INSERT ON customer
FOR EACH ROW
BEGIN
SET NEW.name = CONCAT_WS(', ', NEW.lastname, NEW.firstname);
END;
but i'm getting this error:
i've tried remove begin and end, but nothings happens, maybe must I to try on the shell, or in txt file? what is the best way to do it outside pgadmin and manage the carriage returns, in mysql i've used to use delimeter on the shell.
You need create procedure and call her in trigger. Example:
create or replace function trigger_function()
returns trigger as
$$
begin
NEW.name = CONCAT_WS(', ', NEW.lastname, NEW.firstname);
return NEW;
end;
$$
language plpgsql;
CREATE TRIGGER name_update
BEFORE UPDATE ON customer
FOR EACH ROW
EXECUTE PROCEDURE trigger_function();

Postgresql Trigger to insert rows

I'm stuck for days with triggers on Postgresql (and Mysql as well). I just want to insert newly filled rows to another table. The original data comes from an external form (OpenDataKit) and goes to "intermediate" tables. I can't understand why the form cannot send the data anymore once the trigger is created... Note that all actions work without the trigger, when I do the insertions by hand. I would greatly appreciate some help to understand what I am doing wrong. I am now testing with Postgresql 9.5, but I got similar issue with MySQL 5.1.
-- CREATE procedure:
CREATE OR REPLACE FUNCTION proc_natobs() RETURNS TRIGGER AS
$BODY$
DECLARE
BEGIN
INSERT INTO lieu (id_lieu, wgs_lat, wgs_lon, date_obs, geom)
SELECT id_loc,"GPS_TEL_LAT", "GPS_TEL_LNG", "DATE_OBS", ST_SetSRID(ST_POINT("GPS_TEL_LNG","GPS_TEL_LAT"), 4326)
FROM "FORMULAIRE_NATOBS_REPEAT_LOC", "FORMULAIRE_NATOBS_CORE"
WHERE "FORMULAIRE_NATOBS_CORE"."_URI" = "FORMULAIRE_NATOBS_REPEAT_LOC"."_TOP_LEVEL_AURI"
AND "FORMULAIRE_NATOBS_REPEAT_LOC".id_loc IN (SELECT max(id_loc) FROM "FORMULAIRE_NATOBS_REPEAT_LOC");
INSERT INTO i_lieu_observateurs (id_lieu, id_auteur)
SELECT id_loc, CAST("AUTEUR" AS integer)
FROM "FORMULAIRE_NATOBS_CORE", "FORMULAIRE_NATOBS_REPEAT_LOC"
WHERE "FORMULAIRE_NATOBS_REPEAT_LOC"."_TOP_LEVEL_AURI" = "FORMULAIRE_NATOBS_CORE"."_URI"
AND id_loc IN (SELECT max(id_loc) FROM "FORMULAIRE_NATOBS_REPEAT_LOC")
UNION
SELECT id_loc, CAST("OBSERVATEURS" AS integer)
FROM "FORMULAIRE_NATOBS_REPEAT_LOC", "FORMULAIRE_NATOBS_REPEAT_OBSERVATEUR"
WHERE "FORMULAIRE_NATOBS_REPEAT_LOC"."_TOP_LEVEL_AURI" = "FORMULAIRE_NATOBS_REPEAT_OBSERVATEUR"."_TOP_LEVEL_AURI"
AND id_loc IN (SELECT max(id_loc) FROM "FORMULAIRE_NATOBS_REPEAT_LOC")
;
END;
$BODY$
LANGUAGE 'plpgsql';
-- CREATE the trigger:
CREATE TRIGGER trigger_natobs AFTER INSERT
ON "FORMULAIRE_NATOBS_REPEAT_LOC"
FOR EACH ROW
EXECUTE PROCEDURE proc_natobs();
So, when the ODK form inserts new rows in FORMULAIRE_NATOBS_REPEAT_LOC (for which I have created a serial ID to facilitate the SQL queries), I try to insert this row (combined with information from other intermediate tables) into table "lieu" for the first action of the trigger, and into table i_lieu_observation (composed by a double primary key) for the second action. I tested also with a trigger composed by the first action only, but it does not work either. The Android app that sends the form crashes until I remove the trigger.
Thanks in advance!
You need to use the special NEW variable in the trigger to access the newly inserted data. So you need something like:
CREATE OR REPLACE FUNCTION proc_natobs() RETURNS TRIGGER AS
$BODY$
DECLARE
BEGIN
INSERT INTO lieu (id_lieu, wgs_lat, wgs_lon, date_obs, geom)
SELECT new.id_loc,"GPS_TEL_LAT", "GPS_TEL_LNG", "DATE_OBS", ST_SetSRID(ST_POINT("GPS_TEL_LNG","GPS_TEL_LAT"), 4326)
FROM "FORMULAIRE_NATOBS_CORE"
WHERE "FORMULAIRE_NATOBS_CORE"."_URI" = new."_TOP_LEVEL_AURI";
INSERT INTO i_lieu_observateurs (id_lieu, id_auteur)
SELECT new.id_loc, CAST("AUTEUR" AS integer)
FROM "FORMULAIRE_NATOBS_CORE"
WHERE new."_TOP_LEVEL_AURI" = "FORMULAIRE_NATOBS_CORE"."_URI"
UNION
SELECT new.id_loc, CAST("OBSERVATEURS" AS integer)
FROM "FORMULAIRE_NATOBS_REPEAT_OBSERVATEUR"
WHERE new."_TOP_LEVEL_AURI" = "FORMULAIRE_NATOBS_REPEAT_OBSERVATEUR"."_TOP_LEVEL_AURI";
RETURN new;
END;
$BODY$
LANGUAGE 'plpgsql';
-- CREATE the trigger:
CREATE TRIGGER trigger_natobs AFTER INSERT
ON "FORMULAIRE_NATOBS_REPEAT_LOC"
FOR EACH ROW
EXECUTE PROCEDURE proc_natobs();
Because I don't know which fields come from which tables, I cannot make the above totally correct. In the same way as I have written new.id_loc, you will need to put new.field_name for all fields coming from the formulaire_natobs_repeat_loc table.
HTH
Try this
CREATE OR REPLACE FUNCTION proc_natobs() RETURNS TRIGGER AS
$BODY$
BEGIN
IF(TG_OP = 'INSERT') THEN
INSERT INTO lieu (id_lieu, wgs_lat, wgs_lon, date_obs, geom)
SELECT id_loc,"GPS_TEL_LAT", "GPS_TEL_LNG", "DATE_OBS", ST_SetSRID(ST_POINT("GPS_TEL_LNG","GPS_TEL_LAT"), 4326)
FROM "FORMULAIRE_NATOBS_REPEAT_LOC" loc, "FORMULAIRE_NATOBS_CORE" core
WHERE core."_URI" = loc."_TOP_LEVEL_AURI"
AND loc.id_loc =new.id_loc;
INSERT INTO i_lieu_observateurs (id_lieu, id_auteur)
SELECT id_loc as id,
CAST("AUTEUR" AS integer) as auteur
FROM "FORMULAIRE_NATOBS_CORE" core, "FORMULAIRE_NATOBS_REPEAT_LOC" loc
WHERE loc."_TOP_LEVEL_AURI" = core."_URI"
AND loc.id_loc =new.id_loc;
UNION
SELECT id_loc as id,
CAST("OBSERVATEURS" AS integer) as auteur
FROM "FORMULAIRE_NATOBS_REPEAT_LOC" loc, "FORMULAIRE_NATOBS_REPEAT_OBSERVATEUR" obs
WHERE loc."_TOP_LEVEL_AURI" = obs."_TOP_LEVEL_AURI"
AND loc.id_loc =new.id_loc;
END IF;
Return new;
END;
$BODY$
LANGUAGE 'plpgsql';
-- CREATE the trigger:
CREATE TRIGGER trigger_natobs AFTER INSERT
ON "FORMULAIRE_NATOBS_REPEAT_LOC"
FOR EACH ROW
EXECUTE PROCEDURE proc_natobs();
Hope it work for you.

Dynamic Change Column Values with NEW in Trigger Function

Is it possible to dynamically change column values in NEW with a Trigger Function?
Triggers:
CREATE TRIGGER lowercase_username_on_insert_trigger BEFORE INSERT OR UPDATE ON users
FOR EACH ROW EXECUTE PROCEDURE lowercase_on_insert('username');
CREATE TRIGGER lowercase_email_on_insert_trigger BEFORE INSERT OR UPDATE ON users
FOR EACH ROW EXECUTE PROCEDURE lowercase_on_insert('email');
Trigger Function:
CREATE OR REPLACE FUNCTION lowercase_on_insert()
RETURNS trigger
AS $lowercase_on_insert$
BEGIN
-- e.g., NEW.username = LOWER(NEW.username)
-- -OR- NEW.email = LOWER(NEW.email)
EXECUTE FORMAT('NEW.%s = LOWER(NEW.%s);', TG_ARGV[0], TG_ARGV[0]);
RETURN NEW;
END
$lowercase_on_insert$ LANGUAGE plpgsql;
I get an ERROR: syntax error at or new "NEW" whenever I run an INSERT.
This is my table:
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(55) NOT NULL UNIQUE,
email VARCHAR(120) NOT NULL UNIQUE
);
Change your Trigger function as below
CREATE OR REPLACE FUNCTION lowercase() RETURNS trigger AS $$
BEGIN
NEW.username = LOWER(NEW.username);
NEW.email = LOWER(NEW.email);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
and assign
CREATE TRIGGER tgrr BEFORE INSERT OR UPDATE ON users
FOR EACH ROW EXECUTE PROCEDURE lowercase();
OR
You can directly apply lower() function in Insert/Update like this
insert into users(username,email) values (lower('Jerry'),lower('JeRRY#mail.COM'));
update users set username=lower('JERRY'),email=lower('JERRY#MAIL.COM') where id =1

Infinite recursion detected when updating from function (psycopg2, python 2.7, postgres 9.3)

I have a simple table:
CREATE TABLE IF NOT EXISTS someTable (
row_id smallserial PRIMARY KEY,
name text NOT NULL,
creation_date timestamp with time zone DEFAULT current_timestamp,
last_updated_date timestamp with time zone DEFAULT current_timestamp,
created_by text DEFAULT "current_user"(),
last_updated_by text DEFAULT "current_user"()
);
with the following rule:
CREATE OR REPLACE RULE log_update_some_table AS
ON UPDATE TO someTable
DO ALSO
UPDATE someTable
SET last_updated_date = current_timestamp,
last_updated_by = current_user;
and a very simple function in plpgsql:
CREATE OR REPLACE FUNCTION test_update ()
RETURNS void AS $$
BEGIN
UPDATE someTable
SET name = 'test'
WHERE row_id = 1;
END;
$$ LANGUAGE plpgsql;
One would think the function would run just fine, but I get the following error:
psycopg2.ProgrammingError: infinite recursion detected in rules for relation "sometable"
CONTEXT: SQL statement "UPDATE someTable
SET name = 'test'
WHERE row_id = 1"
PL/pgSQL function test_update() line 3 at SQL statement
Why isn't this working and how do I fix it? Thanks!
So your update rule on someTable triggers an update on someTable which executes the rule which updates someTable which executes the rule...
I'd use a simple trigger instead, something like this:
create or replace function log_update_some_table() returns trigger as $$
begin
NEW.last_updated_date = current_timestamp;
NEW.last_updated_by = current_user;
return NEW;
end;
$$ language plpgsql;
create trigger log_update_some_table_trigger
before update on someTable
for each row execute procedure log_update_some_table();
should do the trick. That will modify the row before the update happens rather than adding another update (which triggers the recursion problem) to the queue.