Triggers in postgresql - postgresql

I am new to PostgreSQL and I am currently working on triggers but am stuck at one point.
I have two tables Student and Room.
Room id is the primary key in Room and foreign key in Student.
If I am inserting in Student, then it should check in Room whether the new data exist or not.
This is a foreign key check constraint. I hope anyone can help me with it

I dont know your code and I dont get the meaning, but I can answer generally.
In PostgreSQL a trigger is normally defined in two steps:
Define a trigger function using the CREATE FUNCTION
Bind this created trigger function to a table using CREATE TRIGGER
A trigger function is a common function, except that is has a return value type trigger (in addition, it does not take any arguments).
CREATE OR REPLACE FUNCTION trigger_function()
RETURNS trigger
AS $$ ... $$;
binding:
CREATE TRIGGER trigger_name
AFTER INSERT ON table_name
FOR EACH ROW EXECUTE PROCEDURE trigger_function();
In addition, please also consult the excellent PG documentation at PostgreSQL 9.4 Triggers

Related

Postgresql: trigger on foreign table to execute function to truncate/insert into local table

I would like to create trigger to execute function to truncate local database table and insert new data.
Trigger execution must start after new row have insert in foreign database table.
I have read a lot about creating triggers on foreign table, but for me its not working. Trigger seems to not execute function when new row will be inserted in foreign table. It seems like trigger cant see this new row insert event.
What I did:
Created foreign table in my local database, lets call it 'foreign_table'. I tested, I can read data.
Created function to truncate local table and insert new data:
CREATE or replace FUNCTION public.reset_insert_table()
RETURNS TRIGGER
LANGUAGE 'plpgsql'
SET search_path=public
AS $BODY$
BEGIN
create temporary table temporary_table_tmp
as select * from public.table1;
TRUNCATE TABLE public.table2;
insert into table2
select * from temporary_table_tmp;
DROP table temporary_table_tmp;
END;
$BODY$;
Created trigger to launch function 'reset_insert_table()'
CREATE TRIGGER local_table_update
AFTER INSERT
ON 'foreign_table'
FOR EACH ROW EXECUTE PROCEDURE reset_insert_table();
Made test: inserted new row in foreign database table 'foreign_table', but I cant see that table is truncated and new data is not inserted. Insertion to foreign_tale was done in foreign database.
Problem was also testing does this trigger function work, executing manually will produce error:
EXECUTE PROCEDURE reset_insert_table();
ERROR: syntax error at or near "execute"
Tried also CALL and SELECT.
I created same function for testing but instead defining 'RETURNS TRIGGER'used 'RETURNS VOID' and function is working.
Can anyone tell why my solution is not working and does trigger on foreign tables must see events happening in foreign tables?
According to your comments, you seem to be using logical replication.
While data modifications are replayed on the standby with logical replication, the parameter session_replication_role is set to replica to keep triggers and foreign key constraints from working.
If you want a trigger to be triggered by the replay of data via logical replication, you have to declare it as a replica trigger:
ALTER TABLE a2 ENABLE REPLICA TRIGGER trigger_name;

Before and After trigger on the same event? Fill a child table PostgreSQL

Situation
I have a database in PostgreSQL 9.5 used to store object locations by time.
I have a main table named "position" with the columns (only relevant):
position_id
position_timestamp
object_id
It is partitioned into 100 child tables on object_id with the condition:
CREATE TABLE position_object_id_00
( CHECK object_id%100 = 0 )
INHERITS ( position );
And so on for the others children. I partitioned with a modulus relation to distribute equally the objects.
Each child is indexed on position_id and object_id (two different indexes).
The trigger to redirect inserts on children is:
CREATE TRIGGER insert_position_trigger
BEFORE INSERT ON position
FOR EACH ROW EXECUTE PROCEDURE insert_position();
And the procedure insert_position() looks for the right child table to insert the data, inserts it and then return the NEW object:
CREATE OR REPLACE FUNCTION insert_position() RETURNS TRIGGER AS $insert_position$
DECLARE
BEGIN
--Look for child table
[...]
--Insert data in right child table
[...]
RETURN NEW;
END;
$insert_position$ LANGUAGE plpgsql;
I have a summary table object_last_known_position with the same columns that is updated with the trigger:
CREATE TRIGGER update_object_last_known_position
AFTER INSERT OR UPDATE ON position
FOR EACH ROW
EXECUTE PROCEDURE update_object_last_known_position();
The procedure update_object_last_known_position() basically checks if the position_timestamp is more recent, then delete the older entry and create a new entry with the data passed on the INSERT or UPDATE query (NEW).
Issue
So these two triggers react to the same event: insert on position, one is before, the other is after
Returning new for insert_position() allows me to use NEW in the trigger update_object_last_known_position(), and this is absolutely necessary. But doing that, it also insert the data on the master table position. So my data is now duplicated.
I tried to put the two triggers before, they both execute when I insert data if I let it like that, but if I remove the "return new" from the procedure insert_position(), update_object_last_known_position() is not executed.
I am stuck with this issue and I didn't find a way to execute both of these triggers without filling the master table position when I insert data.
So if you have any ideas, I will really appreciate :)
Thank you for your help!
EDIT
Solution
Thanks to the answer
I "merged" my two triggers: insert_position() now calls update_object_last_known_position directly. For that, I modified update_object_last_known_position to a stored procedure with a parameter. The parameter is the id of the position insert_position() just created, so I am able to find it and retrieve information.
(Calling update_object_last_known_position inside the other trigger means we cannot use NEW anymore)
And obviously return type for insert_position() is now NULL, and everything works fine :)
If I understand you correctly you are trying to:
Stop the insert, and replace it with an insert into another table (determined by the trigger)
Update a summary table (delete/insert) to point to the new row.
Your problem is that 1 stops 2 from happening? That's logical because you've stopped the insert so you've stopped any processing on the insert as well.
So to solve this you have a couple of options (options 1 and 2 are similar)
Call update_object_last_known_position() from insert_position() and only have one trigger
Create a wrapper method for both insert_position() and update_object_last_known_position() and have only one trigger.
Put the trigger for update_object_last_known_position() on all of the tables that insert_position() might insert into.

Apply a single trigger procedure to many different tables

