How to log delete queries on Postgresql? - postgresql

I created a function which writes information about table deletions.
And another function which simply adds a trigger call after delete.
But I would like to store the whole row as string into my table.
According to Postgresql Documentation it should work by adding "OLD.*" into a text based column. But it fails telling me that I try to put too many columns into this table.
OLD is from type RECORD. And i want to have it in my text field like "value1,value2,value3" or it could be "colname:value,colname2:value". I dont care, I just want to see the row which has been deleted.
Another approach can be to log all delete queries from pg_stat_activity. But I don't know how to do that. Simply accessing pg_stat_activity every second would cause too much traffic I guess.
My table is simple:
create table delete_history (date timestamp, tablename varchar(100), data text);
This is my function:
CREATE or REPLACE FUNCTION ondelete() RETURNS TRIGGER AS $$
BEGIN
INSERT INTO delete_history VALUES (CURRENT_TIMESTAMP, TG_TABLE_NAME, OLD.*);
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
This is my trigger:
CREATE OR REPLACE FUNCTION history_create_triggers() RETURNS void
AS $$
DECLARE
r RECORD;
BEGIN
FOR r IN SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type='BASE TABLE' LOOP
EXECUTE 'CREATE TRIGGER log_history AFTER DELETE ON public.' || r.table_name || ' FOR EACH ROW EXECUTE PROCEDURE ondelete();';
END LOOP;
END;
$$ LANGUAGE plpgsql;

You can convert type record into text:
CREATE or REPLACE FUNCTION ondelete() RETURNS TRIGGER AS $$
BEGIN
INSERT INTO delete_history VALUES (CURRENT_TIMESTAMP, TG_TABLE_NAME, OLD::text);
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
sql fiddle demo
another approach could be converting your row into JSON with row_to_json function (if you have version 9.2):
CREATE or REPLACE FUNCTION ondelete() RETURNS TRIGGER AS $$
BEGIN
INSERT INTO delete_history VALUES (CURRENT_TIMESTAMP, TG_TABLE_NAME, row_to_json(OLD));
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
sql fiddle demo
Another approach can be convert your data to hstore
CREATE or REPLACE FUNCTION ondelete() RETURNS TRIGGER AS $$
BEGIN
INSERT INTO delete_history VALUES (CURRENT_TIMESTAMP, TG_TABLE_NAME, hstore(OLD));
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
I can't test it now - sqlfiddle is not allowing to use hstore.

Related

Save execute results into a table

