aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/execTuples.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/execTuples.c')
-rw-r--r--src/backend/executor/execTuples.c1013
1 files changed, 1013 insertions, 0 deletions
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
new file mode 100644
index 00000000000..8e7b5283dc6
--- /dev/null
+++ b/src/backend/executor/execTuples.c
@@ -0,0 +1,1013 @@
+/*-------------------------------------------------------------------------
+ *
+ * execTuples.c--
+ * Routines dealing with the executor tuple tables. These are used to
+ * ensure that the executor frees copies of tuples (made by
+ * ExecTargetList) properly.
+ *
+ * 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.).
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ *
+ * TABLE CREATE/DELETE
+ * ExecCreateTupleTable - create a new tuple table
+ * ExecDestroyTupleTable - destroy a table
+ *
+ * SLOT RESERVERATION
+ * ExecAllocTableSlot - find an available slot in the table
+ *
+ * SLOT ACCESSORS
+ * ExecStoreTuple - store a tuple in the table
+ * ExecFetchTuple - fetch a tuple from the table
+ * ExecClearTuple - clear contents of a table slot
+ * ExecSlotPolicy - return slot's tuple pfree policy
+ * ExecSetSlotPolicy - diddle the slot policy
+ * ExecSlotDescriptor - type of tuple in a slot
+ * ExecSetSlotDescriptor - set a slot's tuple descriptor
+ * ExecSetSlotDescriptorIsNew - diddle the slot-desc-is-new flag
+ * ExecSetNewSlotDescriptor - set a desc and the is-new-flag all at once
+ * ExecSlotBuffer - return buffer of tuple in slot
+ * ExecSetSlotBuffer - set the buffer for tuple in slot
+ * ExecIncrSlotBufferRefcnt - bump the refcnt of the slot buffer
+ *
+ * SLOT STATUS PREDICATES
+ * TupIsNull - true when slot contains no tuple
+ * ExecSlotDescriptorIsNew - true if we're now storing a different
+ * type of tuple in a slot
+ *
+ * CONVENIENCE INITIALIZATION ROUTINES
+ * ExecInitResultTupleSlot \ convience routines to initialize
+ * ExecInitScanTupleSlot \ the various tuple slots for nodes
+ * ExecInitMarkedTupleSlot / which store copies of tuples.
+ * ExecInitOuterTupleSlot /
+ * ExecInitHashTupleSlot /
+ *
+ * old routines:
+ * ExecGetTupType - get type of tuple returned by this node
+ * ExecTypeFromTL - form a TupleDesc from a target list
+ *
+ * EXAMPLE OF HOW TABLE ROUTINES WORK
+ * Suppose we have a query such as retrieve (EMP.name) and we have
+ * a single SeqScan node in the query plan.
+ *
+ * At ExecStart()
+ * ----------------
+ * - InitPlan() calls ExecCreateTupleTable() to create the tuple
+ * table which will hold tuples processed by the executor.
+ *
+ * - ExecInitSeqScan() calls ExecInitScanTupleSlot() and
+ * ExecInitResultTupleSlot() to reserve places in the tuple
+ * table for the tuples returned by the access methods and the
+ * tuples resulting from preforming target list projections.
+ *
+ * During ExecRun()
+ * ----------------
+ * - 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 ExecTargetList() and place it into the result tuple
+ * slot.
+ *
+ * - ExecutePlan() calls ExecRetrieve() which gets the tuple out of
+ * the slot passed to it by calling ExecFetchTuple(). this tuple
+ * is then returned.
+ *
+ * At ExecEnd()
+ * ----------------
+ * - EndPlan() calls ExecDestroyTupleTable() 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). Note that much of
+ * this information is also kept in the ExprContext of each node.
+ * Soon the executor will be redesigned and ExprContext's will contain
+ * only slot pointers. -cim 3/14/91
+ *
+ * NOTES
+ * The tuple table stuff is relatively new, put here to alleviate
+ * the process growth problems in the executor. The other routines
+ * are old (from the original lisp system) and may someday become
+ * obsolete. -cim 6/23/90
+ *
+ * In the implementation of nested-dot queries such as
+ * "retrieve (EMP.hobbies.all)", a single scan may return tuples
+ * of many types, so now we return pointers to tuple descriptors
+ * along with tuples returned via the tuple table. This means
+ * we now have a bunch of routines to diddle the slot descriptors
+ * too. -cim 1/18/90
+ *
+ * The tuple table stuff depends on the executor/tuptable.h macros,
+ * and the TupleTableSlot node in execnodes.h.
+ *
+ */
+
+#include "executor/executor.h"
+#undef ExecStoreTuple
+
+#include "access/tupdesc.h"
+#include "utils/palloc.h"
+#include "utils/lsyscache.h"
+#include "storage/bufmgr.h"
+#include "parser/catalog_utils.h"
+
+/* ----------------------------------------------------------------
+ * tuple table create/delete functions
+ * ----------------------------------------------------------------
+ */
+/* --------------------------------
+ * ExecCreateTupleTable
+ *
+ * This creates a new tuple table of the specified initial
+ * size. If the size is insufficient, ExecAllocTableSlot()
+ * will grow the table as necessary.
+ *
+ * This should be used by InitPlan() to allocate the table.
+ * The table's address will be stored in the EState structure.
+ * --------------------------------
+ */
+TupleTable /* return: address of table */
+ExecCreateTupleTable(int initialSize) /* initial number of slots in table */
+{
+ TupleTable newtable; /* newly allocated table */
+ TupleTableSlot* array; /* newly allocated slot array */
+
+ /* ----------------
+ * sanity checks
+ * ----------------
+ */
+ Assert(initialSize >= 1);
+
+ /* ----------------
+ * Now allocate our new table along with space for the pointers
+ * to the tuples.
+ */
+
+ newtable = (TupleTable) palloc(sizeof(TupleTableData));
+ array = (TupleTableSlot*) palloc(initialSize * sizeof(TupleTableSlot));
+
+ /* ----------------
+ * clean out the slots we just allocated
+ * ----------------
+ */
+ memset(array, 0, initialSize * sizeof(TupleTableSlot));
+
+ /* ----------------
+ * initialize the new table and return it to the caller.
+ * ----------------
+ */
+ newtable->size = initialSize;
+ newtable->next = 0;
+ newtable->array = array;
+
+ return newtable;
+}
+
+/* --------------------------------
+ * ExecDestroyTupleTable
+ *
+ * This pfrees the storage assigned to the tuple table and
+ * optionally pfrees the contents of the table also.
+ * It is expected that this routine be called by EndPlan().
+ * --------------------------------
+ */
+void
+ExecDestroyTupleTable(TupleTable table, /* tuple table */
+ bool shouldFree) /* true if we should free slot contents */
+{
+ int next; /* next avaliable slot */
+ TupleTableSlot *array; /* start of table array */
+ int i; /* counter */
+
+ /* ----------------
+ * sanity checks
+ * ----------------
+ */
+ Assert(table != NULL);
+
+ /* ----------------
+ * get information from the table
+ * ----------------
+ */
+ array = table->array;
+ next = table->next;
+
+ /* ----------------
+ * first free all the valid pointers in the tuple array
+ * if that's what the caller wants..
+ *
+ * Note: we do nothing about the Buffer and Tuple Descriptor's
+ * we store in the slots. This may have to change (ex: we should
+ * probably worry about pfreeing tuple descs too) -cim 3/14/91
+ * ----------------
+ */
+ if (shouldFree)
+ for (i = 0; i < next; i++) {
+ TupleTableSlot slot;
+ HeapTuple tuple;
+
+ slot = array[i];
+ tuple = slot.val;
+
+ if (tuple != NULL) {
+ slot.val = (HeapTuple)NULL;
+ if (slot.ttc_shouldFree) {
+ /* ----------------
+ * since a tuple may contain a pointer to
+ * lock information allocated along with the
+ * tuple, we have to be careful to free any
+ * rule locks also -cim 1/17/90
+ * ----------------
+ */
+ pfree(tuple);
+ }
+ }
+ }
+
+ /* ----------------
+ * finally free the tuple array and the table itself.
+ * ----------------
+ */
+ pfree(array);
+ pfree(table);
+
+}
+
+
+/* ----------------------------------------------------------------
+ * tuple table slot reservation functions
+ * ----------------------------------------------------------------
+ */
+/* --------------------------------
+ * ExecAllocTableSlot
+ *
+ * This routine is used to reserve slots in the table for
+ * use by the various plan nodes. It is expected to be
+ * called by the node init routines (ex: ExecInitNestLoop).
+ * once per slot needed by the node. Not all nodes need
+ * slots (some just pass tuples around).
+ * --------------------------------
+ */
+TupleTableSlot* /* return: the slot allocated in the tuple table */
+ExecAllocTableSlot(TupleTable table)
+{
+ int slotnum; /* new slot number */
+
+ /* ----------------
+ * sanity checks
+ * ----------------
+ */
+ Assert(table != NULL);
+
+ /* ----------------
+ * if our table is full we have to allocate a larger
+ * size table. Since ExecAllocTableSlot() is only called
+ * before the table is ever used to store tuples, we don't
+ * have to worry about the contents of the old table.
+ * If this changes, then we will have to preserve the contents.
+ * -cim 6/23/90
+ *
+ * Unfortunately, we *cannot* do this. All of the nodes in
+ * the plan that have already initialized their slots will have
+ * pointers into _freed_ memory. This leads to bad ends. We
+ * now count the number of slots we will need and create all the
+ * slots we will need ahead of time. The if below should never
+ * happen now. Give a WARN if it does. -mer 4 Aug 1992
+ * ----------------
+ */
+ if (table->next >= table->size) {
+ /*
+ * int newsize = NewTableSize(table->size);
+ *
+ * pfree(table->array);
+ * table->array = (Pointer) palloc(newsize * TableSlotSize);
+ * bzero(table->array, newsize * TableSlotSize);
+ * table->size = newsize;
+ */
+ elog(NOTICE, "Plan requires more slots than are available");
+ elog(WARN, "send mail to your local executor guru to fix this");
+ }
+
+ /* ----------------
+ * at this point, space in the table is guaranteed so we
+ * reserve the next slot, initialize and return it.
+ * ----------------
+ */
+ slotnum = table->next;
+ table->next++;
+
+ table->array[slotnum].type = T_TupleTableSlot;
+
+ return &(table->array[slotnum]);
+}
+
+/* ----------------------------------------------------------------
+ * tuple table slot accessor functions
+ * ----------------------------------------------------------------
+ */
+
+/* --------------------------------
+ * ExecStoreTuple
+ *
+ * This function is used to store a tuple into a specified
+ * slot in the tuple table. Note: the only slots which should
+ * be called with shouldFree == false are those slots used to
+ * store tuples not allocated with pfree(). Currently the
+ * seqscan and indexscan nodes use this for the tuples returned
+ * by amgetattr, which are actually pointers onto disk pages.
+ * --------------------------------
+ */
+TupleTableSlot* /* return: slot passed */
+ExecStoreTuple(HeapTuple tuple, /* tuple to store */
+ TupleTableSlot* slot, /* slot in which to store tuple */
+ Buffer buffer, /* buffer associated with tuple */
+ bool shouldFree) /* true if we call pfree() when we gc. */
+{
+ /* ----------------
+ * sanity checks
+ * ----------------
+ */
+ Assert(slot != NULL);
+
+ /* clear out the slot first */
+ ExecClearTuple(slot);
+
+ /* ----------------
+ * store the new tuple into the specified slot and
+ * return the slot into which we stored the tuple.
+ * ----------------
+ */
+ slot->val = tuple;
+ slot->ttc_buffer = buffer;
+ slot->ttc_shouldFree = shouldFree;
+
+ return slot;
+}
+
+/* --------------------------------
+ * ExecClearTuple
+ *
+ * This function is used to clear out a slot in the tuple table.
+ * --------------------------------
+ */
+TupleTableSlot* /* return: slot passed */
+ExecClearTuple(TupleTableSlot* slot) /* slot in which to store tuple */
+{
+ HeapTuple oldtuple; /* prior contents of slot */
+
+ /* ----------------
+ * sanity checks
+ * ----------------
+ */
+ Assert(slot != NULL);
+
+ /* ----------------
+ * get information from the tuple table
+ * ----------------
+ */
+ oldtuple = slot->val;
+
+ /* ----------------
+ * free the old contents of the specified slot if necessary.
+ * ----------------
+ */
+ if (slot->ttc_shouldFree && oldtuple != NULL) {
+ /* ----------------
+ * since a tuple may contain a pointer to
+ * lock information allocated along with the
+ * tuple, we have to be careful to free any
+ * rule locks also -cim 1/17/90
+ * ----------------
+ */
+ pfree(oldtuple);
+ }
+
+ /* ----------------
+ * store NULL into the specified slot and return the slot.
+ * - also set buffer to InvalidBuffer -cim 3/14/91
+ * ----------------
+ */
+ slot->val = (HeapTuple)NULL;
+
+ if (BufferIsValid(slot->ttc_buffer))
+ ReleaseBuffer(slot->ttc_buffer);
+
+ slot->ttc_buffer = InvalidBuffer;
+ slot->ttc_shouldFree = true;
+
+ return slot;
+}
+
+
+/* --------------------------------
+ * ExecSlotPolicy
+ *
+ * This function is used to get the call/don't call pfree
+ * setting of a slot. Most executor routines don't need this.
+ * It's only when you do tricky things like marking tuples for
+ * merge joins that you need to diddle the slot policy.
+ * --------------------------------
+ */
+bool /* return: slot policy */
+ExecSlotPolicy(TupleTableSlot* slot) /* slot to inspect */
+{
+ return slot->ttc_shouldFree;
+}
+
+/* --------------------------------
+ * ExecSetSlotPolicy
+ *
+ * This function is used to change the call/don't call pfree
+ * setting of a slot. Most executor routines don't need this.
+ * It's only when you do tricky things like marking tuples for
+ * merge joins that you need to diddle the slot policy.
+ * --------------------------------
+ */
+bool /* return: old slot policy */
+ExecSetSlotPolicy(TupleTableSlot* slot, /* slot to change */
+ bool shouldFree) /* true if we call pfree() when we gc. */
+{
+ bool old_shouldFree = slot->ttc_shouldFree;
+ slot->ttc_shouldFree = shouldFree;
+
+ return old_shouldFree;
+}
+
+/* --------------------------------
+ * ExecSlotDescriptor
+ *
+ * This function is used to get the tuple descriptor associated
+ * with the slot's tuple.
+ *
+ * Now a macro in tuptable.h -mer 5 March 1992
+ * --------------------------------
+ */
+
+/* --------------------------------
+ * ExecSetSlotDescriptor
+ *
+ * This function is used to set the tuple descriptor associated
+ * with the slot's tuple.
+ * --------------------------------
+ */
+TupleDesc /* return: old slot tuple descriptor */
+ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
+ TupleDesc tupdesc) /* tuple descriptor */
+{
+ TupleDesc old_tupdesc = slot->ttc_tupleDescriptor;
+
+ slot->ttc_tupleDescriptor = tupdesc;
+ return old_tupdesc;
+}
+
+/* --------------------------------
+ * ExecSetSlotDescriptorIsNew
+ *
+ * This function is used to change the setting of the "isNew" flag
+ * --------------------------------
+ */
+void
+ExecSetSlotDescriptorIsNew(TupleTableSlot *slot,/* slot to change */
+ bool isNew) /* "isNew" setting */
+{
+ slot->ttc_descIsNew = isNew;
+}
+
+/* --------------------------------
+ * ExecSetNewSlotDescriptor
+ *
+ * This function is used to set the tuple descriptor associated
+ * with the slot's tuple, and set the "isNew" flag at the same time.
+ * --------------------------------
+ */
+TupleDesc /* return: old slot tuple descriptor */
+ExecSetNewSlotDescriptor(TupleTableSlot *slot, /* slot to change */
+ TupleDesc tupdesc) /* tuple descriptor */
+{
+ TupleDesc old_tupdesc = slot->ttc_tupleDescriptor;
+ slot->ttc_tupleDescriptor = tupdesc;
+ slot->ttc_descIsNew = true;
+
+ return old_tupdesc;
+}
+
+/* --------------------------------
+ * ExecSlotBuffer
+ *
+ * This function is used to get the tuple descriptor associated
+ * with the slot's tuple. Be very careful with this as it does not
+ * balance the reference counts. If the buffer returned is stored
+ * someplace else, then also use ExecIncrSlotBufferRefcnt().
+ *
+ * Now a macro in tuptable.h
+ * --------------------------------
+ */
+
+/* --------------------------------
+ * ExecSetSlotBuffer
+ *
+ * This function is used to set the tuple descriptor associated
+ * with the slot's tuple. Be very careful with this as it does not
+ * balance the reference counts. If we're using this then we should
+ * also use ExecIncrSlotBufferRefcnt().
+ * --------------------------------
+ */
+Buffer /* return: old slot buffer */
+ExecSetSlotBuffer(TupleTableSlot *slot, /* slot to change */
+ Buffer b) /* tuple descriptor */
+{
+ Buffer oldb = slot->ttc_buffer;
+ slot->ttc_buffer = b;
+
+ return oldb;
+}
+
+/* --------------------------------
+ * ExecIncrSlotBufferRefcnt
+ *
+ * When we pass around buffers in the tuple table, we have to
+ * be careful to increment reference counts appropriately.
+ * This is used mainly in the mergejoin code.
+ * --------------------------------
+ */
+void
+ExecIncrSlotBufferRefcnt(TupleTableSlot *slot) /* slot to bump refcnt */
+{
+/* Buffer b = SlotBuffer((TupleTableSlot*) slot); */
+ Buffer b = slot->ttc_buffer;
+ if (BufferIsValid(b))
+ IncrBufferRefCount(b);
+}
+
+/* ----------------------------------------------------------------
+ * tuple table slot status predicates
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ * TupIsNull
+ *
+ * This is used mainly to detect when there are no more
+ * tuples to process.
+ * ----------------
+ */
+bool /* return: true if tuple in slot is NULL */
+TupIsNull(TupleTableSlot* slot) /* slot to check */
+{
+ HeapTuple tuple; /* contents of slot (returned) */
+
+ /* ----------------
+ * if the slot itself is null then we return true
+ * ----------------
+ */
+ if (slot == NULL)
+ return true;
+
+ /* ----------------
+ * get information from the slot and return true or
+ * false depending on the contents of the slot.
+ * ----------------
+ */
+ tuple = slot->val;
+
+ return
+ (tuple == NULL ? true : false);
+}
+
+/* --------------------------------
+ * ExecSlotDescriptorIsNew
+ *
+ * This function is used to check if the tuple descriptor
+ * associated with this slot has just changed. ie: we are
+ * now storing a new type of tuple in this slot
+ * --------------------------------
+ */
+bool /* return: descriptor "is new" */
+ExecSlotDescriptorIsNew(TupleTableSlot *slot) /* slot to inspect */
+{
+/* bool isNew = SlotTupleDescriptorIsNew((TupleTableSlot*) slot);
+ return isNew; */
+ return slot->ttc_descIsNew;
+}
+
+/* ----------------------------------------------------------------
+ * convenience initialization routines
+ * ----------------------------------------------------------------
+ */
+/* --------------------------------
+ * ExecInit{Result,Scan,Raw,Marked,Outer,Hash}TupleSlot
+ *
+ * These are convenience routines to initialize the specfied slot
+ * in nodes inheriting the appropriate state.
+ * --------------------------------
+ */
+#define INIT_SLOT_DEFS \
+ TupleTable tupleTable; \
+ TupleTableSlot* slot
+
+#define INIT_SLOT_ALLOC \
+ tupleTable = (TupleTable) estate->es_tupleTable; \
+ slot = ExecAllocTableSlot(tupleTable); \
+ slot->val = (HeapTuple)NULL; \
+ slot->ttc_shouldFree = true; \
+ slot->ttc_tupleDescriptor = (TupleDesc)NULL; \
+ slot->ttc_whichplan = -1;\
+ slot->ttc_descIsNew = true;
+
+/* ----------------
+ * ExecInitResultTupleSlot
+ * ----------------
+ */
+void
+ExecInitResultTupleSlot(EState *estate, CommonState *commonstate)
+{
+ INIT_SLOT_DEFS;
+ INIT_SLOT_ALLOC;
+ commonstate->cs_ResultTupleSlot = (TupleTableSlot *) slot;
+}
+
+/* ----------------
+ * ExecInitScanTupleSlot
+ * ----------------
+ */
+void
+ExecInitScanTupleSlot(EState *estate, CommonScanState *commonscanstate)
+{
+ INIT_SLOT_DEFS;
+ INIT_SLOT_ALLOC;
+ commonscanstate->css_ScanTupleSlot = (TupleTableSlot *)slot;
+}
+
+/* ----------------
+ * ExecInitMarkedTupleSlot
+ * ----------------
+ */
+void
+ExecInitMarkedTupleSlot(EState *estate, MergeJoinState *mergestate)
+{
+ INIT_SLOT_DEFS;
+ INIT_SLOT_ALLOC;
+ mergestate->mj_MarkedTupleSlot = (TupleTableSlot *) slot;
+}
+
+/* ----------------
+ * ExecInitOuterTupleSlot
+ * ----------------
+ */
+void
+ExecInitOuterTupleSlot(EState *estate, HashJoinState *hashstate)
+{
+ INIT_SLOT_DEFS;
+ INIT_SLOT_ALLOC;
+ hashstate->hj_OuterTupleSlot = slot;
+}
+
+/* ----------------
+ * ExecInitHashTupleSlot
+ * ----------------
+ */
+void
+ExecInitHashTupleSlot(EState *estate, HashJoinState *hashstate)
+{
+ INIT_SLOT_DEFS;
+ INIT_SLOT_ALLOC;
+ hashstate->hj_HashTupleSlot = slot;
+}
+
+TupleTableSlot *
+NodeGetResultTupleSlot(Plan *node)
+{
+ TupleTableSlot *slot;
+
+ switch(nodeTag(node)) {
+
+ case T_Result:
+ {
+ ResultState *resstate = ((Result *)node)->resstate;
+ slot = resstate->cstate.cs_ResultTupleSlot;
+ }
+ break;
+
+ case T_SeqScan:
+ {
+ CommonScanState *scanstate = ((SeqScan *)node)->scanstate;
+ slot = scanstate->cstate.cs_ResultTupleSlot;
+ }
+ break;
+
+ case T_NestLoop:
+ {
+ NestLoopState *nlstate = ((NestLoop *)node)->nlstate;
+ slot = nlstate->jstate.cs_ResultTupleSlot;
+ }
+ break;
+
+ case T_Append:
+ {
+ Append *n = (Append *)node;
+ AppendState *unionstate;
+ List *unionplans;
+ int whichplan;
+ Plan *subplan;
+
+ unionstate = n->unionstate;
+ unionplans = n->unionplans;
+ whichplan = unionstate->as_whichplan;
+
+ subplan = (Plan*) nth(whichplan, unionplans);
+ slot = NodeGetResultTupleSlot(subplan);
+ break;
+ }
+
+ case T_IndexScan:
+ {
+ CommonScanState *scanstate = ((IndexScan *)node)->scan.scanstate;
+ slot = scanstate->cstate.cs_ResultTupleSlot;
+ }
+ break;
+
+ case T_Material:
+ {
+ MaterialState *matstate = ((Material *)node)->matstate;
+ slot = matstate->csstate.css_ScanTupleSlot;
+ }
+ break;
+
+ case T_Sort:
+ {
+ SortState *sortstate = ((Sort *)node)->sortstate;
+ slot = sortstate->csstate.css_ScanTupleSlot;
+ }
+ break;
+
+ case T_Agg:
+ {
+ AggState *aggstate = ((Agg *)node)->aggstate;
+ slot = aggstate->csstate.cstate.cs_ResultTupleSlot;
+ }
+ break;
+
+ case T_Group:
+ {
+ GroupState *grpstate = ((Group *)node)->grpstate;
+ slot = grpstate->csstate.cstate.cs_ResultTupleSlot;
+ }
+ break;
+
+ case T_Hash:
+ {
+ HashState *hashstate = ((Hash *)node)->hashstate;
+ slot = hashstate->cstate.cs_ResultTupleSlot;
+ }
+ break;
+
+ case T_Unique:
+ {
+ UniqueState *uniquestate = ((Unique *)node)->uniquestate;
+ slot = uniquestate->cs_ResultTupleSlot;
+ }
+ break;
+
+ case T_MergeJoin:
+ {
+ MergeJoinState *mergestate = ((MergeJoin *)node)->mergestate;
+ slot = mergestate->jstate.cs_ResultTupleSlot;
+ }
+ break;
+
+ case T_HashJoin:
+ {
+ HashJoinState *hashjoinstate = ((HashJoin *)node)->hashjoinstate;
+ slot = hashjoinstate->jstate.cs_ResultTupleSlot;
+ }
+ break;
+
+ case T_Tee:
+ {
+ TeeState *teestate = ((Tee*)node)->teestate;
+ slot = teestate->cstate.cs_ResultTupleSlot;
+ }
+ break;
+
+ default:
+ /* ----------------
+ * should never get here
+ * ----------------
+ */
+ elog(WARN, "NodeGetResultTupleSlot: node not yet supported: %d ",
+ nodeTag(node));
+
+ return NULL;
+ }
+ return slot;
+}
+
+/* ----------------------------------------------------------------
+ * ExecGetTupType
+ *
+ * this gives you the tuple descriptor for tuples returned
+ * by this node. I really wish I could ditch this routine,
+ * but since not all nodes store their type info in the same
+ * place, we have to do something special for each node type.
+ *
+ * Soon, the system will have to adapt to deal with changing
+ * tuple descriptors as we deal with dynamic tuple types
+ * being returned from procedure nodes. Perhaps then this
+ * routine can be retired. -cim 6/3/91
+ *
+ * old comments
+ * This routine just gets the type information out of the
+ * node's state. If you already have a node's state, you
+ * can get this information directly, but this is a useful
+ * routine if you want to get the type information from
+ * the node's inner or outer subplan easily without having
+ * to inspect the subplan.. -cim 10/16/89
+ *
+ * Assume that for existential nodes, we get the targetlist out
+ * of the right node's targetlist
+ * ----------------------------------------------------------------
+ */
+
+TupleDesc
+ExecGetTupType(Plan *node)
+{
+ TupleTableSlot *slot;
+ TupleDesc tupType;
+
+ if (node == NULL)
+ return NULL;
+
+ slot = NodeGetResultTupleSlot(node);
+ tupType = slot->ttc_tupleDescriptor;
+ return tupType;
+}
+
+/*
+TupleDesc
+ExecCopyTupType(TupleDesc td, int natts)
+{
+ TupleDesc newTd;
+ int i;
+
+ newTd = CreateTemplateTupleDesc(natts);
+ i = 0;
+ while (i < natts)
+ {
+ newTd[i] =
+ (AttributeTupleForm)palloc(sizeof(FormData_pg_attribute));
+ memmove(newTd[i], td[i], sizeof(FormData_pg_attribute));
+ i++;
+ }
+ return newTd;
+}
+*/
+
+/* ----------------------------------------------------------------
+ * ExecTypeFromTL
+ *
+ * Currently there are about 4 different places where we create
+ * TupleDescriptors. They should all be merged, or perhaps
+ * be rewritten to call BuildDesc().
+ *
+ * old comments
+ * Forms attribute type info from the target list in the node.
+ * It assumes all domains are individually specified in the target list.
+ * It fails if the target list contains something like Emp.all
+ * which represents all the attributes from EMP relation.
+ *
+ * Conditions:
+ * The inner and outer subtrees should be initialized because it
+ * might be necessary to know the type infos of the subtrees.
+ * ----------------------------------------------------------------
+ */
+TupleDesc
+ExecTypeFromTL(List *targetList)
+{
+ List *tlcdr;
+ TupleDesc typeInfo;
+ Resdom *resdom;
+ Oid restype;
+ int len;
+
+ /* ----------------
+ * examine targetlist - if empty then return NULL
+ * ----------------
+ */
+ len = ExecTargetListLength(targetList);
+
+ if (len == 0)
+ return NULL;
+
+ /* ----------------
+ * allocate a new typeInfo
+ * ----------------
+ */
+ typeInfo = CreateTemplateTupleDesc(len);
+
+ /* ----------------
+ * notes: get resdom from (resdom expr)
+ * get_typbyval comes from src/lib/l-lisp/lsyscache.c
+ * ----------------
+ */
+ tlcdr = targetList;
+ while (tlcdr != NIL) {
+ TargetEntry *tle = lfirst(tlcdr);
+ if (tle->resdom != NULL) {
+ resdom = tle->resdom;
+ restype = resdom->restype;
+
+ TupleDescInitEntry(typeInfo,
+ resdom->resno,
+ resdom->resname,
+ get_id_typname(restype),
+ 0,
+ false);
+
+/*
+ ExecSetTypeInfo(resdom->resno - 1,
+ typeInfo,
+ (Oid) restype,
+ resdom->resno,
+ resdom->reslen,
+ resdom->resname->data,
+ get_typbyval(restype),
+ get_typalign(restype));
+*/
+ }
+ else {
+ Resdom *fjRes;
+ List *fjTlistP;
+ List *fjList = lfirst(tlcdr);
+#ifdef SETS_FIXED
+ TargetEntry *tle;
+ Fjoin *fjNode = ((TargetEntry *)lfirst(fjList))->fjoin;
+
+ tle = fjNode->fj_innerNode; /* ??? */
+#endif
+ fjRes = tle->resdom;
+ restype = fjRes->restype;
+
+ TupleDescInitEntry(typeInfo,
+ fjRes->resno,
+ fjRes->resname,
+ get_id_typname(restype),
+ 0,
+ false);
+/*
+ ExecSetTypeInfo(fjRes->resno - 1,
+ typeInfo,
+ (Oid) restype,
+ fjRes->resno,
+ fjRes->reslen,
+ (char *) fjRes->resname,
+ get_typbyval(restype),
+ get_typalign(restype));
+*/
+
+ foreach(fjTlistP, lnext(fjList)) {
+ TargetEntry *fjTle = lfirst(fjTlistP);
+
+ fjRes = fjTle->resdom;
+
+ TupleDescInitEntry(typeInfo,
+ fjRes->resno,
+ fjRes->resname,
+ get_id_typname(restype),
+ 0,
+ false);
+
+/*
+ ExecSetTypeInfo(fjRes->resno - 1,
+ typeInfo,
+ (Oid) fjRes->restype,
+ fjRes->resno,
+ fjRes->reslen,
+ (char *) fjRes->resname,
+ get_typbyval(fjRes->restype),
+ get_typalign(fjRes->restype));
+*/
+ }
+ }
+
+ tlcdr = lnext(tlcdr);
+ }
+
+ return typeInfo;
+}
+
+