Postgresql, how to add multiple table for one trigger - postgresql

I have a question: how to add multiple table for one trigger?
Is that possible, or should I just make another 2 triggers for 2 different tables?

Create a new function
CREATE OR REPLACE FUNCTION updated_timestamp_func()
RETURNS TRIGGER
LANGUAGE plpgsql AS
'
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
';
Then create a trigger for each table that has the column updated_at
DO $$
DECLARE
t text;
BEGIN
FOR t IN
SELECT table_name FROM information_schema.columns WHERE column_name = 'updated_at'
LOOP
EXECUTE format('CREATE TRIGGER trigger_update_timestamp
BEFORE UPDATE ON %I
FOR EACH ROW EXECUTE PROCEDURE updated_timestamp_func()', t,t);
END loop;
END;
$$ language 'plpgsql';

Iterate one trigger for all tables
(For Example Foreign Tables)
DO
$$
DECLARE
r RECORD;
BEGIN
FOR r IN SELECT *
FROM information_schema.tables
where table_type = 'FOREIGN TABLE'
and table_schema = 'public'
and table_name <> 'django_migrations'
LOOP
RAISE NOTICE 'CREATE TRIGGER FOR: %', r.table_name::text;
EXECUTE 'CREATE TRIGGER trg_insert_ids
BEFORE INSERT
on ' || r.table_name || ' FOR EACH ROW
EXECUTE PROCEDURE insert_ids();';
END LOOP;
END;
$$ LANGUAGE plpgsql;

You need to define a trigger for each table, so if you have two tables, you need two triggers.
However, multiple triggers can use the same trigger function.

Related

Using NEW.* inside EXECUTE regarding psql

I checked all related questions on SO but none helped in my case.
I have 2 loops(outside for the tables and inside for the columns). Tables are represented by 'r', and columns by 'm'. While being inside the 'm' loop which is supposed to send column values to the to-be-created trigger function. When I try to use 'NEW.m' (with trying many different formatting attempts) compiler always gives error.
Can you kindly advice on it please? Br
FOR r IN SELECT table_name FROM information_schema.tables LOOP
FOR m IN SELECT column_name FROM information_schema.columns WHERE (table_name = r.table_name ) LOOP
function_name := 'dictionary_functions_foreach_trigger';
EXECUTE format('CREATE OR REPLACE FUNCTION %s()
RETURNS trigger AS
$BODY$
DECLARE
BEGIN
IF NEW.m IS NOT NULL AND NEW.m IN (SELECT key FROM tableX.tableX_key)
THEN RETURN NEW;
END IF;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION %s()
OWNER TO mydb;',function_name, function_name);
EXECUTE 'CREATE TRIGGER ' || function_name || ' BEFORE INSERT OR UPDATE ON ' || belonging_to_schema || '.' || r.table_name || ' FOR EACH ROW EXECUTE PROCEDURE ' || function_name || '();';
----Trigger Functions after edit-
EXECUTE format(
'CREATE OR REPLACE FUNCTION %s()
RETURNS trigger AS
$BODY$
DECLARE
insideIs text := %s ;
BEGIN
FOR %s IN 0..(TG_NARGS-1) LOOP
IF %I= TG_ARGV[%s]
THEN insideIs := %s ;
END IF;
END LOOP;
IF NEW.%I IS NOT NULL AND (insideIs =%s) AND NEW.%I IN (SELECT key FROM tableX.tableX_key)
THEN RETURN NEW;
ELSE RETURN OLD;
END IF;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION %s()
OWNER TO mydb;' , function_name, 'notInside', 'i' , m.column_name, 'i' , 'ok', m.column_name, 'ok', m.column_name ,function_name);
You need to use another placeholder for the column name, they way you have written it, the column name "m" is hardcoded in the function.
You also don't really need the outer loop, as the table_name is also available in information_schema.columns.
Your trigger would also fail with a runtime error if the condition is not true as you don't have a return in that case. If you want to abort the statement, use return null;
You should also use format() for the create trigger statement.
FOR m IN SELECT table_schema, table_name, column_name
FROM information_schema.columns
WHERE table_name in (...)
LOOP
function_name := 'dictionary_functions_foreach_trigger';
EXECUTE format('CREATE OR REPLACE FUNCTION %I()
RETURNS trigger AS
$BODY$
DECLARE
BEGIN
IF NEW.%I IS NOT NULL AND NEW.%I IN (SELECT key FROM tableX.tableX_key) THEN
RETURN NEW;
END IF;
RETURN null; --<< you need some kind of return here!
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION %s()
OWNER TO mydb;', function_name, m.column_name, m.column_name, function_name, function_name);
EXECUTE format('CREATE TRIGGER %I BEFORE INSERT OR UPDATE ON %I.%I FOR EACH ROW EXECUTE PROCEDURE %I()',
function_name, m.table_schema, m.table_name, function_name);
END LOOP;
Online example

Dynamically assign trigger name postgresql

Hi I have a Registra_cambios () function; which want to assign to all tables in my database, I wonder if you can concatenate the trigger name with the record (table name) my cursor to not have the same trigger name on all tables
create trigger example t_log_ "record" ()
CREATE OR REPLACE FUNCTION ActiveTriggers() returns void as $$
DECLARE
r record;
c CURSOR FOR SELECT table_name as tab FROM information_schema.tables WHERE table_schema='public' AND table_type='BASE TABLE';
BEGIN
FOR r IN c LOOP
create trigger t_log_r before insert or update or delete
on r.tab
for each row
execute procedure Registra_cambios();
END LOOP;
END;
$$ LANGUAGE plpgsql;
soemthing like?..
do
$$
declare
r record;
begin
for r in (SELECT table_name as tab FROM information_schema.tables WHERE table_schema='public' AND table_type='BASE TABLE';) loop
execute 'create trigger t_log_r_'||r.tab||' before insert or update or delete
on '||r.tab||'
for each row
execute procedure Registra_cambios()';
end loop;
end;
$$
;

Simple PostgreSQL plpgsql to create a new table using existing table

I'm new to plpgsql. I'm sure there is some really simple way to do this, but for some reason I'm having a lot of trouble trying to figure out how to do this.
I'm simply trying to loop through the list of existing tables and execute
CREATE TABLE z_existing_table_name AS SELECT * FROM existing_table_name WITH DATA
So far, I have this:
CREATE OR REPLACE FUNCTION create_backup_row()
RETURNS RECORD
AS $$
DECLARE
row RECORD;
BEGIN
FOR row IN SELECT * FROM information_schema.tables WHERE table_catalog = 'my_db' and table_schema = 'public'
LOOP
EXECUTE 'CREATE TABLE z_' || t.table_name || ' as ' || t.table_name
END LOOP;
END;
$$ LANGUAGE plpgsql;
It would be an added bonus if I can make this function re-runnable. Something like drop table if exist then create table ...
#Steven, use below procedure,
-- Function: create_backup_row()
-- DROP FUNCTION create_backup_row();
CREATE OR REPLACE FUNCTION create_backup_row()
RETURNS integer AS
$BODY$
DECLARE
v_table text;
BEGIN
FOR v_table IN
SELECT table_name
FROM information_schema.tables
WHERE table_catalog = 'my_db'
AND table_schema = 'public'
AND table_name not ilike '%z_%' -- to skip the table with z_ when we rerun it.
LOOP
EXECUTE ' DROP TABLE IF EXISTS z_' || v_table ;
EXECUTE 'CREATE TABLE z_' || v_table || ' as SELECT * FROM ' || v_table ;
END LOOP;
return 1;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION create_backup_row()
OWNER TO postgres;

Loop through all user tables and insert row in each

for some reason I just can not figure this out. I have a seperate schema in PostgreSQL for notification related tables for each user connected to the server. My plan is to have each user create a TEMP table to receive extra notification info from since Xojo doesn't support PostgreSQL payloads.
I feel like I'm starting to get close so I'll just post my code that is in my trigger function.
DECLARE
my_table RECORD;
BEGIN
FOR my_table IN
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'information_schema'
LOOP
INSERT INTO my_table.table_name (effected_row_id)
VALUES (NEW.effected_row_id);
END LOOP;
END;
Tell me if I'm wrong, but I believe my main problem is figuring out how to use the table name returned from the SELECT statement in the INSERT statement.
EDIT:
This is my current trigger function
-- Function: notification.my_insert_trigger_function()
-- DROP FUNCTION notification.my_insert_trigger_function();
CREATE OR REPLACE FUNCTION notification.my_insert_trigger_function()
RETURNS trigger AS
$BODY$DECLARE
my_table RECORD;
BEGIN
FOR my_table IN
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'notification' AND table_name <> 'notification_global' AND table_name <> 'switcher'
LOOP
EXECUTE(FORMAT($f$
INSERT INTO %s (effected_row_username)
VALUES (%s);
$f$, 'notification.' || my_table.table_name, NEW.effected_row_username));
END LOOP;
RETURN new;
END;$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION notification.my_insert_trigger_function()
OWNER TO serveradmin;
You need to use dynamic commands in your trigger function.
The funcion format() is often very helpful.
DECLARE
my_table RECORD;
BEGIN
FOR my_table IN
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'information_schema'
LOOP
EXECUTE(FORMAT($f$
INSERT INTO %s (effected_row_id)
VALUES (%s);
$f$, my_table.tablename, NEW.effected_row_id));
END LOOP;
END;

For loop with dynamic table name in Postgresql 9.1?

I have a plpgslq function which does some data processing and would like to write a for loop, however my table name is not known at design time. Is there any possible way to achieve this? Here is sample code snippet of what I want to achieve:
-- Function: check_data()
-- DROP FUNCTION check_data();
CREATE OR REPLACE FUNCTION check_data()
RETURNS character varying AS
$BODY$declare
dyn_rec record;
tbl_name record;
begin
-- sample dynamic tables
tbl_name := 'cars';
tbl_name := 'trucks';
tbl_name := 'bicycles';
for dyn_rec in select * from format($$s%$$,tbl_name) loop
raise notice 'item is %',dyn_rec.item_no;
end loop;
return 'Processing Ok';
end;$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION check_data()
OWNER TO postgres;
You cannot use a variable as table or column identifier in plpgsql embedded SQL ever. A solution is dynamic SQL - EXECUTE or FOR IN EXECUTE statements:
DO $$
DECLARE
tables text[] = ARRAY['table1','table2'];
table_name text;
rec record;
BEGIN
FOREACH table_name IN ARRAY tables
LOOP
FOR r IN EXECUTE format('SELECT * FROM %I', table_name)
LOOP
RAISE NOTICE '%', rec;
END LOOP;
END LOOP;
END; $$