I got a trouble when a try to execute a trigger. Let's suppose we have 2 tables and I want to copy data from table A to table B but each table got a unique constraint.
create table test1 (
test_name varchar);
create unique index test1_uc on test1 USING btree (test_name);
create table test2 (
test_name2 varchar);
create unique index test2_uc on test2 USING btree (test_name2);
CREATE OR REPLACE FUNCTION trig_test()
RETURNS trigger AS
$$
BEGIN
IF pg_trigger_depth() <> 1 THEN
RETURN NEW;
END IF;
INSERT INTO test2(test_name2)
VALUES(NEW.test_name2)
ON CONFLICT (test_name2) DO NOTHING;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trigger_test
AFTER INSERT
ON test2
FOR EACH ROW
EXECUTE PROCEDURE trig_test();
insert into test2 values ('test');
insert into test2 values ('test'); //should do nothing ?
But I get this error:
ERROR: duplicate key value violates unique constraint "test2_uc" DETAIL: Key (test_name2)=(test) already exists.
What's wrong with the trigger?
Your example is broken. Source and target are the same in your
INSERT
in the trigger, which is bound to raise a unique violation every time (except when inserting NULL) - suppressed byON CONFLICT (test_name2) DO NOTHING
, so nothing ever happens in the trigger.You also forget about the unique constraint in your original
INSERT
. See below.Start with a less confusing setup:
And it's more efficient to move
pg_trigger_depth()
to the trigger itself. So this would work, copying rows inserted intotest1
totest2
(and not the other way), only for the first level of trigger depth:I kept it as
AFTER
trigger. Can be aBEFORE
trigger as well, but there you'd needRETURN NEW;
.Why
(pg_trigger_depth() < 1)
?Note that you trap unique violations in
test2
this way (nothing happens), but unique violations intest1
would still raise an exception unless you haveON CONFLICT ... DO NOTHING
there as well. Your test is wishful thinking:Has to be:
Alternative: Chain two
INSERT
with a CTEIf you have control over
INSERT
commands ontest1
, you can do this instead of the trigger:Related: