Skip to content
Advertisement

Using NEW and COPY in a Postgres Trigger

I’m trying to copy the last inserted row from a table into a csv file using a trigger.

CREATE TRIGGER new_tbc_order
  AFTER INSERT
  ON trig_test
  FOR EACH ROW
  EXECUTE PROCEDURE write_last_tbc_order();



CREATE OR REPLACE FUNCTION write_last_tbc_order()
RETURNS TRIGGER 
LANGUAGE plpgsql
as $$
BEGIN
    EXECUTE 'COPY (
             select i.id, i.paid, i.no_items FROM (SELECT NEW.*) AS i 
         ) TO ''/Users/fred/Desktop/last_tbc_order.csv'' csv;' USING NEW;
    RETURN NEW;
END; $$

I’ve tried this in various incarnations, with or without EXECUTE but I’m still getting the error.

ERROR:  missing FROM-clause entry for table "new"
LINE 1: ...opy (select i.id, i.paid, i.no_items FROM (SELECT NEW.*) AS ...
                                                         ^

Just cannot get it to access the NEW data.

Where am I going wrong ?

Advertisement

Answer

Adrian’s answer inspired me to experiment more with the NEW record where it was actually available. The actual answer, as usual, turned out to be simple-ish:

CREATE OR REPLACE FUNCTION write_last_tbc_order()
RETURNS TRIGGER 
AS 
$$
BEGIN
    EXECUTE 'copy (select '''||NEW.id||''','''||NEW.paid||''','''||NEW.no_items||''') 
to ''/Users/shaun/Desktop/last_tbc_order.csv'' csv;' USING NEW;
    RETURN NEW;
END; 
$$
LANGUAGE plpgsql;

Thus keeping the NEW record outside of the copy statement and building it dynamically before the execute.

This still required the USING NEW part at the end to make it work correctly. Also required all the quoting.

UPDATE:

The above code does require almost everything to be running as a Postgres Superuser, (as mentioned by several commenters), which is not ideal.

To get around this, you can create a PlPython function as follows:

CREATE OR REPLACE FUNCTION write_last_tbc_order() 
RETURNS TRIGGER AS
'
import os
row = str(TD["new"]["id"]) + "," + TD["new"]["paid"] + "," + str(TD["new"]["noitems"])
path = "/tmp/db-data/last_order.csv";
with open(path,"w") as o:
    os.chmod(path, 0o644)
    o.write(row)
return None
'
LANGUAGE 'plpythonu';

This function must be created as a Superuser, but it can be used in triggers created by standard users, and those triggers also work when inserts are fired by standard users.

The place to where the file is written must have write permission for the postgres user in every part of the path, so I created a subdirectory of /tmp with 777 permissions.

Also needed to add in the os chmod 644 statement to make the file readable by other users on the system. Without this, the file is created with permissions of 600.

FURTHER NOTES:

In the end Apache doesn’t like you to setup a virtual directory inside the /tmp directory so in the end had to create another called /tmp-wh specifically for this purpose.

User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement