summaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/ri_triggers.c
diff options
context:
space:
mode:
authorAndres Freund <andres@anarazel.de>2019-02-26 20:30:28 -0800
committerAndres Freund <andres@anarazel.de>2019-02-26 20:31:38 -0800
commitff11e7f4b9ae017585c3ba146db7ba39c31f209a (patch)
tree37828b8d2ab7bda386b276129b456793c7ac908b /src/backend/utils/adt/ri_triggers.c
parentb8d71745eac0a12740a70dc78cbcdedadade37f8 (diff)
downloadpostgresql-ff11e7f4b9ae017585c3ba146db7ba39c31f209a.tar.gz
Use slots in trigger infrastructure, except for the actual invocation.
In preparation for abstracting table storage, convert trigger.c to track tuples in slots. Which also happens to make code calling triggers simpler. As the calling interface for triggers themselves is not changed in this patch, HeapTuples still are extracted from the slot at that time. But that's handled solely inside trigger.c, not visible to callers. It's quite likely that we'll want to revise the external trigger interface, but that's a separate large project. As part of this work the slots used for old/new/return tuples are moved from EState into ResultRelInfo, as different updated tables might need different slots. The slots are now also now created on-demand, which is good both from an efficiency POV, but also makes the modifying code simpler. Author: Andres Freund, Amit Khandekar and Ashutosh Bapat Discussion: https://postgr.es/m/20180703070645.wchpu5muyto5n647@alap3.anarazel.de
Diffstat (limited to 'src/backend/utils/adt/ri_triggers.c')
-rw-r--r--src/backend/utils/adt/ri_triggers.c187
1 files changed, 110 insertions, 77 deletions
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index e1aa3d0044..5b4579fe83 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -192,7 +192,7 @@ static int ri_constraint_cache_valid_count = 0;
* ----------
*/
static bool ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
- HeapTuple old_row,
+ TupleTableSlot *oldslot,
const RI_ConstraintInfo *riinfo);
static Datum ri_restrict(TriggerData *trigdata, bool is_no_action);
static Datum ri_setnull(TriggerData *trigdata);
@@ -205,12 +205,12 @@ static void ri_GenerateQual(StringInfo buf,
Oid opoid,
const char *rightop, Oid rightoptype);
static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
-static int ri_NullCheck(TupleDesc tupdesc, HeapTuple tup,
+static int ri_NullCheck(TupleDesc tupdesc, TupleTableSlot *slot,
const RI_ConstraintInfo *riinfo, bool rel_is_pk);
static void ri_BuildQueryKey(RI_QueryKey *key,
const RI_ConstraintInfo *riinfo,
int32 constr_queryno);
-static bool ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
+static bool ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
const RI_ConstraintInfo *riinfo, bool rel_is_pk);
static bool ri_AttributesEqual(Oid eq_opr, Oid typeid,
Datum oldvalue, Datum newvalue);
@@ -232,14 +232,14 @@ static SPIPlanPtr ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
static bool ri_PerformCheck(const RI_ConstraintInfo *riinfo,
RI_QueryKey *qkey, SPIPlanPtr qplan,
Relation fk_rel, Relation pk_rel,
- HeapTuple old_tuple, HeapTuple new_tuple,
+ TupleTableSlot *oldslot, TupleTableSlot *newslot,
bool detectNewRows, int expect_OK);
-static void ri_ExtractValues(Relation rel, HeapTuple tup,
+static void ri_ExtractValues(Relation rel, TupleTableSlot *slot,
const RI_ConstraintInfo *riinfo, bool rel_is_pk,
Datum *vals, char *nulls);
static void ri_ReportViolation(const RI_ConstraintInfo *riinfo,
Relation pk_rel, Relation fk_rel,
- HeapTuple violator, TupleDesc tupdesc,
+ TupleTableSlot *violator, TupleDesc tupdesc,
int queryno) pg_attribute_noreturn();
@@ -255,8 +255,7 @@ RI_FKey_check(TriggerData *trigdata)
const RI_ConstraintInfo *riinfo;
Relation fk_rel;
Relation pk_rel;
- HeapTuple new_row;
- Buffer new_row_buf;
+ TupleTableSlot *newslot;
RI_QueryKey qkey;
SPIPlanPtr qplan;
int i;
@@ -268,15 +267,9 @@ RI_FKey_check(TriggerData *trigdata)
trigdata->tg_relation, false);
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
- {
- new_row = trigdata->tg_newtuple;
- new_row_buf = trigdata->tg_newtuplebuf;
- }
+ newslot = trigdata->tg_newslot;
else
- {
- new_row = trigdata->tg_trigtuple;
- new_row_buf = trigdata->tg_trigtuplebuf;
- }
+ newslot = trigdata->tg_trigslot;
/*
* We should not even consider checking the row if it is no longer valid,
@@ -285,14 +278,26 @@ RI_FKey_check(TriggerData *trigdata)
* checked). Test its liveness according to SnapshotSelf. We need pin
* and lock on the buffer to call HeapTupleSatisfiesVisibility. Caller
* should be holding pin, but not lock.
+ *
+ * XXX: Note that the buffer-tuple specificity will be removed in the near
+ * future.
*/
- LockBuffer(new_row_buf, BUFFER_LOCK_SHARE);
- if (!HeapTupleSatisfiesVisibility(new_row, SnapshotSelf, new_row_buf))
+ if (TTS_IS_BUFFERTUPLE(newslot))
{
- LockBuffer(new_row_buf, BUFFER_LOCK_UNLOCK);
- return PointerGetDatum(NULL);
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) newslot;
+
+ Assert(BufferIsValid(bslot->buffer));
+
+ LockBuffer(bslot->buffer, BUFFER_LOCK_SHARE);
+ if (!HeapTupleSatisfiesVisibility(bslot->base.tuple, SnapshotSelf, bslot->buffer))
+ {
+ LockBuffer(bslot->buffer, BUFFER_LOCK_UNLOCK);
+ return PointerGetDatum(NULL);
+ }
+ LockBuffer(bslot->buffer, BUFFER_LOCK_UNLOCK);
}
- LockBuffer(new_row_buf, BUFFER_LOCK_UNLOCK);
+ else
+ elog(ERROR, "expected buffer tuple");
/*
* Get the relation descriptors of the FK and PK tables.
@@ -308,7 +313,7 @@ RI_FKey_check(TriggerData *trigdata)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("MATCH PARTIAL not yet implemented")));
- switch (ri_NullCheck(RelationGetDescr(fk_rel), new_row, riinfo, false))
+ switch (ri_NullCheck(RelationGetDescr(fk_rel), newslot, riinfo, false))
{
case RI_KEYS_ALL_NULL:
@@ -438,7 +443,7 @@ RI_FKey_check(TriggerData *trigdata)
*/
ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel,
- NULL, new_row,
+ NULL, newslot,
false,
SPI_OK_SELECT);
@@ -506,7 +511,7 @@ RI_FKey_check_upd(PG_FUNCTION_ARGS)
*/
static bool
ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
- HeapTuple old_row,
+ TupleTableSlot *oldslot,
const RI_ConstraintInfo *riinfo)
{
SPIPlanPtr qplan;
@@ -515,7 +520,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
bool result;
/* Only called for non-null rows */
- Assert(ri_NullCheck(RelationGetDescr(pk_rel), old_row, riinfo, true) == RI_KEYS_NONE_NULL);
+ Assert(ri_NullCheck(RelationGetDescr(pk_rel), oldslot, riinfo, true) == RI_KEYS_NONE_NULL);
if (SPI_connect() != SPI_OK_CONNECT)
elog(ERROR, "SPI_connect failed");
@@ -573,7 +578,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
*/
result = ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel,
- old_row, NULL,
+ oldslot, NULL,
true, /* treat like update */
SPI_OK_SELECT);
@@ -691,7 +696,7 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
const RI_ConstraintInfo *riinfo;
Relation fk_rel;
Relation pk_rel;
- HeapTuple old_row;
+ TupleTableSlot *old_slot;
RI_QueryKey qkey;
SPIPlanPtr qplan;
@@ -709,7 +714,7 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
*/
fk_rel = table_open(riinfo->fk_relid, RowShareLock);
pk_rel = trigdata->tg_relation;
- old_row = trigdata->tg_trigtuple;
+ old_slot = trigdata->tg_trigslot;
switch (riinfo->confmatchtype)
{
@@ -733,7 +738,7 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
* allow another row to be substituted.
*/
if (is_no_action &&
- ri_Check_Pk_Match(pk_rel, fk_rel, old_row, riinfo))
+ ri_Check_Pk_Match(pk_rel, fk_rel, old_slot, riinfo))
{
table_close(fk_rel, RowShareLock);
return PointerGetDatum(NULL);
@@ -801,7 +806,7 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
*/
ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel,
- old_row, NULL,
+ old_slot, NULL,
true, /* must detect new rows */
SPI_OK_SELECT);
@@ -845,7 +850,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
const RI_ConstraintInfo *riinfo;
Relation fk_rel;
Relation pk_rel;
- HeapTuple old_row;
+ TupleTableSlot *old_slot;
RI_QueryKey qkey;
SPIPlanPtr qplan;
int i;
@@ -869,7 +874,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
*/
fk_rel = table_open(riinfo->fk_relid, RowExclusiveLock);
pk_rel = trigdata->tg_relation;
- old_row = trigdata->tg_trigtuple;
+ old_slot = trigdata->tg_trigslot;
switch (riinfo->confmatchtype)
{
@@ -941,7 +946,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
*/
ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel,
- old_row, NULL,
+ old_slot, NULL,
true, /* must detect new rows */
SPI_OK_DELETE);
@@ -985,8 +990,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
const RI_ConstraintInfo *riinfo;
Relation fk_rel;
Relation pk_rel;
- HeapTuple new_row;
- HeapTuple old_row;
+ TupleTableSlot *new_slot;
+ TupleTableSlot *old_slot;
RI_QueryKey qkey;
SPIPlanPtr qplan;
int i;
@@ -1012,8 +1017,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
*/
fk_rel = table_open(riinfo->fk_relid, RowExclusiveLock);
pk_rel = trigdata->tg_relation;
- new_row = trigdata->tg_newtuple;
- old_row = trigdata->tg_trigtuple;
+ new_slot = trigdata->tg_newslot;
+ old_slot = trigdata->tg_trigslot;
switch (riinfo->confmatchtype)
{
@@ -1097,7 +1102,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
*/
ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel,
- old_row, new_row,
+ old_slot, new_slot,
true, /* must detect new rows */
SPI_OK_UPDATE);
@@ -1180,7 +1185,7 @@ ri_setnull(TriggerData *trigdata)
const RI_ConstraintInfo *riinfo;
Relation fk_rel;
Relation pk_rel;
- HeapTuple old_row;
+ TupleTableSlot *old_slot;
RI_QueryKey qkey;
SPIPlanPtr qplan;
int i;
@@ -1199,7 +1204,7 @@ ri_setnull(TriggerData *trigdata)
*/
fk_rel = table_open(riinfo->fk_relid, RowExclusiveLock);
pk_rel = trigdata->tg_relation;
- old_row = trigdata->tg_trigtuple;
+ old_slot = trigdata->tg_trigslot;
switch (riinfo->confmatchtype)
{
@@ -1284,7 +1289,7 @@ ri_setnull(TriggerData *trigdata)
*/
ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel,
- old_row, NULL,
+ old_slot, NULL,
true, /* must detect new rows */
SPI_OK_UPDATE);
@@ -1367,7 +1372,7 @@ ri_setdefault(TriggerData *trigdata)
const RI_ConstraintInfo *riinfo;
Relation fk_rel;
Relation pk_rel;
- HeapTuple old_row;
+ TupleTableSlot *old_slot;
RI_QueryKey qkey;
SPIPlanPtr qplan;
@@ -1385,7 +1390,7 @@ ri_setdefault(TriggerData *trigdata)
*/
fk_rel = table_open(riinfo->fk_relid, RowExclusiveLock);
pk_rel = trigdata->tg_relation;
- old_row = trigdata->tg_trigtuple;
+ old_slot = trigdata->tg_trigslot;
switch (riinfo->confmatchtype)
{
@@ -1471,7 +1476,7 @@ ri_setdefault(TriggerData *trigdata)
*/
ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel,
- old_row, NULL,
+ old_slot, NULL,
true, /* must detect new rows */
SPI_OK_UPDATE);
@@ -1530,7 +1535,7 @@ ri_setdefault(TriggerData *trigdata)
*/
bool
RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
- HeapTuple old_row, HeapTuple new_row)
+ TupleTableSlot *old_slot, TupleTableSlot *new_slot)
{
const RI_ConstraintInfo *riinfo;
@@ -1548,11 +1553,11 @@ RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
* If any old key value is NULL, the row could not have been
* referenced by an FK row, so no check is needed.
*/
- if (ri_NullCheck(RelationGetDescr(pk_rel), old_row, riinfo, true) != RI_KEYS_NONE_NULL)
+ if (ri_NullCheck(RelationGetDescr(pk_rel), old_slot, riinfo, true) != RI_KEYS_NONE_NULL)
return false;
/* If all old and new key values are equal, no check is needed */
- if (new_row && ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
+ if (new_slot && ri_KeysEqual(pk_rel, old_slot, new_slot, riinfo, true))
return false;
/* Else we need to fire the trigger. */
@@ -1587,9 +1592,12 @@ RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
*/
bool
RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
- HeapTuple old_row, HeapTuple new_row)
+ TupleTableSlot *old_slot, TupleTableSlot *new_slot)
{
const RI_ConstraintInfo *riinfo;
+ Datum xminDatum;
+ TransactionId xmin;
+ bool isnull;
/*
* Get arguments.
@@ -1604,7 +1612,7 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
* If any new key value is NULL, the row must satisfy the
* constraint, so no check is needed.
*/
- if (ri_NullCheck(RelationGetDescr(fk_rel), new_row, riinfo, false) != RI_KEYS_NONE_NULL)
+ if (ri_NullCheck(RelationGetDescr(fk_rel), new_slot, riinfo, false) != RI_KEYS_NONE_NULL)
return false;
/*
@@ -1615,11 +1623,14 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
* UPDATE check. (We could skip this if we knew the INSERT
* trigger already fired, but there is no easy way to know that.)
*/
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data)))
+ xminDatum = slot_getsysattr(old_slot, MinTransactionIdAttributeNumber, &isnull);
+ Assert(!isnull);
+ xmin = DatumGetTransactionId(xminDatum);
+ if (TransactionIdIsCurrentTransactionId(xmin))
return true;
/* If all old and new key values are equal, no check is needed */
- if (ri_KeysEqual(fk_rel, old_row, new_row, riinfo, false))
+ if (ri_KeysEqual(fk_rel, old_slot, new_slot, riinfo, false))
return false;
/* Else we need to fire the trigger. */
@@ -1635,7 +1646,7 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
* invalidated before the constraint is to be checked, but we
* should queue the event to apply the check later.
*/
- switch (ri_NullCheck(RelationGetDescr(fk_rel), new_row, riinfo, false))
+ switch (ri_NullCheck(RelationGetDescr(fk_rel), new_slot, riinfo, false))
{
case RI_KEYS_ALL_NULL:
return false;
@@ -1653,11 +1664,14 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
* UPDATE check. (We could skip this if we knew the INSERT
* trigger already fired, but there is no easy way to know that.)
*/
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data)))
+ xminDatum = slot_getsysattr(old_slot, MinTransactionIdAttributeNumber, &isnull);
+ Assert(!isnull);
+ xmin = DatumGetTransactionId(xminDatum);
+ if (TransactionIdIsCurrentTransactionId(xmin))
return true;
/* If all old and new key values are equal, no check is needed */
- if (ri_KeysEqual(fk_rel, old_row, new_row, riinfo, false))
+ if (ri_KeysEqual(fk_rel, old_slot, new_slot, riinfo, false))
return false;
/* Else we need to fire the trigger. */
@@ -1911,10 +1925,17 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
/* Did we find a tuple violating the constraint? */
if (SPI_processed > 0)
{
+ TupleTableSlot *slot;
HeapTuple tuple = SPI_tuptable->vals[0];
TupleDesc tupdesc = SPI_tuptable->tupdesc;
RI_ConstraintInfo fake_riinfo;
+ slot = MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual);
+
+ heap_deform_tuple(tuple, tupdesc,
+ slot->tts_values, slot->tts_isnull);
+ ExecStoreVirtualTuple(slot);
+
/*
* The columns to look at in the result tuple are 1..N, not whatever
* they are in the fk_rel. Hack up riinfo so that the subroutines
@@ -1934,7 +1955,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
* disallows partially-null FK rows.
*/
if (fake_riinfo.confmatchtype == FKCONSTR_MATCH_FULL &&
- ri_NullCheck(tupdesc, tuple, &fake_riinfo, false) != RI_KEYS_NONE_NULL)
+ ri_NullCheck(tupdesc, slot, &fake_riinfo, false) != RI_KEYS_NONE_NULL)
ereport(ERROR,
(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
@@ -1951,8 +1972,10 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
*/
ri_ReportViolation(&fake_riinfo,
pk_rel, fk_rel,
- tuple, tupdesc,
+ slot, tupdesc,
RI_PLAN_CHECK_LOOKUPPK);
+
+ ExecDropSingleTupleTableSlot(slot);
}
if (SPI_finish() != SPI_OK_FINISH)
@@ -2355,7 +2378,7 @@ static bool
ri_PerformCheck(const RI_ConstraintInfo *riinfo,
RI_QueryKey *qkey, SPIPlanPtr qplan,
Relation fk_rel, Relation pk_rel,
- HeapTuple old_tuple, HeapTuple new_tuple,
+ TupleTableSlot *old_slot, TupleTableSlot *new_slot,
bool detectNewRows, int expect_OK)
{
Relation query_rel,
@@ -2398,17 +2421,17 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
}
/* Extract the parameters to be passed into the query */
- if (new_tuple)
+ if (new_slot)
{
- ri_ExtractValues(source_rel, new_tuple, riinfo, source_is_pk,
+ ri_ExtractValues(source_rel, new_slot, riinfo, source_is_pk,
vals, nulls);
- if (old_tuple)
- ri_ExtractValues(source_rel, old_tuple, riinfo, source_is_pk,
+ if (old_slot)
+ ri_ExtractValues(source_rel, old_slot, riinfo, source_is_pk,
vals + riinfo->nkeys, nulls + riinfo->nkeys);
}
else
{
- ri_ExtractValues(source_rel, old_tuple, riinfo, source_is_pk,
+ ri_ExtractValues(source_rel, old_slot, riinfo, source_is_pk,
vals, nulls);
}
@@ -2478,7 +2501,7 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
(SPI_processed == 0) == (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK))
ri_ReportViolation(riinfo,
pk_rel, fk_rel,
- new_tuple ? new_tuple : old_tuple,
+ new_slot ? new_slot : old_slot,
NULL,
qkey->constr_queryno);
@@ -2489,11 +2512,10 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
* Extract fields from a tuple into Datum/nulls arrays
*/
static void
-ri_ExtractValues(Relation rel, HeapTuple tup,
+ri_ExtractValues(Relation rel, TupleTableSlot *slot,
const RI_ConstraintInfo *riinfo, bool rel_is_pk,
Datum *vals, char *nulls)
{
- TupleDesc tupdesc = rel->rd_att;
const int16 *attnums;
int i;
bool isnull;
@@ -2505,8 +2527,7 @@ ri_ExtractValues(Relation rel, HeapTuple tup,
for (i = 0; i < riinfo->nkeys; i++)
{
- vals[i] = heap_getattr(tup, attnums[i], tupdesc,
- &isnull);
+ vals[i] = slot_getattr(slot, attnums[i], &isnull);
nulls[i] = isnull ? 'n' : ' ';
}
}
@@ -2523,7 +2544,7 @@ ri_ExtractValues(Relation rel, HeapTuple tup,
static void
ri_ReportViolation(const RI_ConstraintInfo *riinfo,
Relation pk_rel, Relation fk_rel,
- HeapTuple violator, TupleDesc tupdesc,
+ TupleTableSlot *violatorslot, TupleDesc tupdesc,
int queryno)
{
StringInfoData key_names;
@@ -2598,12 +2619,24 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
for (idx = 0; idx < riinfo->nkeys; idx++)
{
int fnum = attnums[idx];
+ Form_pg_attribute att = TupleDescAttr(tupdesc, fnum - 1);
char *name,
*val;
+ Datum datum;
+ bool isnull;
- name = SPI_fname(tupdesc, fnum);
- val = SPI_getvalue(violator, tupdesc, fnum);
- if (!val)
+ name = NameStr(att->attname);
+
+ datum = slot_getattr(violatorslot, fnum, &isnull);
+ if (!isnull)
+ {
+ Oid foutoid;
+ bool typisvarlena;
+
+ getTypeOutputInfo(att->atttypid, &foutoid, &typisvarlena);
+ val = OidOutputFunctionCall(foutoid, datum);
+ }
+ else
val = "null";
if (idx > 0)
@@ -2656,7 +2689,7 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
*/
static int
ri_NullCheck(TupleDesc tupDesc,
- HeapTuple tup,
+ TupleTableSlot *slot,
const RI_ConstraintInfo *riinfo, bool rel_is_pk)
{
const int16 *attnums;
@@ -2671,7 +2704,7 @@ ri_NullCheck(TupleDesc tupDesc,
for (i = 0; i < riinfo->nkeys; i++)
{
- if (heap_attisnull(tup, attnums[i], tupDesc))
+ if (slot_attisnull(slot, attnums[i]))
nonenull = false;
else
allnull = false;
@@ -2822,10 +2855,9 @@ ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan)
* ----------
*/
static bool
-ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
+ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
const RI_ConstraintInfo *riinfo, bool rel_is_pk)
{
- TupleDesc tupdesc = RelationGetDescr(rel);
const int16 *attnums;
const Oid *eq_oprs;
int i;
@@ -2841,6 +2873,7 @@ ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
eq_oprs = riinfo->ff_eq_oprs;
}
+ /* XXX: could be worthwhile to fetch all necessary attrs at once */
for (i = 0; i < riinfo->nkeys; i++)
{
Datum oldvalue;
@@ -2850,14 +2883,14 @@ ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
/*
* Get one attribute's oldvalue. If it is NULL - they're not equal.
*/
- oldvalue = heap_getattr(oldtup, attnums[i], tupdesc, &isnull);
+ oldvalue = slot_getattr(oldslot, attnums[i], &isnull);
if (isnull)
return false;
/*
* Get one attribute's newvalue. If it is NULL - they're not equal.
*/
- newvalue = heap_getattr(newtup, attnums[i], tupdesc, &isnull);
+ newvalue = slot_getattr(newslot, attnums[i], &isnull);
if (isnull)
return false;