Event trigger to change owner on CREATE - postgresql

I'm trying to create a PostgreSQL event trigger, that fires whenever a user creates anything (table, view, function, sequence...) and sets the owner of this newly created thing to a certain role.
So far I have the even trigger itself, that fires on any CREATE command:
CREATE EVENT TRIGGER setOwnerToMyRole ON ddl_command_end
WHEN tg_tag LIKE 'CREATE%'
EXECUTE PROCEDURE setOwnerToMyRole();
But I am having problems with the function itself:
CREATE FUNCTION setOwnerToMyRole() RETURNS event_trigger LANGUAGE plpgsql
AS $$ BEGIN
ALTER <type> <name> OWNER TO myRole
END; $$;
How do I get the type (as in table, view, etc.) and how do i get the name of the newly created thing?
edit:
Looking at this question and this question and of course CREATE EVENT TRIGGER and Trigger Procedures, this is currently not really possible :(

This works pretty well for me, although it's a little broader net than needed. It is based on code at https://blog.hagander.net/setting-owner-at-create-table-237/
CREATE OR REPLACE FUNCTION trg_create_set_owner()
RETURNS event_trigger
LANGUAGE plpgsql
AS $$
DECLARE
obj record;
BEGIN
-- postgresql 9.5 or later:
-- FOR obj in SELECT table_name FROM pg_event_trigger_ddl_commands() WHERE command_tag='CREATE TABLE'
FOR obj IN SELECT tablename FROM pg_tables WHERE tableowner = current_user LOOP
EXECUTE format('ALTER TABLE %s OWNER TO my_group', obj.tablename);
END LOOP;
END;
$$;
CREATE EVENT TRIGGER trg_create_set_owner
ON ddl_command_end
WHEN tag IN ('CREATE TABLE', 'CREATE TABLE AS')
EXECUTE PROCEDURE trg_create_set_owner();

Guess, I should answer this question properly and not just in an edit:
What I want is currently not possible. Perhaps a future update to Postgres will add more functionality to eventtriggers.
Sources:
http://www.postgresql.org/docs/current/static/sql-createeventtrigger.html
http://www.postgresql.org/docs/current/static/plpgsql-trigger.html
How to get the name of the altered table in a Postgres event trigger?
How to get SQL text from Postgres event trigger

Related

Syntax error while creating a trigger in PostgreSQL

I created a table name Student in PostgreSQL and then I tried defining a trigger on that table but it's showing an error message in doing so.
Trigger Syntax:
CREATE TRIGGER bi_Student BEFORE INSERT ON Student as $$
FOR EACH ROW
BEGIN
raise notice 'Successfully inserted into table(%)', user;
end $$;
Table Creation Command:
create table Student(Stu_id int, Stu_Name text, Stu_Age int, Stu_address char(30));
Actually I tried to declare the execution statements directly inside the trigger only rather than calling any procedure/ function from the trigger which is working fine but I want to do in this way in PostgreSQL.
PostgreSQL doesn't support it. You need trigger function always.
As documented in the manual you need a trigger function
create function my_trigger_function()
returns trigger
as
$$
begin
raise notice 'Successfully inserted into table(%)', user;
return new; --<< important (see the manual for details)
end
$$
language plpgsql;
Not sure what you intend with the user parameter there, as that is not the table name, but the current database user. If you want to display the actual table name, you need to use TG_RELNAME instead - which is an implicit variable available in the trigger function.
And a trigger definition
CREATE TRIGGER bi_Student
BEFORE INSERT ON Student
FOR EACH ROW
execute function my_trigger_function();

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)

generic trigger to capture table data for audit in postgres

