PL/pgSQL: Update a table using a record - postgresql

I have an insert trigger in a PostgreSQL 9.5 database which copies the row being inserted to another table:
CREATE OR REPLACE FUNCTION data_record_insert () RETURNS TRIGGER AS $x$
BEGIN
EXECUTE 'INSERT INTO data_record_backup VALUES ($1.*)' USING new;
RETURN new;
END;
$x$ LANGUAGE PLPGSQL;
However, I want to be able to use Postgres' INSERT ... ON CONFLICT upsert statement to insert rows into these tables but can't figure out how to write the trigger. It'd be something like this:
CREATE OR REPLACE FUNCTION data_record_insert () RETURNS TRIGGER AS $x$
BEGIN
EXECUTE 'INSERT INTO data_record_backup VALUES ($1.*)
ON CONFLICT (pk) DO UPDATE SET column_names($1) = $1.*' USING new;
RETURN new;
END;
$x$ LANGUAGE PLPGSQL;
But obviously that SET column_names($1) = $1.* is wrong, it's just something I made up. Is there some way to achieve this?

Related

A trigger that detects that an UPDATE wouldn't change a row

I wrote the following trigger:
CREATE FUNCTION trig_func() RETURNS trigger AS $$
BEGIN
IF NEW = OLD
THEN -- update would do nothing, doing something...
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trig BEFORE UPDATE ON some_table
FOR EACH ROW EXECUTE PROCEDURE trig_func();
It makes it clear what I'd like to achieve, but what is the proper thing to put in place of NEW = OLD?
The is distinct from operator can compare complete rows and will handle nulls correctly.
So you want
if new is not distinct from old then
...
end if;

PostgreSQL: Checking for NEW and OLD in a function for a trigger

I want to create a trigger which counts rows and updates a field in an other table. My current solution works for INSERT statements but failes when I DELETE a row.
My current function:
CREATE OR REPLACE FUNCTION update_table_count()
RETURNS trigger AS
$$
DECLARE updatecount INT;
BEGIN
Select count(*) into updatecount
From source_table
Where id = new.id;
Update dest_table set count=updatecount
Where id = new.id;
RETURN NEW;
END;
$$
LANGUAGE 'plpgsql';
The trigger is a pretty basic one, looking like.
CREATE TRIGGER count_trigger
AFTER INSERT OR DELETE
ON source_table
FOR EACH ROW
EXECUTE PROCEDURE update_table_count();
When I excute a DELETE statement the following error occurs:
ERROR: record "new" is not assigned yet
DETAIL: The tuple structure of a not-yet-assigned record is indeterminate.
I know one solution could be to create just one set of trigger and function for the DELETE and one for the INSERT statement. But I want to do it a bit more elegant and want to know, if there is a solution to check if NEW or OLD is present in the current context and just implement an IF ELSE block. But I dont know how to check for this context sensitive items.
Thanks for your help
The usual approach to make a trigger function do different things depending on how the trigger was fired is to check the trigger operation through TG_OP
CREATE OR REPLACE FUNCTION update_table_count()
RETURNS trigger AS
$$
DECLARE
updatecount INT;
BEGIN
if tg_op = 'UPDATE' then
select count(*) into updatecount from source_table where id = new.id;
update dest_table set count=updatecount where id = new.id;
elsif tg_op = 'DELETE' then
... do something else
end if;
RETURN NEW;
END;
$$
LANGUAGE plpgsql;
Unrelated, but: the language name is an identifier. Do not quote it using single quotes.
From PostgreSQL's documentation:
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, for example, if NEW is NULL, then the trigger was invoked on DELETE.

PostgreSql Trigger does not work when called 'insert'

The code below is my code to create trigger function to change column "pass".
create or replace function change_pass()
returns trigger as
$$
begin
NEW.pass := 'XXXXXXXXX';
return NEW;
end
$$
language plpgsql;
create trigger change_pass
AFTER insert or update on "D_ACCOUNT"
for each row execute procedure change_pass();
When i called insert, i did not see any changes in my data.
Can anyone explain to me where i was wrong?
You need a BEFORE trigger to change values in the NEW record:
create trigger change_pass
BEFORE insert or update on "D_ACCOUNT"
for each row execute procedure change_pass();

Why is my dblink trigger not updating?

I have setup a trigger on my table to update inserted rows using a dblink. I used dblink because want the trigger to update the row async.
I have tested the dblink update in pgAdmin SQL query tool succesfully. However, when I insert a row the trigger runs but no rows are updated.
Is there something I'm missing regarding dblink?
CREATE OR REPLACE FUNCTION locates_data.async_update_geom()
RETURNS trigger AS
$BODY$
BEGIN
-- as this is an after trigger, NEW contains all the information we need even for INSERT
perform dblink('dbname=devtable00 host=10.1.1.98 port=5432 user=admin password=*****', 'update locates_data.request set geom = ST_Transform(ST_setSRID(ST_MakePoint('||NEW.longitude||','||NEW.latitude||'), 4326),3857) where request_pk = '||NEW.request_pk||'');
RAISE NOTICE 'UPDATING geo data for %, [%,%]' , NEW.request_pk, NEW.latitude, NEW.longitude;
RETURN NEW; -- result is ignored since this is an AFTER trigger
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION locates_data.async_update_geom()

Postgres trigger that will insert a row into another table before it's deleted

I have a table (entry_table) filled with various geographic location data currently used by clients for our front end Web feature service. If a user deleted their entry in the WFS it is deleted in our postgres database. I would like to create a trigger that will run an INSERT command to copy the row(roughly 25 columns of data) to a second table(historical_entry_table) so if the entries are needed again in the future they can be easily retrieved.
Here's what I have working so far. I'm new to Triggers so I know the syntax is off. Not sure where to go from here. Im running postgres 8.4
In the table :
CREATE TRIGGER trigger_name BEFORE DELETE
ON entry_table
FOR EACH ROW
EXECUTE PROCEDURE trigger_backup_row
The Function itself:
CREATE OR REPLACE FUNCTION trigger_backup_row()
LANGUAGE SQL
RETURNS trigger AS
$BODY$
BEGIN
INSERT INTO historical_entry_table (col1, col2, etc) values (OLD.col1, OLD.col2, OL
RETURN NULL:
END;
$BODY$
I will put a working example here:
CREATE OR REPLACE FUNCTION trigger_backup_row()
RETURNS trigger AS
$BODY$
BEGIN
INSERT INTO PCIcards_backup (MODEL, SUBSYSTEM_DEVICE, ADAPTER, MAPPING) values (NEW.MODEL, NEW.SUBSYSTEM_DEVICE, NEW.ADAPTER, NEW.MAPPING);
RETURN NEW;
END;
$BODY$
language PLPGSQL
As you can see, you only need to set the language at the end of a file with param PLPGSQL because SQL cannot return triggers.
you have just about got it. your code here, updated :
CREATE OR REPLACE FUNCTION trigger_backup_row()
RETURN trigger AS
$BODY$
BEGIN
INSERT INTO historical_entry_table (col1, col2, etc) values (OLD.col1, OLD.col2, OLD.etc);
END;
$BODY$
Posting the correct version of the last answer:
CREATE OR REPLACE FUNCTION trigger_backup_row()
RETURN trigger
LANGUAGE plpgsql
AS
$$
BEGIN
INSERT INTO historical_entry_table (col1, col2, etc) values (OLD.col1, OLD.col2, OLD.etc);
RETURN new;
END;
$$