From 441b8930f0467e50a928329a6b9889c487408e0f Mon Sep 17 00:00:00 2001 From: Hari Babu Date: Wed, 30 Aug 2017 17:24:35 +1000 Subject: [PATCH 5/8] slot hooks are added to storage AM The tuple is removed as part of the slot and added an void pointer to store the tuple data that can understand only by the storage AM routine. The slot utility functions are reorganized to use two storageAM routines to satify the current functionality. Currently the slot supports minimum tuple also. --- src/backend/access/common/heaptuple.c | 302 +---------------------- src/backend/access/heap/heapam_common.c | 404 +++++++++++++++++++++++++++++++ src/backend/access/heap/heapam_storage.c | 1 + src/backend/commands/copy.c | 2 +- src/backend/commands/createas.c | 2 +- src/backend/commands/matview.c | 2 +- src/backend/commands/trigger.c | 15 +- src/backend/executor/execExprInterp.c | 26 +- src/backend/executor/execReplication.c | 92 ++----- src/backend/executor/execTuples.c | 269 +++++++++----------- src/backend/executor/nodeForeignscan.c | 2 +- src/backend/executor/nodeModifyTable.c | 24 +- src/backend/executor/tqueue.c | 2 +- src/backend/replication/logical/worker.c | 5 +- src/include/access/heapam_common.h | 2 + src/include/access/htup_details.h | 15 +- src/include/executor/tuptable.h | 54 +++-- 17 files changed, 644 insertions(+), 575 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index 13ee528e26..5ed0f15ac4 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -57,6 +57,7 @@ #include "postgres.h" +#include "access/storageamapi.h" #include "access/sysattr.h" #include "access/tuptoaster.h" #include "executor/tuptable.h" @@ -1021,111 +1022,6 @@ heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, } } -/* - * slot_deform_tuple - * Given a TupleTableSlot, extract data from the slot's physical tuple - * into its Datum/isnull arrays. Data is extracted up through the - * natts'th column (caller must ensure this is a legal column number). - * - * This is essentially an incremental version of heap_deform_tuple: - * on each call we extract attributes up to the one needed, without - * re-computing information about previously extracted attributes. - * slot->tts_nvalid is the number of attributes already extracted. - */ -static void -slot_deform_tuple(TupleTableSlot *slot, int natts) -{ - HeapTuple tuple = slot->tts_tuple; - TupleDesc tupleDesc = slot->tts_tupleDescriptor; - Datum *values = slot->tts_values; - bool *isnull = slot->tts_isnull; - HeapTupleHeader tup = tuple->t_data; - bool hasnulls = HeapTupleHasNulls(tuple); - int attnum; - char *tp; /* ptr to tuple data */ - long off; /* offset in tuple data */ - bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */ - bool slow; /* can we use/set attcacheoff? */ - - /* - * Check whether the first call for this tuple, and initialize or restore - * loop state. - */ - attnum = slot->tts_nvalid; - if (attnum == 0) - { - /* Start from the first attribute */ - off = 0; - slow = false; - } - else - { - /* Restore state from previous execution */ - off = slot->tts_off; - slow = slot->tts_slow; - } - - tp = (char *) tup + tup->t_hoff; - - for (; attnum < natts; attnum++) - { - Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum); - - if (hasnulls && att_isnull(attnum, bp)) - { - values[attnum] = (Datum) 0; - isnull[attnum] = true; - slow = true; /* can't use attcacheoff anymore */ - continue; - } - - isnull[attnum] = false; - - if (!slow && thisatt->attcacheoff >= 0) - off = thisatt->attcacheoff; - else if (thisatt->attlen == -1) - { - /* - * We can only cache the offset for a varlena attribute if the - * offset is already suitably aligned, so that there would be no - * pad bytes in any case: then the offset will be valid for either - * an aligned or unaligned value. - */ - if (!slow && - off == att_align_nominal(off, thisatt->attalign)) - thisatt->attcacheoff = off; - else - { - off = att_align_pointer(off, thisatt->attalign, -1, - tp + off); - slow = true; - } - } - else - { - /* not varlena, so safe to use att_align_nominal */ - off = att_align_nominal(off, thisatt->attalign); - - if (!slow) - thisatt->attcacheoff = off; - } - - values[attnum] = fetchatt(thisatt, tp + off); - - off = att_addlength_pointer(off, thisatt->attlen, tp + off); - - if (thisatt->attlen <= 0) - slow = true; /* can't use attcacheoff anymore */ - } - - /* - * Save state for next execution - */ - slot->tts_nvalid = attnum; - slot->tts_off = off; - slot->tts_slow = slow; -} - /* * slot_getattr * This function fetches an attribute of the slot's current tuple. @@ -1141,91 +1037,7 @@ slot_deform_tuple(TupleTableSlot *slot, int natts) Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull) { - HeapTuple tuple = slot->tts_tuple; - TupleDesc tupleDesc = slot->tts_tupleDescriptor; - HeapTupleHeader tup; - - /* - * system attributes are handled by heap_getsysattr - */ - if (attnum <= 0) - { - if (tuple == NULL) /* internal error */ - elog(ERROR, "cannot extract system attribute from virtual tuple"); - if (tuple == &(slot->tts_minhdr)) /* internal error */ - elog(ERROR, "cannot extract system attribute from minimal tuple"); - return heap_getsysattr(tuple, attnum, tupleDesc, isnull); - } - - /* - * fast path if desired attribute already cached - */ - if (attnum <= slot->tts_nvalid) - { - *isnull = slot->tts_isnull[attnum - 1]; - return slot->tts_values[attnum - 1]; - } - - /* - * return NULL if attnum is out of range according to the tupdesc - */ - if (attnum > tupleDesc->natts) - { - *isnull = true; - return (Datum) 0; - } - - /* - * otherwise we had better have a physical tuple (tts_nvalid should equal - * natts in all virtual-tuple cases) - */ - if (tuple == NULL) /* internal error */ - elog(ERROR, "cannot extract attribute from empty tuple slot"); - - /* - * return NULL if attnum is out of range according to the tuple - * - * (We have to check this separately because of various inheritance and - * table-alteration scenarios: the tuple could be either longer or shorter - * than the tupdesc.) - */ - tup = tuple->t_data; - if (attnum > HeapTupleHeaderGetNatts(tup)) - { - *isnull = true; - return (Datum) 0; - } - - /* - * check if target attribute is null: no point in groveling through tuple - */ - if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits)) - { - *isnull = true; - return (Datum) 0; - } - - /* - * If the attribute's column has been dropped, we force a NULL result. - * This case should not happen in normal use, but it could happen if we - * are executing a plan cached before the column was dropped. - */ - if (TupleDescAttr(tupleDesc, attnum - 1)->attisdropped) - { - *isnull = true; - return (Datum) 0; - } - - /* - * Extract the attribute, along with any preceding attributes. - */ - slot_deform_tuple(slot, attnum); - - /* - * The result is acquired from tts_values array. - */ - *isnull = slot->tts_isnull[attnum - 1]; - return slot->tts_values[attnum - 1]; + return slot->tts_storageslotam->slot_getattr(slot, attnum, isnull); } /* @@ -1237,40 +1049,7 @@ slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull) void slot_getallattrs(TupleTableSlot *slot) { - int tdesc_natts = slot->tts_tupleDescriptor->natts; - int attnum; - HeapTuple tuple; - - /* Quick out if we have 'em all already */ - if (slot->tts_nvalid == tdesc_natts) - return; - - /* - * otherwise we had better have a physical tuple (tts_nvalid should equal - * natts in all virtual-tuple cases) - */ - tuple = slot->tts_tuple; - if (tuple == NULL) /* internal error */ - elog(ERROR, "cannot extract attribute from empty tuple slot"); - - /* - * load up any slots available from physical tuple - */ - attnum = HeapTupleHeaderGetNatts(tuple->t_data); - attnum = Min(attnum, tdesc_natts); - - slot_deform_tuple(slot, attnum); - - /* - * If tuple doesn't have all the atts indicated by tupleDesc, read the - * rest as null - */ - for (; attnum < tdesc_natts; attnum++) - { - slot->tts_values[attnum] = (Datum) 0; - slot->tts_isnull[attnum] = true; - } - slot->tts_nvalid = tdesc_natts; + slot->tts_storageslotam->slot_virtualize_tuple(slot, slot->tts_tupleDescriptor->natts); } /* @@ -1281,43 +1060,7 @@ slot_getallattrs(TupleTableSlot *slot) void slot_getsomeattrs(TupleTableSlot *slot, int attnum) { - HeapTuple tuple; - int attno; - - /* Quick out if we have 'em all already */ - if (slot->tts_nvalid >= attnum) - return; - - /* Check for caller error */ - if (attnum <= 0 || attnum > slot->tts_tupleDescriptor->natts) - elog(ERROR, "invalid attribute number %d", attnum); - - /* - * otherwise we had better have a physical tuple (tts_nvalid should equal - * natts in all virtual-tuple cases) - */ - tuple = slot->tts_tuple; - if (tuple == NULL) /* internal error */ - elog(ERROR, "cannot extract attribute from empty tuple slot"); - - /* - * load up any slots available from physical tuple - */ - attno = HeapTupleHeaderGetNatts(tuple->t_data); - attno = Min(attno, attnum); - - slot_deform_tuple(slot, attno); - - /* - * If tuple doesn't have all the atts indicated by tupleDesc, read the - * rest as null - */ - for (; attno < attnum; attno++) - { - slot->tts_values[attno] = (Datum) 0; - slot->tts_isnull[attno] = true; - } - slot->tts_nvalid = attnum; + slot->tts_storageslotam->slot_virtualize_tuple(slot, attnum); } /* @@ -1328,42 +1071,11 @@ slot_getsomeattrs(TupleTableSlot *slot, int attnum) bool slot_attisnull(TupleTableSlot *slot, int attnum) { - HeapTuple tuple = slot->tts_tuple; - TupleDesc tupleDesc = slot->tts_tupleDescriptor; - - /* - * system attributes are handled by heap_attisnull - */ - if (attnum <= 0) - { - if (tuple == NULL) /* internal error */ - elog(ERROR, "cannot extract system attribute from virtual tuple"); - if (tuple == &(slot->tts_minhdr)) /* internal error */ - elog(ERROR, "cannot extract system attribute from minimal tuple"); - return heap_attisnull(tuple, attnum); - } - - /* - * fast path if desired attribute already cached - */ - if (attnum <= slot->tts_nvalid) - return slot->tts_isnull[attnum - 1]; - - /* - * return NULL if attnum is out of range according to the tupdesc - */ - if (attnum > tupleDesc->natts) - return true; + bool isnull; - /* - * otherwise we had better have a physical tuple (tts_nvalid should equal - * natts in all virtual-tuple cases) - */ - if (tuple == NULL) /* internal error */ - elog(ERROR, "cannot extract attribute from empty tuple slot"); + slot->tts_storageslotam->slot_getattr(slot, attnum, &isnull); - /* and let the tuple tell it */ - return heap_attisnull(tuple, attnum); + return isnull; } /* diff --git a/src/backend/access/heap/heapam_common.c b/src/backend/access/heap/heapam_common.c index 502f6dbccb..6cb6c5be30 100644 --- a/src/backend/access/heap/heapam_common.c +++ b/src/backend/access/heap/heapam_common.c @@ -159,4 +159,408 @@ HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin) return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin); } +/*----------------------- + * + * Slot storage handler API + * ---------------------- + */ + +static HeapTuple +heapam_get_tuple(TupleTableSlot *slot, bool palloc_copy) +{ + HeapTuple tup; + HeapamTuple *stuple = (HeapamTuple *)slot->tts_storage; + + if (stuple) + { + if (stuple->hst_mintuple) + { + tup = heap_tuple_from_minimal_tuple(stuple->hst_mintuple); + } + else + { + if (!palloc_copy) + tup = stuple->hst_heaptuple; + else + tup = heap_copytuple(stuple->hst_heaptuple); + } + } + else + { + tup = heap_form_tuple(slot->tts_tupleDescriptor, + slot->tts_values, + slot->tts_isnull); + } + + return tup; +} + +static MinimalTuple +heapam_get_min_tuple(TupleTableSlot *slot, bool palloc_copy) +{ + MinimalTuple tup; + HeapamTuple *stuple = (HeapamTuple *)slot->tts_storage; + + if (stuple) + { + if (stuple->hst_mintuple) + { + if (!palloc_copy) + tup = stuple->hst_mintuple; + else + tup = heap_copy_minimal_tuple(stuple->hst_mintuple); + } + else + { + tup = minimal_tuple_from_heap_tuple(stuple->hst_heaptuple); + } + } + else + { + tup = heap_form_minimal_tuple(slot->tts_tupleDescriptor, + slot->tts_values, + slot->tts_isnull); + } + + return tup; +} + + +/* + * slot_deform_tuple + * Given a TupleTableSlot, extract data from the slot's physical tuple + * into its Datum/isnull arrays. Data is extracted up through the + * natts'th column (caller must ensure this is a legal column number). + * + * This is essentially an incremental version of heap_deform_tuple: + * on each call we extract attributes up to the one needed, without + * re-computing information about previously extracted attributes. + * slot->tts_nvalid is the number of attributes already extracted. + */ +static void +slot_deform_tuple(TupleTableSlot *slot, int natts) +{ + HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage; + HeapTuple tuple = stuple ? stuple->hst_heaptuple : NULL; + TupleDesc tupleDesc = slot->tts_tupleDescriptor; + Datum *values = slot->tts_values; + bool *isnull = slot->tts_isnull; + HeapTupleHeader tup = tuple->t_data; + bool hasnulls = HeapTupleHasNulls(tuple); + int attnum; + char *tp; /* ptr to tuple data */ + long off; /* offset in tuple data */ + bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */ + bool slow; /* can we use/set attcacheoff? */ + + /* + * Check whether the first call for this tuple, and initialize or restore + * loop state. + */ + attnum = slot->tts_nvalid; + if (attnum == 0) + { + /* Start from the first attribute */ + off = 0; + slow = false; + } + else + { + /* Restore state from previous execution */ + off = stuple->hst_off; + slow = stuple->hst_slow; + } + + tp = (char *) tup + tup->t_hoff; + + for (; attnum < natts; attnum++) + { + Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum); + + if (hasnulls && att_isnull(attnum, bp)) + { + values[attnum] = (Datum) 0; + isnull[attnum] = true; + slow = true; /* can't use attcacheoff anymore */ + continue; + } + + isnull[attnum] = false; + + if (!slow && thisatt->attcacheoff >= 0) + off = thisatt->attcacheoff; + else if (thisatt->attlen == -1) + { + /* + * We can only cache the offset for a varlena attribute if the + * offset is already suitably aligned, so that there would be no + * pad bytes in any case: then the offset will be valid for either + * an aligned or unaligned value. + */ + if (!slow && + off == att_align_nominal(off, thisatt->attalign)) + thisatt->attcacheoff = off; + else + { + off = att_align_pointer(off, thisatt->attalign, -1, + tp + off); + slow = true; + } + } + else + { + /* not varlena, so safe to use att_align_nominal */ + off = att_align_nominal(off, thisatt->attalign); + + if (!slow) + thisatt->attcacheoff = off; + } + + values[attnum] = fetchatt(thisatt, tp + off); + + off = att_addlength_pointer(off, thisatt->attlen, tp + off); + + if (thisatt->attlen <= 0) + slow = true; /* can't use attcacheoff anymore */ + } + + /* + * Save state for next execution + */ + slot->tts_nvalid = attnum; + stuple->hst_off = off; + stuple->hst_slow = slow; +} + +static void +heapam_slot_virtualize_tuple(TupleTableSlot *slot, int16 upto) +{ + HeapamTuple *stuple; + HeapTuple tuple; + int attno; + + /* Quick out if we have 'em all already */ + if (slot->tts_nvalid >= upto) + return; + + /* Check for caller error */ + if (upto <= 0 || upto > slot->tts_tupleDescriptor->natts) + elog(ERROR, "invalid attribute number %d", upto); + + /* + * otherwise we had better have a physical tuple (tts_nvalid should equal + * natts in all virtual-tuple cases) + */ + stuple = slot->tts_storage; /* XXX SlotGetTupleStorage(slot) ??? */ + tuple = stuple->hst_heaptuple; + if (tuple == NULL) /* internal error */ + elog(ERROR, "cannot extract attribute from empty tuple slot"); + + /* + * load up any slots available from physical tuple + */ + attno = HeapTupleHeaderGetNatts(tuple->t_data); + attno = Min(attno, upto); + + slot_deform_tuple(slot, attno); + + /* + * If tuple doesn't have all the atts indicated by tupleDesc, read the + * rest as null + */ + for (; attno < upto; attno++) + { + slot->tts_values[attno] = (Datum) 0; + slot->tts_isnull[attno] = true; + } + slot->tts_nvalid = upto; +} + +static void +heapam_slot_update_tuple_tableoid(TupleTableSlot *slot, Oid tableoid) +{ + HeapTuple tuple; + + tuple = heapam_get_tuple(slot, false); + tuple->t_tableOid = tableoid; +} + +static void +heapam_slot_store_tuple(TupleTableSlot *slot, StorageTuple tuple, bool shouldFree, bool minimum_tuple) +{ + HeapamTuple *stuple; + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(slot->tts_mcxt); + + stuple = (HeapamTuple *)palloc0(sizeof(HeapamTuple)); + + if (!minimum_tuple) + { + stuple->hst_heaptuple = tuple; + stuple->hst_slow = false; + stuple->hst_off = 0; + stuple->hst_mintuple = NULL; + slot->tts_shouldFreeMin = false; + slot->tts_shouldFree = shouldFree; + } + else + { + stuple->hst_mintuple = tuple; + stuple->hst_minhdr.t_len = ((MinimalTuple)tuple)->t_len + MINIMAL_TUPLE_OFFSET; + stuple->hst_minhdr.t_data = (HeapTupleHeader) ((char *) tuple - MINIMAL_TUPLE_OFFSET); + stuple->hst_heaptuple = &stuple->hst_minhdr; + slot->tts_shouldFreeMin = shouldFree; + } + + MemoryContextSwitchTo(oldcontext); + + slot->tts_tid = ((HeapTuple)tuple)->t_self; + slot->tts_storage = stuple; +} + +static void +heapam_slot_clear_tuple(TupleTableSlot *slot) +{ + HeapamTuple *stuple; + + /* XXX should this be an Assert() instead? */ + if (slot->tts_isempty) + return; + + stuple = slot->tts_storage; + if (stuple == NULL) + return; + + if (slot->tts_shouldFree) + heap_freetuple(stuple->hst_heaptuple); + + if (slot->tts_shouldFreeMin) + heap_free_minimal_tuple(stuple->hst_mintuple); + + slot->tts_shouldFree = false; + slot->tts_shouldFreeMin = false; + + pfree(stuple); + slot->tts_storage = NULL; +} + +/* + * slot_getattr + * This function fetches an attribute of the slot's current tuple. + * It is functionally equivalent to heap_getattr, but fetches of + * multiple attributes of the same tuple will be optimized better, + * because we avoid O(N^2) behavior from multiple calls of + * nocachegetattr(), even when attcacheoff isn't usable. + * + * A difference from raw heap_getattr is that attnums beyond the + * slot's tupdesc's last attribute will be considered NULL even + * when the physical tuple is longer than the tupdesc. + */ +static Datum +heapam_slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull) +{ + HeapamTuple *stuple = slot->tts_storage; + HeapTuple tuple = stuple ? stuple->hst_heaptuple : NULL; + TupleDesc tupleDesc = slot->tts_tupleDescriptor; + HeapTupleHeader tup; + + /* + * system attributes are handled by heap_getsysattr + */ + if (attnum <= 0) + { + if (tuple == NULL) /* internal error */ + elog(ERROR, "cannot extract system attribute from virtual tuple"); + if (tuple == &(stuple->hst_minhdr)) /* internal error */ + elog(ERROR, "cannot extract system attribute from minimal tuple"); + return heap_getsysattr(tuple, attnum, tupleDesc, isnull); + } + + /* + * fast path if desired attribute already cached + */ + if (attnum <= slot->tts_nvalid) + { + *isnull = slot->tts_isnull[attnum - 1]; + return slot->tts_values[attnum - 1]; + } + + /* + * return NULL if attnum is out of range according to the tupdesc + */ + if (attnum > tupleDesc->natts) + { + *isnull = true; + return (Datum) 0; + } + + /* + * otherwise we had better have a physical tuple (tts_nvalid should equal + * natts in all virtual-tuple cases) + */ + if (tuple == NULL) /* internal error */ + elog(ERROR, "cannot extract attribute from empty tuple slot"); + + /* + * return NULL if attnum is out of range according to the tuple + * + * (We have to check this separately because of various inheritance and + * table-alteration scenarios: the tuple could be either longer or shorter + * than the tupdesc.) + */ + tup = tuple->t_data; + if (attnum > HeapTupleHeaderGetNatts(tup)) + { + *isnull = true; + return (Datum) 0; + } + + /* + * check if target attribute is null: no point in groveling through tuple + */ + if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits)) + { + *isnull = true; + return (Datum) 0; + } + + /* + * If the attribute's column has been dropped, we force a NULL result. + * This case should not happen in normal use, but it could happen if we + * are executing a plan cached before the column was dropped. + */ + if (TupleDescAttr(tupleDesc, (attnum - 1))->attisdropped) + { + *isnull = true; + return (Datum) 0; + } + + /* + * Extract the attribute, along with any preceding attributes. + */ + slot_deform_tuple(slot, attnum); + + /* + * The result is acquired from tts_values array. + */ + *isnull = slot->tts_isnull[attnum - 1]; + return slot->tts_values[attnum - 1]; +} + +StorageSlotAmRoutine* +heapam_storage_slot_handler(void) +{ + StorageSlotAmRoutine *amroutine = palloc(sizeof(StorageSlotAmRoutine)); + + amroutine->slot_store_tuple = heapam_slot_store_tuple; + amroutine->slot_virtualize_tuple = heapam_slot_virtualize_tuple; + amroutine->slot_clear_tuple = heapam_slot_clear_tuple; + amroutine->slot_tuple = heapam_get_tuple; + amroutine->slot_min_tuple = heapam_get_min_tuple; + amroutine->slot_getattr = heapam_slot_getattr; + amroutine->slot_update_tableoid = heapam_slot_update_tuple_tableoid; + + return amroutine; +} diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c index 1bd4bfaa33..7d7ac759e3 100644 --- a/src/backend/access/heap/heapam_storage.c +++ b/src/backend/access/heap/heapam_storage.c @@ -1665,6 +1665,7 @@ heapam_storage_handler(PG_FUNCTION_ARGS) { StorageAmRoutine *amroutine = makeNode(StorageAmRoutine); + amroutine->slot_storageam = heapam_storage_slot_handler; amroutine->snapshot_satisfies[MVCC_VISIBILITY] = HeapTupleSatisfiesMVCC; amroutine->snapshot_satisfies[SELF_VISIBILITY] = HeapTupleSatisfiesSelf; diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index c6fa44563c..f1f546a321 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -2697,7 +2697,7 @@ CopyFrom(CopyState cstate) if (slot == NULL) /* "do nothing" */ skip_tuple = true; else /* trigger might have changed tuple */ - tuple = ExecMaterializeSlot(slot); + tuple = ExecHeapifySlot(slot); } if (!skip_tuple) diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c index e60210cb24..a0ec444d33 100644 --- a/src/backend/commands/createas.c +++ b/src/backend/commands/createas.c @@ -588,7 +588,7 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self) * get the heap tuple out of the tuple table slot, making sure we have a * writable copy */ - tuple = ExecMaterializeSlot(slot); + tuple = ExecHeapifySlot(slot); /* * force assignment of new OID (see comments in ExecInsert) diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c index d2e0376511..b440740e28 100644 --- a/src/backend/commands/matview.c +++ b/src/backend/commands/matview.c @@ -497,7 +497,7 @@ transientrel_receive(TupleTableSlot *slot, DestReceiver *self) * get the heap tuple out of the tuple table slot, making sure we have a * writable copy */ - tuple = ExecMaterializeSlot(slot); + tuple = ExecHeapifySlot(slot); heap_insert(myState->transientrel, tuple, diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index e75a59d299..2f530169b8 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -2289,7 +2289,7 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot) { TriggerDesc *trigdesc = relinfo->ri_TrigDesc; - HeapTuple slottuple = ExecMaterializeSlot(slot); + HeapTuple slottuple = ExecHeapifySlot(slot); HeapTuple newtuple = slottuple; HeapTuple oldtuple; TriggerData LocTriggerData; @@ -2370,7 +2370,7 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot) { TriggerDesc *trigdesc = relinfo->ri_TrigDesc; - HeapTuple slottuple = ExecMaterializeSlot(slot); + HeapTuple slottuple = ExecHeapifySlot(slot); HeapTuple newtuple = slottuple; HeapTuple oldtuple; TriggerData LocTriggerData; @@ -2728,7 +2728,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, TupleTableSlot *slot) { TriggerDesc *trigdesc = relinfo->ri_TrigDesc; - HeapTuple slottuple = ExecMaterializeSlot(slot); + HeapTuple slottuple = ExecHeapifySlot(slot); HeapTuple newtuple = slottuple; TriggerData LocTriggerData; HeapTuple trigtuple; @@ -2770,7 +2770,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, if (newSlot != NULL) { slot = ExecFilterJunk(relinfo->ri_junkFilter, newSlot); - slottuple = ExecMaterializeSlot(slot); + slottuple = ExecHeapifySlot(slot); newtuple = slottuple; } @@ -2879,7 +2879,7 @@ ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, TupleTableSlot *slot) { TriggerDesc *trigdesc = relinfo->ri_TrigDesc; - HeapTuple slottuple = ExecMaterializeSlot(slot); + HeapTuple slottuple = ExecHeapifySlot(slot); HeapTuple newtuple = slottuple; TriggerData LocTriggerData; HeapTuple oldtuple; @@ -4013,14 +4013,13 @@ AfterTriggerExecute(AfterTriggerEvent event, * because we start with a minimal tuple that ExecFetchSlotTuple() * must materialize anyway. */ - LocTriggerData.tg_trigtuple = - ExecMaterializeSlot(trig_tuple_slot1); + LocTriggerData.tg_trigtuple = ExecHeapifySlot(trig_tuple_slot1); LocTriggerData.tg_trigtuplebuf = InvalidBuffer; LocTriggerData.tg_newtuple = ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_UPDATE) ? - ExecMaterializeSlot(trig_tuple_slot2) : NULL; + ExecHeapifySlot(trig_tuple_slot2) : NULL; LocTriggerData.tg_newtuplebuf = InvalidBuffer; break; diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index bd8a15d6c3..47757386ee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -503,12 +503,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) int attnum = op->d.var.attnum; /* these asserts must match defenses in slot_getattr */ - Assert(innerslot->tts_tuple != NULL); - Assert(innerslot->tts_tuple != &(innerslot->tts_minhdr)); + Assert(innerslot->tts_storage != NULL); + //hari Assert(innerslot->tts_storageslotam->slot_is_physical_tuple(innerslot)); /* heap_getsysattr has sufficient defenses against bad attnums */ - *op->resvalue = heap_getsysattr(innerslot->tts_tuple, attnum, - innerslot->tts_tupleDescriptor, + *op->resvalue = slot_getattr(innerslot, + attnum, op->resnull); EEO_NEXT(); @@ -519,12 +519,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) int attnum = op->d.var.attnum; /* these asserts must match defenses in slot_getattr */ - Assert(outerslot->tts_tuple != NULL); - Assert(outerslot->tts_tuple != &(outerslot->tts_minhdr)); - + Assert(outerslot->tts_storage != NULL); + //hari Assert(outerslot->tts_storageslotam->slot_is_physical_tuple(outerslot)); /* heap_getsysattr has sufficient defenses against bad attnums */ - *op->resvalue = heap_getsysattr(outerslot->tts_tuple, attnum, - outerslot->tts_tupleDescriptor, + + *op->resvalue = slot_getattr(outerslot, + attnum, op->resnull); EEO_NEXT(); @@ -535,12 +535,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) int attnum = op->d.var.attnum; /* these asserts must match defenses in slot_getattr */ - Assert(scanslot->tts_tuple != NULL); - Assert(scanslot->tts_tuple != &(scanslot->tts_minhdr)); + Assert(scanslot->tts_storage != NULL); + //hari Assert(scanslot->tts_storageslotam->slot_is_physical_tuple(scanslot)); /* heap_getsysattr has sufficient defenses against bad attnums */ - *op->resvalue = heap_getsysattr(scanslot->tts_tuple, attnum, - scanslot->tts_tupleDescriptor, + *op->resvalue = slot_getattr(scanslot, + attnum, op->resnull); EEO_NEXT(); diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c index 5a75e0211f..6700f0ad80 100644 --- a/src/backend/executor/execReplication.c +++ b/src/backend/executor/execReplication.c @@ -171,7 +171,7 @@ retry: HTSU_Result res; HeapTupleData locktup; - ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self); + ItemPointerCopy(&outslot->tts_tid, &locktup.t_self); PushActiveSnapshot(GetLatestSnapshot()); @@ -211,59 +211,6 @@ retry: return found; } -/* - * Compare the tuple and slot and check if they have equal values. - * - * We use binary datum comparison which might return false negatives but - * that's the best we can do here as there may be multiple notions of - * equality for the data types and table columns don't specify which one - * to use. - */ -static bool -tuple_equals_slot(TupleDesc desc, HeapTuple tup, TupleTableSlot *slot) -{ - Datum values[MaxTupleAttributeNumber]; - bool isnull[MaxTupleAttributeNumber]; - int attrnum; - - heap_deform_tuple(tup, desc, values, isnull); - - /* Check equality of the attributes. */ - for (attrnum = 0; attrnum < desc->natts; attrnum++) - { - Form_pg_attribute att; - TypeCacheEntry *typentry; - - /* - * If one value is NULL and other is not, then they are certainly not - * equal - */ - if (isnull[attrnum] != slot->tts_isnull[attrnum]) - return false; - - /* - * If both are NULL, they can be considered equal. - */ - if (isnull[attrnum]) - continue; - - att = TupleDescAttr(desc, attrnum); - - typentry = lookup_type_cache(att->atttypid, TYPECACHE_EQ_OPR_FINFO); - if (!OidIsValid(typentry->eq_opr_finfo.fn_oid)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_FUNCTION), - errmsg("could not identify an equality operator for type %s", - format_type_be(att->atttypid)))); - - if (!DatumGetBool(FunctionCall2(&typentry->eq_opr_finfo, - values[attrnum], - slot->tts_values[attrnum]))) - return false; - } - - return true; -} /* * Search the relation 'rel' for tuple using the sequential scan. @@ -279,7 +226,8 @@ bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, TupleTableSlot *searchslot, TupleTableSlot *outslot) { - HeapTuple scantuple; + TupleTableSlot *scanslot; + HeapTuple scantuple; HeapScanDesc scan; SnapshotData snap; TransactionId xwait; @@ -292,6 +240,8 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, InitDirtySnapshot(snap); scan = heap_beginscan(rel, &snap, 0, NULL); + scanslot = MakeSingleTupleTableSlot(desc); + retry: found = false; @@ -300,12 +250,12 @@ retry: /* Try to find the tuple */ while ((scantuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { - if (!tuple_equals_slot(desc, scantuple, searchslot)) + ExecStoreTuple(scantuple, scanslot, InvalidBuffer, false); + if (!ExecSlotCompare(scanslot, searchslot)) continue; found = true; - ExecStoreTuple(scantuple, outslot, InvalidBuffer, false); - ExecMaterializeSlot(outslot); + ExecCopySlot(outslot, scanslot); xwait = TransactionIdIsValid(snap.xmin) ? snap.xmin : snap.xmax; @@ -329,7 +279,7 @@ retry: HTSU_Result res; HeapTupleData locktup; - ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self); + ItemPointerCopy(&outslot->tts_tid, &locktup.t_self); PushActiveSnapshot(GetLatestSnapshot()); @@ -362,6 +312,7 @@ retry: } heap_endscan(scan); + ExecDropSingleTupleTableSlot(scanslot); return found; } @@ -404,7 +355,7 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot) ExecConstraints(resultRelInfo, slot, estate); /* Store the slot into tuple that we can inspect. */ - tuple = ExecMaterializeSlot(slot); + tuple = ExecHeapifySlot(slot); /* OK, store the tuple and create index entries for it */ simple_heap_insert(rel, tuple); @@ -442,6 +393,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, HeapTuple tuple; ResultRelInfo *resultRelInfo = estate->es_result_relation_info; Relation rel = resultRelInfo->ri_RelationDesc; + ItemPointer tid = &(searchslot->tts_tid); /* For now we support only tables. */ Assert(rel->rd_rel->relkind == RELKIND_RELATION); @@ -453,7 +405,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, resultRelInfo->ri_TrigDesc->trig_update_before_row) { slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo, - &searchslot->tts_tuple->t_self, + tid, NULL, slot); if (slot == NULL) /* "do nothing" */ @@ -469,21 +421,20 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, ExecConstraints(resultRelInfo, slot, estate); /* Store the slot into tuple that we can write. */ - tuple = ExecMaterializeSlot(slot); + tuple = ExecHeapifySlot(slot); /* OK, update the tuple and index entries for it */ - simple_heap_update(rel, &searchslot->tts_tuple->t_self, - slot->tts_tuple); + simple_heap_update(rel, tid, tuple); if (resultRelInfo->ri_NumIndices > 0 && - !HeapTupleIsHeapOnly(slot->tts_tuple)) - recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self), + !HeapTupleIsHeapOnly(tuple)) + recheckIndexes = ExecInsertIndexTuples(slot, tid, estate, false, NULL, NIL); /* AFTER ROW UPDATE Triggers */ ExecARUpdateTriggers(estate, resultRelInfo, - &searchslot->tts_tuple->t_self, + tid, NULL, tuple, recheckIndexes, NULL); list_free(recheckIndexes); @@ -503,6 +454,7 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate, bool skip_tuple = false; ResultRelInfo *resultRelInfo = estate->es_result_relation_info; Relation rel = resultRelInfo->ri_RelationDesc; + ItemPointer tid = &(searchslot->tts_tid); /* For now we support only tables. */ Assert(rel->rd_rel->relkind == RELKIND_RELATION); @@ -514,7 +466,7 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate, resultRelInfo->ri_TrigDesc->trig_update_before_row) { skip_tuple = !ExecBRDeleteTriggers(estate, epqstate, resultRelInfo, - &searchslot->tts_tuple->t_self, + tid, NULL); } @@ -523,11 +475,11 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate, List *recheckIndexes = NIL; /* OK, delete the tuple */ - simple_heap_delete(rel, &searchslot->tts_tuple->t_self); + simple_heap_delete(rel, tid); /* AFTER ROW DELETE Triggers */ ExecARDeleteTriggers(estate, resultRelInfo, - &searchslot->tts_tuple->t_self, NULL, NULL); + tid, NULL, NULL); list_free(recheckIndexes); } diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 51d2c5d166..b7a2cbc023 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -81,6 +81,7 @@ */ #include "postgres.h" +#include "access/heapam_common.h" #include "access/htup_details.h" #include "access/tuptoaster.h" #include "funcapi.h" @@ -113,16 +114,15 @@ MakeTupleTableSlot(void) TupleTableSlot *slot = makeNode(TupleTableSlot); slot->tts_isempty = true; - slot->tts_shouldFree = false; - slot->tts_shouldFreeMin = false; - slot->tts_tuple = NULL; slot->tts_tupleDescriptor = NULL; slot->tts_mcxt = CurrentMemoryContext; - slot->tts_buffer = InvalidBuffer; slot->tts_nvalid = 0; slot->tts_values = NULL; slot->tts_isnull = NULL; - slot->tts_mintuple = NULL; + slot->tts_tupleOid = InvalidOid; + slot->tts_tableOid = InvalidOid; + slot->tts_storageslotam = heapam_storage_slot_handler(); + slot->tts_storage = NULL; return slot; } @@ -205,6 +205,54 @@ MakeSingleTupleTableSlot(TupleDesc tupdesc) return slot; } +/* -------------------------------- + * ExecSlotCompare + * + * This is a slot comparision function to find out + * whether both the slots are same or not? + * -------------------------------- + */ +bool +ExecSlotCompare(TupleTableSlot *slot1, TupleTableSlot *slot2) +{ + int attrnum; + + Assert (slot1->tts_tupleDescriptor->natts == slot2->tts_tupleDescriptor->natts); + + slot_getallattrs(slot1); + slot_getallattrs(slot2); + + /* Check equality of the attributes. */ + for (attrnum = 0; attrnum < slot1->tts_tupleDescriptor->natts; attrnum++) + { + Form_pg_attribute att; + TypeCacheEntry *typentry; + + /* + * If one value is NULL and other is not, then they are certainly not + * equal + */ + if (slot1->tts_isnull[attrnum] != slot2->tts_isnull[attrnum]) + return false; + + att = TupleDescAttr(slot1->tts_tupleDescriptor, attrnum); + + typentry = lookup_type_cache(att->atttypid, TYPECACHE_EQ_OPR_FINFO); + if (!OidIsValid(typentry->eq_opr_finfo.fn_oid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("could not identify an equality operator for type %s", + format_type_be(att->atttypid)))); + + if (!DatumGetBool(FunctionCall2(&typentry->eq_opr_finfo, + slot1->tts_values[attrnum], + slot2->tts_values[attrnum]))) + return false; + } + + return true; +} + /* -------------------------------- * ExecDropSingleTupleTableSlot * @@ -317,7 +365,7 @@ ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */ * -------------------------------- */ TupleTableSlot * -ExecStoreTuple(HeapTuple tuple, +ExecStoreTuple(void *tuple, TupleTableSlot *slot, Buffer buffer, bool shouldFree) @@ -328,47 +376,27 @@ ExecStoreTuple(HeapTuple tuple, Assert(tuple != NULL); Assert(slot != NULL); Assert(slot->tts_tupleDescriptor != NULL); + Assert(slot->tts_storageslotam != NULL); /* passing shouldFree=true for a tuple on a disk page is not sane */ Assert(BufferIsValid(buffer) ? (!shouldFree) : true); /* * Free any old physical tuple belonging to the slot. */ - if (slot->tts_shouldFree) - heap_freetuple(slot->tts_tuple); - if (slot->tts_shouldFreeMin) - heap_free_minimal_tuple(slot->tts_mintuple); + slot->tts_storageslotam->slot_clear_tuple(slot); /* XXX ?? */ /* - * Store the new tuple into the specified slot. + * Store the new tuple into the specified slot, and mark the slot as no + * longer empty. This clears any previously stored physical tuple. */ + /* XXX should we pass the buffer down to the storageAM perhaps? */ + slot->tts_storageslotam->slot_store_tuple(slot, tuple, shouldFree, false); + slot->tts_isempty = false; - slot->tts_shouldFree = shouldFree; - slot->tts_shouldFreeMin = false; - slot->tts_tuple = tuple; - slot->tts_mintuple = NULL; /* Mark extracted state invalid */ slot->tts_nvalid = 0; - /* - * If tuple is on a disk page, keep the page pinned as long as we hold a - * pointer into it. We assume the caller already has such a pin. - * - * This is coded to optimize the case where the slot previously held a - * tuple on the same disk page: in that case releasing and re-acquiring - * the pin is a waste of cycles. This is a common situation during - * seqscans, so it's worth troubling over. - */ - if (slot->tts_buffer != buffer) - { - if (BufferIsValid(slot->tts_buffer)) - ReleaseBuffer(slot->tts_buffer); - slot->tts_buffer = buffer; - if (BufferIsValid(buffer)) - IncrBufferRefCount(buffer); - } - return slot; } @@ -395,31 +423,18 @@ ExecStoreMinimalTuple(MinimalTuple mtup, /* * Free any old physical tuple belonging to the slot. */ - if (slot->tts_shouldFree) - heap_freetuple(slot->tts_tuple); - if (slot->tts_shouldFreeMin) - heap_free_minimal_tuple(slot->tts_mintuple); - - /* - * Drop the pin on the referenced buffer, if there is one. - */ - if (BufferIsValid(slot->tts_buffer)) - ReleaseBuffer(slot->tts_buffer); - - slot->tts_buffer = InvalidBuffer; + slot->tts_storageslotam->slot_clear_tuple(slot); /* XXX ?? */ /* * Store the new tuple into the specified slot. */ slot->tts_isempty = false; - slot->tts_shouldFree = false; - slot->tts_shouldFreeMin = shouldFree; - slot->tts_tuple = &slot->tts_minhdr; - slot->tts_mintuple = mtup; - - slot->tts_minhdr.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET; - slot->tts_minhdr.t_data = (HeapTupleHeader) ((char *) mtup - MINIMAL_TUPLE_OFFSET); - /* no need to set t_self or t_tableOid since we won't allow access */ + /* + * Store the new tuple into the specified slot, and mark the slot as no + * longer empty. This clears any previously stored physical tuple. + */ + /* XXX should we pass the buffer down to the storageAM perhaps? */ + slot->tts_storageslotam->slot_store_tuple(slot, mtup, false, true); /* Mark extracted state invalid */ slot->tts_nvalid = 0; @@ -444,25 +459,9 @@ ExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */ Assert(slot != NULL); /* - * Free the old physical tuple if necessary. + * Tell the storage AM to release any resource associated with the slot. */ - if (slot->tts_shouldFree) - heap_freetuple(slot->tts_tuple); - if (slot->tts_shouldFreeMin) - heap_free_minimal_tuple(slot->tts_mintuple); - - slot->tts_tuple = NULL; - slot->tts_mintuple = NULL; - slot->tts_shouldFree = false; - slot->tts_shouldFreeMin = false; - - /* - * Drop the pin on the referenced buffer, if there is one. - */ - if (BufferIsValid(slot->tts_buffer)) - ReleaseBuffer(slot->tts_buffer); - - slot->tts_buffer = InvalidBuffer; + slot->tts_storageslotam->slot_clear_tuple(slot); /* * Mark it empty. @@ -541,7 +540,7 @@ ExecStoreAllNullTuple(TupleTableSlot *slot) * however the "system columns" of the result will not be meaningful. * -------------------------------- */ -HeapTuple +StorageTuple ExecCopySlotTuple(TupleTableSlot *slot) { /* @@ -550,20 +549,7 @@ ExecCopySlotTuple(TupleTableSlot *slot) Assert(slot != NULL); Assert(!slot->tts_isempty); - /* - * If we have a physical tuple (either format) then just copy it. - */ - if (TTS_HAS_PHYSICAL_TUPLE(slot)) - return heap_copytuple(slot->tts_tuple); - if (slot->tts_mintuple) - return heap_tuple_from_minimal_tuple(slot->tts_mintuple); - - /* - * Otherwise we need to build a tuple from the Datum array. - */ - return heap_form_tuple(slot->tts_tupleDescriptor, - slot->tts_values, - slot->tts_isnull); + return slot->tts_storageslotam->slot_tuple(slot, true); } /* -------------------------------- @@ -582,21 +568,19 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot) Assert(slot != NULL); Assert(!slot->tts_isempty); - /* - * If we have a physical tuple then just copy it. Prefer to copy - * tts_mintuple since that's a tad cheaper. - */ - if (slot->tts_mintuple) - return heap_copy_minimal_tuple(slot->tts_mintuple); - if (slot->tts_tuple) - return minimal_tuple_from_heap_tuple(slot->tts_tuple); + return slot->tts_storageslotam->slot_min_tuple(slot, true); +} +void +ExecSlotUpdateTupleTableoid(TupleTableSlot *slot, Oid tableoid) +{ /* - * Otherwise we need to build a tuple from the Datum array. + * sanity checks */ - return heap_form_minimal_tuple(slot->tts_tupleDescriptor, - slot->tts_values, - slot->tts_isnull); + Assert(slot != NULL); + Assert(!slot->tts_isempty); + + slot->tts_storageslotam->slot_update_tableoid(slot, tableoid); } /* -------------------------------- @@ -614,25 +598,34 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot) * Hence, the result must be treated as read-only. * -------------------------------- */ -HeapTuple +StorageTuple ExecFetchSlotTuple(TupleTableSlot *slot) { + MemoryContext oldContext; + StorageTuple tup; + /* * sanity checks */ Assert(slot != NULL); Assert(!slot->tts_isempty); - /* - * If we have a regular physical tuple then just return it. - */ - if (TTS_HAS_PHYSICAL_TUPLE(slot)) - return slot->tts_tuple; + if (slot->tts_shouldFree) + return slot->tts_storageslotam->slot_tuple(slot, false); /* - * Otherwise materialize the slot... + * Otherwise, copy or build a tuple, and store it into the slot. + * + * We may be called in a context that is shorter-lived than the tuple + * slot, but we have to ensure that the materialized tuple will survive + * anyway. */ - return ExecMaterializeSlot(slot); + oldContext = MemoryContextSwitchTo(slot->tts_mcxt); + tup = ExecCopySlotTuple(slot); + ExecStoreTuple(tup, slot, InvalidBuffer, true); + MemoryContextSwitchTo(oldContext); + + return tup; } /* -------------------------------- @@ -652,6 +645,7 @@ MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot) { MemoryContext oldContext; + MinimalTuple tup; /* * sanity checks @@ -659,11 +653,8 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot) Assert(slot != NULL); Assert(!slot->tts_isempty); - /* - * If we have a minimal physical tuple (local or not) then just return it. - */ - if (slot->tts_mintuple) - return slot->tts_mintuple; + if (slot->tts_shouldFreeMin) + return slot->tts_storageslotam->slot_min_tuple(slot, false); /* * Otherwise, copy or build a minimal tuple, and store it into the slot. @@ -673,18 +664,11 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot) * anyway. */ oldContext = MemoryContextSwitchTo(slot->tts_mcxt); - slot->tts_mintuple = ExecCopySlotMinimalTuple(slot); - slot->tts_shouldFreeMin = true; + tup = ExecCopySlotMinimalTuple(slot); + ExecStoreMinimalTuple(tup, slot, true); MemoryContextSwitchTo(oldContext); - /* - * Note: we may now have a situation where we have a local minimal tuple - * attached to a virtual or non-local physical tuple. There seems no harm - * in that at the moment, but if any materializes, we should change this - * function to force the slot into minimal-tuple-only state. - */ - - return slot->tts_mintuple; + return tup; } /* -------------------------------- @@ -713,18 +697,19 @@ ExecFetchSlotTupleDatum(TupleTableSlot *slot) * Force a slot into the "materialized" state. * * This causes the slot's tuple to be a local copy not dependent on - * any external storage. A pointer to the contained tuple is returned. + * any external storage. * * A typical use for this operation is to prepare a computed tuple * for being stored on disk. The original data may or may not be * virtual, but in any case we need a private copy for heap_insert - * to scribble on. + * to scribble on. XXX is this comment good? * -------------------------------- */ -HeapTuple +void ExecMaterializeSlot(TupleTableSlot *slot) { MemoryContext oldContext; + HeapTuple tup; /* * sanity checks @@ -732,12 +717,8 @@ ExecMaterializeSlot(TupleTableSlot *slot) Assert(slot != NULL); Assert(!slot->tts_isempty); - /* - * If we have a regular physical tuple, and it's locally palloc'd, we have - * nothing to do. - */ - if (slot->tts_tuple && slot->tts_shouldFree) - return slot->tts_tuple; + if (slot->tts_shouldFree) + return; /* * Otherwise, copy or build a physical tuple, and store it into the slot. @@ -747,18 +728,10 @@ ExecMaterializeSlot(TupleTableSlot *slot) * anyway. */ oldContext = MemoryContextSwitchTo(slot->tts_mcxt); - slot->tts_tuple = ExecCopySlotTuple(slot); - slot->tts_shouldFree = true; + tup = ExecCopySlotTuple(slot); + ExecStoreTuple(tup, slot, InvalidBuffer, true); MemoryContextSwitchTo(oldContext); - /* - * Drop the pin on the referenced buffer, if there is one. - */ - if (BufferIsValid(slot->tts_buffer)) - ReleaseBuffer(slot->tts_buffer); - - slot->tts_buffer = InvalidBuffer; - /* * Mark extracted state invalid. This is important because the slot is * not supposed to depend any more on the previous external data; we @@ -768,17 +741,15 @@ ExecMaterializeSlot(TupleTableSlot *slot) * that we have not pfree'd tts_mintuple, if there is one.) */ slot->tts_nvalid = 0; +} - /* - * On the same principle of not depending on previous remote storage, - * forget the mintuple if it's not local storage. (If it is local - * storage, we must not pfree it now, since callers might have already - * fetched datum pointers referencing it.) - */ - if (!slot->tts_shouldFreeMin) - slot->tts_mintuple = NULL; +StorageTuple +ExecHeapifySlot(TupleTableSlot *slot) +{ + ExecMaterializeSlot(slot); + Assert(slot->tts_storage != NULL); - return slot->tts_tuple; + return slot->tts_storageslotam->slot_tuple(slot, false); } /* -------------------------------- diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index 20892d6d5f..02f6c816aa 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -62,7 +62,7 @@ ForeignNext(ForeignScanState *node) */ if (plan->fsSystemCol && !TupIsNull(slot)) { - HeapTuple tup = ExecMaterializeSlot(slot); + HeapTuple tup = ExecHeapifySlot(slot); tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation); } diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index b39e48b222..dd8f792404 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -171,7 +171,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo, * initialize t_tableOid before evaluating them. */ Assert(!TupIsNull(econtext->ecxt_scantuple)); - tuple = ExecMaterializeSlot(econtext->ecxt_scantuple); + tuple = ExecHeapifySlot(econtext->ecxt_scantuple); tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc); } econtext->ecxt_outertuple = planSlot; @@ -271,7 +271,7 @@ ExecInsert(ModifyTableState *mtstate, * get the heap tuple out of the tuple table slot, making sure we have a * writable copy */ - tuple = ExecMaterializeSlot(slot); + tuple = ExecHeapifySlot(slot); /* * get information on the (current) result relation @@ -406,7 +406,7 @@ ExecInsert(ModifyTableState *mtstate, return NULL; /* trigger might have changed tuple */ - tuple = ExecMaterializeSlot(slot); + tuple = ExecHeapifySlot(slot); } /* INSTEAD OF ROW INSERT Triggers */ @@ -419,7 +419,7 @@ ExecInsert(ModifyTableState *mtstate, return NULL; /* trigger might have changed tuple */ - tuple = ExecMaterializeSlot(slot); + tuple = ExecHeapifySlot(slot); newId = InvalidOid; } @@ -437,7 +437,7 @@ ExecInsert(ModifyTableState *mtstate, return NULL; /* FDW might have changed tuple */ - tuple = ExecMaterializeSlot(slot); + tuple = ExecHeapifySlot(slot); /* * AFTER ROW Triggers or RETURNING expressions might reference the @@ -746,7 +746,7 @@ ExecDelete(ModifyTableState *mtstate, */ if (slot->tts_isempty) ExecStoreAllNullTuple(slot); - tuple = ExecMaterializeSlot(slot); + tuple = ExecHeapifySlot(slot); tuple->t_tableOid = RelationGetRelid(resultRelationDesc); } else @@ -897,7 +897,7 @@ ldelete:; * Before releasing the target tuple again, make sure rslot has a * local copy of any pass-by-reference values. */ - ExecMaterializeSlot(rslot); + ExecHeapifySlot(rslot); ExecClearTuple(slot); if (BufferIsValid(delbuffer)) @@ -958,7 +958,7 @@ ExecUpdate(ModifyTableState *mtstate, * get the heap tuple out of the tuple table slot, making sure we have a * writable copy */ - tuple = ExecMaterializeSlot(slot); + tuple = ExecHeapifySlot(slot); /* * get information on the (current) result relation @@ -977,7 +977,7 @@ ExecUpdate(ModifyTableState *mtstate, return NULL; /* trigger might have changed tuple */ - tuple = ExecMaterializeSlot(slot); + tuple = ExecHeapifySlot(slot); } /* INSTEAD OF ROW UPDATE Triggers */ @@ -991,7 +991,7 @@ ExecUpdate(ModifyTableState *mtstate, return NULL; /* trigger might have changed tuple */ - tuple = ExecMaterializeSlot(slot); + tuple = ExecHeapifySlot(slot); } else if (resultRelInfo->ri_FdwRoutine) { @@ -1007,7 +1007,7 @@ ExecUpdate(ModifyTableState *mtstate, return NULL; /* FDW might have changed tuple */ - tuple = ExecMaterializeSlot(slot); + tuple = ExecHeapifySlot(slot); /* * AFTER ROW Triggers or RETURNING expressions might reference the @@ -1123,7 +1123,7 @@ lreplace:; { *tupleid = hufd.ctid; slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot); - tuple = ExecMaterializeSlot(slot); + tuple = ExecHeapifySlot(slot); goto lreplace; } } diff --git a/src/backend/executor/tqueue.c b/src/backend/executor/tqueue.c index e9a5d5a1a5..9a47276274 100644 --- a/src/backend/executor/tqueue.c +++ b/src/backend/executor/tqueue.c @@ -58,7 +58,7 @@ tqueueReceiveSlot(TupleTableSlot *slot, DestReceiver *self) shm_mq_result result; /* Send the tuple itself. */ - tuple = ExecMaterializeSlot(slot); + tuple = ExecHeapifySlot(slot); result = shm_mq_send(tqueue->queue, tuple->t_len, tuple->t_data, false); /* Check for failure. */ diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index bc6d8246a7..a60be0049d 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -724,9 +724,12 @@ apply_handle_update(StringInfo s) */ if (found) { + HeapTuple tuple; + /* Process and store remote tuple in the slot */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); - ExecStoreTuple(localslot->tts_tuple, remoteslot, InvalidBuffer, false); + tuple = ExecHeapifySlot(localslot); + ExecStoreTuple(tuple, remoteslot, InvalidBuffer, false); slot_modify_cstrings(remoteslot, rel, newtup.values, newtup.changed); MemoryContextSwitchTo(oldctx); diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h index ff63cf3615..1fe15ede56 100644 --- a/src/include/access/heapam_common.h +++ b/src/include/access/heapam_common.h @@ -40,6 +40,8 @@ extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer, uint16 infomask, TransactionId xid); extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple); extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin); +typedef struct StorageSlotAmRoutine StorageSlotAmRoutine; +extern StorageSlotAmRoutine* heapam_storage_slot_handler(void); /* * SetHintBits() diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h index fa04a63b76..9539d67bec 100644 --- a/src/include/access/htup_details.h +++ b/src/include/access/htup_details.h @@ -20,6 +20,19 @@ #include "access/transam.h" #include "storage/bufpage.h" +/* + * Opaque tuple representation for executor's TupleTableSlot tts_storage + * (XXX This should probably live in a separate header) + */ +typedef struct HeapamTuple +{ + HeapTuple hst_heaptuple; + bool hst_slow; + long hst_off; + MinimalTuple hst_mintuple; /* minimal tuple, or NULL if none */ + HeapTupleData hst_minhdr; /* workspace for minimal-tuple-only case */ +} HeapamTuple; + /* * MaxTupleAttributeNumber limits the number of (user) columns in a tuple. * The key limit on this value is that the size of the fixed overhead for @@ -653,7 +666,7 @@ struct MinimalTupleData /* * GETSTRUCT - given a HeapTuple pointer, return address of the user data */ -#define GETSTRUCT(TUP) ((char *) ((TUP)->t_data) + (TUP)->t_data->t_hoff) +#define GETSTRUCT(TUP) ((char *) (((HeapTuple)(TUP))->t_data) + ((HeapTuple)(TUP))->t_data->t_hoff) /* * Accessor macros to be used with HeapTuple pointers. diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h index 55f4cce4ee..84dc293122 100644 --- a/src/include/executor/tuptable.h +++ b/src/include/executor/tuptable.h @@ -18,9 +18,25 @@ #include "access/tupdesc.h" #include "storage/buf.h" +/* + * Forward declare StorageAmRoutine to avoid including storageamapi.h here + */ +struct StorageSlotAmRoutine; + +/* + * Forward declare StorageTuple to avoid including storageamapi.h here + */ +typedef void *StorageTuple; + /*---------- * The executor stores tuples in a "tuple table" which is a List of - * independent TupleTableSlots. There are several cases we need to handle: + * independent TupleTableSlots. + * + * XXX The "html-commented out" text below no longer reflects reality, as + * physical tuples are now responsibility of storage AMs. But we have kept + * "minimal tuples". Adjust this comment! + * + * * * The Datum/isnull arrays of a TupleTableSlot serve double duty. When the * slot contains a virtual tuple, they are the authoritative data. When the @@ -82,11 +99,6 @@ * When tts_shouldFree is true, the physical tuple is "owned" by the slot * and should be freed when the slot's reference to the tuple is dropped. * - * If tts_buffer is not InvalidBuffer, then the slot is holding a pin - * on the indicated buffer page; drop the pin when we release the - * slot's reference to that buffer. (tts_shouldFree should always be - * false in such a case, since presumably tts_tuple is pointing at the - * buffer page.) * * tts_nvalid indicates the number of valid columns in the tts_values/isnull * arrays. When the slot is holding a "virtual" tuple this must be equal @@ -114,24 +126,21 @@ typedef struct TupleTableSlot { NodeTag type; bool tts_isempty; /* true = slot is empty */ - bool tts_shouldFree; /* should pfree tts_tuple? */ - bool tts_shouldFreeMin; /* should pfree tts_mintuple? */ - bool tts_slow; /* saved state for slot_deform_tuple */ - HeapTuple tts_tuple; /* physical tuple, or NULL if virtual */ + ItemPointerData tts_tid; /* XXX describe */ TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */ MemoryContext tts_mcxt; /* slot itself is in this context */ - Buffer tts_buffer; /* tuple's buffer, or InvalidBuffer */ + Oid tts_tableOid; /* XXX describe */ + Oid tts_tupleOid; /* XXX describe */ int tts_nvalid; /* # of valid values in tts_values */ + uint32 tts_speculativeToken; /* XXX describe */ + bool tts_shouldFree; + bool tts_shouldFreeMin; Datum *tts_values; /* current per-attribute values */ bool *tts_isnull; /* current per-attribute isnull flags */ - MinimalTuple tts_mintuple; /* minimal tuple, or NULL if none */ - HeapTupleData tts_minhdr; /* workspace for minimal-tuple-only case */ - long tts_off; /* saved state for slot_deform_tuple */ + struct StorageSlotAmRoutine *tts_storageslotam; /* storage AM */ + void *tts_storage; /* storage AM's opaque space */ } TupleTableSlot; -#define TTS_HAS_PHYSICAL_TUPLE(slot) \ - ((slot)->tts_tuple != NULL && (slot)->tts_tuple != &((slot)->tts_minhdr)) - /* * TupIsNull -- is a TupleTableSlot empty? */ @@ -143,9 +152,10 @@ extern TupleTableSlot *MakeTupleTableSlot(void); extern TupleTableSlot *ExecAllocTableSlot(List **tupleTable); extern void ExecResetTupleTable(List *tupleTable, bool shouldFree); extern TupleTableSlot *MakeSingleTupleTableSlot(TupleDesc tupdesc); +extern bool ExecSlotCompare(TupleTableSlot *slot1, TupleTableSlot *slot2); extern void ExecDropSingleTupleTableSlot(TupleTableSlot *slot); extern void ExecSetSlotDescriptor(TupleTableSlot *slot, TupleDesc tupdesc); -extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple, +extern TupleTableSlot *ExecStoreTuple(void *tuple, TupleTableSlot *slot, Buffer buffer, bool shouldFree); @@ -155,12 +165,14 @@ extern TupleTableSlot *ExecStoreMinimalTuple(MinimalTuple mtup, extern TupleTableSlot *ExecClearTuple(TupleTableSlot *slot); extern TupleTableSlot *ExecStoreVirtualTuple(TupleTableSlot *slot); extern TupleTableSlot *ExecStoreAllNullTuple(TupleTableSlot *slot); -extern HeapTuple ExecCopySlotTuple(TupleTableSlot *slot); +extern StorageTuple ExecCopySlotTuple(TupleTableSlot *slot); extern MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot *slot); -extern HeapTuple ExecFetchSlotTuple(TupleTableSlot *slot); +extern void ExecSlotUpdateTupleTableoid(TupleTableSlot *slot, Oid tableoid); +extern StorageTuple ExecFetchSlotTuple(TupleTableSlot *slot); extern MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot); extern Datum ExecFetchSlotTupleDatum(TupleTableSlot *slot); -extern HeapTuple ExecMaterializeSlot(TupleTableSlot *slot); +extern void ExecMaterializeSlot(TupleTableSlot *slot); +extern StorageTuple ExecHeapifySlot(TupleTableSlot *slot); extern TupleTableSlot *ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot); -- 2.14.1.windows.1