Need help to create a generic trigger to log all tables.
i have table named "system" and need to log it.
the log table name system_audit is created with all columns of "system" table along with additional three columns named
modified_dt,modified_by and modified_type.
modified_dt will be current_timestamp
modified_by will be the user
and modified_type specifies whether its insert,update or delete.(Need to capture new data for insert/update and old for delete)
How to write a function to capture the above said data. Also it needs to be dynamics, so that i can use it across all the tables in my schema
Note: All audit tables contains the modified_dt,modified_by and modified_type as mandatory.
I got a couple of codes from the net, but it is not working, I was working on with oracle before, and new to postgres, not sure on how to code it properly.Please help
I have managed to create a generic function and its working fine.Thanks for the help.
create or replace function audit.fn__audit()
returns trigger as
$func$
declare
col_name text:='';
audit_table_name text := TG_TABLE_NAME || '_audit';
begin
if TG_OP = 'UPDATE' or TG_OP = 'INSERT' THEN
EXECUTE format('INSERT INTO audit.%1$I SELECT ($1).*,current_timestamp,user,'''||TG_OP||'''',audit_table_name) using NEW;
else
EXECUTE format('INSERT INTO audit.%1$I SELECT ($1).*,current_timestamp,user,'''||TG_OP||'''',audit_table_name) using old;
end if;
return new;
END $func$
LANGUAGE plpgsql VOLATILE;

Get Table Name in Event Trigger

I have an event trigger that executes on create, alter and drop table.
create event trigger CustomizeTable
on ddl_command_end
when tag in ( 'create table', 'alter table', 'drop table' )
execute procedure CustomizeTable();
Within the procedure I want to create a trigger on the newly created table.
create or replace function CustomizeTable() returns event_trigger as
$$
begin
EXECUTE 'create trigger DoAudit after update on XXXXXX...
end;
$$
language plpgsql;
How can I get the table name within the event trigger?
I tried using TG_TABLE_NAME as explained here but it seems that this only works on non-event-triggers.
You'll have to use the event trigger information functions described in the documentation.
For example, to get the name a newly created table, use
SELECT objid::regclass
FROM pg_event_trigger_ddl_commands();

PostgreSQL trigger to replicate changes from one table to another

I have a database named info. In the database I have 4 different tables info1, info2, info3 and info4.
I want to create a stored procedure so that whenever I make changes in table info1 (like INSERT, DELETE or UPDATE) the same changes should appear in the other three tables.
I am using PostgreSQL for this and I don't know how to perform this query.
Please explain it with an example.
Thanks in advance!
Basically you need to read the manual to understand what you are doing. #Michael provided links.
There are many different ways how you can go about this. Here are two typical examples for UPDATE and DELETE:
Create trigger function for UPDATE:
CREATE OR REPLACE FUNCTION trg_info1_upaft()
RETURNS trigger AS
$BODY$
BEGIN
UPDATE info2
SET col1 = NEW.col1
--more?
WHERE info2.info1_id = NEW.info1_id;
UPDATE info3
SET col1 = NEW.col1
--more?
WHERE info3.info1_id = NEW.info1_id;
-- more?
RETURN NULL; -- because trigger is meant for AFTER UPDATE
END;
$BODY$
LANGUAGE plpgsql;
Create the trigger making use of it:
CREATE TRIGGER upaft
AFTER UPDATE
ON info1
FOR EACH ROW
EXECUTE PROCEDURE trg_info1_upaft();
For DELETE:
CREATE OR REPLACE FUNCTION trg_info1_delaft()
RETURNS trigger AS
$BODY$
BEGIN
DELETE FROM info2
WHERE info1_id = OLD.info1_id;
-- more?
RETURN NULL; -- because trigger is meant for AFTER DELETE
END;
$BODY$
LANGUAGE plpgsql;
CREATE TRIGGER delaft
AFTER DELETE
ON info1
FOR EACH ROW
EXECUTE PROCEDURE trg_info1_delaft();
For such changes you should use trigger: http://www.postgresql.org/docs/current/interactive/triggers.html
You will have to create function that inserts/updates/deletes new data into other tables and then show PostgreSQL with CREATE TRIGGER http://www.postgresql.org/docs/current/interactive/sql-createtrigger.html to call that function every time data in source table is changed.