mirror of
https://github.com/postgres/postgres.git
synced 2025-04-29 13:56:47 +03:00
Fix failure to handle conflicts in non-arbiter exclusion constraints.
ExecInsertIndexTuples treated an exclusion constraint as subject to noDupErr processing even when it was not listed in arbiterIndexes, and would therefore not error out for a conflict in such a constraint, instead returning it as an arbiter-index failure. That led to an infinite loop in ExecInsert, since ExecCheckIndexConstraints ignored the index as-intended and therefore didn't throw the expected error. To fix, make the exclusion constraint code path use the same condition as the index_insert call does to decide whether no-error-for-duplicates behavior is appropriate. While at it, refactor a little bit to avoid unnecessary list_member_oid calls. (That surely wouldn't save anything worth noticing, but I find the code a bit clearer this way.) Per bug report from Heikki Rauhala. Back-patch to 9.5 where ON CONFLICT was introduced. Report: <4C976D6B-76B4-434C-8052-D009F7B7AEDA@reaktor.fi>
This commit is contained in:
parent
29a2195de6
commit
9c810a2edc
@ -259,6 +259,9 @@ ExecCloseIndices(ResultRelInfo *resultRelInfo)
|
|||||||
* the same is done for non-deferred constraints, but report
|
* the same is done for non-deferred constraints, but report
|
||||||
* if conflict was speculative or deferred conflict to caller)
|
* 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.
|
* CAUTION: this must not be called for a HOT update.
|
||||||
* We can't defend against that here for lack of info.
|
* We can't defend against that here for lack of info.
|
||||||
* Should we change the API to make it safer?
|
* Should we change the API to make it safer?
|
||||||
@ -308,19 +311,15 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
|
|||||||
{
|
{
|
||||||
Relation indexRelation = relationDescs[i];
|
Relation indexRelation = relationDescs[i];
|
||||||
IndexInfo *indexInfo;
|
IndexInfo *indexInfo;
|
||||||
|
bool applyNoDupErr;
|
||||||
IndexUniqueCheck checkUnique;
|
IndexUniqueCheck checkUnique;
|
||||||
bool satisfiesConstraint;
|
bool satisfiesConstraint;
|
||||||
bool arbiter;
|
|
||||||
|
|
||||||
if (indexRelation == NULL)
|
if (indexRelation == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
indexInfo = indexInfoArray[i];
|
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 the index is marked as read-only, ignore it */
|
||||||
if (!indexInfo->ii_ReadyForInserts)
|
if (!indexInfo->ii_ReadyForInserts)
|
||||||
continue;
|
continue;
|
||||||
@ -358,6 +357,12 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
|
|||||||
values,
|
values,
|
||||||
isnull);
|
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.
|
* The index AM does the actual insertion, plus uniqueness checking.
|
||||||
*
|
*
|
||||||
@ -373,7 +378,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
|
|||||||
*/
|
*/
|
||||||
if (!indexRelation->rd_index->indisunique)
|
if (!indexRelation->rd_index->indisunique)
|
||||||
checkUnique = UNIQUE_CHECK_NO;
|
checkUnique = UNIQUE_CHECK_NO;
|
||||||
else if (noDupErr && (arbiterIndexes == NIL || arbiter))
|
else if (applyNoDupErr)
|
||||||
checkUnique = UNIQUE_CHECK_PARTIAL;
|
checkUnique = UNIQUE_CHECK_PARTIAL;
|
||||||
else if (indexRelation->rd_index->indimmediate)
|
else if (indexRelation->rd_index->indimmediate)
|
||||||
checkUnique = UNIQUE_CHECK_YES;
|
checkUnique = UNIQUE_CHECK_YES;
|
||||||
@ -407,7 +412,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
|
|||||||
bool violationOK;
|
bool violationOK;
|
||||||
CEOUC_WAIT_MODE waitMode;
|
CEOUC_WAIT_MODE waitMode;
|
||||||
|
|
||||||
if (noDupErr)
|
if (applyNoDupErr)
|
||||||
{
|
{
|
||||||
violationOK = true;
|
violationOK = true;
|
||||||
waitMode = CEOUC_LIVELOCK_PREVENTING_WAIT;
|
waitMode = CEOUC_LIVELOCK_PREVENTING_WAIT;
|
||||||
|
@ -704,3 +704,26 @@ insert into dropcol(key, keep1, keep2) values(1, '5', 5) on conflict(key)
|
|||||||
|
|
||||||
;
|
;
|
||||||
DROP TABLE dropcol;
|
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;
|
||||||
|
@ -407,3 +407,17 @@ insert into dropcol(key, keep1, keep2) values(1, '5', 5) on conflict(key)
|
|||||||
;
|
;
|
||||||
|
|
||||||
DROP TABLE dropcol;
|
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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user