mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-11-21 03:13:05 +08:00
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
This commit is contained in:
parent
c35a3fb5e0
commit
0997e0af27
@ -60,6 +60,7 @@
|
||||
#include "access/heaptoast.h"
|
||||
#include "access/htup_details.h"
|
||||
#include "access/tupdesc_details.h"
|
||||
#include "access/xact.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "funcapi.h"
|
||||
#include "nodes/nodeFuncs.h"
|
||||
@ -148,6 +149,22 @@ tts_virtual_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
|
||||
return 0; /* silence compiler warnings */
|
||||
}
|
||||
|
||||
/*
|
||||
* VirtualTupleTableSlots never have storage tuples. We generally
|
||||
* shouldn't get here, but provide a user-friendly message if we do.
|
||||
*/
|
||||
static bool
|
||||
tts_virtual_is_current_xact_tuple(TupleTableSlot *slot)
|
||||
{
|
||||
Assert(!TTS_EMPTY(slot));
|
||||
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("don't have a storage tuple in this context")));
|
||||
|
||||
return false; /* silence compiler warnings */
|
||||
}
|
||||
|
||||
/*
|
||||
* To materialize a virtual slot all the datums that aren't passed by value
|
||||
* 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)
|
||||
slot->tts_tupleDescriptor, isnull);
|
||||
}
|
||||
|
||||
static bool
|
||||
tts_heap_is_current_xact_tuple(TupleTableSlot *slot)
|
||||
{
|
||||
HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
|
||||
TransactionId xmin;
|
||||
|
||||
Assert(!TTS_EMPTY(slot));
|
||||
|
||||
/*
|
||||
* In some code paths it's possible to get here with a non-materialized
|
||||
* slot, in which case we can't check if tuple is created by the current
|
||||
* transaction.
|
||||
*/
|
||||
if (!hslot->tuple)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("don't have a storage tuple in this context")));
|
||||
|
||||
xmin = HeapTupleHeaderGetRawXmin(hslot->tuple->t_data);
|
||||
|
||||
return TransactionIdIsCurrentTransactionId(xmin);
|
||||
}
|
||||
|
||||
static void
|
||||
tts_heap_materialize(TupleTableSlot *slot)
|
||||
{
|
||||
@ -521,6 +561,18 @@ tts_minimal_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
|
||||
return 0; /* silence compiler warnings */
|
||||
}
|
||||
|
||||
static bool
|
||||
tts_minimal_is_current_xact_tuple(TupleTableSlot *slot)
|
||||
{
|
||||
Assert(!TTS_EMPTY(slot));
|
||||
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("don't have a storage tuple in this context")));
|
||||
|
||||
return false; /* silence compiler warnings */
|
||||
}
|
||||
|
||||
static void
|
||||
tts_minimal_materialize(TupleTableSlot *slot)
|
||||
{
|
||||
@ -714,6 +766,29 @@ tts_buffer_heap_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
|
||||
slot->tts_tupleDescriptor, isnull);
|
||||
}
|
||||
|
||||
static bool
|
||||
tts_buffer_is_current_xact_tuple(TupleTableSlot *slot)
|
||||
{
|
||||
BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
|
||||
TransactionId xmin;
|
||||
|
||||
Assert(!TTS_EMPTY(slot));
|
||||
|
||||
/*
|
||||
* In some code paths it's possible to get here with a non-materialized
|
||||
* slot, in which case we can't check if tuple is created by the current
|
||||
* transaction.
|
||||
*/
|
||||
if (!bslot->base.tuple)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("don't have a storage tuple in this context")));
|
||||
|
||||
xmin = HeapTupleHeaderGetRawXmin(bslot->base.tuple->t_data);
|
||||
|
||||
return TransactionIdIsCurrentTransactionId(xmin);
|
||||
}
|
||||
|
||||
static void
|
||||
tts_buffer_heap_materialize(TupleTableSlot *slot)
|
||||
{
|
||||
@ -1029,6 +1104,7 @@ const TupleTableSlotOps TTSOpsVirtual = {
|
||||
.getsomeattrs = tts_virtual_getsomeattrs,
|
||||
.getsysattr = tts_virtual_getsysattr,
|
||||
.materialize = tts_virtual_materialize,
|
||||
.is_current_xact_tuple = tts_virtual_is_current_xact_tuple,
|
||||
.copyslot = tts_virtual_copyslot,
|
||||
|
||||
/*
|
||||
@ -1048,6 +1124,7 @@ const TupleTableSlotOps TTSOpsHeapTuple = {
|
||||
.clear = tts_heap_clear,
|
||||
.getsomeattrs = tts_heap_getsomeattrs,
|
||||
.getsysattr = tts_heap_getsysattr,
|
||||
.is_current_xact_tuple = tts_heap_is_current_xact_tuple,
|
||||
.materialize = tts_heap_materialize,
|
||||
.copyslot = tts_heap_copyslot,
|
||||
.get_heap_tuple = tts_heap_get_heap_tuple,
|
||||
@ -1065,6 +1142,7 @@ const TupleTableSlotOps TTSOpsMinimalTuple = {
|
||||
.clear = tts_minimal_clear,
|
||||
.getsomeattrs = tts_minimal_getsomeattrs,
|
||||
.getsysattr = tts_minimal_getsysattr,
|
||||
.is_current_xact_tuple = tts_minimal_is_current_xact_tuple,
|
||||
.materialize = tts_minimal_materialize,
|
||||
.copyslot = tts_minimal_copyslot,
|
||||
|
||||
@ -1082,6 +1160,7 @@ const TupleTableSlotOps TTSOpsBufferHeapTuple = {
|
||||
.clear = tts_buffer_heap_clear,
|
||||
.getsomeattrs = tts_buffer_heap_getsomeattrs,
|
||||
.getsysattr = tts_buffer_heap_getsysattr,
|
||||
.is_current_xact_tuple = tts_buffer_is_current_xact_tuple,
|
||||
.materialize = tts_buffer_heap_materialize,
|
||||
.copyslot = tts_buffer_heap_copyslot,
|
||||
.get_heap_tuple = tts_buffer_heap_get_heap_tuple,
|
||||
|
@ -1260,9 +1260,6 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
|
||||
{
|
||||
const RI_ConstraintInfo *riinfo;
|
||||
int ri_nullcheck;
|
||||
Datum xminDatum;
|
||||
TransactionId xmin;
|
||||
bool isnull;
|
||||
|
||||
/*
|
||||
* AfterTriggerSaveEvent() handles things such that this function is never
|
||||
@ -1330,10 +1327,7 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
|
||||
* this if we knew the INSERT trigger already fired, but there is no easy
|
||||
* way to know that.)
|
||||
*/
|
||||
xminDatum = slot_getsysattr(oldslot, MinTransactionIdAttributeNumber, &isnull);
|
||||
Assert(!isnull);
|
||||
xmin = DatumGetTransactionId(xminDatum);
|
||||
if (TransactionIdIsCurrentTransactionId(xmin))
|
||||
if (slot_is_current_xact_tuple(oldslot))
|
||||
return true;
|
||||
|
||||
/* If all old and new key values are equal, no check is needed */
|
||||
|
@ -166,6 +166,12 @@ struct TupleTableSlotOps
|
||||
*/
|
||||
Datum (*getsysattr) (TupleTableSlot *slot, int attnum, bool *isnull);
|
||||
|
||||
/*
|
||||
* Check if the tuple is created by the current transaction. Throws an
|
||||
* error if the slot doesn't contain the storage tuple.
|
||||
*/
|
||||
bool (*is_current_xact_tuple) (TupleTableSlot *slot);
|
||||
|
||||
/*
|
||||
* Make the contents of the slot solely depend on the slot, and not on
|
||||
* underlying resources (like another memory context, buffers, etc).
|
||||
@ -426,6 +432,21 @@ slot_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
|
||||
return slot->tts_ops->getsysattr(slot, attnum, isnull);
|
||||
}
|
||||
|
||||
/*
|
||||
* slot_is_current_xact_tuple - check if the slot's current tuple is created
|
||||
* by the current transaction.
|
||||
*
|
||||
* If the slot does not contain a storage tuple, this will throw an error.
|
||||
* Hence before calling this function, callers should make sure that the
|
||||
* slot type supports storage tuples and that there is currently one inside
|
||||
* the slot.
|
||||
*/
|
||||
static inline bool
|
||||
slot_is_current_xact_tuple(TupleTableSlot *slot)
|
||||
{
|
||||
return slot->tts_ops->is_current_xact_tuple(slot);
|
||||
}
|
||||
|
||||
/*
|
||||
* ExecClearTuple - clear the slot's contents
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user