Skip to content

Commit 0997e0a

Browse files
committed
Add TupleTableSlotOps.is_current_xact_tuple() method
This allows us to abstract how/whether table AM uses transaction identifiers. A custom table AM can use a custom slot, which may not store xmin directly, but determine the tuple belonging to the current transaction in the other way. Discussion: https://postgr.es/m/CAPpHfdurb9ycV8udYqM%3Do0sPS66PJ4RCBM1g-bBpvzUfogY0EA%40mail.gmail.com Reviewed-by: Matthias van de Meent, Mark Dilger, Pavel Borisov Reviewed-by: Nikita Malakhov, Japin Li
1 parent c35a3fb commit 0997e0a

File tree

3 files changed

+101
-7
lines changed

3 files changed

+101
-7
lines changed

src/backend/executor/execTuples.c

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
#include "access/heaptoast.h"
6161
#include "access/htup_details.h"
6262
#include "access/tupdesc_details.h"
63+
#include "access/xact.h"
6364
#include "catalog/pg_type.h"
6465
#include "funcapi.h"
6566
#include "nodes/nodeFuncs.h"
@@ -148,6 +149,22 @@ tts_virtual_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
148149
return 0; /* silence compiler warnings */
149150
}
150151

152+
/*
153+
* VirtualTupleTableSlots never have storage tuples. We generally
154+
* shouldn't get here, but provide a user-friendly message if we do.
155+
*/
156+
static bool
157+
tts_virtual_is_current_xact_tuple(TupleTableSlot *slot)
158+
{
159+
Assert(!TTS_EMPTY(slot));
160+
161+
ereport(ERROR,
162+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
163+
errmsg("don't have a storage tuple in this context")));
164+
165+
return false; /* silence compiler warnings */
166+
}
167+
151168
/*
152169
* To materialize a virtual slot all the datums that aren't passed by value
153170
* have to be copied into the slot's memory context. To do so, compute the
@@ -354,6 +371,29 @@ tts_heap_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
354371
slot->tts_tupleDescriptor, isnull);
355372
}
356373

374+
static bool
375+
tts_heap_is_current_xact_tuple(TupleTableSlot *slot)
376+
{
377+
HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
378+
TransactionId xmin;
379+
380+
Assert(!TTS_EMPTY(slot));
381+
382+
/*
383+
* In some code paths it's possible to get here with a non-materialized
384+
* slot, in which case we can't check if tuple is created by the current
385+
* transaction.
386+
*/
387+
if (!hslot->tuple)
388+
ereport(ERROR,
389+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
390+
errmsg("don't have a storage tuple in this context")));
391+
392+
xmin = HeapTupleHeaderGetRawXmin(hslot->tuple->t_data);
393+
394+
return TransactionIdIsCurrentTransactionId(xmin);
395+
}
396+
357397
static void
358398
tts_heap_materialize(TupleTableSlot *slot)
359399
{
@@ -521,6 +561,18 @@ tts_minimal_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
521561
return 0; /* silence compiler warnings */
522562
}
523563

564+
static bool
565+
tts_minimal_is_current_xact_tuple(TupleTableSlot *slot)
566+
{
567+
Assert(!TTS_EMPTY(slot));
568+
569+
ereport(ERROR,
570+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
571+
errmsg("don't have a storage tuple in this context")));
572+
573+
return false; /* silence compiler warnings */
574+
}
575+
524576
static void
525577
tts_minimal_materialize(TupleTableSlot *slot)
526578
{
@@ -714,6 +766,29 @@ tts_buffer_heap_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
714766
slot->tts_tupleDescriptor, isnull);
715767
}
716768

769+
static bool
770+
tts_buffer_is_current_xact_tuple(TupleTableSlot *slot)
771+
{
772+
BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
773+
TransactionId xmin;
774+
775+
Assert(!TTS_EMPTY(slot));
776+
777+
/*
778+
* In some code paths it's possible to get here with a non-materialized
779+
* slot, in which case we can't check if tuple is created by the current
780+
* transaction.
781+
*/
782+
if (!bslot->base.tuple)
783+
ereport(ERROR,
784+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
785+
errmsg("don't have a storage tuple in this context")));
786+
787+
xmin = HeapTupleHeaderGetRawXmin(bslot->base.tuple->t_data);
788+
789+
return TransactionIdIsCurrentTransactionId(xmin);
790+
}
791+
717792
static void
718793
tts_buffer_heap_materialize(TupleTableSlot *slot)
719794
{
@@ -1029,6 +1104,7 @@ const TupleTableSlotOps TTSOpsVirtual = {
10291104
.getsomeattrs = tts_virtual_getsomeattrs,
10301105
.getsysattr = tts_virtual_getsysattr,
10311106
.materialize = tts_virtual_materialize,
1107+
.is_current_xact_tuple = tts_virtual_is_current_xact_tuple,
10321108
.copyslot = tts_virtual_copyslot,
10331109

10341110
/*
@@ -1048,6 +1124,7 @@ const TupleTableSlotOps TTSOpsHeapTuple = {
10481124
.clear = tts_heap_clear,
10491125
.getsomeattrs = tts_heap_getsomeattrs,
10501126
.getsysattr = tts_heap_getsysattr,
1127+
.is_current_xact_tuple = tts_heap_is_current_xact_tuple,
10511128
.materialize = tts_heap_materialize,
10521129
.copyslot = tts_heap_copyslot,
10531130
.get_heap_tuple = tts_heap_get_heap_tuple,
@@ -1065,6 +1142,7 @@ const TupleTableSlotOps TTSOpsMinimalTuple = {
10651142
.clear = tts_minimal_clear,
10661143
.getsomeattrs = tts_minimal_getsomeattrs,
10671144
.getsysattr = tts_minimal_getsysattr,
1145+
.is_current_xact_tuple = tts_minimal_is_current_xact_tuple,
10681146
.materialize = tts_minimal_materialize,
10691147
.copyslot = tts_minimal_copyslot,
10701148

@@ -1082,6 +1160,7 @@ const TupleTableSlotOps TTSOpsBufferHeapTuple = {
10821160
.clear = tts_buffer_heap_clear,
10831161
.getsomeattrs = tts_buffer_heap_getsomeattrs,
10841162
.getsysattr = tts_buffer_heap_getsysattr,
1163+
.is_current_xact_tuple = tts_buffer_is_current_xact_tuple,
10851164
.materialize = tts_buffer_heap_materialize,
10861165
.copyslot = tts_buffer_heap_copyslot,
10871166
.get_heap_tuple = tts_buffer_heap_get_heap_tuple,

src/backend/utils/adt/ri_triggers.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,9 +1260,6 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
12601260
{
12611261
const RI_ConstraintInfo *riinfo;
12621262
int ri_nullcheck;
1263-
Datum xminDatum;
1264-
TransactionId xmin;
1265-
bool isnull;
12661263

12671264
/*
12681265
* AfterTriggerSaveEvent() handles things such that this function is never
@@ -1330,10 +1327,7 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
13301327
* this if we knew the INSERT trigger already fired, but there is no easy
13311328
* way to know that.)
13321329
*/
1333-
xminDatum = slot_getsysattr(oldslot, MinTransactionIdAttributeNumber, &isnull);
1334-
Assert(!isnull);
1335-
xmin = DatumGetTransactionId(xminDatum);
1336-
if (TransactionIdIsCurrentTransactionId(xmin))
1330+
if (slot_is_current_xact_tuple(oldslot))
13371331
return true;
13381332

13391333
/* If all old and new key values are equal, no check is needed */

src/include/executor/tuptable.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,12 @@ struct TupleTableSlotOps
166166
*/
167167
Datum (*getsysattr) (TupleTableSlot *slot, int attnum, bool *isnull);
168168

169+
/*
170+
* Check if the tuple is created by the current transaction. Throws an
171+
* error if the slot doesn't contain the storage tuple.
172+
*/
173+
bool (*is_current_xact_tuple) (TupleTableSlot *slot);
174+
169175
/*
170176
* Make the contents of the slot solely depend on the slot, and not on
171177
* underlying resources (like another memory context, buffers, etc).
@@ -426,6 +432,21 @@ slot_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
426432
return slot->tts_ops->getsysattr(slot, attnum, isnull);
427433
}
428434

435+
/*
436+
* slot_is_current_xact_tuple - check if the slot's current tuple is created
437+
* by the current transaction.
438+
*
439+
* If the slot does not contain a storage tuple, this will throw an error.
440+
* Hence before calling this function, callers should make sure that the
441+
* slot type supports storage tuples and that there is currently one inside
442+
* the slot.
443+
*/
444+
static inline bool
445+
slot_is_current_xact_tuple(TupleTableSlot *slot)
446+
{
447+
return slot->tts_ops->is_current_xact_tuple(slot);
448+
}
449+
429450
/*
430451
* ExecClearTuple - clear the slot's contents
431452
*/

0 commit comments

Comments
 (0)