1170 lines
37 KiB
C++
Executable File
1170 lines
37 KiB
C++
Executable File
/* -------------------------------------------------------------------------
|
|
*
|
|
* execTuples.cpp
|
|
* Routines dealing with TupleTableSlots. These are used for resource
|
|
* management associated with tuples (eg, releasing buffer pins for
|
|
* tuples in disk buffers, or freeing the memory occupied by transient
|
|
* tuples). Slots also provide access abstraction that lets us implement
|
|
* "virtual" tuples to reduce data-copying overhead.
|
|
*
|
|
* Routines dealing with the type information for tuples. Currently,
|
|
* the type information for a tuple is an array of FormData_pg_attribute.
|
|
* This information is needed by routines manipulating tuples
|
|
* (getattribute, formtuple, etc.).
|
|
*
|
|
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
|
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* src/gausskernel/runtime/executor/execTuples.cpp
|
|
*
|
|
* -------------------------------------------------------------------------
|
|
* INTERFACE ROUTINES
|
|
*
|
|
* SLOT CREATION/DESTRUCTION
|
|
* MakeTupleTableSlot - create an empty slot
|
|
* ExecAllocTableSlot - create a slot within a tuple table
|
|
* ExecResetTupleTable - clear and optionally delete a tuple table
|
|
* MakeSingleTupleTableSlot - make a standalone slot, set its descriptor
|
|
* ExecDropSingleTupleTableSlot - destroy a standalone slot
|
|
*
|
|
* SLOT ACCESSORS
|
|
* ExecSetSlotDescriptor - set a slot's tuple descriptor
|
|
* ExecStoreTuple - store a physical tuple in the slot
|
|
* ExecStoreMinimalTuple - store a minimal physical tuple in the slot
|
|
* ExecClearTuple - clear contents of a slot
|
|
* ExecStoreVirtualTuple - mark slot as containing a virtual tuple
|
|
* ExecCopySlotTuple - build a physical tuple from a slot
|
|
* ExecCopySlotMinimalTuple - build a minimal physical tuple from a slot
|
|
* ExecMaterializeSlot - convert virtual to physical storage
|
|
* ExecCopySlot - copy one slot's contents to another
|
|
*
|
|
* CONVENIENCE INITIALIZATION ROUTINES
|
|
* ExecInitResultTupleSlot \ convenience routines to initialize
|
|
* ExecInitScanTupleSlot \ the various tuple slots for nodes
|
|
* ExecInitExtraTupleSlot / which store copies of tuples.
|
|
* ExecInitNullTupleSlot /
|
|
*
|
|
* Routines that probably belong somewhere else:
|
|
* ExecTypeFromTL - form a TupleDesc from a target list
|
|
*
|
|
* EXAMPLE OF HOW TABLE ROUTINES WORK
|
|
* Suppose we have a query such as SELECT emp.name FROM emp and we have
|
|
* a single SeqScan node in the query plan.
|
|
*
|
|
* At ExecutorStart()
|
|
* ----------------
|
|
* - ExecInitSeqScan() calls ExecInitScanTupleSlot() and
|
|
* ExecInitResultTupleSlot() to construct TupleTableSlots
|
|
* for the tuples returned by the access methods and the
|
|
* tuples resulting from performing target list projections.
|
|
*
|
|
* During ExecutorRun()
|
|
* ----------------
|
|
* - SeqNext() calls ExecStoreTuple() to place the tuple returned
|
|
* by the access methods into the scan tuple slot.
|
|
*
|
|
* - ExecSeqScan() calls ExecStoreTuple() to take the result
|
|
* tuple from ExecProject() and place it into the result tuple slot.
|
|
*
|
|
* - ExecutePlan() calls ExecSelect(), which passes the result slot
|
|
* to printtup(), which uses getallattrs() to extract the
|
|
* individual Datums for printing.
|
|
*
|
|
* At ExecutorEnd()
|
|
* ----------------
|
|
* - EndPlan() calls ExecResetTupleTable() to clean up any remaining
|
|
* tuples left over from executing the query.
|
|
*
|
|
* The important thing to watch in the executor code is how pointers
|
|
* to the slots containing tuples are passed instead of the tuples
|
|
* themselves. This facilitates the communication of related information
|
|
* (such as whether or not a tuple should be pfreed, what buffer contains
|
|
* this tuple, the tuple's tuple descriptor, etc). It also allows us
|
|
* to avoid physically constructing projection tuples in many cases.
|
|
*/
|
|
#include "postgres.h"
|
|
#include "knl/knl_variable.h"
|
|
|
|
#include "funcapi.h"
|
|
#include "access/tableam.h"
|
|
#include "catalog/pg_type.h"
|
|
#include "nodes/nodeFuncs.h"
|
|
#include "storage/buf/bufmgr.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/lsyscache.h"
|
|
#include "utils/typcache.h"
|
|
#include "utils/memutils.h"
|
|
|
|
static TupleDesc ExecTypeFromTLInternal(List* target_list, bool has_oid, bool skip_junk, bool mark_dropped = false, TableAmType tam = TAM_HEAP);
|
|
|
|
/* ----------------------------------------------------------------
|
|
* tuple table create/delete functions
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
/* --------------------------------
|
|
* MakeTupleTableSlot
|
|
*
|
|
* Basic routine to make an empty TupleTableSlot.
|
|
* --------------------------------
|
|
*/
|
|
TupleTableSlot* MakeTupleTableSlot(bool has_tuple_mcxt, TableAmType tupslotTableAm)
|
|
{
|
|
TupleTableSlot* slot = makeNode(TupleTableSlot);
|
|
Assert(tupslotTableAm == TAM_HEAP || tupslotTableAm == TAM_USTORE);
|
|
|
|
slot->tts_isempty = true;
|
|
slot->tts_shouldFree = false;
|
|
slot->tts_shouldFreeMin = false;
|
|
slot->tts_tuple = NULL;
|
|
slot->tts_tupleDescriptor = NULL;
|
|
#ifdef PGXC
|
|
slot->tts_shouldFreeRow = false;
|
|
slot->tts_dataRow = NULL;
|
|
slot->tts_dataLen = -1;
|
|
slot->tts_attinmeta = NULL;
|
|
#endif
|
|
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_per_tuple_mcxt = has_tuple_mcxt ? AllocSetContextCreate(slot->tts_mcxt,
|
|
"SlotPerTupleMcxt",
|
|
ALLOCSET_DEFAULT_MINSIZE,
|
|
ALLOCSET_DEFAULT_INITSIZE,
|
|
ALLOCSET_DEFAULT_MAXSIZE) : NULL;
|
|
|
|
slot->tts_tupslotTableAm = tupslotTableAm;
|
|
|
|
return slot;
|
|
}
|
|
|
|
/* --------------------------------
|
|
* ExecAllocTableSlot
|
|
*
|
|
* Create a tuple table slot within a tuple table (which is just a List).
|
|
* --------------------------------
|
|
*/
|
|
TupleTableSlot* ExecAllocTableSlot(List** tuple_table, TableAmType tupslotTableAm)
|
|
{
|
|
TupleTableSlot* slot = MakeTupleTableSlot();
|
|
|
|
*tuple_table = lappend(*tuple_table, slot);
|
|
|
|
slot->tts_tupslotTableAm = tupslotTableAm;
|
|
|
|
return slot;
|
|
}
|
|
|
|
/* --------------------------------
|
|
* ExecResetTupleTable
|
|
*
|
|
* This releases any resources (buffer pins, tupdesc refcounts)
|
|
* held by the tuple table, and optionally releases the memory
|
|
* occupied by the tuple table data structure.
|
|
* It is expected that this routine be called by EndPlan().
|
|
* --------------------------------
|
|
*/
|
|
void ExecResetTupleTable(List* tuple_table, /* tuple table */
|
|
bool should_free) /* true if we should free memory */
|
|
{
|
|
ListCell* lc = NULL;
|
|
|
|
foreach (lc, tuple_table) {
|
|
TupleTableSlot* slot = (TupleTableSlot*)lfirst(lc);
|
|
|
|
/* Sanity checks */
|
|
Assert(IsA(slot, TupleTableSlot));
|
|
|
|
/* Always release resources and reset the slot to empty */
|
|
(void)ExecClearTuple(slot);
|
|
if (slot->tts_tupleDescriptor) {
|
|
ReleaseTupleDesc(slot->tts_tupleDescriptor);
|
|
slot->tts_tupleDescriptor = NULL;
|
|
}
|
|
|
|
/* If shouldFree, release memory occupied by the slot itself */
|
|
if (should_free) {
|
|
if (slot->tts_values)
|
|
pfree_ext(slot->tts_values);
|
|
if (slot->tts_isnull)
|
|
pfree_ext(slot->tts_isnull);
|
|
if (slot->tts_per_tuple_mcxt)
|
|
MemoryContextDelete(slot->tts_per_tuple_mcxt);
|
|
pfree_ext(slot);
|
|
}
|
|
}
|
|
|
|
/* If shouldFree, release the list structure */
|
|
if (should_free)
|
|
list_free_ext(tuple_table);
|
|
}
|
|
|
|
TupleTableSlot* ExecMakeTupleSlot(Tuple tuple, TableScanDesc tableScan, TupleTableSlot* slot, TableAmType tableAm)
|
|
{
|
|
if (unlikely(RELATION_CREATE_BUCKET(tableScan->rs_rd))) {
|
|
tableScan = ((HBktTblScanDesc)tableScan)->currBktScan;
|
|
}
|
|
|
|
if (tuple != NULL) {
|
|
Assert(tableScan != NULL);
|
|
slot->tts_tupslotTableAm = tableAm;
|
|
return ExecStoreTuple(tuple, /* tuple to store */
|
|
slot, /* slot to store in */
|
|
tableScan->rs_cbuf, /* buffer associated with this tuple */
|
|
false); /* don't pfree this pointer */
|
|
}
|
|
|
|
return ExecClearTuple(slot);
|
|
}
|
|
|
|
/* --------------------------------
|
|
* MakeSingleTupleTableSlot
|
|
*
|
|
* This is a convenience routine for operations that need a
|
|
* standalone TupleTableSlot not gotten from the main executor
|
|
* tuple table. It makes a single slot and initializes it
|
|
* to use the given tuple descriptor.
|
|
* --------------------------------
|
|
*/
|
|
TupleTableSlot* MakeSingleTupleTableSlot(TupleDesc tup_desc, bool allocSlotCxt, TableAmType tupslotTableAm)
|
|
{
|
|
bool bHasSlotMemContxt = true;
|
|
|
|
if(allocSlotCxt) {
|
|
bHasSlotMemContxt = false;
|
|
}
|
|
|
|
TupleTableSlot* slot = MakeTupleTableSlot(bHasSlotMemContxt, tupslotTableAm);
|
|
ExecSetSlotDescriptor(slot, tup_desc);
|
|
|
|
return slot;
|
|
}
|
|
|
|
/* --------------------------------
|
|
* ExecDropSingleTupleTableSlot
|
|
*
|
|
* Release a TupleTableSlot made with MakeSingleTupleTableSlot.
|
|
* DON'T use this on a slot that's part of a tuple table list!
|
|
* --------------------------------
|
|
*/
|
|
void ExecDropSingleTupleTableSlot(TupleTableSlot* slot)
|
|
{
|
|
/* This should match ExecResetTupleTable's processing of one slot */
|
|
Assert(IsA(slot, TupleTableSlot));
|
|
(void)ExecClearTuple(slot);
|
|
if (slot->tts_tupleDescriptor != NULL) {
|
|
ReleaseTupleDesc(slot->tts_tupleDescriptor);
|
|
}
|
|
|
|
if (slot->tts_values != NULL) {
|
|
pfree_ext(slot->tts_values);
|
|
}
|
|
|
|
if (slot->tts_isnull != NULL) {
|
|
pfree_ext(slot->tts_isnull);
|
|
}
|
|
|
|
if (slot->tts_per_tuple_mcxt != NULL) {
|
|
MemoryContextDelete(slot->tts_per_tuple_mcxt);
|
|
}
|
|
pfree_ext(slot);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* tuple table slot accessor functions
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
/* --------------------------------
|
|
* ExecSetSlotDescriptor
|
|
*
|
|
* This function is used to set the tuple descriptor associated
|
|
* with the slot's tuple. The passed descriptor must have lifespan
|
|
* at least equal to the slot's. If it is a reference-counted descriptor
|
|
* then the reference count is incremented for as long as the slot holds
|
|
* a reference.
|
|
* --------------------------------
|
|
*/
|
|
void ExecSetSlotDescriptor(TupleTableSlot* slot, /* slot to change */
|
|
TupleDesc tup_desc) /* new tuple descriptor */
|
|
{
|
|
/* For safety, make sure slot is empty before changing it */
|
|
(void)ExecClearTuple(slot);
|
|
|
|
/*
|
|
* Release any old descriptor. Also release old Datum/isnull arrays if
|
|
* present (we don't bother to check if they could be re-used).
|
|
*/
|
|
if (slot->tts_tupleDescriptor != NULL) {
|
|
ReleaseTupleDesc(slot->tts_tupleDescriptor);
|
|
}
|
|
#ifdef PGXC
|
|
/* XXX there in no routine to release AttInMetadata instance */
|
|
if (slot->tts_attinmeta != NULL) {
|
|
slot->tts_attinmeta = NULL;
|
|
}
|
|
#endif
|
|
|
|
if (slot->tts_values != NULL) {
|
|
pfree_ext(slot->tts_values);
|
|
}
|
|
if (slot->tts_isnull != NULL) {
|
|
pfree_ext(slot->tts_isnull);
|
|
}
|
|
/*
|
|
* Install the new descriptor; if it's refcounted, bump its refcount.
|
|
*/
|
|
slot->tts_tupleDescriptor = tup_desc;
|
|
PinTupleDesc(tup_desc);
|
|
|
|
/*
|
|
* Allocate Datum/isnull arrays of the appropriate size. These must have
|
|
* the same lifetime as the slot, so allocate in the slot's own context.
|
|
*/
|
|
slot->tts_values = (Datum*)MemoryContextAlloc(slot->tts_mcxt, tup_desc->natts * sizeof(Datum));
|
|
slot->tts_isnull = (bool*)MemoryContextAlloc(slot->tts_mcxt, tup_desc->natts * sizeof(bool));
|
|
}
|
|
|
|
/* --------------------------------
|
|
* ExecStoreTuple
|
|
*
|
|
* This function is used to store a physical tuple into a specified
|
|
* slot in the tuple table.
|
|
*
|
|
* tuple: tuple to store
|
|
* slot: slot to store it in
|
|
* buffer: disk buffer if tuple is in a disk page, else InvalidBuffer
|
|
* shouldFree: true if ExecClearTuple should pfree_ext() the tuple
|
|
* when done with it
|
|
*
|
|
* If 'buffer' is not InvalidBuffer, the tuple table code acquires a pin
|
|
* on the buffer which is held until the slot is cleared, so that the tuple
|
|
* won't go away on us.
|
|
*
|
|
* shouldFree is normally set 'true' for tuples constructed on-the-fly.
|
|
* It must always be 'false' for tuples that are stored in disk pages,
|
|
* since we don't want to try to pfree those.
|
|
*
|
|
* Another case where it is 'false' is when the referenced tuple is held
|
|
* in a tuple table slot belonging to a lower-level executor Proc node.
|
|
* In this case the lower-level slot retains ownership and responsibility
|
|
* for eventually releasing the tuple. When this method is used, we must
|
|
* be certain that the upper-level Proc node will lose interest in the tuple
|
|
* sooner than the lower-level one does! If you're not certain, copy the
|
|
* lower-level tuple with heap_copytuple and let the upper-level table
|
|
* slot assume ownership of the copy!
|
|
*
|
|
* Return value is just the passed-in slot pointer.
|
|
*
|
|
* NOTE: before PostgreSQL 8.1, this function would accept a NULL tuple
|
|
* pointer and effectively behave like ExecClearTuple (though you could
|
|
* still specify a buffer to pin, which would be an odd combination).
|
|
* This saved a couple lines of code in a few places, but seemed more likely
|
|
* to mask logic errors than to be really useful, so it's now disallowed.
|
|
* --------------------------------
|
|
*/
|
|
TupleTableSlot* ExecStoreTuple(Tuple tuple, TupleTableSlot* slot, Buffer buffer, bool should_free)
|
|
{
|
|
/*
|
|
* sanity checks
|
|
*/
|
|
Assert(tuple != NULL);
|
|
Assert(slot != NULL);
|
|
Assert(slot->tts_tupleDescriptor != NULL);
|
|
|
|
tableam_tslot_store_tuple(tuple, slot, buffer, should_free);
|
|
|
|
return slot;
|
|
}
|
|
|
|
/* --------------------------------
|
|
* ExecStoreMinimalTuple
|
|
*
|
|
* Like ExecStoreTuple, but insert a "minimal" tuple into the slot.
|
|
*
|
|
* No 'buffer' parameter since minimal tuples are never stored in relations.
|
|
* --------------------------------
|
|
*/
|
|
TupleTableSlot* ExecStoreMinimalTuple(MinimalTuple mtup, TupleTableSlot* slot, bool should_free)
|
|
{
|
|
/*
|
|
* sanity checks
|
|
*/
|
|
Assert(mtup != NULL);
|
|
Assert(slot != NULL);
|
|
Assert(slot->tts_tupleDescriptor != NULL);
|
|
|
|
/*
|
|
* store the minimal tuple in the slot.
|
|
*/
|
|
tableam_tslot_store_minimal_tuple(mtup, slot, should_free);
|
|
|
|
return slot;
|
|
}
|
|
|
|
/* --------------------------------
|
|
* ExecClearTuple
|
|
*
|
|
* This function is used to clear out a slot in the tuple table.
|
|
*
|
|
* NB: only the tuple is cleared, not the tuple descriptor (if any).
|
|
* --------------------------------
|
|
*/
|
|
TupleTableSlot* ExecClearTuple(TupleTableSlot* slot) /* return: slot passed slot in which to store tuple */
|
|
{
|
|
/*
|
|
* sanity checks
|
|
*/
|
|
Assert(slot != NULL);
|
|
|
|
/*
|
|
* clear the physical tuple or minimal tuple if present via TableAm.
|
|
*/
|
|
if (slot->tts_shouldFree || slot->tts_shouldFreeMin)
|
|
{
|
|
Assert(slot->tts_tupleDescriptor != NULL);
|
|
tableam_tslot_clear(slot);
|
|
}
|
|
|
|
/*
|
|
* tts_tuple may still be valid if tts_shouldFree is false, Original caller doesn't want this slot to free the tuple.
|
|
*/
|
|
slot->tts_tuple = NULL;
|
|
slot->tts_mintuple = NULL;
|
|
slot->tts_shouldFree = false;
|
|
slot->tts_shouldFreeMin = false;
|
|
|
|
#ifdef PGXC
|
|
if (slot->tts_shouldFreeRow) {
|
|
pfree_ext(slot->tts_dataRow);
|
|
}
|
|
slot->tts_shouldFreeRow = false;
|
|
slot->tts_dataRow = NULL;
|
|
slot->tts_dataLen = -1;
|
|
slot->tts_xcnodeoid = 0;
|
|
#endif
|
|
|
|
/*
|
|
* 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 it empty.
|
|
*/
|
|
slot->tts_isempty = true;
|
|
slot->tts_nvalid = 0;
|
|
|
|
// Row uncompression use slot->tts_per_tuple_mcxt in some case, So we need
|
|
// reset memory context. This memory context is introduced by PGXC and it only used
|
|
// in function 'slot_deform_datarow'. PGXC also do reset in function 'FetchTuple'.
|
|
// So it is safe
|
|
//
|
|
ResetSlotPerTupleContext(slot);
|
|
return slot;
|
|
}
|
|
|
|
/* --------------------------------
|
|
* ExecStoreVirtualTuple
|
|
* Mark a slot as containing a virtual tuple.
|
|
*
|
|
* The protocol for loading a slot with virtual tuple data is:
|
|
* * Call ExecClearTuple to mark the slot empty.
|
|
* * Store data into the Datum/isnull arrays.
|
|
* * Call ExecStoreVirtualTuple to mark the slot valid.
|
|
* This is a bit unclean but it avoids one round of data copying.
|
|
* --------------------------------
|
|
*/
|
|
TupleTableSlot* ExecStoreVirtualTuple(TupleTableSlot* slot)
|
|
{
|
|
/*
|
|
* sanity checks
|
|
*/
|
|
Assert(slot != NULL);
|
|
Assert(slot->tts_tupleDescriptor != NULL);
|
|
Assert(slot->tts_isempty);
|
|
|
|
slot->tts_isempty = false;
|
|
slot->tts_nvalid = slot->tts_tupleDescriptor->natts;
|
|
|
|
if (slot->tts_tupslotTableAm != slot->tts_tupleDescriptor->tdTableAmType) {
|
|
// XXX: Should tts_tupleDescriptor be cloned before changing its contents
|
|
// as some time it can be direct reference to the rd_att in RelationData.
|
|
slot->tts_tupleDescriptor->tdTableAmType = slot->tts_tupslotTableAm;
|
|
}
|
|
|
|
return slot;
|
|
}
|
|
|
|
/* --------------------------------
|
|
* ExecStoreAllNullTuple
|
|
* Set up the slot to contain a null in every column.
|
|
*
|
|
* At first glance this might sound just like ExecClearTuple, but it's
|
|
* entirely different: the slot ends up full, not empty.
|
|
* --------------------------------
|
|
*/
|
|
TupleTableSlot* ExecStoreAllNullTuple(TupleTableSlot* slot)
|
|
{
|
|
/*
|
|
* sanity checks
|
|
*/
|
|
Assert(slot != NULL);
|
|
Assert(slot->tts_tupleDescriptor != NULL);
|
|
|
|
/* Clear any old contents */
|
|
(void)ExecClearTuple(slot);
|
|
|
|
/*
|
|
* Fill all the columns of the virtual tuple with nulls
|
|
*/
|
|
errno_t rc = EOK;
|
|
|
|
rc = memset_s(slot->tts_values,
|
|
slot->tts_tupleDescriptor->natts * sizeof(Datum),
|
|
0,
|
|
slot->tts_tupleDescriptor->natts * sizeof(Datum));
|
|
securec_check(rc, "\0", "\0");
|
|
rc = memset_s(slot->tts_isnull,
|
|
slot->tts_tupleDescriptor->natts * sizeof(bool),
|
|
true,
|
|
slot->tts_tupleDescriptor->natts * sizeof(bool));
|
|
securec_check(rc, "\0", "\0");
|
|
|
|
return ExecStoreVirtualTuple(slot);
|
|
}
|
|
|
|
/* --------------------------------
|
|
* ExecCopySlotTuple
|
|
* Obtain a copy of a slot's regular physical tuple. The copy is
|
|
* palloc'd in the current memory context.
|
|
* The slot itself is undisturbed.
|
|
*
|
|
* This works even if the slot contains a virtual or minimal tuple;
|
|
* however the "system columns" of the result will not be meaningful.
|
|
* --------------------------------
|
|
*/
|
|
HeapTuple ExecCopySlotTuple(TupleTableSlot* slot)
|
|
{
|
|
/*
|
|
* sanity checks
|
|
*/
|
|
Assert(slot != NULL);
|
|
Assert(!slot->tts_isempty);
|
|
|
|
return tableam_tslot_copy_heap_tuple(slot);
|
|
}
|
|
|
|
/* --------------------------------
|
|
* ExecCopySlotMinimalTuple
|
|
* Obtain a copy of a slot's minimal physical tuple. The copy is
|
|
* palloc'd in the current memory context.
|
|
* The slot itself is undisturbed.
|
|
* --------------------------------
|
|
*/
|
|
MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot* slot, bool need_transform_anyarray)
|
|
{
|
|
/*
|
|
* sanity checks
|
|
*/
|
|
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 != NULL) {
|
|
return heap_copy_minimal_tuple(slot->tts_mintuple);
|
|
}
|
|
if (slot->tts_tuple != NULL) {
|
|
return heapFormMinimalTuple((HeapTuple) slot->tts_tuple,
|
|
slot->tts_tupleDescriptor,
|
|
(BufferIsValid(slot->tts_buffer) ? BufferGetPage(slot->tts_buffer) : NULL));
|
|
}
|
|
|
|
#ifdef PGXC
|
|
/*
|
|
* Ensure values are extracted from data row to the Datum array
|
|
*/
|
|
if (slot->tts_dataRow != NULL) {
|
|
tableam_tslot_getallattrs(slot);
|
|
}
|
|
#endif
|
|
/*
|
|
* Otherwise we need to build a tuple from the Datum array.
|
|
*/
|
|
return heap_form_minimal_tuple(slot->tts_tupleDescriptor, slot->tts_values, slot->tts_isnull);
|
|
}
|
|
|
|
/* --------------------------------
|
|
* ExecFetchSlotTuple
|
|
* Fetch the slot's regular physical tuple.
|
|
*
|
|
* If the slot contains a virtual tuple, we convert it to physical
|
|
* form. The slot retains ownership of the physical tuple.
|
|
* If it contains a minimal tuple we convert to regular form and store
|
|
* that in addition to the minimal tuple (not instead of, because
|
|
* callers may hold pointers to Datums within the minimal tuple).
|
|
*
|
|
* The main difference between this and ExecMaterializeSlot() is that this
|
|
* does not guarantee that the contained tuple is local storage.
|
|
* Hence, the result must be treated as read-only.
|
|
* --------------------------------
|
|
*/
|
|
HeapTuple ExecFetchSlotTuple(TupleTableSlot* slot)
|
|
{
|
|
/*
|
|
* sanity checks
|
|
*/
|
|
Assert(slot != NULL);
|
|
Assert(!slot->tts_isempty);
|
|
|
|
return tableam_tslot_get_heap_tuple(slot);
|
|
}
|
|
|
|
/* --------------------------------
|
|
* ExecFetchSlotMinimalTuple
|
|
* Fetch the slot's minimal physical tuple.
|
|
*
|
|
* If the slot contains a virtual tuple, we convert it to minimal
|
|
* physical form. The slot retains ownership of the minimal tuple.
|
|
* If it contains a regular tuple we convert to minimal form and store
|
|
* that in addition to the regular tuple (not instead of, because
|
|
* callers may hold pointers to Datums within the regular tuple).
|
|
*
|
|
* As above, the result must be treated as read-only.
|
|
* --------------------------------
|
|
*/
|
|
MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot* slot)
|
|
{
|
|
/*
|
|
* sanity checks
|
|
*/
|
|
Assert(slot != NULL);
|
|
|
|
return tableam_tslot_get_minimal_tuple(slot);
|
|
}
|
|
|
|
/* --------------------------------
|
|
* ExecFetchSlotTupleDatum
|
|
* Fetch the slot's tuple as a composite-type Datum.
|
|
*
|
|
* We convert the slot's contents to local physical-tuple form,
|
|
* and fill in the Datum header fields. Note that the result
|
|
* always points to storage owned by the slot.
|
|
* --------------------------------
|
|
*/
|
|
Datum ExecFetchSlotTupleDatum(TupleTableSlot* slot)
|
|
{
|
|
HeapTuple tup;
|
|
HeapTupleHeader td;
|
|
TupleDesc tup_desc;
|
|
|
|
/* Make sure we can scribble on the slot contents ... */
|
|
tup = ExecMaterializeSlot(slot);
|
|
/* ... and set up the composite-Datum header fields, in case not done */
|
|
td = tup->t_data;
|
|
tup_desc = slot->tts_tupleDescriptor;
|
|
HeapTupleHeaderSetDatumLength(td, tup->t_len);
|
|
HeapTupleHeaderSetTypeId(td, tup_desc->tdtypeid);
|
|
HeapTupleHeaderSetTypMod(td, tup_desc->tdtypmod);
|
|
return PointerGetDatum(td);
|
|
}
|
|
|
|
/* --------------------------------
|
|
* ExecMaterializeSlot
|
|
* 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.
|
|
*
|
|
* 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.
|
|
* --------------------------------
|
|
*/
|
|
HeapTuple ExecMaterializeSlot(TupleTableSlot* slot)
|
|
{
|
|
/*
|
|
* sanity checks
|
|
*/
|
|
Assert(slot != NULL);
|
|
Assert(!slot->tts_isempty);
|
|
|
|
return tableam_tslot_materialize(slot);
|
|
}
|
|
|
|
/* --------------------------------
|
|
* ExecCopySlot
|
|
* Copy the source slot's contents into the destination slot.
|
|
*
|
|
* The destination acquires a private copy that will not go away
|
|
* if the source is cleared.
|
|
*
|
|
* The caller must ensure the slots have compatible tupdescs.
|
|
* --------------------------------
|
|
*/
|
|
TupleTableSlot* ExecCopySlot(TupleTableSlot* dst_slot, TupleTableSlot* src_slot)
|
|
{
|
|
HeapTuple new_tuple;
|
|
MemoryContext old_context;
|
|
|
|
/*
|
|
* There might be ways to optimize this when the source is virtual, but
|
|
* for now just always build a physical copy. Make sure it is in the
|
|
* right context.
|
|
*/
|
|
old_context = MemoryContextSwitchTo(dst_slot->tts_mcxt);
|
|
new_tuple = ExecCopySlotTuple(src_slot);
|
|
MemoryContextSwitchTo(old_context);
|
|
|
|
return ExecStoreTuple(new_tuple, dst_slot, InvalidBuffer, true);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* convenience initialization routines
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
/* --------------------------------
|
|
* ExecInit{Result,Scan,Extra}TupleSlot
|
|
*
|
|
* These are convenience routines to initialize the specified slot
|
|
* in nodes inheriting the appropriate state. ExecInitExtraTupleSlot
|
|
* is used for initializing special-purpose slots.
|
|
* --------------------------------
|
|
*/
|
|
/* ----------------
|
|
* ExecInitResultTupleSlot
|
|
* ----------------
|
|
*/
|
|
void ExecInitResultTupleSlot(EState* estate, PlanState* plan_state, TableAmType tam)
|
|
{
|
|
plan_state->ps_ResultTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable, tam);
|
|
}
|
|
|
|
/* ----------------
|
|
* ExecInitScanTupleSlot
|
|
* ----------------
|
|
*/
|
|
void ExecInitScanTupleSlot(EState* estate, ScanState* scan_state, TableAmType tam)
|
|
{
|
|
scan_state->ss_ScanTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable, tam);
|
|
}
|
|
|
|
/* ----------------
|
|
* ExecInitExtraTupleSlot
|
|
* ----------------
|
|
*/
|
|
TupleTableSlot* ExecInitExtraTupleSlot(EState* estate, TableAmType tam)
|
|
{
|
|
return ExecAllocTableSlot(&estate->es_tupleTable, tam);
|
|
}
|
|
|
|
/* ----------------
|
|
* ExecInitNullTupleSlot
|
|
*
|
|
* Build a slot containing an all-nulls tuple of the given type.
|
|
* This is used as a substitute for an input tuple when performing an
|
|
* outer join.
|
|
* ----------------
|
|
*/
|
|
TupleTableSlot* ExecInitNullTupleSlot(EState* estate, TupleDesc tup_type)
|
|
{
|
|
TupleTableSlot* slot = ExecInitExtraTupleSlot(estate);
|
|
|
|
ExecSetSlotDescriptor(slot, tup_type);
|
|
|
|
return ExecStoreAllNullTuple(slot);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecTypeFromTL
|
|
*
|
|
* Generate a tuple descriptor for the result tuple of a targetlist.
|
|
* (A parse/plan tlist must be passed, not an ExprState tlist.)
|
|
* Note that resjunk columns, if any, are included in the result.
|
|
*
|
|
* Currently there are about 4 different places where we create
|
|
* TupleDescriptors. They should all be merged, or perhaps
|
|
* be rewritten to call BuildDesc().
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
TupleDesc ExecTypeFromTL(List* target_list, bool has_oid, bool mark_dropped, TableAmType tam)
|
|
{
|
|
return ExecTypeFromTLInternal(target_list, has_oid, false, mark_dropped, tam);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecCleanTypeFromTL
|
|
*
|
|
* Same as above, but resjunk columns are omitted from the result.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
TupleDesc ExecCleanTypeFromTL(List* target_list, bool has_oid, TableAmType tam)
|
|
{
|
|
return ExecTypeFromTLInternal(target_list, has_oid, true, false, tam);
|
|
}
|
|
|
|
static TupleDesc ExecTypeFromTLInternal(List* target_list, bool has_oid, bool skip_junk, bool mark_dropped, TableAmType tam)
|
|
{
|
|
TupleDesc type_info;
|
|
ListCell* l = NULL;
|
|
int len;
|
|
int cur_resno = 1;
|
|
|
|
if (skip_junk)
|
|
len = ExecCleanTargetListLength(target_list);
|
|
else
|
|
len = ExecTargetListLength(target_list);
|
|
type_info = CreateTemplateTupleDesc(len, has_oid, tam);
|
|
|
|
foreach (l, target_list) {
|
|
TargetEntry* tle = (TargetEntry*)lfirst(l);
|
|
|
|
if (skip_junk && tle->resjunk)
|
|
continue;
|
|
|
|
TupleDescInitEntry(
|
|
type_info, cur_resno, tle->resname, exprType((Node*)tle->expr), exprTypmod((Node*)tle->expr), 0);
|
|
TupleDescInitEntryCollation(type_info, cur_resno, exprCollation((Node*)tle->expr));
|
|
|
|
/* mark dropped column, maybe we can find another way some day */
|
|
if (mark_dropped && strstr(tle->resname, "........pg.dropped.")) {
|
|
type_info->attrs[cur_resno - 1]->attisdropped = true;
|
|
}
|
|
|
|
cur_resno++;
|
|
}
|
|
|
|
return type_info;
|
|
}
|
|
|
|
/*
|
|
* ExecTypeFromExprList - build a tuple descriptor from a list of Exprs
|
|
*
|
|
* Caller must also supply a list of field names (String nodes).
|
|
*/
|
|
TupleDesc ExecTypeFromExprList(List* expr_list, List* names_list, TableAmType tam)
|
|
{
|
|
TupleDesc type_info;
|
|
ListCell* le = NULL;
|
|
ListCell* ln = NULL;
|
|
int cur_resno = 1;
|
|
|
|
Assert(list_length(expr_list) == list_length(names_list));
|
|
|
|
type_info = CreateTemplateTupleDesc(list_length(expr_list), false, tam);
|
|
|
|
forboth(le, expr_list, ln, names_list)
|
|
{
|
|
Node* e = (Node*)lfirst(le);
|
|
char* n = strVal(lfirst(ln));
|
|
|
|
TupleDescInitEntry(type_info, cur_resno, n, exprType(e), exprTypmod(e), 0);
|
|
TupleDescInitEntryCollation(type_info, cur_resno, exprCollation(e));
|
|
cur_resno++;
|
|
}
|
|
|
|
return type_info;
|
|
}
|
|
|
|
/*
|
|
* BlessTupleDesc - make a completed tuple descriptor useful for SRFs
|
|
*
|
|
* Rowtype Datums returned by a function must contain valid type information.
|
|
* This happens "for free" if the tupdesc came from a relcache entry, but
|
|
* not if we have manufactured a tupdesc for a transient RECORD datatype.
|
|
* In that case we have to notify typcache.c of the existence of the type.
|
|
*/
|
|
TupleDesc BlessTupleDesc(TupleDesc tup_desc)
|
|
{
|
|
if (tup_desc->tdtypeid == RECORDOID && tup_desc->tdtypmod < 0) {
|
|
assign_record_type_typmod(tup_desc);
|
|
}
|
|
return tup_desc; /* just for notational convenience */
|
|
}
|
|
|
|
/*
|
|
* TupleDescGetSlot - Initialize a slot based on the supplied tupledesc
|
|
*
|
|
* Note: this is obsolete; it is sufficient to call BlessTupleDesc on
|
|
* the tupdesc. We keep it around just for backwards compatibility with
|
|
* existing user-written SRFs.
|
|
*/
|
|
TupleTableSlot* TupleDescGetSlot(TupleDesc tup_desc)
|
|
{
|
|
TupleTableSlot* slot = NULL;
|
|
|
|
/* The useful work is here */
|
|
BlessTupleDesc(tup_desc);
|
|
|
|
/* Make a standalone slot */
|
|
slot = MakeSingleTupleTableSlot(tup_desc);
|
|
|
|
/* Return the slot */
|
|
return slot;
|
|
}
|
|
|
|
/*
|
|
* TupleDescGetAttInMetadata - Build an AttInMetadata structure based on the
|
|
* supplied TupleDesc. AttInMetadata can be used in conjunction with C strings
|
|
* to produce a properly formed tuple.
|
|
*/
|
|
AttInMetadata* TupleDescGetAttInMetadata(TupleDesc tup_desc)
|
|
{
|
|
int natts = tup_desc->natts;
|
|
int i;
|
|
Oid att_type_id;
|
|
Oid att_in_func_id;
|
|
FmgrInfo* att_in_func_info = NULL;
|
|
Oid* att_io_params = NULL;
|
|
int32* att_typ_mods = NULL;
|
|
AttInMetadata* att_in_meta = NULL;
|
|
|
|
att_in_meta = (AttInMetadata*)palloc(sizeof(AttInMetadata));
|
|
|
|
/* "Bless" the tupledesc so that we can make rowtype datums with it */
|
|
att_in_meta->tupdesc = BlessTupleDesc(tup_desc);
|
|
|
|
/*
|
|
* Gather info needed later to call the "in" function for each attribute
|
|
*/
|
|
att_in_func_info = (FmgrInfo*)palloc0(natts * sizeof(FmgrInfo));
|
|
att_io_params = (Oid*)palloc0(natts * sizeof(Oid));
|
|
att_typ_mods = (int32*)palloc0(natts * sizeof(int32));
|
|
|
|
for (i = 0; i < natts; i++) {
|
|
/* Ignore dropped attributes */
|
|
if (!tup_desc->attrs[i]->attisdropped) {
|
|
att_type_id = tup_desc->attrs[i]->atttypid;
|
|
getTypeInputInfo(att_type_id, &att_in_func_id, &att_io_params[i]);
|
|
fmgr_info(att_in_func_id, &att_in_func_info[i]);
|
|
att_typ_mods[i] = tup_desc->attrs[i]->atttypmod;
|
|
}
|
|
}
|
|
att_in_meta->attinfuncs = att_in_func_info;
|
|
att_in_meta->attioparams = att_io_params;
|
|
att_in_meta->atttypmods = att_typ_mods;
|
|
|
|
return att_in_meta;
|
|
}
|
|
|
|
/*
|
|
* BuildTupleFromCStrings - build a HeapTuple given user data in C string form.
|
|
* values is an array of C strings, one for each attribute of the return tuple.
|
|
* A NULL string pointer indicates we want to create a NULL field.
|
|
*/
|
|
HeapTuple BuildTupleFromCStrings(AttInMetadata* att_in_meta, char** values)
|
|
{
|
|
TupleDesc tup_desc = att_in_meta->tupdesc;
|
|
int natts = tup_desc->natts;
|
|
Datum* d_values = NULL;
|
|
bool* nulls = NULL;
|
|
int i;
|
|
HeapTuple tuple;
|
|
|
|
d_values = (Datum*)palloc(natts * sizeof(Datum));
|
|
nulls = (bool*)palloc(natts * sizeof(bool));
|
|
|
|
/* Call the "in" function for each non-dropped attribute */
|
|
for (i = 0; i < natts; i++) {
|
|
if (!tup_desc->attrs[i]->attisdropped) {
|
|
/* Non-dropped attributes */
|
|
d_values[i] = InputFunctionCall(
|
|
&att_in_meta->attinfuncs[i], values[i], att_in_meta->attioparams[i], att_in_meta->atttypmods[i]);
|
|
|
|
nulls[i] = (values[i] == NULL);
|
|
} else {
|
|
/* Handle dropped attributes by setting to NULL */
|
|
d_values[i] = (Datum)0;
|
|
nulls[i] = true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Form a tuple
|
|
*/
|
|
tuple = (HeapTuple)tableam_tops_form_tuple(tup_desc, d_values, nulls, HEAP_TUPLE);
|
|
|
|
/*
|
|
* Release locally palloc'd space. XXX would probably be good to pfree
|
|
* values of pass-by-reference datums, as well.
|
|
*/
|
|
pfree_ext(d_values);
|
|
pfree_ext(nulls);
|
|
|
|
return tuple;
|
|
}
|
|
|
|
/*
|
|
* Functions for sending tuples to the frontend (or other specified destination)
|
|
* as though it is a SELECT result. These are used by utility commands that
|
|
* need to project directly to the destination and don't need or want full
|
|
* table function capability. Currently used by EXPLAIN and SHOW ALL.
|
|
*/
|
|
TupOutputState* begin_tup_output_tupdesc(DestReceiver* dest, TupleDesc tup_desc)
|
|
{
|
|
TupOutputState* tstate = NULL;
|
|
|
|
tstate = (TupOutputState*)palloc(sizeof(TupOutputState));
|
|
|
|
tstate->slot = MakeSingleTupleTableSlot(tup_desc);
|
|
tstate->dest = dest;
|
|
|
|
(*tstate->dest->rStartup)(tstate->dest, (int)CMD_SELECT, tup_desc);
|
|
|
|
return tstate;
|
|
}
|
|
|
|
/*
|
|
* write a single tuple
|
|
*/
|
|
void do_tup_output(TupOutputState* tstate, Datum* values, size_t values_len, const bool* is_null, size_t is_null_len)
|
|
{
|
|
Assert(values != NULL);
|
|
Assert(values_len != 0);
|
|
Assert(is_null != NULL);
|
|
Assert(is_null_len != 0);
|
|
TupleTableSlot* slot = tstate->slot;
|
|
int natts = slot->tts_tupleDescriptor->natts;
|
|
errno_t rc = EOK;
|
|
|
|
/* make sure the slot is clear */
|
|
(void)ExecClearTuple(slot);
|
|
|
|
/* insert data */
|
|
rc = memcpy_s(slot->tts_values, natts * sizeof(Datum), values, natts * sizeof(Datum));
|
|
securec_check(rc, "\0", "\0");
|
|
rc = memcpy_s(slot->tts_isnull, natts * sizeof(bool), is_null, natts * sizeof(bool));
|
|
securec_check(rc, "\0", "\0");
|
|
|
|
/* mark slot as containing a virtual tuple */
|
|
ExecStoreVirtualTuple(slot);
|
|
|
|
/* send the tuple to the receiver */
|
|
(*tstate->dest->receiveSlot)(slot, tstate->dest);
|
|
|
|
/* clean up */
|
|
(void)ExecClearTuple(slot);
|
|
}
|
|
|
|
/*
|
|
* write a chunk of text, breaking at newline characters
|
|
*
|
|
* Should only be used with a single-TEXT-attribute tupdesc.
|
|
*/
|
|
int do_text_output_multiline(TupOutputState* tstate, char* text)
|
|
{
|
|
Datum values[1];
|
|
bool is_null[1] = {false};
|
|
int tuple_count = 0;
|
|
|
|
while (*text) {
|
|
char* eol = NULL;
|
|
int len;
|
|
|
|
eol = strchr(text, '\n');
|
|
if (eol != NULL) {
|
|
len = eol - text;
|
|
eol++;
|
|
} else {
|
|
len = strlen(text);
|
|
eol = text;
|
|
eol += len;
|
|
}
|
|
|
|
values[0] = PointerGetDatum(cstring_to_text_with_len(text, len));
|
|
do_tup_output(tstate, values, 1, is_null, 1);
|
|
tuple_count++;
|
|
pfree(DatumGetPointer(values[0]));
|
|
text = eol;
|
|
}
|
|
return tuple_count;
|
|
}
|
|
|
|
void end_tup_output(TupOutputState* tstate)
|
|
{
|
|
(*tstate->dest->rShutdown)(tstate->dest);
|
|
/* note that destroying the dest is not ours to do */
|
|
ExecDropSingleTupleTableSlot(tstate->slot);
|
|
pfree_ext(tstate);
|
|
}
|
|
|
|
#ifdef PGXC
|
|
/* --------------------------------
|
|
* ExecStoreDataRowTuple
|
|
*
|
|
* Store a buffer in DataRow message format into the slot.
|
|
*
|
|
* --------------------------------
|
|
*/
|
|
TupleTableSlot* ExecStoreDataRowTuple(char* msg, size_t len, Oid msgnode_oid, TupleTableSlot* slot, bool should_free)
|
|
{
|
|
/*
|
|
* sanity checks
|
|
*/
|
|
Assert(msg != NULL);
|
|
Assert(len > 0);
|
|
Assert(slot != NULL);
|
|
Assert(slot->tts_tupleDescriptor != NULL);
|
|
|
|
/*
|
|
* Free any old physical tuple belonging to the slot.
|
|
*/
|
|
if (slot->tts_shouldFree && (HeapTuple)slot->tts_tuple != NULL) {
|
|
heap_freetuple((HeapTuple)slot->tts_tuple);
|
|
slot->tts_tuple = NULL;
|
|
slot->tts_shouldFree = false;
|
|
}
|
|
if (slot->tts_shouldFreeMin) {
|
|
heap_free_minimal_tuple(slot->tts_mintuple);
|
|
}
|
|
/*
|
|
* if msg == slot->tts_dataRow then we would
|
|
* free the dataRow in the slot loosing the contents in msg. It is safe
|
|
* to reset shouldFreeRow, since it will be overwritten just below.
|
|
*/
|
|
if (msg == slot->tts_dataRow) {
|
|
slot->tts_shouldFreeRow = false;
|
|
}
|
|
if (slot->tts_shouldFreeRow) {
|
|
pfree_ext(slot->tts_dataRow);
|
|
}
|
|
ResetSlotPerTupleContext(slot);
|
|
|
|
/*
|
|
* Drop the pin on the referenced buffer, if there is one.
|
|
*/
|
|
if (BufferIsValid(slot->tts_buffer)) {
|
|
ReleaseBuffer(slot->tts_buffer);
|
|
}
|
|
slot->tts_buffer = InvalidBuffer;
|
|
|
|
/*
|
|
* Store the new tuple into the specified slot.
|
|
*/
|
|
slot->tts_isempty = false;
|
|
slot->tts_shouldFree = false;
|
|
slot->tts_shouldFreeMin = false;
|
|
slot->tts_shouldFreeRow = should_free;
|
|
slot->tts_tuple = NULL;
|
|
slot->tts_mintuple = NULL;
|
|
slot->tts_dataRow = msg;
|
|
slot->tts_dataLen = len;
|
|
slot->tts_xcnodeoid = msgnode_oid;
|
|
/* Mark extracted state invalid */
|
|
slot->tts_nvalid = 0;
|
|
|
|
return slot;
|
|
}
|
|
#endif
|