diff options
Diffstat (limited to 'src/backend/utils/adt/ri_triggers.c')
-rw-r--r-- | src/backend/utils/adt/ri_triggers.c | 187 |
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 e1aa3d0044f..5b4579fe83e 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; |