Below is a simplified postgres stored procedure I am trying to run:
create or replace procedure my_schema.tst(suffix varchar)
as $$
begin
execute(' select *
into my_schema.MyTable_'||suffix||'
From my_schema.MyTable
');
end;
$$
language plpgsql;
When I attempt to run using something like:
call my_schema.tst('test');
I get this error Invalid operation: EXECUTE of SELECT ... INTO is not supported;
Is it possible to execute a dynamic query that creates a new table? I have seen examples that look like:
Execute('... some query ...') into Table;
but for my use case I need the resulting tablename to be passed as a variable.
In PostgreSQL you can use INSERT INTO tname SELECT...
create or replace procedure my_schema.tst(suffix varchar)
as $$
begin
execute ' INSERT INTO my_schema.MyTable_'||suffix||' SELECT *
FROM my_schema.MyTable
';
end;
$$
language plpgsql;
or Use CREATE TABLE tname AS SELECT..., :
create or replace procedure my_schema.tst(suffix varchar)
as $$
begin
execute ' CREATE TABLE my_schema.MyTable_'||suffix||' as SELECT *
FROM my_schema.MyTable
';
end;
$$
language plpgsql;

How can I set the date when some row is created? I'm trying to use only Triggers and Functions at PostgreSQL

I have a table called recipes and my goal is for each time that I insert a new row, the column created_at, of this new row, automatically receives the value NOW().
I manage to do something like this on the column updated_at creating this function and trigger, and it's working fine.
CREATE OR REPLACE FUNCTION public.trigger_set_timestamp()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER set_timestamp
BEFORE UPDATE ON recipes
FOR EACH ROW
EXECUTE PROCEDURE trigger_set_timestamp();
But after I changed the code to set the created_at nothing happens and the Postgres doesn't point any errors.
CREATE OR REPLACE FUNCTION public.trigger_set_timestamp_created_at()
RETURNS trigger AS $$
BEGIN
NEW.created_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER set_timestamp_created_at
AFTER INSERT ON recipes
FOR EACH ROW
EXECUTE PROCEDURE trigger_set_timestamp_created_at();

Alias column name in Postgres notify

I am using trigger in Postgres database to call function and send newly inserted row to NodeJs application
CREATE OR REPLACE FUNCTION triggerFunction() RETURNS trigger AS $$
DECLARE
BEGIN
PERFORM pg_notify('tableName', row_to_json(NEW)::text );
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
This returns the whole row in json format. However I need to change one of the column name while this row is returned.
Unfortunately AS keywork doesnt work in the row to json with NEW.COLUMN_NAME AS NEW_COLUMN. How can we achieve the solution for this?
CREATE OR REPLACE FUNCTION triggerFunction() RETURNS trigger AS $$
DECLARE
ret json;
BEGIN
select row_to_json(x) into ret from
(select NEW.abc as def, NEW.jkl, NEW.col3) x;
PERFORM pg_notify('tableName', ret::text );
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Trigger to update table column when new row is insert in postgresql?

I want to write trigger to update table when new row is insert. I am using updatea Query like
UPDATE table SET
geom = ST_SetSRID(ST_MakePoint(longitude, latitude), 4326)
You need to use a BEFORE trigger and then assign the new value:
CREATE OR REPLACE FUNCTION function_update()
RETURNS trigger AS
$BODY$
BEGIN
new.geom := ST_SetSRID(ST_MakePoint(new.longitude, new.latitude), 4326);
RETURN new;
END;
$BODY$
LANGUAGE plpgsql;
This can only be done in a BEFORE trigger:
CREATE TRIGGER triggerinsert
BEFORE INSERT
ON rdpr
FOR EACH ROW
EXECUTE PROCEDURE function_update();
I Solved by writing trigger function like
CREATE OR REPLACE FUNCTION function_update()
RETURNS trigger AS
$BODY$
BEGIN
new.geom := ST_SetSRID(ST_MakePoint(new.longitude, new.latitude), 4326);
RETURN new;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION function_update()
OWNER TO postgres;
and i wrote trigger to update
CREATE TRIGGER triggerinsert
Before INSERT
ON rdpr
FOR EACH ROW
EXECUTE PROCEDURE function_update();

how to circumvent missing record type on insert

I'd like to make a copy of a row in one table addressed by a field in another table, like this:
CREATE OR REPLACE FUNCTION f_ins_up_vorb()
RETURNS TRIGGER AS $$
DECLARE
dienst dienst%ROWTYPE;
account record;
BEGIN
-- ...
EXECUTE format('SELECT * FROM %s WHERE id=$1',dienst.tabelle)
USING NEW.id INTO account;
EXECUTE 'INSERT INTO ' || dienst.tabelle || 'shadow SELECT ($1).*, now(), $2' USING account, jobid;
RETURN NEW;
END
$$ LANGUAGE plpgsql;
But this yields:
ERROR: record type has not been registered
CONTEXT: SQL statement "INSERT INTO accountadshadow SELECT ($1).*, now(), $2"
PL/pgSQL function f_ins_up_vorb() line 30 at EXECUTE statement
The tables addressed by dienst.tabelle have no common type but the target table (dienst.tabelle || 'shadow') is always a superset of the source table. So this should always work (and does work in a trigger function, where I use NEW, which seems to have a record type).
Is there any way around this?
Try something like:
CREATE OR REPLACE FUNCTION f_ins_up_vorb()
RETURNS TRIGGER AS $$
DECLARE
dienst dienst%ROWTYPE;
BEGIN
-- ...
EXECUTE 'INSERT INTO '||dienst.tabelle||'shadow
SELECT *, now(), $2
FROM '||dienst.tabelle||'
WHERE id=$1'
USING NEW.id, jobid;
RETURN NEW;
END
$$ LANGUAGE plpgsql;
If you are trying to create some kind of log trigger - read this page first.