aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/constraint.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands/constraint.c')
-rw-r--r--src/backend/commands/constraint.c68
1 files changed, 37 insertions, 31 deletions
diff --git a/src/backend/commands/constraint.c b/src/backend/commands/constraint.c
index f9ada29af84..cd04e4ea81b 100644
--- a/src/backend/commands/constraint.c
+++ b/src/backend/commands/constraint.c
@@ -15,6 +15,7 @@
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/tableam.h"
#include "catalog/index.h"
#include "commands/trigger.h"
#include "executor/executor.h"
@@ -41,7 +42,7 @@ unique_key_recheck(PG_FUNCTION_ARGS)
{
TriggerData *trigdata = castNode(TriggerData, fcinfo->context);
const char *funcname = "unique_key_recheck";
- HeapTuple new_row;
+ ItemPointerData checktid;
ItemPointerData tmptid;
Relation indexRel;
IndexInfo *indexInfo;
@@ -73,28 +74,30 @@ unique_key_recheck(PG_FUNCTION_ARGS)
* Get the new data that was inserted/updated.
*/
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
- new_row = trigdata->tg_trigtuple;
+ checktid = trigdata->tg_trigslot->tts_tid;
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
- new_row = trigdata->tg_newtuple;
+ checktid = trigdata->tg_newslot->tts_tid;
else
{
ereport(ERROR,
(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
errmsg("function \"%s\" must be fired for INSERT or UPDATE",
funcname)));
- new_row = NULL; /* keep compiler quiet */
+ ItemPointerSetInvalid(&checktid); /* keep compiler quiet */
}
+ slot = table_slot_create(trigdata->tg_relation, NULL);
+
/*
- * If the new_row is now dead (ie, inserted and then deleted within our
- * transaction), we can skip the check. However, we have to be careful,
- * 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 --- 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.
+ * If the row pointed at by checktid is now dead (ie, inserted and then
+ * deleted within our transaction), we can skip the check. However, we
+ * have to be careful, because this trigger gets queued only in response
+ * to index insertions; which means it does not get queued e.g. 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 ---
+ * 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
@@ -103,13 +106,23 @@ unique_key_recheck(PG_FUNCTION_ARGS)
* it's possible the index entry has also been marked dead, and even
* removed.
*/
- tmptid = new_row->t_self;
- if (!heap_hot_search(&tmptid, trigdata->tg_relation, SnapshotSelf, NULL))
+ tmptid = checktid;
{
- /*
- * All rows in the HOT chain are dead, so skip the check.
- */
- return PointerGetDatum(NULL);
+ IndexFetchTableData *scan = table_index_fetch_begin(trigdata->tg_relation);
+ bool call_again = false;
+
+ if (!table_index_fetch_tuple(scan, &tmptid, SnapshotSelf, slot,
+ &call_again, NULL))
+ {
+ /*
+ * All rows referenced by the index entry are dead, so skip the
+ * check.
+ */
+ ExecDropSingleTupleTableSlot(slot);
+ table_index_fetch_end(scan);
+ return PointerGetDatum(NULL);
+ }
+ table_index_fetch_end(scan);
}
/*
@@ -122,14 +135,6 @@ unique_key_recheck(PG_FUNCTION_ARGS)
indexInfo = BuildIndexInfo(indexRel);
/*
- * The heap tuple must be put into a slot for FormIndexDatum.
- */
- slot = MakeSingleTupleTableSlot(RelationGetDescr(trigdata->tg_relation),
- &TTSOpsHeapTuple);
-
- ExecStoreHeapTuple(new_row, slot, false);
-
- /*
* Typically the index won't have expressions, but if it does we need an
* EState to evaluate them. We need it for exclusion constraints too,
* even if they are just on simple columns.
@@ -163,11 +168,12 @@ 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. Passing t_self is
- * correct even if t_self is now dead, because that is the TID the
- * index will know about.
+ * that has already been inserted is unique. Passing the tuple's tid
+ * (i.e. unmodified by table_index_fetch_tuple()) is correct even if
+ * the row is now dead, because that is the TID the index will know
+ * about.
*/
- index_insert(indexRel, values, isnull, &(new_row->t_self),
+ index_insert(indexRel, values, isnull, &checktid,
trigdata->tg_relation, UNIQUE_CHECK_EXISTING,
indexInfo);
}