* 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
{
/*
* 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);
{
/*
* 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, false);
}
CREATE TABLE deferred_excl (
f1 int,
+ f2 int,
CONSTRAINT deferred_excl_con EXCLUDE (f1 WITH =) INITIALLY DEFERRED
);
INSERT INTO deferred_excl VALUES(3); -- no fail here
COMMIT; -- should fail here
+-- bug #13148: deferred constraint versus HOT update
+BEGIN;
+INSERT INTO deferred_excl VALUES(2, 1); -- no fail here
+DELETE FROM deferred_excl WHERE f1 = 2 AND f2 IS NULL; -- remove old row
+UPDATE deferred_excl SET f2 = 2 WHERE f1 = 2;
+COMMIT; -- should not fail
+
+SELECT * FROM deferred_excl;
+
ALTER TABLE deferred_excl DROP CONSTRAINT deferred_excl_con;
-- This should fail, but worth testing because of HOT updates
-- Check deferred exclusion constraint
CREATE TABLE deferred_excl (
f1 int,
+ f2 int,
CONSTRAINT deferred_excl_con EXCLUDE (f1 WITH =) INITIALLY DEFERRED
);
NOTICE: CREATE TABLE / EXCLUDE will create implicit index "deferred_excl_con" for table "deferred_excl"
COMMIT; -- should fail here
ERROR: conflicting key value violates exclusion constraint "deferred_excl_con"
DETAIL: Key (f1)=(3) conflicts with existing key (f1)=(3).
+-- bug #13148: deferred constraint versus HOT update
+BEGIN;
+INSERT INTO deferred_excl VALUES(2, 1); -- no fail here
+DELETE FROM deferred_excl WHERE f1 = 2 AND f2 IS NULL; -- remove old row
+UPDATE deferred_excl SET f2 = 2 WHERE f1 = 2;
+COMMIT; -- should not fail
+SELECT * FROM deferred_excl;
+ f1 | f2
+----+----
+ 1 |
+ 2 | 2
+(2 rows)
+
ALTER TABLE deferred_excl DROP CONSTRAINT deferred_excl_con;
-- This should fail, but worth testing because of HOT updates
UPDATE deferred_excl SET f1 = 3;