Postgresql insert trigger does not work - sql

New row is added to table A and i need trigger which will automatically insert row in table B after row has been inserted in table A.
CREATE FUNCTION insertblocked (
)
RETURNS trigger AS
$body$
BEGIN
INSERT INTO tableB (blocked.id,blocked.number,blocked.date)
VALUES (new.id,new.prefix,now())
RETURN NEW;
END
$body$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER;
CREATE TRIGGER insertblocked
AFTER INSERT
ON public.tableA FOR EACH ROW
EXECUTE PROCEDURE insertblocked();
Please help and advise, why is sql compiler returning and error
QUERY: INSERT INTO blocked (blocked.id,blocked.number,blocked.date) VALUES ( $1 , $2 ,now()) RETURN $3

You're missing a semi-column ; at the end of your insert statement.

Related

Replace insert with rule in PostgreSQL

I want to replace INSERT into a table with a PostgreSQL rule.
Here is my two tables:
Resource:
uid (PK): UUID
type (NOT_NULL): ENUM
SpecificResource:
uid (PK, FK on resource.uid): UUID
content (NOT_NULL): JSONB
I want any user to the database to be able to make insert/update/delete on SpecificResource directly without the need to insert/update/delete on Resource.
Here is my unsuccessful try that triggers an infinite recursion loop (indeed because I try to re-insert in specific_resource table with a RULE (...) DO INSTEAD :
CREATE OR REPLACE RULE insert_specific_resource
AS ON INSERT TO specific_resource
DO INSTEAD (
INSERT INTO resource (uid, type)
VALUES (NEW.uid, 'SPECIFIC_RESOURCE');
INSERT INTO specific_resource (uid)
VALUES (new.uid)
);
create or replace function specific_resource_tf()
returns trigger language plpgsql as
$$
begin
insert into resource(uid, type) VALUES (new.uid, 'SPECIFIC_RESOURCE');
return new;
end;
$$;
create trigger specific_resource_t
before insert on specific_resource
for each row
execute procedure specific_resource_tf();
Explanation
After creating trigger specific_resource_t it will be executed before each insert in table specific_resource. The trigger invokes function specific_resource_tf which does what your rule was intended to - inserts a record into resource before proceeding with the insert in table specific_resource.
Illustration (with temporary tables and function)
-- drop table if exists specific_resource; drop table if exists resource;
create temp table resource (uid integer primary key, type text);
create temp table specific_resource (uid integer references resource(uid), content JSONB);
create or replace function pg_temp.specific_resource_tf()
returns trigger language plpgsql as $$
begin
insert into resource(uid, type) VALUES (new.uid, 'SPECIFIC_RESOURCE');
return new;
end;
$$;
create trigger specific_resource_t
before insert on specific_resource
for each row execute procedure pg_temp.specific_resource_tf();
insert into specific_resource values (22, '"test"');
-- does insert in both tables
CREATE OR REPLACE FUNCTION add_specific_plus_resource()
RETURNS TRIGGER LANGUAGE PLPGSQL AS
$$
BEGIN
INSERT INTO resource(uid, type)
VALUES (uid, 'SPECIFIC');
RETURN NEW;
END;
$$;
CREATE OR REPLACE TRIGGER add_specific_resource
BEFORE INSERT ON specific_resource
FOR EACH ROW EXECUTE PROCEDURE add_specific_plus_resource();
The following INSERT works well to add a row into resource but not into specific_resource.
INSERT INTO specific_resource(uid, content)
VALUES ('123e4567-e89b-12d3-a456-426614174000', '"test"');

trigger insert only if tupel satisfies condition

I want to create a trigger which checks before insert if the tupel which is supposed to be inserted holds a specific condition (which also depends on another table).
For example:
create trigger or replace check_tupel
before insert on A
for each row
execute
if exists (select x,y from B where B.x = A.x and B.y = A.y)
Oh I am using postgreSQL 13.
EDIT: Yes I know that I can do this without a trigger, but I am asking for a solution with a trigger for a reason.
I hope there is a way to do this... My other idea was to create a UDF which gets called before insert but I do not know how to check the condition in this UDF and only insert if the function returns true.
If you simply wanna automatically validate a record before inserting it on table A based on table B using a User Defined Function, you do not need a trigger at all. Consider adding a simple CHECK CONSTRAINT:
CREATE TABLE a (
x int,
y int,
CONSTRAINT exists_in_b CHECK (NOT myfunc(x,y))
);
Demo: db<>fiddle
CREATE TABLE b (x int,
y int);
INSERT INTO b VALUES (42,42);
CREATE OR REPLACE FUNCTION myfunc(x int, y int)
RETURNS BOOLEAN AS $BODY$
SELECT EXISTS (SELECT 1 FROM b WHERE b.y =$1 AND b.x=$2 )
$BODY$
LANGUAGE sql;
CREATE TABLE a (
x int,
y int,
CONSTRAINT exists_in_b CHECK (NOT myfunc(x,y)) -- here the magic happens
);
Now, if we try to insert a value that our function does not validate, it raises an exception:
INSERT INTO a VALUES (42,42);
ERROR: new row for relation "a" violates check constraint "exists_in_b"
DETAIL: Failing row contains (42, 42).
SQL state: 23514
EDIT (See comments): Solution using a trigger
CREATE OR REPLACE FUNCTION myfunc()
RETURNS TRIGGER AS $BODY$
BEGIN
IF EXISTS (SELECT 1 FROM b WHERE b.y =NEW.y AND b.x=NEW.x ) THEN
RAISE EXCEPTION 'tuple already exists in "b": % %', NEW.x,NEW.y;
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
CREATE TRIGGER check_tupel
BEFORE INSERT OR UPDATE ON a
FOR EACH ROW EXECUTE PROCEDURE myfunc();
Demo: db<>fiddle
So you need a trigger solution, sounds like a homework problem. The question then becomes do you want to:
abort the entire operation
slightly squash the row but continue the remainder of the operation.
The following does the second: (See demo)
create or replace
function check_b_has_a()
returns trigger
language plpgsql
as $$
begin
if exists
( select null
from b
where (b.x,b.y) =
(new.x, new.y)
)
then
return null;
else
return new;
end if;
end;
$$;
create trigger a_bir
before insert
on a
for each row
execute function check_b_has_a();

Move Data from One table to other table by using trigger in postgreSQL

I have the requirement to move data from one table to another table when the value of one of the columns is updated. And I just want to move the updated row to the new table.
Below is my trigger that I have written. The issue with my code is, that it is moving all the data and not just the row which was updated. Can anyone give a suggestion?
create or replace function moveToAC1ControlHist()
returns trigger as $$
begin if NEW.file_status='CO'
then
insert into ac1_control_hist (file_status,identitifier)
(
select file_status,identitifier
from
ac1_control where new.FILE_STATUS = 'CO'
);
end if;
return new;
end;
$$ language plpgsql;
create TRIGGER AC1_CONTROL_TRIGGER AFTER update of file_status ON AC1_CONTROL
FOR EACH ROW when (new.file_status ='CO')EXECUTE PROCEDURE moveToAC1ControlHist();
I think the logic you want is:
create or replace function moveToAC1ControlHist()
returns trigger as
$$
begin
insert into ac1_control_hist (file_status,identitifier)
values (new.file_status, new.identitifier);
return null;
end;
$$ language plpgsql;
create trigger ac1_control_trigger
after update of file_status on ac1_control
for each row
when (new.file_status ='co')
execute function movetoac1controlhist()
;
Rationale:
you just want to copy (part of) the row being updated, so there is no need to select; you can access the values of the current row with new in a row-level trigger
the trigger definition filters on new file_status that is equal to 'CO', so there is no need for a if construct in the function
this is an after trigger, so you can just return null - the result is discarded anyway

How to use a newly inserted value from another table in PostgreSQL?

Assuming I have two tables final table and table_1, I want to use the the newest values from table_1 and insert them with a trigger in the final_table with every INSERT ON table_1. When I create the triggerfunction inserttrigger() as shown in the example and create the trigger, I get the newest value times the number of rows in table_1. How to write the trigger proper that I get only the single newest record in table1?
Doing:
-- Create tables and inserting example values
CREATE TABLE final_table(id INTEGER, value_fin INTEGER);
CREATE TABLE table_1(id INTEGER, value INTEGER);
INSERT INTO table_1 VALUES(1, 200), (2,203), (3, 209);
-- Create Triggerfunction
CREATE OR REPLACE FUNCTION inserttrigger()
RETURNS TRIGGER AS
$func$
BEGIN
INSERT INTO final_table
SELECT latest.id, latest.value
FROM (SELECT NEW.id, NEW.value FROM table_1) AS latest;
RETURN NEW;
END;
$func$ language plpgsql;
-- Create Trigger
CREATE TRIGGER final_table_update BEFORE INSERT ON table_1
FOR EACH ROW EXECUTE PROCEDURE inserttrigger() ;
--Insert example values
INSERT INTO table_1 VALUES(4, 215);
Results in:
SELECT * FROM final_table
id | value_fin
4 215
4 215
4 215
But should look like:
id | value_fin
4 215
While:
CREATE TRIGGER final_table_update BEFORE INSERT ON table_1
EXECUTE PROCEDURE inserttrigger() ;
Results in:
ERROR: record "new" is not assigned yet
DETAIL: The tuple structure of a not-yet-assigned record is indeterminate.
I would recommend the VALUES() syntax:
CREATE OR REPLACE FUNCTION inserttrigger()
RETURNS TRIGGER AS
$func$
BEGIN
INSERT INTO final_table VALUES(NEW.id, NEW.value);
RETURN NEW;
END;
$func$ language plpgsql;
CREATE TRIGGER final_table_update BEFORE INSERT ON table_1
FOR EACH ROW EXECUTE PROCEDURE inserttrigger();
Note that you could also get the same behavior with a common-table-expression and the returning syntax, which avoids the need for a trigger:
with t1 as (
insert into table_1(id, value_fin) values(4, 215)
returning id, value_fin
)
insert into final_table(id, value) select id, value_fin from t1

Trigger to delete past records in postgresql

I want only to maintain present 1 month records log details. need to delete past record log details.I tried this code however could not work this,
create sequence userseq1;
CREATE TABLE users
( id integer NOT NULL DEFAULT nextval('userseq1'::regclass)
);
INSERT INTO users VALUES(126);
CREATE TABLE History
( userid integer
, createdate timestamp
);
CREATE OR REPLACE FUNCTION recordcreatetime() RETURNS trigger language plpgsql
AS $$
DECLARE
BEGIN
INSERT INTO History values(new.id,NOW() );
RETURN NEW;
END;
$$;
CREATE TRIGGER user_hist
BEFORE INSERT ON users
FOR EACH ROW
EXECUTE procedure recordcreatetime();
However it is working to insert values sequencing one by one adding.I want to delete the previous 1 month record Log details.I tried this below code and it is not working
CREATE OR REPLACE FUNCTION trf_keep_row_number_steady()
RETURNS TRIGGER AS
$body$
DECLARE
BEGIN
IF (SELECT count(createdate) FROM history) > rownum_limit
THEN
DELETE FROM history
WHERE createdate = (SELECT min(createdate) FROM history);
END IF;
END;
$body$
LANGUAGE plpgsql;
CREATE TRIGGER tr_keep_row_number_steady
AFTER INSERT ON history
FOR EACH ROW EXECUTE PROCEDURE trf_keep_row_number_steady();
I can see in your second code block, you have a trigger on history table and you are trying to DELETE FROM history in that same trigger.
Insert / Update / Delete on a table through a trigger on the same table is not allowed. Please think of some other alternative, e.g., running a separate DELETE statement for the required cleanup of rows before or after your main INSERT statement.