mirror of
https://github.com/postgres/postgres.git
synced 2025-05-05 09:19:17 +03: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/heaptoast.h"
|
||||||
#include "access/htup_details.h"
|
#include "access/htup_details.h"
|
||||||
#include "access/tupdesc_details.h"
|
#include "access/tupdesc_details.h"
|
||||||
|
#include "access/xact.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "funcapi.h"
|
#include "funcapi.h"
|
||||||
#include "nodes/nodeFuncs.h"
|
#include "nodes/nodeFuncs.h"
|
||||||
@ -148,6 +149,22 @@ tts_virtual_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
|
|||||||
return 0; /* silence compiler warnings */
|
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
|
* 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
|
* 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);
|
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
|
static void
|
||||||
tts_heap_materialize(TupleTableSlot *slot)
|
tts_heap_materialize(TupleTableSlot *slot)
|
||||||
{
|
{
|
||||||
@ -521,6 +561,18 @@ tts_minimal_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
|
|||||||
return 0; /* silence compiler warnings */
|
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
|
static void
|
||||||
tts_minimal_materialize(TupleTableSlot *slot)
|
tts_minimal_materialize(TupleTableSlot *slot)
|
||||||
{
|
{
|
||||||
@ -714,6 +766,29 @@ tts_buffer_heap_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
|
|||||||
slot->tts_tupleDescriptor, 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
|
static void
|
||||||
tts_buffer_heap_materialize(TupleTableSlot *slot)
|
tts_buffer_heap_materialize(TupleTableSlot *slot)
|
||||||
{
|
{
|
||||||
@ -1029,6 +1104,7 @@ const TupleTableSlotOps TTSOpsVirtual = {
|
|||||||
.getsomeattrs = tts_virtual_getsomeattrs,
|
.getsomeattrs = tts_virtual_getsomeattrs,
|
||||||
.getsysattr = tts_virtual_getsysattr,
|
.getsysattr = tts_virtual_getsysattr,
|
||||||
.materialize = tts_virtual_materialize,
|
.materialize = tts_virtual_materialize,
|
||||||
|
.is_current_xact_tuple = tts_virtual_is_current_xact_tuple,
|
||||||
.copyslot = tts_virtual_copyslot,
|
.copyslot = tts_virtual_copyslot,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1048,6 +1124,7 @@ const TupleTableSlotOps TTSOpsHeapTuple = {
|
|||||||
.clear = tts_heap_clear,
|
.clear = tts_heap_clear,
|
||||||
.getsomeattrs = tts_heap_getsomeattrs,
|
.getsomeattrs = tts_heap_getsomeattrs,
|
||||||
.getsysattr = tts_heap_getsysattr,
|
.getsysattr = tts_heap_getsysattr,
|
||||||
|
.is_current_xact_tuple = tts_heap_is_current_xact_tuple,
|
||||||
.materialize = tts_heap_materialize,
|
.materialize = tts_heap_materialize,
|
||||||
.copyslot = tts_heap_copyslot,
|
.copyslot = tts_heap_copyslot,
|
||||||
.get_heap_tuple = tts_heap_get_heap_tuple,
|
.get_heap_tuple = tts_heap_get_heap_tuple,
|
||||||
@ -1065,6 +1142,7 @@ const TupleTableSlotOps TTSOpsMinimalTuple = {
|
|||||||
.clear = tts_minimal_clear,
|
.clear = tts_minimal_clear,
|
||||||
.getsomeattrs = tts_minimal_getsomeattrs,
|
.getsomeattrs = tts_minimal_getsomeattrs,
|
||||||
.getsysattr = tts_minimal_getsysattr,
|
.getsysattr = tts_minimal_getsysattr,
|
||||||
|
.is_current_xact_tuple = tts_minimal_is_current_xact_tuple,
|
||||||
.materialize = tts_minimal_materialize,
|
.materialize = tts_minimal_materialize,
|
||||||
.copyslot = tts_minimal_copyslot,
|
.copyslot = tts_minimal_copyslot,
|
||||||
|
|
||||||
@ -1082,6 +1160,7 @@ const TupleTableSlotOps TTSOpsBufferHeapTuple = {
|
|||||||
.clear = tts_buffer_heap_clear,
|
.clear = tts_buffer_heap_clear,
|
||||||
.getsomeattrs = tts_buffer_heap_getsomeattrs,
|
.getsomeattrs = tts_buffer_heap_getsomeattrs,
|
||||||
.getsysattr = tts_buffer_heap_getsysattr,
|
.getsysattr = tts_buffer_heap_getsysattr,
|
||||||
|
.is_current_xact_tuple = tts_buffer_is_current_xact_tuple,
|
||||||
.materialize = tts_buffer_heap_materialize,
|
.materialize = tts_buffer_heap_materialize,
|
||||||
.copyslot = tts_buffer_heap_copyslot,
|
.copyslot = tts_buffer_heap_copyslot,
|
||||||
.get_heap_tuple = tts_buffer_heap_get_heap_tuple,
|
.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;
|
const RI_ConstraintInfo *riinfo;
|
||||||
int ri_nullcheck;
|
int ri_nullcheck;
|
||||||
Datum xminDatum;
|
|
||||||
TransactionId xmin;
|
|
||||||
bool isnull;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* AfterTriggerSaveEvent() handles things such that this function is never
|
* 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
|
* this if we knew the INSERT trigger already fired, but there is no easy
|
||||||
* way to know that.)
|
* way to know that.)
|
||||||
*/
|
*/
|
||||||
xminDatum = slot_getsysattr(oldslot, MinTransactionIdAttributeNumber, &isnull);
|
if (slot_is_current_xact_tuple(oldslot))
|
||||||
Assert(!isnull);
|
|
||||||
xmin = DatumGetTransactionId(xminDatum);
|
|
||||||
if (TransactionIdIsCurrentTransactionId(xmin))
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
/* If all old and new key values are equal, no check is needed */
|
/* 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);
|
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
|
* Make the contents of the slot solely depend on the slot, and not on
|
||||||
* underlying resources (like another memory context, buffers, etc).
|
* 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);
|
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
|
* ExecClearTuple - clear the slot's contents
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user