return;
if (!HeapTupleSatisfiesVisibility(tuple, estate->es_snapshot, buffer))
- ereport(ERROR,
- (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
+ {
+ /*
+ * We should not raise a serialization failure if the conflict is
+ * against a tuple inserted by our own transaction, even if it's not
+ * visible to our snapshot. (This would happen, for example, if
+ * conflicting keys are proposed for insertion in a single command.)
+ */
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple->t_data)))
+ ereport(ERROR,
+ (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
+ }
}
/*
--- /dev/null
+Parsed test spec with 2 sessions
+
+starting permutation: beginrr1 beginrr2 donothing1 c1 donothing2 c2 show
+step beginrr1: BEGIN ISOLATION LEVEL REPEATABLE READ;
+step beginrr2: BEGIN ISOLATION LEVEL REPEATABLE READ;
+step donothing1: INSERT INTO ints(key, val) VALUES(1, 'donothing1') ON CONFLICT DO NOTHING;
+step c1: COMMIT;
+step donothing2: INSERT INTO ints(key, val) VALUES(1, 'donothing2'), (1, 'donothing3') ON CONFLICT DO NOTHING;
+step c2: COMMIT;
+step show: SELECT * FROM ints;
+key val
+
+1 donothing1
+
+starting permutation: beginrr1 beginrr2 donothing2 c2 donothing1 c1 show
+step beginrr1: BEGIN ISOLATION LEVEL REPEATABLE READ;
+step beginrr2: BEGIN ISOLATION LEVEL REPEATABLE READ;
+step donothing2: INSERT INTO ints(key, val) VALUES(1, 'donothing2'), (1, 'donothing3') ON CONFLICT DO NOTHING;
+step c2: COMMIT;
+step donothing1: INSERT INTO ints(key, val) VALUES(1, 'donothing1') ON CONFLICT DO NOTHING;
+step c1: COMMIT;
+step show: SELECT * FROM ints;
+key val
+
+1 donothing2
+
+starting permutation: beginrr1 beginrr2 donothing1 donothing2 c1 c2 show
+step beginrr1: BEGIN ISOLATION LEVEL REPEATABLE READ;
+step beginrr2: BEGIN ISOLATION LEVEL REPEATABLE READ;
+step donothing1: INSERT INTO ints(key, val) VALUES(1, 'donothing1') ON CONFLICT DO NOTHING;
+step donothing2: INSERT INTO ints(key, val) VALUES(1, 'donothing2'), (1, 'donothing3') ON CONFLICT DO NOTHING; <waiting ...>
+step c1: COMMIT;
+step donothing2: <... completed>
+error in steps c1 donothing2: ERROR: could not serialize access due to concurrent update
+step c2: COMMIT;
+step show: SELECT * FROM ints;
+key val
+
+1 donothing1
+
+starting permutation: beginrr1 beginrr2 donothing2 donothing1 c2 c1 show
+step beginrr1: BEGIN ISOLATION LEVEL REPEATABLE READ;
+step beginrr2: BEGIN ISOLATION LEVEL REPEATABLE READ;
+step donothing2: INSERT INTO ints(key, val) VALUES(1, 'donothing2'), (1, 'donothing3') ON CONFLICT DO NOTHING;
+step donothing1: INSERT INTO ints(key, val) VALUES(1, 'donothing1') ON CONFLICT DO NOTHING; <waiting ...>
+step c2: COMMIT;
+step donothing1: <... completed>
+error in steps c2 donothing1: ERROR: could not serialize access due to concurrent update
+step c1: COMMIT;
+step show: SELECT * FROM ints;
+key val
+
+1 donothing2
+
+starting permutation: begins1 begins2 donothing1 c1 donothing2 c2 show
+step begins1: BEGIN ISOLATION LEVEL SERIALIZABLE;
+step begins2: BEGIN ISOLATION LEVEL SERIALIZABLE;
+step donothing1: INSERT INTO ints(key, val) VALUES(1, 'donothing1') ON CONFLICT DO NOTHING;
+step c1: COMMIT;
+step donothing2: INSERT INTO ints(key, val) VALUES(1, 'donothing2'), (1, 'donothing3') ON CONFLICT DO NOTHING;
+step c2: COMMIT;
+step show: SELECT * FROM ints;
+key val
+
+1 donothing1
+
+starting permutation: begins1 begins2 donothing2 c2 donothing1 c1 show
+step begins1: BEGIN ISOLATION LEVEL SERIALIZABLE;
+step begins2: BEGIN ISOLATION LEVEL SERIALIZABLE;
+step donothing2: INSERT INTO ints(key, val) VALUES(1, 'donothing2'), (1, 'donothing3') ON CONFLICT DO NOTHING;
+step c2: COMMIT;
+step donothing1: INSERT INTO ints(key, val) VALUES(1, 'donothing1') ON CONFLICT DO NOTHING;
+step c1: COMMIT;
+step show: SELECT * FROM ints;
+key val
+
+1 donothing2
+
+starting permutation: begins1 begins2 donothing1 donothing2 c1 c2 show
+step begins1: BEGIN ISOLATION LEVEL SERIALIZABLE;
+step begins2: BEGIN ISOLATION LEVEL SERIALIZABLE;
+step donothing1: INSERT INTO ints(key, val) VALUES(1, 'donothing1') ON CONFLICT DO NOTHING;
+step donothing2: INSERT INTO ints(key, val) VALUES(1, 'donothing2'), (1, 'donothing3') ON CONFLICT DO NOTHING; <waiting ...>
+step c1: COMMIT;
+step donothing2: <... completed>
+error in steps c1 donothing2: ERROR: could not serialize access due to concurrent update
+step c2: COMMIT;
+step show: SELECT * FROM ints;
+key val
+
+1 donothing1
+
+starting permutation: begins1 begins2 donothing2 donothing1 c2 c1 show
+step begins1: BEGIN ISOLATION LEVEL SERIALIZABLE;
+step begins2: BEGIN ISOLATION LEVEL SERIALIZABLE;
+step donothing2: INSERT INTO ints(key, val) VALUES(1, 'donothing2'), (1, 'donothing3') ON CONFLICT DO NOTHING;
+step donothing1: INSERT INTO ints(key, val) VALUES(1, 'donothing1') ON CONFLICT DO NOTHING; <waiting ...>
+step c2: COMMIT;
+step donothing1: <... completed>
+error in steps c2 donothing1: ERROR: could not serialize access due to concurrent update
+step c1: COMMIT;
+step show: SELECT * FROM ints;
+key val
+
+1 donothing2
test: lock-update-delete
test: lock-update-traversal
test: insert-conflict-do-nothing
+test: insert-conflict-do-nothing-2
test: insert-conflict-do-update
test: insert-conflict-do-update-2
test: insert-conflict-do-update-3
--- /dev/null
+# INSERT...ON CONFLICT DO NOTHING test with multiple rows
+# in higher isolation levels
+
+setup
+{
+ CREATE TABLE ints (key int primary key, val text);
+}
+
+teardown
+{
+ DROP TABLE ints;
+}
+
+session "s1"
+step "beginrr1" { BEGIN ISOLATION LEVEL REPEATABLE READ; }
+step "begins1" { BEGIN ISOLATION LEVEL SERIALIZABLE; }
+step "donothing1" { INSERT INTO ints(key, val) VALUES(1, 'donothing1') ON CONFLICT DO NOTHING; }
+step "c1" { COMMIT; }
+step "show" { SELECT * FROM ints; }
+
+session "s2"
+step "beginrr2" { BEGIN ISOLATION LEVEL REPEATABLE READ; }
+step "begins2" { BEGIN ISOLATION LEVEL SERIALIZABLE; }
+step "donothing2" { INSERT INTO ints(key, val) VALUES(1, 'donothing2'), (1, 'donothing3') ON CONFLICT DO NOTHING; }
+step "c2" { COMMIT; }
+
+permutation "beginrr1" "beginrr2" "donothing1" "c1" "donothing2" "c2" "show"
+permutation "beginrr1" "beginrr2" "donothing2" "c2" "donothing1" "c1" "show"
+permutation "beginrr1" "beginrr2" "donothing1" "donothing2" "c1" "c2" "show"
+permutation "beginrr1" "beginrr2" "donothing2" "donothing1" "c2" "c1" "show"
+permutation "begins1" "begins2" "donothing1" "c1" "donothing2" "c2" "show"
+permutation "begins1" "begins2" "donothing2" "c2" "donothing1" "c1" "show"
+permutation "begins1" "begins2" "donothing1" "donothing2" "c1" "c2" "show"
+permutation "begins1" "begins2" "donothing2" "donothing1" "c2" "c1" "show"
(1 row)
drop table twoconstraints;
+-- check handling of self-conflicts at various isolation levels
+create table selfconflict (f1 int primary key, f2 int);
+begin transaction isolation level read committed;
+insert into selfconflict values (1,1), (1,2) on conflict do nothing;
+commit;
+begin transaction isolation level repeatable read;
+insert into selfconflict values (2,1), (2,2) on conflict do nothing;
+commit;
+begin transaction isolation level serializable;
+insert into selfconflict values (3,1), (3,2) on conflict do nothing;
+commit;
+begin transaction isolation level read committed;
+insert into selfconflict values (4,1), (4,2) on conflict(f1) do update set f2 = 0;
+ERROR: ON CONFLICT DO UPDATE command cannot affect row a second time
+HINT: Ensure that no rows proposed for insertion within the same command have duplicate constrained values.
+commit;
+begin transaction isolation level repeatable read;
+insert into selfconflict values (5,1), (5,2) on conflict(f1) do update set f2 = 0;
+ERROR: ON CONFLICT DO UPDATE command cannot affect row a second time
+HINT: Ensure that no rows proposed for insertion within the same command have duplicate constrained values.
+commit;
+begin transaction isolation level serializable;
+insert into selfconflict values (6,1), (6,2) on conflict(f1) do update set f2 = 0;
+ERROR: ON CONFLICT DO UPDATE command cannot affect row a second time
+HINT: Ensure that no rows proposed for insertion within the same command have duplicate constrained values.
+commit;
+select * from selfconflict;
+ f1 | f2
+----+----
+ 1 | 1
+ 2 | 1
+ 3 | 1
+(3 rows)
+
+drop table selfconflict;
on conflict on constraint twoconstraints_f2_excl do nothing; -- do nothing
select * from twoconstraints;
drop table twoconstraints;
+
+-- check handling of self-conflicts at various isolation levels
+
+create table selfconflict (f1 int primary key, f2 int);
+
+begin transaction isolation level read committed;
+insert into selfconflict values (1,1), (1,2) on conflict do nothing;
+commit;
+
+begin transaction isolation level repeatable read;
+insert into selfconflict values (2,1), (2,2) on conflict do nothing;
+commit;
+
+begin transaction isolation level serializable;
+insert into selfconflict values (3,1), (3,2) on conflict do nothing;
+commit;
+
+begin transaction isolation level read committed;
+insert into selfconflict values (4,1), (4,2) on conflict(f1) do update set f2 = 0;
+commit;
+
+begin transaction isolation level repeatable read;
+insert into selfconflict values (5,1), (5,2) on conflict(f1) do update set f2 = 0;
+commit;
+
+begin transaction isolation level serializable;
+insert into selfconflict values (6,1), (6,2) on conflict(f1) do update set f2 = 0;
+commit;
+
+select * from selfconflict;
+
+drop table selfconflict;