From 52f814db92329bc2f713860d6d222a5db2a64c26 Mon Sep 17 00:00:00 2001 From: Hari Babu Date: Thu, 29 Mar 2018 16:17:54 +1100 Subject: [PATCH 05/16] slot hooks are added to table 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 table AM routine. The slot utility functions are reorganized to use two table AM routines to satify the current functionality. Currently the slot supports minimum tuple also. JIT changes of Tuple deform are not proper. --- contrib/postgres_fdw/postgres_fdw.c | 2 +- src/backend/access/common/heaptuple.c | 316 +------------------- src/backend/access/heap/heapam_handler.c | 2 + src/backend/access/table/Makefile | 2 +- src/backend/access/table/tableam_common.c | 462 ++++++++++++++++++++++++++++++ src/backend/commands/copy.c | 4 +- src/backend/commands/createas.c | 2 +- src/backend/commands/matview.c | 2 +- src/backend/commands/trigger.c | 15 +- src/backend/executor/execExprInterp.c | 35 ++- src/backend/executor/execReplication.c | 90 ++---- src/backend/executor/execTuples.c | 286 ++++++++---------- src/backend/executor/nodeForeignscan.c | 2 +- src/backend/executor/nodeModifyTable.c | 26 +- src/backend/executor/tqueue.c | 2 +- src/backend/jit/llvm/llvmjit.c | 6 +- src/backend/jit/llvm/llvmjit_deform.c | 18 +- src/backend/jit/llvm/llvmjit_expr.c | 23 +- src/backend/jit/llvm/llvmjit_types.c | 3 +- src/backend/replication/logical/worker.c | 5 +- src/include/access/htup_details.h | 17 +- src/include/access/tableam_common.h | 37 +++ src/include/access/tableamapi.h | 2 + src/include/executor/tuptable.h | 61 ++-- src/include/jit/llvmjit.h | 3 +- 25 files changed, 782 insertions(+), 641 deletions(-) create mode 100644 src/backend/access/table/tableam_common.c diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 78b0f43ca8..c3cc5ad9d9 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -3944,7 +3944,7 @@ apply_returning_filter(PgFdwDirectModifyState *dmstate, */ if (dmstate->hasSystemCols) { - HeapTuple resultTup = ExecMaterializeSlot(resultSlot); + HeapTuple resultTup = ExecHeapifySlot(resultSlot); /* ctid */ if (dmstate->ctidAttno) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index 104172184f..25e48deaa1 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -57,6 +57,7 @@ #include "postgres.h" +#include "access/tableamapi.h" #include "access/sysattr.h" #include "access/tupdesc_details.h" #include "access/tuptoaster.h" @@ -80,7 +81,7 @@ /* * Return the missing value of an attribute, or NULL if there isn't one. */ -static Datum +Datum getmissingattr(TupleDesc tupleDesc, int attnum, bool *isnull) { @@ -1397,111 +1398,6 @@ heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, values[attnum] = getmissingattr(tupleDesc, attnum + 1, &isnull[attnum]); } -/* - * 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 */ - uint32 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. @@ -1517,89 +1413,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 or missing value 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)) - return getmissingattr(slot->tts_tupleDescriptor, attnum, isnull); - - /* - * 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_slottableam->slot_getattr(slot, attnum, isnull, false); } /* @@ -1611,40 +1425,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); - - attnum = slot->tts_nvalid; - - /* - * If tuple doesn't have all the atts indicated by tupleDesc, read the - * rest as NULLS or missing values. - */ - if (attnum < tdesc_natts) - slot_getmissingattrs(slot, attnum, tdesc_natts); - - slot->tts_nvalid = tdesc_natts; + slot->tts_slottableam->slot_virtualize_tuple(slot, slot->tts_tupleDescriptor->natts); } /* @@ -1655,43 +1436,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); - - attno = slot->tts_nvalid; - - /* - * If tuple doesn't have all the atts indicated by attnum, read the rest - * as NULLs or missing values - */ - if (attno < attnum) - slot_getmissingattrs(slot, attno, attnum); - - slot->tts_nvalid = attnum; + slot->tts_slottableam->slot_virtualize_tuple(slot, attnum); } /* @@ -1702,42 +1447,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, tupleDesc); - } + bool isnull; - /* - * 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; + slot->tts_slottableam->slot_getattr(slot, attnum, &isnull, false); - /* - * 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"); - - /* and let the tuple tell it */ - return heap_attisnull(tuple, attnum, tupleDesc); + return isnull; } /* @@ -1751,19 +1465,9 @@ bool slot_getsysattr(TupleTableSlot *slot, int attnum, Datum *value, bool *isnull) { - HeapTuple tuple = slot->tts_tuple; - Assert(attnum < 0); /* else caller error */ - if (tuple == NULL || - tuple == &(slot->tts_minhdr)) - { - /* No physical tuple, or minimal tuple, so fail */ - *value = (Datum) 0; - *isnull = true; - return false; - } - *value = heap_getsysattr(tuple, attnum, slot->tts_tupleDescriptor, isnull); - return true; + *value = slot->tts_slottableam->slot_getattr(slot, attnum, isnull, true); + return *isnull ? false : true; } /* diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 61086fe64c..96daa6a5ef 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -35,5 +35,7 @@ heap_tableam_handler(PG_FUNCTION_ARGS) amroutine->snapshot_satisfiesUpdate = HeapTupleSatisfiesUpdate; amroutine->snapshot_satisfiesVacuum = HeapTupleSatisfiesVacuum; + amroutine->slot_storageam = slot_tableam_handler; + PG_RETURN_POINTER(amroutine); } diff --git a/src/backend/access/table/Makefile b/src/backend/access/table/Makefile index 496b7387c6..ff0989ed24 100644 --- a/src/backend/access/table/Makefile +++ b/src/backend/access/table/Makefile @@ -12,6 +12,6 @@ subdir = src/backend/access/table top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global -OBJS = tableamapi.o +OBJS = tableamapi.o tableam_common.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/access/table/tableam_common.c b/src/backend/access/table/tableam_common.c new file mode 100644 index 0000000000..121cdbff99 --- /dev/null +++ b/src/backend/access/table/tableam_common.c @@ -0,0 +1,462 @@ +/*------------------------------------------------------------------------- + * + * tableam_common.c + * table access method code that is common across all pluggable + * table access modules + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/table/tableam_common.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/heapam.h" +#include "access/htup_details.h" +#include "access/tableam_common.h" +#include "access/subtrans.h" +#include "access/transam.h" +#include "access/xact.h" +#include "access/xlog.h" +#include "storage/bufmgr.h" +#include "storage/procarray.h" + +/*----------------------- + * + * Slot table AM 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 (HeapTupleHeaderGetNatts(stuple->hst_heaptuple->t_data) < + slot->tts_tupleDescriptor->natts) + { + MemoryContext oldContext = MemoryContextSwitchTo(slot->tts_mcxt); + + tup = heap_expand_tuple(stuple->hst_heaptuple, + slot->tts_tupleDescriptor); + if (slot->tts_shouldFree) + heap_freetuple(stuple->hst_heaptuple); + stuple->hst_heaptuple = tup; + slot->tts_shouldFree = true; + MemoryContextSwitchTo(oldContext); + } + + 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 + { + if (HeapTupleHeaderGetNatts(stuple->hst_heaptuple->t_data) + < slot->tts_tupleDescriptor->natts) + tup = minimal_expand_tuple(stuple->hst_heaptuple, + slot->tts_tupleDescriptor); + 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); + + attno = slot->tts_nvalid; + + /* + * If tuple doesn't have all the atts indicated by tupleDesc/upto attnum, + * read the rest as NULLS or missing values. + */ + if (attno < upto) + slot_getmissingattrs(slot, attno, upto); + + 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, TableTuple 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; + if (slot->tts_tupleDescriptor->tdhasoid) + slot->tts_tupleOid = HeapTupleGetOid((HeapTuple) tuple); + 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, bool noerror) +{ + 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 */ + { + if (noerror) + goto no_error_return; + elog(ERROR, "cannot extract system attribute from virtual tuple"); + } + if (tuple == &(stuple->hst_minhdr)) /* internal error */ + { + if (noerror) + goto no_error_return; + 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 or missing value 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)) + return getmissingattr(slot->tts_tupleDescriptor, attnum, isnull); + + /* + * 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]; + +no_error_return: + *isnull = true; + return (Datum) 0; +} + +SlotTableAmRoutine * +slot_tableam_handler(void) +{ + SlotTableAmRoutine *amroutine = palloc(sizeof(SlotTableAmRoutine)); + + 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/commands/copy.c b/src/backend/commands/copy.c index 3a66cb5025..72de5274f2 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -2712,7 +2712,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) @@ -2786,7 +2786,7 @@ CopyFrom(CopyState cstate) goto next_tuple; /* FDW might have changed tuple */ - tuple = ExecMaterializeSlot(slot); + tuple = ExecHeapifySlot(slot); /* * AFTER ROW Triggers might reference the tableoid diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c index 3d82edbf58..ff2b7b75e9 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 e1eb7c374b..1359455579 100644 --- a/src/backend/commands/matview.c +++ b/src/backend/commands/matview.c @@ -484,7 +484,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 57519fe8d6..f46fd0a935 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -2516,7 +2516,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; @@ -2597,7 +2597,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; @@ -2955,7 +2955,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; @@ -2997,7 +2997,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, if (newSlot != NULL) { slot = ExecFilterJunk(relinfo->ri_junkFilter, newSlot); - slottuple = ExecMaterializeSlot(slot); + slottuple = ExecHeapifySlot(slot); newtuple = slottuple; } @@ -3111,7 +3111,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; @@ -4246,14 +4246,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 9d6e25aae5..35b0da13ae 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -494,13 +494,15 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) Datum d; /* 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 */ - d = heap_getsysattr(innerslot->tts_tuple, attnum, - innerslot->tts_tupleDescriptor, - op->resnull); + d = slot_getattr(innerslot, attnum, op->resnull); *op->resvalue = d; EEO_NEXT(); @@ -512,13 +514,14 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) Datum d; /* 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 */ - d = heap_getsysattr(outerslot->tts_tuple, attnum, - outerslot->tts_tupleDescriptor, - op->resnull); + d = slot_getattr(outerslot, attnum, op->resnull); *op->resvalue = d; EEO_NEXT(); @@ -530,13 +533,15 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) Datum d; /* 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 */ - d = heap_getsysattr(scanslot->tts_tuple, attnum, - scanslot->tts_tupleDescriptor, - op->resnull); + d = slot_getattr(scanslot, attnum, op->resnull); *op->resvalue = d; EEO_NEXT(); diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c index 41e857e378..ec42cf7801 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()); @@ -217,59 +217,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. @@ -285,6 +232,7 @@ bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, TupleTableSlot *searchslot, TupleTableSlot *outslot) { + TupleTableSlot *scanslot; HeapTuple scantuple; HeapScanDesc scan; SnapshotData snap; @@ -298,6 +246,8 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, InitDirtySnapshot(snap); scan = heap_beginscan(rel, &snap, 0, NULL); + scanslot = MakeSingleTupleTableSlot(desc); + retry: found = false; @@ -306,12 +256,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; @@ -335,7 +285,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()); @@ -374,6 +324,7 @@ retry: } heap_endscan(scan); + ExecDropSingleTupleTableSlot(scanslot); return found; } @@ -418,7 +369,7 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot) ExecPartitionCheck(resultRelInfo, slot, estate, true); /* 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); @@ -456,6 +407,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); @@ -467,7 +419,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" */ @@ -485,21 +437,20 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, ExecPartitionCheck(resultRelInfo, slot, estate, true); /* 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); @@ -519,6 +470,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); @@ -530,7 +482,7 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate, resultRelInfo->ri_TrigDesc->trig_delete_before_row) { skip_tuple = !ExecBRDeleteTriggers(estate, epqstate, resultRelInfo, - &searchslot->tts_tuple->t_self, + tid, NULL); } @@ -539,11 +491,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 0beb7f80be..77fdc26fd0 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -82,6 +82,7 @@ #include "postgres.h" #include "access/htup_details.h" +#include "access/tableam_common.h" #include "access/tuptoaster.h" #include "funcapi.h" #include "catalog/pg_type.h" @@ -131,15 +132,16 @@ MakeTupleTableSlot(TupleDesc tupleDesc) slot->tts_isempty = true; slot->tts_shouldFree = false; slot->tts_shouldFreeMin = false; - slot->tts_tuple = NULL; slot->tts_fixedTupleDescriptor = tupleDesc != NULL; slot->tts_tupleDescriptor = tupleDesc; 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_slottableam = slot_tableam_handler(); + slot->tts_storage = NULL; if (tupleDesc != NULL) { @@ -236,6 +238,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 * @@ -353,7 +403,7 @@ ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */ * -------------------------------- */ TupleTableSlot * -ExecStoreTuple(HeapTuple tuple, +ExecStoreTuple(void *tuple, TupleTableSlot *slot, Buffer buffer, bool shouldFree) @@ -364,47 +414,27 @@ ExecStoreTuple(HeapTuple tuple, Assert(tuple != NULL); Assert(slot != NULL); Assert(slot->tts_tupleDescriptor != NULL); + Assert(slot->tts_slottableam != 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_slottableam->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_slottableam->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; } @@ -431,31 +461,19 @@ 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_slottableam->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_slottableam->slot_store_tuple(slot, mtup, false, true); /* Mark extracted state invalid */ slot->tts_nvalid = 0; @@ -480,25 +498,9 @@ ExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */ Assert(slot != NULL); /* - * Free the old physical tuple if necessary. - */ - 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. + * Tell the table AM to release any resource associated with the slot. */ - if (BufferIsValid(slot->tts_buffer)) - ReleaseBuffer(slot->tts_buffer); - - slot->tts_buffer = InvalidBuffer; + slot->tts_slottableam->slot_clear_tuple(slot); /* * Mark it empty. @@ -577,7 +579,7 @@ ExecStoreAllNullTuple(TupleTableSlot *slot) * however the "system columns" of the result will not be meaningful. * -------------------------------- */ -HeapTuple +TableTuple ExecCopySlotTuple(TupleTableSlot *slot) { /* @@ -586,20 +588,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_slottableam->slot_tuple(slot, true); } /* -------------------------------- @@ -618,28 +607,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) - { - if (HeapTupleHeaderGetNatts(slot->tts_tuple->t_data) - < slot->tts_tupleDescriptor->natts) - return minimal_expand_tuple(slot->tts_tuple, - slot->tts_tupleDescriptor); - else - return minimal_tuple_from_heap_tuple(slot->tts_tuple); - } + return slot->tts_slottableam->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_slottableam->slot_update_tableoid(slot, tableoid); } /* -------------------------------- @@ -657,38 +637,34 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot) * Hence, the result must be treated as read-only. * -------------------------------- */ -HeapTuple +TableTuple ExecFetchSlotTuple(TupleTableSlot *slot) { + MemoryContext oldContext; + TableTuple 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)) - { - if (HeapTupleHeaderGetNatts(slot->tts_tuple->t_data) < - slot->tts_tupleDescriptor->natts) - { - HeapTuple tuple; - MemoryContext oldContext = MemoryContextSwitchTo(slot->tts_mcxt); - - tuple = heap_expand_tuple(slot->tts_tuple, - slot->tts_tupleDescriptor); - MemoryContextSwitchTo(oldContext); - slot = ExecStoreTuple(tuple, slot, InvalidBuffer, true); - } - return slot->tts_tuple; - } + if (slot->tts_shouldFree) + return slot->tts_slottableam->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; } /* -------------------------------- @@ -708,6 +684,7 @@ MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot) { MemoryContext oldContext; + MinimalTuple tup; /* * sanity checks @@ -715,11 +692,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_slottableam->slot_min_tuple(slot, false); /* * Otherwise, copy or build a minimal tuple, and store it into the slot. @@ -729,18 +703,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; } /* -------------------------------- @@ -769,18 +736,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 @@ -788,12 +756,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. @@ -803,18 +767,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 @@ -824,17 +780,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; +TableTuple +ExecHeapifySlot(TupleTableSlot *slot) +{ + ExecMaterializeSlot(slot); + Assert(slot->tts_storage != NULL); - return slot->tts_tuple; + return slot->tts_slottableam->slot_tuple(slot, false); } /* -------------------------------- diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index a2a28b7ec2..869eebf274 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 85c67772de..4300ac44bd 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -181,7 +181,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; @@ -281,7 +281,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 @@ -322,7 +322,7 @@ ExecInsert(ModifyTableState *mtstate, return NULL; /* trigger might have changed tuple */ - tuple = ExecMaterializeSlot(slot); + tuple = ExecHeapifySlot(slot); } /* INSTEAD OF ROW INSERT Triggers */ @@ -335,7 +335,7 @@ ExecInsert(ModifyTableState *mtstate, return NULL; /* trigger might have changed tuple */ - tuple = ExecMaterializeSlot(slot); + tuple = ExecHeapifySlot(slot); newId = InvalidOid; } @@ -353,7 +353,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 @@ -697,7 +697,7 @@ ExecDelete(ModifyTableState *mtstate, */ if (slot->tts_isempty) ExecStoreAllNullTuple(slot); - tuple = ExecMaterializeSlot(slot); + tuple = ExecHeapifySlot(slot); tuple->t_tableOid = RelationGetRelid(resultRelationDesc); } else @@ -882,7 +882,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)) @@ -944,7 +944,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 @@ -963,7 +963,7 @@ ExecUpdate(ModifyTableState *mtstate, return NULL; /* trigger might have changed tuple */ - tuple = ExecMaterializeSlot(slot); + tuple = ExecHeapifySlot(slot); } /* INSTEAD OF ROW UPDATE Triggers */ @@ -977,7 +977,7 @@ ExecUpdate(ModifyTableState *mtstate, return NULL; /* trigger might have changed tuple */ - tuple = ExecMaterializeSlot(slot); + tuple = ExecHeapifySlot(slot); } else if (resultRelInfo->ri_FdwRoutine) { @@ -993,7 +993,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 @@ -1243,7 +1243,7 @@ lreplace:; { *tupleid = hufd.ctid; slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot); - tuple = ExecMaterializeSlot(slot); + tuple = ExecHeapifySlot(slot); goto lreplace; } } @@ -1719,7 +1719,7 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate, estate->es_result_relation_info = partrel; /* Get the heap tuple out of the given slot. */ - tuple = ExecMaterializeSlot(slot); + tuple = ExecHeapifySlot(slot); /* * If we're capturing transition tuples, we might need to convert from the diff --git a/src/backend/executor/tqueue.c b/src/backend/executor/tqueue.c index ecdbe7f79f..12b9fef894 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/jit/llvm/llvmjit.c b/src/backend/jit/llvm/llvmjit.c index daae964b1c..bcfb270954 100644 --- a/src/backend/jit/llvm/llvmjit.c +++ b/src/backend/jit/llvm/llvmjit.c @@ -77,7 +77,8 @@ LLVMValueRef FuncStrlen; LLVMValueRef FuncVarsizeAny; LLVMValueRef FuncSlotGetsomeattrs; LLVMValueRef FuncSlotGetmissingattrs; -LLVMValueRef FuncHeapGetsysattr; +LLVMValueRef FuncSlotGetattr; +LLVMValueRef FuncExecHeapifySlot; LLVMValueRef FuncMakeExpandedObjectReadOnlyInternal; LLVMValueRef FuncExecEvalArrayRefSubscript; LLVMValueRef FuncExecAggTransReparent; @@ -815,7 +816,8 @@ llvm_create_types(void) FuncVarsizeAny = LLVMGetNamedFunction(mod, "varsize_any"); FuncSlotGetsomeattrs = LLVMGetNamedFunction(mod, "slot_getsomeattrs"); FuncSlotGetmissingattrs = LLVMGetNamedFunction(mod, "slot_getmissingattrs"); - FuncHeapGetsysattr = LLVMGetNamedFunction(mod, "heap_getsysattr"); + FuncSlotGetattr = LLVMGetNamedFunction(mod, "slot_getattr"); + FuncExecHeapifySlot = LLVMGetNamedFunction(mod, "ExecHeapifySlot"); FuncMakeExpandedObjectReadOnlyInternal = LLVMGetNamedFunction(mod, "MakeExpandedObjectReadOnlyInternal"); FuncExecEvalArrayRefSubscript = LLVMGetNamedFunction(mod, "ExecEvalArrayRefSubscript"); FuncExecAggTransReparent = LLVMGetNamedFunction(mod, "ExecAggTransReparent"); diff --git a/src/backend/jit/llvm/llvmjit_deform.c b/src/backend/jit/llvm/llvmjit_deform.c index 795f67114e..ebe85cc1e7 100644 --- a/src/backend/jit/llvm/llvmjit_deform.c +++ b/src/backend/jit/llvm/llvmjit_deform.c @@ -167,13 +167,21 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc, int natts) l_load_struct_gep(b, v_slot, FIELDNO_TUPLETABLESLOT_ISNULL, "tts_ISNULL"); - v_slotoffp = LLVMBuildStructGEP(b, v_slot, FIELDNO_TUPLETABLESLOT_OFF, ""); - v_slowp = LLVMBuildStructGEP(b, v_slot, FIELDNO_TUPLETABLESLOT_SLOW, ""); + v_slotoffp = l_sizet_const(0); + v_slowp = l_pbool_const(true); v_nvalidp = LLVMBuildStructGEP(b, v_slot, FIELDNO_TUPLETABLESLOT_NVALID, ""); - v_tupleheaderp = - l_load_struct_gep(b, v_slot, FIELDNO_TUPLETABLESLOT_TUPLE, - "tupleheader"); + { + LLVMValueRef v_params[1]; + + v_params[0] = v_slot; + + v_tupleheaderp = LLVMBuildCall(b, + llvm_get_decl(mod, FuncExecHeapifySlot), + v_params, lengthof(v_params), + ""); + } + v_tuplep = l_load_struct_gep(b, v_tupleheaderp, FIELDNO_HEAPTUPLEDATA_DATA, "tuple"); diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c index 36c5f7d500..c8ecbdb766 100644 --- a/src/backend/jit/llvm/llvmjit_expr.c +++ b/src/backend/jit/llvm/llvmjit_expr.c @@ -408,9 +408,7 @@ llvm_compile_expr(ExprState *state) { int attnum = op->d.var.attnum; LLVMValueRef v_attnum; - LLVMValueRef v_tuple; - LLVMValueRef v_tupleDescriptor; - LLVMValueRef v_params[4]; + LLVMValueRef v_params[3]; LLVMValueRef v_syscol; LLVMValueRef v_slot; @@ -423,26 +421,13 @@ llvm_compile_expr(ExprState *state) Assert(op->d.var.attnum < 0); - v_tuple = l_load_struct_gep(b, v_slot, - FIELDNO_TUPLETABLESLOT_TUPLE, - "v.tuple"); - - /* - * Could optimize this a bit for fixed descriptors, but - * this shouldn't be that critical a path. - */ - v_tupleDescriptor = - l_load_struct_gep(b, v_slot, - FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR, - "v.tupledesc"); v_attnum = l_int32_const(attnum); - v_params[0] = v_tuple; + v_params[0] = v_slot; v_params[1] = v_attnum; - v_params[2] = v_tupleDescriptor; - v_params[3] = v_resnullp; + v_params[2] = v_resnullp; v_syscol = LLVMBuildCall(b, - llvm_get_decl(mod, FuncHeapGetsysattr), + llvm_get_decl(mod, FuncSlotGetattr), v_params, lengthof(v_params), ""); LLVMBuildStore(b, v_syscol, v_resvaluep); diff --git a/src/backend/jit/llvm/llvmjit_types.c b/src/backend/jit/llvm/llvmjit_types.c index 42304d0640..c3facd3e86 100644 --- a/src/backend/jit/llvm/llvmjit_types.c +++ b/src/backend/jit/llvm/llvmjit_types.c @@ -99,7 +99,8 @@ void *referenced_functions[] = varsize_any, slot_getsomeattrs, slot_getmissingattrs, - heap_getsysattr, + slot_getattr, + ExecHeapifySlot, MakeExpandedObjectReadOnlyInternal, ExecEvalArrayRefSubscript, ExecAggTransReparent, diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 0d2b795e39..091cfd00bd 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -751,9 +751,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/htup_details.h b/src/include/access/htup_details.h index 1867a70f6f..4db9303abb 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 @@ -665,7 +678,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. @@ -796,6 +809,8 @@ extern Datum fastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, /* prototypes for functions in common/heaptuple.c */ +extern Datum getmissingattr(TupleDesc tupleDesc, + int attnum, bool *isnull); extern Size heap_compute_data_size(TupleDesc tupleDesc, Datum *values, bool *isnull); extern void heap_fill_tuple(TupleDesc tupleDesc, diff --git a/src/include/access/tableam_common.h b/src/include/access/tableam_common.h index 78b24d76c7..fd44cd0b94 100644 --- a/src/include/access/tableam_common.h +++ b/src/include/access/tableam_common.h @@ -21,6 +21,7 @@ #include "access/transam.h" #include "access/xact.h" #include "access/xlog.h" +#include "executor/tuptable.h" #include "storage/bufpage.h" #include "storage/bufmgr.h" @@ -38,4 +39,40 @@ typedef enum HEAPTUPLE_DELETE_IN_PROGRESS /* deleting xact is still in progress */ } HTSV_Result; +/* + * slot table AM routine functions + */ +typedef void (*SlotStoreTuple_function) (TupleTableSlot *slot, + TableTuple tuple, + bool shouldFree, + bool minumumtuple); +typedef void (*SlotClearTuple_function) (TupleTableSlot *slot); +typedef Datum (*SlotGetattr_function) (TupleTableSlot *slot, + int attnum, bool *isnull, bool noerror); +typedef void (*SlotVirtualizeTuple_function) (TupleTableSlot *slot, int16 upto); + +typedef HeapTuple (*SlotGetTuple_function) (TupleTableSlot *slot, bool palloc_copy); +typedef MinimalTuple (*SlotGetMinTuple_function) (TupleTableSlot *slot, bool palloc_copy); + +typedef void (*SlotUpdateTableoid_function) (TupleTableSlot *slot, Oid tableoid); + +typedef void (*SpeculativeAbort_function) (Relation rel, + TupleTableSlot *slot); + +typedef struct SlotTableAmRoutine +{ + /* Operations on TupleTableSlot */ + SlotStoreTuple_function slot_store_tuple; + SlotVirtualizeTuple_function slot_virtualize_tuple; + SlotClearTuple_function slot_clear_tuple; + SlotGetattr_function slot_getattr; + SlotGetTuple_function slot_tuple; + SlotGetMinTuple_function slot_min_tuple; + SlotUpdateTableoid_function slot_update_tableoid; +} SlotTableAmRoutine; + +typedef SlotTableAmRoutine * (*slot_tableam_hook) (void); + +extern SlotTableAmRoutine * slot_tableam_handler(void); + #endif /* TABLEAM_COMMON_H */ diff --git a/src/include/access/tableamapi.h b/src/include/access/tableamapi.h index 4bd50b48f1..03d6cd42f3 100644 --- a/src/include/access/tableamapi.h +++ b/src/include/access/tableamapi.h @@ -41,6 +41,8 @@ typedef struct TableAmRoutine SnapshotSatisfiesUpdate_function snapshot_satisfiesUpdate; /* HeapTupleSatisfiesUpdate */ SnapshotSatisfiesVacuum_function snapshot_satisfiesVacuum; /* HeapTupleSatisfiesVacuum */ + slot_tableam_hook slot_storageam; + } TableAmRoutine; extern TableAmRoutine * GetTableAmRoutine(Oid amhandler); diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h index 0b874d9763..fb39c3ef27 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 SlotTableAmRoutine + */ +struct SlotTableAmRoutine; + +/* + * Forward declare TableTuple to avoid including table_common.h here + */ +typedef void *TableTuple; + /*---------- * 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 table 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 @@ -116,30 +128,24 @@ typedef struct TupleTableSlot bool tts_isempty; /* true = slot is empty */ bool tts_shouldFree; /* should pfree tts_tuple? */ bool tts_shouldFreeMin; /* should pfree tts_mintuple? */ -#define FIELDNO_TUPLETABLESLOT_SLOW 4 - bool tts_slow; /* saved state for slot_deform_tuple */ -#define FIELDNO_TUPLETABLESLOT_TUPLE 5 - HeapTuple tts_tuple; /* physical tuple, or NULL if virtual */ -#define FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR 6 +#define FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR 5 TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */ MemoryContext tts_mcxt; /* slot itself is in this context */ - Buffer tts_buffer; /* tuple's buffer, or InvalidBuffer */ -#define FIELDNO_TUPLETABLESLOT_NVALID 9 +#define FIELDNO_TUPLETABLESLOT_NVALID 7 int tts_nvalid; /* # of valid values in tts_values */ -#define FIELDNO_TUPLETABLESLOT_VALUES 10 +#define FIELDNO_TUPLETABLESLOT_VALUES 8 Datum *tts_values; /* current per-attribute values */ -#define FIELDNO_TUPLETABLESLOT_ISNULL 11 +#define FIELDNO_TUPLETABLESLOT_ISNULL 9 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 */ -#define FIELDNO_TUPLETABLESLOT_OFF 14 - uint32 tts_off; /* saved state for slot_deform_tuple */ bool tts_fixedTupleDescriptor; /* descriptor can't be changed */ + ItemPointerData tts_tid; /* XXX describe */ + Oid tts_tableOid; /* XXX describe */ + Oid tts_tupleOid; /* XXX describe */ + uint32 tts_speculativeToken; /* XXX describe */ + struct SlotTableAmRoutine *tts_slottableam; /* table AM */ + void *tts_storage; /* table 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? */ @@ -151,9 +157,10 @@ extern TupleTableSlot *MakeTupleTableSlot(TupleDesc desc); extern TupleTableSlot *ExecAllocTableSlot(List **tupleTable, TupleDesc desc); 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); @@ -163,12 +170,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 TableTuple ExecCopySlotTuple(TupleTableSlot *slot); extern MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot *slot); -extern HeapTuple ExecFetchSlotTuple(TupleTableSlot *slot); +extern void ExecSlotUpdateTupleTableoid(TupleTableSlot *slot, Oid tableoid); +extern TableTuple ExecFetchSlotTuple(TupleTableSlot *slot); extern MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot); extern Datum ExecFetchSlotTupleDatum(TupleTableSlot *slot); -extern HeapTuple ExecMaterializeSlot(TupleTableSlot *slot); +extern void ExecMaterializeSlot(TupleTableSlot *slot); +extern TableTuple ExecHeapifySlot(TupleTableSlot *slot); extern TupleTableSlot *ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot); diff --git a/src/include/jit/llvmjit.h b/src/include/jit/llvmjit.h index b0093db49d..0be8ee16c6 100644 --- a/src/include/jit/llvmjit.h +++ b/src/include/jit/llvmjit.h @@ -79,7 +79,8 @@ extern LLVMValueRef FuncStrlen; extern LLVMValueRef FuncVarsizeAny; extern LLVMValueRef FuncSlotGetsomeattrs; extern LLVMValueRef FuncSlotGetmissingattrs; -extern LLVMValueRef FuncHeapGetsysattr; +extern LLVMValueRef FuncSlotGetattr; +extern LLVMValueRef FuncExecHeapifySlot; extern LLVMValueRef FuncMakeExpandedObjectReadOnlyInternal; extern LLVMValueRef FuncExecEvalArrayRefSubscript; extern LLVMValueRef FuncExecAggTransReparent; -- 2.16.1.windows.4