diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index c819d19db4..0e2d834ed1 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -259,6 +259,9 @@ ExecCloseIndices(ResultRelInfo *resultRelInfo) * the same is done for non-deferred constraints, but report * if conflict was speculative or deferred conflict to caller) * + * If 'arbiterIndexes' is nonempty, noDupErr applies only to + * those indexes. NIL means noDupErr applies to all indexes. + * * CAUTION: this must not be called for a HOT update. * We can't defend against that here for lack of info. * Should we change the API to make it safer? @@ -308,19 +311,15 @@ ExecInsertIndexTuples(TupleTableSlot *slot, { Relation indexRelation = relationDescs[i]; IndexInfo *indexInfo; + bool applyNoDupErr; IndexUniqueCheck checkUnique; bool satisfiesConstraint; - bool arbiter; if (indexRelation == NULL) continue; indexInfo = indexInfoArray[i]; - /* Record if speculative insertion arbiter */ - arbiter = list_member_oid(arbiterIndexes, - indexRelation->rd_index->indexrelid); - /* If the index is marked as read-only, ignore it */ if (!indexInfo->ii_ReadyForInserts) continue; @@ -358,6 +357,12 @@ ExecInsertIndexTuples(TupleTableSlot *slot, values, isnull); + /* Check whether to apply noDupErr to this index */ + applyNoDupErr = noDupErr && + (arbiterIndexes == NIL || + list_member_oid(arbiterIndexes, + indexRelation->rd_index->indexrelid)); + /* * The index AM does the actual insertion, plus uniqueness checking. * @@ -373,7 +378,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot, */ if (!indexRelation->rd_index->indisunique) checkUnique = UNIQUE_CHECK_NO; - else if (noDupErr && (arbiterIndexes == NIL || arbiter)) + else if (applyNoDupErr) checkUnique = UNIQUE_CHECK_PARTIAL; else if (indexRelation->rd_index->indimmediate) checkUnique = UNIQUE_CHECK_YES; @@ -407,7 +412,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot, bool violationOK; CEOUC_WAIT_MODE waitMode; - if (noDupErr) + if (applyNoDupErr) { violationOK = true; waitMode = CEOUC_LIVELOCK_PREVENTING_WAIT; diff --git a/src/test/regress/expected/insert_conflict.out b/src/test/regress/expected/insert_conflict.out index 6f4ec867d9..155774ecfa 100644 --- a/src/test/regress/expected/insert_conflict.out +++ b/src/test/regress/expected/insert_conflict.out @@ -704,3 +704,26 @@ insert into dropcol(key, keep1, keep2) values(1, '5', 5) on conflict(key) ; DROP TABLE dropcol; +-- check handling of regular btree constraint along with gist constraint +create table twoconstraints (f1 int unique, f2 box, + exclude using gist(f2 with &&)); +insert into twoconstraints values(1, '((0,0),(1,1))'); +insert into twoconstraints values(1, '((2,2),(3,3))'); -- fail on f1 +ERROR: duplicate key value violates unique constraint "twoconstraints_f1_key" +DETAIL: Key (f1)=(1) already exists. +insert into twoconstraints values(2, '((0,0),(1,2))'); -- fail on f2 +ERROR: conflicting key value violates exclusion constraint "twoconstraints_f2_excl" +DETAIL: Key (f2)=((1,2),(0,0)) conflicts with existing key (f2)=((1,1),(0,0)). +insert into twoconstraints values(2, '((0,0),(1,2))') + on conflict on constraint twoconstraints_f1_key do nothing; -- fail on f2 +ERROR: conflicting key value violates exclusion constraint "twoconstraints_f2_excl" +DETAIL: Key (f2)=((1,2),(0,0)) conflicts with existing key (f2)=((1,1),(0,0)). +insert into twoconstraints values(2, '((0,0),(1,2))') + on conflict on constraint twoconstraints_f2_excl do nothing; -- do nothing +select * from twoconstraints; + f1 | f2 +----+------------- + 1 | (1,1),(0,0) +(1 row) + +drop table twoconstraints; diff --git a/src/test/regress/sql/insert_conflict.sql b/src/test/regress/sql/insert_conflict.sql index 94b1b0aade..190c605562 100644 --- a/src/test/regress/sql/insert_conflict.sql +++ b/src/test/regress/sql/insert_conflict.sql @@ -407,3 +407,17 @@ insert into dropcol(key, keep1, keep2) values(1, '5', 5) on conflict(key) ; DROP TABLE dropcol; + +-- check handling of regular btree constraint along with gist constraint + +create table twoconstraints (f1 int unique, f2 box, + exclude using gist(f2 with &&)); +insert into twoconstraints values(1, '((0,0),(1,1))'); +insert into twoconstraints values(1, '((2,2),(3,3))'); -- fail on f1 +insert into twoconstraints values(2, '((0,0),(1,2))'); -- fail on f2 +insert into twoconstraints values(2, '((0,0),(1,2))') + on conflict on constraint twoconstraints_f1_key do nothing; -- fail on f2 +insert into twoconstraints values(2, '((0,0),(1,2))') + on conflict on constraint twoconstraints_f2_excl do nothing; -- do nothing +select * from twoconstraints; +drop table twoconstraints;