* This information is needed by routines manipulating tuples
* (getattribute, formtuple, etc.).
*
- * Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.26 1999/07/15 15:18:58 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.64 2003/05/06 00:20:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*
* TABLE CREATE/DELETE
* ExecCreateTupleTable - create a new tuple table
- * ExecDestroyTupleTable - destroy a table
+ * ExecDropTupleTable - destroy a table
*
- * SLOT RESERVERATION
+ * SLOT RESERVATION
* 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(Macro)
*
* SLOT STATUS PREDICATES
* TupIsNull - true when slot contains no tuple(Macro)
- * ExecSlotDescriptorIsNew - true if we're now storing a different
- * type of tuple in a slot
*
* CONVENIENCE INITIALIZATION ROUTINES
- * ExecInitResultTupleSlot \ convience routines to initialize
+ * ExecInitResultTupleSlot \ convenience routines to initialize
* ExecInitScanTupleSlot \ the various tuple slots for nodes
- * ExecInitMarkedTupleSlot / which store copies of tuples.
- * ExecInitOuterTupleSlot /
- * ExecInitHashTupleSlot /
+ * ExecInitExtraTupleSlot / which store copies of tuples.
+ * ExecInitNullTupleSlot /
*
- * old routines:
- * ExecGetTupType - get type of tuple returned by this node
+ * Routines that probably belong somewhere else:
* ExecTypeFromTL - form a TupleDesc from a target list
*
* EXAMPLE OF HOW TABLE ROUTINES WORK
* 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.
+ * tuple from ExecProject() 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
*
* At ExecEnd()
* ----------------
- * - EndPlan() calls ExecDestroyTupleTable() to clean up any remaining
+ * - EndPlan() calls ExecDropTupleTable() to clean up any remaining
* tuples left over from executing the query.
*
* The important thing to watch in the executor code is how pointers
* and the TupleTableSlot node in execnodes.h.
*
*/
-#include <string.h>
-
#include "postgres.h"
-
+#include "funcapi.h"
+#include "access/heapam.h"
#include "executor/executor.h"
-#undef ExecStoreTuple
-
-#include "access/tupdesc.h"
-#include "catalog/pg_type.h"
-#include "parser/parse_type.h"
-#include "storage/bufmgr.h"
#include "utils/lsyscache.h"
-static TupleTableSlot *NodeGetResultTupleSlot(Plan *node);
-
/* ----------------------------------------------------------------
* tuple table create/delete functions
TupleTable newtable; /* newly allocated table */
TupleTableSlot *array; /* newly allocated slot array */
- /* ----------------
- * sanity checks
- * ----------------
+ /*
+ * sanity checks
*/
Assert(initialSize >= 1);
- /* ----------------
- * Now allocate our new table along with space for the pointers
- * to the tuples.
+ /*
+ * 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
- * ----------------
+ /*
+ * clean out the slots we just allocated
*/
MemSet(array, 0, initialSize * sizeof(TupleTableSlot));
- /* ----------------
- * initialize the new table and return it to the caller.
- * ----------------
+ /*
+ * initialize the new table and return it to the caller.
*/
newtable->size = initialSize;
newtable->next = 0;
}
/* --------------------------------
- * ExecDestroyTupleTable
+ * ExecDropTupleTable
*
- * This pfrees the storage assigned to the tuple table and
- * optionally pfrees the contents of the table also.
+ * This frees the storage used by the tuple table itself
+ * and optionally frees 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
+ExecDropTupleTable(TupleTable table, /* tuple table */
+ bool shouldFree) /* true if we should free slot
* contents */
{
- int next; /* next avaliable slot */
+ int next; /* next available slot */
TupleTableSlot *array; /* start of table array */
int i; /* counter */
- /* ----------------
- * sanity checks
- * ----------------
+ /*
+ * sanity checks
*/
Assert(table != NULL);
- /* ----------------
- * get information from the table
- * ----------------
+ /*
+ * 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
- * ----------------
+ /*
+ * first free all the valid pointers in the tuple array and drop
+ * refcounts of any referenced buffers, if that's what the caller
+ * wants. (There is probably no good reason for the caller ever not
+ * to want it!)
*/
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);
- }
- }
+ ExecClearTuple(&array[i]);
+ if (array[i].ttc_shouldFreeDesc &&
+ array[i].ttc_tupleDescriptor != NULL)
+ FreeTupleDesc(array[i].ttc_tupleDescriptor);
}
+ }
- /* ----------------
- * finally free the tuple array and the table itself.
- * ----------------
+ /*
+ * finally free the tuple array and the table itself.
*/
pfree(array);
pfree(table);
-
}
*
* 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).
+ * 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 */
+TupleTableSlot *
ExecAllocTableSlot(TupleTable table)
{
int slotnum; /* new slot number */
+ TupleTableSlot *slot;
- /* ----------------
- * sanity checks
- * ----------------
+ /*
+ * 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
+ /*
+ * 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
- * ----------------
+ * 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. Fail if it does.
+ * -mer 4 Aug 1992
*/
if (table->next >= table->size)
- {
+ elog(ERROR, "Plan requires more slots than are available"
+ "\n\tsend mail to your local executor guru to fix this");
- /*
- * 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(ERROR, "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.
- * ----------------
+ /*
+ * 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;
+ slot = &(table->array[slotnum]);
+
+ /* Make sure the allocated slot is valid (and empty) */
+ slot->type = T_TupleTableSlot;
+ slot->val = (HeapTuple) NULL;
+ slot->ttc_shouldFree = true;
+ slot->ttc_descIsNew = true;
+ slot->ttc_shouldFreeDesc = true;
+ slot->ttc_tupleDescriptor = (TupleDesc) NULL;
+ slot->ttc_buffer = InvalidBuffer;
- return &(table->array[slotnum]);
+ return slot;
+}
+
+/* --------------------------------
+ * MakeTupleTableSlot
+ *
+ * This routine makes an empty standalone TupleTableSlot.
+ * It really shouldn't exist, but there are a few places
+ * that do this, so we may as well centralize the knowledge
+ * of what's in one ...
+ * --------------------------------
+ */
+TupleTableSlot *
+MakeTupleTableSlot(void)
+{
+ TupleTableSlot *slot = makeNode(TupleTableSlot);
+
+ /* This should match ExecAllocTableSlot() */
+ slot->val = (HeapTuple) NULL;
+ slot->ttc_shouldFree = true;
+ slot->ttc_descIsNew = true;
+ slot->ttc_shouldFreeDesc = true;
+ slot->ttc_tupleDescriptor = (TupleDesc) NULL;
+ slot->ttc_buffer = InvalidBuffer;
+
+ return slot;
}
/* ----------------------------------------------------------------
* 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.
+ * 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() 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.
* --------------------------------
*/
-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. */
+TupleTableSlot *
+ExecStoreTuple(HeapTuple tuple,
+ TupleTableSlot *slot,
+ Buffer buffer,
+ bool shouldFree)
{
- /* ----------------
- * sanity checks
- * ----------------
+ /*
+ * sanity checks
*/
Assert(slot != NULL);
+ /* passing shouldFree=true for a tuple on a disk page is not sane */
+ Assert(BufferIsValid(buffer) ? (!shouldFree) : true);
- /* clear out the slot first */
+ /* clear out any old contents of the slot */
ExecClearTuple(slot);
- /* ----------------
- * store the new tuple into the specified slot and
- * return the slot into which we stored the tuple.
- * ----------------
+ /*
+ * 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;
+ /*
+ * If tuple is on a disk page, keep the page pinned as long as we hold
+ * a pointer into it.
+ */
+ if (BufferIsValid(buffer))
+ IncrBufferRefCount(buffer);
+
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 * /* return: slot passed */
{
HeapTuple oldtuple; /* prior contents of slot */
- /* ----------------
- * sanity checks
- * ----------------
+ /*
+ * sanity checks
*/
Assert(slot != NULL);
- /* ----------------
- * get information from the tuple table
- * ----------------
+ /*
+ * get information from the tuple table
*/
oldtuple = slot->val;
- /* ----------------
- * free the old contents of the specified slot if necessary.
- * ----------------
+ /*
+ * 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);
- }
+ heap_freetuple(oldtuple);
- /* ----------------
- * store NULL into the specified slot and return the slot.
- * - also set buffer to InvalidBuffer -cim 3/14/91
- * ----------------
- */
slot->val = (HeapTuple) NULL;
+ slot->ttc_shouldFree = true; /* probably useless code... */
+
+ /*
+ * Drop the pin on the referenced buffer, if there is one.
+ */
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.
- * --------------------------------
- */
-#ifdef NOT_USED
-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;
-}
-
-#endif
-
-/* --------------------------------
- * 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
*
* with the slot's tuple.
* --------------------------------
*/
-TupleDesc /* return: old slot tuple descriptor */
+void
ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
- TupleDesc tupdesc) /* tuple descriptor */
+ TupleDesc tupdesc, /* new tuple descriptor */
+ bool shouldFree) /* is desc owned by slot? */
{
- TupleDesc old_tupdesc = slot->ttc_tupleDescriptor;
+ if (slot->ttc_shouldFreeDesc &&
+ slot->ttc_tupleDescriptor != NULL)
+ FreeTupleDesc(slot->ttc_tupleDescriptor);
slot->ttc_tupleDescriptor = tupdesc;
- return old_tupdesc;
+ slot->ttc_shouldFreeDesc = shouldFree;
}
/* --------------------------------
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.
- * --------------------------------
- */
-#ifdef NOT_USED
-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;
-}
-
-#endif
-
-/* --------------------------------
- * 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().
- * --------------------------------
- */
-#ifdef NOT_USED
-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;
-}
-
-#endif
-
/* ----------------------------------------------------------------
* tuple table slot status predicates
* ----------------------------------------------------------------
*/
-/* --------------------------------
- * 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
- * --------------------------------
- */
-#ifdef NOT_USED
-bool /* return: descriptor "is new" */
-ExecSlotDescriptorIsNew(TupleTableSlot *slot) /* slot to inspect */
-{
-/* bool isNew = SlotTupleDescriptorIsNew((TupleTableSlot*) slot);
- return isNew; */
- return slot->ttc_descIsNew;
-}
-
-#endif
-
/* ----------------------------------------------------------------
* convenience initialization routines
* ----------------------------------------------------------------
*/
/* --------------------------------
- * ExecInit{Result,Scan,Raw,Marked,Outer,Hash}TupleSlot
+ * ExecInit{Result,Scan,Extra}TupleSlot
*
- * These are convenience routines to initialize the specfied slot
- * in nodes inheriting the appropriate state.
+ * These are convenience routines to initialize the specified slot
+ * in nodes inheriting the appropriate state. ExecInitExtraTupleSlot
+ * is used for initializing special-purpose slots.
* --------------------------------
*/
#define INIT_SLOT_DEFS \
#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;
+ slot = ExecAllocTableSlot(tupleTable);
/* ----------------
* ExecInitResultTupleSlot
* ----------------
*/
void
-ExecInitResultTupleSlot(EState *estate, CommonState *commonstate)
+ExecInitResultTupleSlot(EState *estate, PlanState *planstate)
{
INIT_SLOT_DEFS;
INIT_SLOT_ALLOC;
- commonstate->cs_ResultTupleSlot = (TupleTableSlot *) slot;
+ planstate->ps_ResultTupleSlot = slot;
}
/* ----------------
* ----------------
*/
void
-ExecInitScanTupleSlot(EState *estate, CommonScanState *commonscanstate)
+ExecInitScanTupleSlot(EState *estate, ScanState *scanstate)
{
INIT_SLOT_DEFS;
INIT_SLOT_ALLOC;
- commonscanstate->css_ScanTupleSlot = (TupleTableSlot *) slot;
+ scanstate->ss_ScanTupleSlot = slot;
}
-#ifdef NOT_USED
/* ----------------
- * ExecInitMarkedTupleSlot
+ * ExecInitExtraTupleSlot
* ----------------
*/
-void
-ExecInitMarkedTupleSlot(EState *estate, MergeJoinState *mergestate)
-{
- INIT_SLOT_DEFS;
- INIT_SLOT_ALLOC;
- mergestate->mj_MarkedTupleSlot = (TupleTableSlot *) slot;
-}
-
-#endif
-
-/* ----------------
- * ExecInitOuterTupleSlot
- * ----------------
- */
-void
-ExecInitOuterTupleSlot(EState *estate, HashJoinState *hashstate)
-{
- INIT_SLOT_DEFS;
- INIT_SLOT_ALLOC;
- hashstate->hj_OuterTupleSlot = slot;
-}
-
-/* ----------------
- * ExecInitHashTupleSlot
- * ----------------
- */
-#ifdef NOT_USED
-void
-ExecInitHashTupleSlot(EState *estate, HashJoinState *hashstate)
+TupleTableSlot *
+ExecInitExtraTupleSlot(EState *estate)
{
INIT_SLOT_DEFS;
INIT_SLOT_ALLOC;
- hashstate->hj_HashTupleSlot = slot;
-}
-
-#endif
-
-static 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 *appendstate;
- List *appendplans;
- int whichplan;
- Plan *subplan;
-
- appendstate = n->appendstate;
- appendplans = n->appendplans;
- whichplan = appendstate->as_whichplan;
-
- subplan = (Plan *) nth(whichplan, appendplans);
- 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;
-
- default:
- /* ----------------
- * should never get here
- * ----------------
- */
- elog(ERROR, "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
+/* ----------------
+ * 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.
+ * ----------------
*/
-
-TupleDesc
-ExecGetTupType(Plan *node)
+TupleTableSlot *
+ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
{
- TupleTableSlot *slot;
- TupleDesc tupType;
+ TupleTableSlot *slot = ExecInitExtraTupleSlot(estate);
- if (node == NULL)
- return NULL;
+ /*
+ * Since heap_getattr() will treat attributes beyond a tuple's t_natts
+ * as being NULL, we can make an all-nulls tuple just by making it be
+ * of zero length. However, the slot descriptor must match the real
+ * tupType.
+ */
+ HeapTuple nullTuple;
+ Datum values[1];
+ char nulls[1];
+ static struct tupleDesc NullTupleDesc; /* we assume this inits to
+ * zeroes */
- slot = NodeGetResultTupleSlot(node);
- tupType = slot->ttc_tupleDescriptor;
- return tupType;
-}
+ ExecSetSlotDescriptor(slot, tupType, false);
-/*
-TupleDesc
-ExecCopyTupType(TupleDesc td, int natts)
-{
- TupleDesc newTd;
- int i;
+ nullTuple = heap_formtuple(&NullTupleDesc, values, nulls);
- newTd = CreateTemplateTupleDesc(natts);
- i = 0;
- while (i < natts)
- {
- newTd[i] = (Form_pg_attribute)palloc(sizeof(FormData_pg_attribute));
- memmove(newTd[i], td[i], sizeof(FormData_pg_attribute));
- i++;
- }
- return newTd;
+ return ExecStoreTuple(nullTuple, slot, InvalidBuffer, true);
}
-*/
/* ----------------------------------------------------------------
* 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().
- *
- * 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)
+ExecTypeFromTL(List *targetList, bool hasoid)
{
- List *tlcdr;
TupleDesc typeInfo;
- Resdom *resdom;
- Oid restype;
+ List *tlitem;
int len;
- /* ----------------
- * examine targetlist - if empty then return NULL
- * ----------------
+ /*
+ * allocate a new typeInfo
*/
len = ExecTargetListLength(targetList);
+ typeInfo = CreateTemplateTupleDesc(len, hasoid);
- if (len == 0)
- return NULL;
-
- /* ----------------
- * allocate a new typeInfo
- * ----------------
+ /*
+ * scan list, generate type info for each entry
*/
- typeInfo = CreateTemplateTupleDesc(len);
+ foreach(tlitem, targetList)
+ {
+ TargetEntry *tle = lfirst(tlitem);
+ Resdom *resdom = tle->resdom;
+
+ TupleDescInitEntry(typeInfo,
+ resdom->resno,
+ resdom->resname,
+ resdom->restype,
+ resdom->restypmod,
+ 0,
+ false);
+ }
- /* ----------------
- * notes: get resdom from (resdom expr)
- * get_typbyval comes from src/lib/l-lisp/lsyscache.c
- * ----------------
+ return typeInfo;
+}
+
+/*
+ * TupleDescGetSlot - Initialize a slot based on the supplied tupledesc
+ */
+TupleTableSlot *
+TupleDescGetSlot(TupleDesc tupdesc)
+{
+ TupleTableSlot *slot;
+
+ /* Make a standalone slot */
+ slot = MakeTupleTableSlot();
+
+ /* Bind the tuple description to the slot */
+ ExecSetSlotDescriptor(slot, tupdesc, true);
+
+ /* 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 tupdesc)
+{
+ int natts = tupdesc->natts;
+ int i;
+ Oid atttypeid;
+ Oid attinfuncid;
+ FmgrInfo *attinfuncinfo;
+ Oid *attelems;
+ int32 *atttypmods;
+ AttInMetadata *attinmeta;
+
+ attinmeta = (AttInMetadata *) palloc(sizeof(AttInMetadata));
+
+ /*
+ * Gather info needed later to call the "in" function for each
+ * attribute
*/
- tlcdr = targetList;
- while (tlcdr != NIL)
+ attinfuncinfo = (FmgrInfo *) palloc(natts * sizeof(FmgrInfo));
+ attelems = (Oid *) palloc(natts * sizeof(Oid));
+ atttypmods = (int32 *) palloc(natts * sizeof(int32));
+
+ for (i = 0; i < natts; i++)
{
- TargetEntry *tle = lfirst(tlcdr);
+ atttypeid = tupdesc->attrs[i]->atttypid;
+ getTypeInputInfo(atttypeid, &attinfuncid, &attelems[i]);
+ fmgr_info(attinfuncid, &attinfuncinfo[i]);
+ atttypmods[i] = tupdesc->attrs[i]->atttypmod;
+ }
+ attinmeta->tupdesc = tupdesc;
+ attinmeta->attinfuncs = attinfuncinfo;
+ attinmeta->attelems = attelems;
+ attinmeta->atttypmods = atttypmods;
- if (tle->resdom != NULL)
- {
- resdom = tle->resdom;
- restype = resdom->restype;
-
- TupleDescInitEntry(typeInfo,
- resdom->resno,
- resdom->resname,
- /* fix for SELECT NULL ... */
- (restype ? restype : UNKNOWNOID),
- resdom->restypmod,
- 0,
- false);
+ return attinmeta;
+}
/*
- ExecSetTypeInfo(resdom->resno - 1,
- typeInfo,
- (Oid) restype,
- resdom->resno,
- resdom->reslen,
- resdom->resname->data,
- get_typbyval(restype),
- get_typalign(restype));
-*/
+ * 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.
+ */
+HeapTuple
+BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
+{
+ TupleDesc tupdesc = attinmeta->tupdesc;
+ int natts = tupdesc->natts;
+ Datum *dvalues;
+ char *nulls;
+ int i;
+ Oid attelem;
+ int32 atttypmod;
+ HeapTuple tuple;
+
+ dvalues = (Datum *) palloc(natts * sizeof(Datum));
+ nulls = (char *) palloc(natts * sizeof(char));
+
+ /* Call the "in" function for each non-null attribute */
+ for (i = 0; i < natts; i++)
+ {
+ if (values[i] != NULL)
+ {
+ attelem = attinmeta->attelems[i];
+ atttypmod = attinmeta->atttypmods[i];
+
+ dvalues[i] = FunctionCall3(&attinmeta->attinfuncs[i],
+ CStringGetDatum(values[i]),
+ ObjectIdGetDatum(attelem),
+ Int32GetDatum(atttypmod));
+ nulls[i] = ' ';
}
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,
- restype,
- fjRes->restypmod,
- 0,
- false);
+ dvalues[i] = (Datum) 0;
+ nulls[i] = 'n';
+ }
+ }
+
+ /*
+ * Form a tuple
+ */
+ tuple = heap_formtuple(tupdesc, dvalues, nulls);
+
+ /*
+ * Release locally palloc'd space. XXX would probably be good to
+ * pfree values of pass-by-reference datums, as well.
+ */
+ pfree(dvalues);
+ pfree(nulls);
+
+ return tuple;
+}
+
/*
- 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,
- restype,
- fjRes->restypmod,
- 0,
- false);
+ * 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(CommandDest dest, TupleDesc tupdesc)
+{
+ TupOutputState *tstate;
+
+ tstate = (TupOutputState *) palloc(sizeof(TupOutputState));
+
+ tstate->metadata = TupleDescGetAttInMetadata(tupdesc);
+ tstate->destfunc = DestToFunction(dest);
+
+ (*tstate->destfunc->setup) (tstate->destfunc, (int) CMD_SELECT,
+ NULL, tupdesc, NIL);
+
+ return tstate;
+}
/*
- ExecSetTypeInfo(fjRes->resno - 1,
- typeInfo,
- (Oid) fjRes->restype,
- fjRes->resno,
- fjRes->reslen,
- (char *) fjRes->resname,
- get_typbyval(fjRes->restype),
- get_typalign(fjRes->restype));
-*/
- }
- }
+ * write a single tuple
+ *
+ * values is a list of the external C string representations of the values
+ * to be projected.
+ */
+void
+do_tup_output(TupOutputState *tstate, char **values)
+{
+ /* build a tuple from the input strings using the tupdesc */
+ HeapTuple tuple = BuildTupleFromCStrings(tstate->metadata, values);
+
+ /* send the tuple to the receiver */
+ (*tstate->destfunc->receiveTuple) (tuple,
+ tstate->metadata->tupdesc,
+ tstate->destfunc);
+ /* clean up */
+ heap_freetuple(tuple);
+}
- tlcdr = lnext(tlcdr);
+/*
+ * write a chunk of text, breaking at newline characters
+ *
+ * NB: scribbles on its input!
+ *
+ * Should only be used with a single-TEXT-attribute tupdesc.
+ */
+void
+do_text_output_multiline(TupOutputState *tstate, char *text)
+{
+ while (*text)
+ {
+ char *eol;
+
+ eol = strchr(text, '\n');
+ if (eol)
+ *eol++ = '\0';
+ else
+ eol = text +strlen(text);
+
+ do_tup_output(tstate, &text);
+ text = eol;
}
+}
- return typeInfo;
+void
+end_tup_output(TupOutputState *tstate)
+{
+ (*tstate->destfunc->cleanup) (tstate->destfunc);
+ /* XXX worth cleaning up the attinmetadata? */
+ pfree(tstate);
}