In my PostgreSQL 9.1 database I have multiple tables and one trigger function.
Right now I am creating the trigger for each table by using that trigger function.
This methodology working fine. My boss has asked me to create the trigger commonly (only one time) by re-using that trigger function. That one trigger function should get used by all the tables in my database.
You can find an example of creating a trigger with dynamic SQL using PL/PgSQL in the Audit Trigger sample for PostgreSQL. The same approach will work with any other DDL.
See the function audit.audit_table and use of format and EXECUTE there.
That said, needing to create tables procedurally can be (but isn't always) a sign of questionable schema design.
Simple example of dynamic SQL creating a table:
CREATE OR REPLACE FUNCTION demo_dynamic_table(tablename text) RETURNS void AS $$
BEGIN
EXECUTE format('CREATE TABLE %I (id serial primary key);', tablename);
END;
$$ LANGUAGE plpgsql;
The same approach works for trigger creation, etc.
You can create PL/pgSQL procedure for table creation and move your trigger creation code inside it

execute a trigger when I create a table

I would like to know if a trigger on a system table of PostgreSQL can be executed when I create a table
I need to add 2 functions on each table of my database and I would like to do it dynamically
Thanks
This can be done with an event trigger:
CREATE OR REPLACE FUNCTION on_create_table_func()
RETURNS event_trigger AS $$
BEGIN
-- your code here
END
$$
LANGUAGE plpgsql;
CREATE EVENT TRIGGER
on_create_table ON ddl_command_end
WHEN TAG IN ('CREATE TABLE')
EXECUTE PROCEDURE on_create_table_func();
Note that there is no way to directly execute any query on the newly created table, or even get its name.
I don't know what you mean by "add 2 functions on each table" since functions don't belong to a specific table, but if you need to perform an operation specific for the new tables, this might not be for you.
I know it's an old question but now it has been implemented in version 9.3, or at least partially
http://www.postgresql.org/docs/9.3/static/event-trigger-definition.html
You're looking for "DDL Triggers". They're not implemented in PostgreSQL. Neither you can add triggers to system tables. Look at this forum entry:
Adding ddl audit trigger

Need help writing a PostgreSQL trigger function

I have two tables representing two different types of imagery. I am using PostGIS to represent the boundaries of those images. Here is a simplified example of those tables:
CREATE TABLE img_format_a (
id SERIAL PRIMARY KEY,
file_path VARCHAR(1000),
boundary GEOGRAPHY(POLYGON, 4326)
);
CREATE TABLE img_format_p (
id SERIAL PRIMARY KEY,
file_path VARCHAR(1000),
boundary GEOGRAPHY(POLYGON, 4326)
);
I also have a cross reference table, which I want to contain all the IDs of the images that overlap each other. Whenever an image of type "A" gets inserted into the database, I want to check to see whether it overlaps any of the existing imagery of type "P" (and vice versa) and insert corresponding entries into the img_a_img_p cross reference table. This table should represent a many-to-many relationship.
My first instinct is to write a trigger to manage thisimg_a_img_p table. I've never created a trigger before, so let me know if this is a silly thing to do, but it seems to make sense to me. So I create the following trigger:
CREATE TRIGGER update_a_p_cross_reference
AFTER INSERT OR DELETE OR UPDATE OF boundary
ON img_format_p FOR EACH ROW
EXECUTE PROCEDURE check_p_cross_reference();
The part where I am getting stuck is with writing the trigger function. My code is in Java and I see that there are tools like PL/pgSQL, but I'm not sure if that's what I should use or if I even need one of those special add-ons.
Essentially all I need the trigger to do is update the cross reference table each time a new image gets inserted into either img_format_a or img_format_p. When a new image is inserted, I would like to use a PostGIS function like ST_Intersects to determine whether the new image overlaps with any of the images in the other table. For each image pair where ST_INTERSECTS returns true, I would like to insert a new entry into img_a_img_p with the ID's of both images. Can someone help me figure out how to write this trigger function? Here is some pseudocode:
SELECT * FROM img_format_p P
WHERE ST_Intersects(A.boundary, P.boundary);
for each match in selection {
INSERT INTO img_a_img_p VALUES (A.id, P.id);
}
You could wrap the usual INSERT ... SELECT idiom in a PL/pgSQL function sort of like this:
create function check_p_cross_reference() returns trigger as
$$
begin
insert into img_a_img_p (img_a_id, img_p_id)
select a.id, p.id
from img_format_a, img_format_p
where p.id = NEW.id
and ST_Intersects(a.boundary, p.boundary);
return null;
end;
$$ language plpgsql;
Triggers have two extra variables, NEW and OLD:
NEW
Data type RECORD; variable holding the new database row for INSERT/UPDATE operations in row-level triggers. This variable is NULL in statement-level triggers and for DELETE operations.
OLD
Data type RECORD; variable holding the old database row for UPDATE/DELETE operations in row-level triggers. This variable is NULL in statement-level triggers and for INSERT operations.
So you can use NEW.id to access the new img_format_p value that's going in. You (currently) can't use the plain SQL language for triggers:
It is not currently possible to write a trigger function in the plain SQL function language.
but PL/pgSQL is pretty close. This would make sense as an AFTER INSERT trigger:
CREATE TRIGGER update_a_p_cross_reference
AFTER INSERT
ON img_format_p FOR EACH ROW
EXECUTE PROCEDURE check_p_cross_reference();
Deletes could be handled with a foreign key on img_a_img_p and a cascading delete. You could use your trigger for UPDATEs as well:
CREATE TRIGGER update_a_p_cross_reference
AFTER INSERT OR UPDATE OF boundary
ON img_format_p FOR EACH ROW
EXECUTE PROCEDURE check_p_cross_reference();
but you'd probably want to clear out the old entries before inserting the new ones with something like:
delete from img_a_img_p where img_p_id = NEW.id;
before the INSERT...SELECT statement.