summaryrefslogtreecommitdiff
path: root/src/backend/commands
diff options
context:
space:
mode:
authorTom Lane2015-05-11 16:25:28 +0000
committerTom Lane2015-05-11 16:25:43 +0000
commit20781765f77c1fb6465aba97d211636ce92e7a0e (patch)
tree0bb028620bb679f030b513f1f0cfcfac3f9e3046 /src/backend/commands
parentb4d4ce1d50bbdf82cd2e2c1c7172b936df01c01d (diff)
Fix incorrect checking of deferred exclusion constraint after a HOT update.
If a row that potentially violates a deferred exclusion constraint is HOT-updated later in the same transaction, the exclusion constraint would be reported as violated when the check finally occurs, even if the row(s) the new row originally conflicted with have since been removed. This happened because the wrong TID was passed to check_exclusion_constraint(), causing the live HOT-updated row to be seen as a conflicting row rather than recognized as the row-under-test. Per bug #13148 from Evan Martin. It's been broken since exclusion constraints were invented, so back-patch to all supported branches.
Diffstat (limited to 'src/backend/commands')
-rw-r--r--src/backend/commands/constraint.c17
1 files changed, 11 insertions, 6 deletions
diff --git a/src/backend/commands/constraint.c b/src/backend/commands/constraint.c
index e49affba9ee..28fccaf381e 100644
--- a/src/backend/commands/constraint.c
+++ b/src/backend/commands/constraint.c
@@ -89,9 +89,10 @@ unique_key_recheck(PG_FUNCTION_ARGS)
* because this trigger gets queued only in response to index insertions;
* which means it does not get queued for HOT updates. The row we are
* called for might now be dead, but have a live HOT child, in which case
- * we still need to make the check. Therefore we have to use
- * heap_hot_search, not just HeapTupleSatisfiesVisibility as is done in
- * the comparable test in RI_FKey_check.
+ * we still need to make the check --- effectively, we're applying the
+ * check against the live child row, although we can use the values from
+ * this row since by definition all columns of interest to us are the
+ * same.
*
* This might look like just an optimization, because the index AM will
* make this identical test before throwing an error. But it's actually
@@ -159,7 +160,9 @@ unique_key_recheck(PG_FUNCTION_ARGS)
{
/*
* Note: this is not a real insert; it is a check that the index entry
- * that has already been inserted is unique.
+ * that has already been inserted is unique. Passing t_self is
+ * correct even if t_self is now dead, because that is the TID the
+ * index will know about.
*/
index_insert(indexRel, values, isnull, &(new_row->t_self),
trigdata->tg_relation, UNIQUE_CHECK_EXISTING);
@@ -168,10 +171,12 @@ unique_key_recheck(PG_FUNCTION_ARGS)
{
/*
* For exclusion constraints we just do the normal check, but now it's
- * okay to throw error.
+ * okay to throw error. In the HOT-update case, we must use the live
+ * HOT child's TID here, else check_exclusion_constraint will think
+ * the child is a conflict.
*/
check_exclusion_constraint(trigdata->tg_relation, indexRel, indexInfo,
- &(new_row->t_self), values, isnull,
+ &tmptid, values, isnull,
estate, false);
}