/*-------------------------------------------------------------------------
*
- * 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.
+ * 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
+ * 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) 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.3 1996/11/08 05:56:01 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.64 2003/05/06 00:20:31 tgl 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.
+ * TABLE CREATE/DELETE
+ * ExecCreateTupleTable - create a new tuple table
+ * ExecDropTupleTable - destroy a table
+ *
+ * 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
+ * ExecSetSlotDescriptor - set a slot's tuple descriptor
+ * ExecSetSlotDescriptorIsNew - diddle the slot-desc-is-new flag
+ *
+ * SLOT STATUS PREDICATES
+ * TupIsNull - true when slot contains no tuple(Macro)
+ *
+ * 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 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 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
+ * is then returned.
+ *
+ * At ExecEnd()
+ * ----------------
+ * - 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
+ * 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 <string.h>
-
#include "postgres.h"
-
+#include "funcapi.h"
+#include "access/heapam.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
+ * tuple table create/delete functions
* ----------------------------------------------------------------
*/
/* --------------------------------
- * ExecCreateTupleTable
+ * 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 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.
+ * 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 /* 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;
+ 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
+ * ExecDropTupleTable
*
- * 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().
+ * 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 contents */
+ExecDropTupleTable(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);
+ int next; /* next available 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 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++)
+ {
+ 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.
- * ----------------
- */
- pfree(array);
- pfree(table);
-
+
+ /*
+ * finally free the tuple array and the table itself.
+ */
+ pfree(array);
+ pfree(table);
}
/* ----------------------------------------------------------------
- * tuple table slot reservation functions
+ * tuple table slot reservation functions
* ----------------------------------------------------------------
*/
/* --------------------------------
- * ExecAllocTableSlot
+ * 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).
+ * 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 */
+TupleTableSlot *
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 slotnum; /* new slot number */
+ TupleTableSlot *slot;
+
/*
- * int newsize = NewTableSize(table->size);
- *
- * pfree(table->array);
- * table->array = (Pointer) palloc(newsize * TableSlotSize);
- * bzero(table->array, newsize * TableSlotSize);
- * table->size = newsize;
+ * sanity checks
*/
- 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]);
-}
+ Assert(table != NULL);
-/* ----------------------------------------------------------------
- * 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;
-}
+ /*
+ * 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. 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");
-/* --------------------------------
- * 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
- * ----------------
+ /*
+ * at this point, space in the table is guaranteed so we reserve the
+ * next slot, initialize and return it.
*/
- 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;
-}
+ slotnum = table->next;
+ table->next++;
+ slot = &(table->array[slotnum]);
-/* --------------------------------
- * 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;
+ /* 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 slot;
}
/* --------------------------------
- * ExecSetSlotPolicy
+ * MakeTupleTableSlot
*
- * 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.
+ * 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 ...
* --------------------------------
*/
-bool /* return: old slot policy */
-ExecSetSlotPolicy(TupleTableSlot* slot, /* slot to change */
- bool shouldFree) /* true if we call pfree() when we gc. */
+TupleTableSlot *
+MakeTupleTableSlot(void)
{
- bool old_shouldFree = slot->ttc_shouldFree;
- slot->ttc_shouldFree = shouldFree;
+ TupleTableSlot *slot = makeNode(TupleTableSlot);
- return old_shouldFree;
+ /* 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;
}
-/* --------------------------------
- * 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
- * --------------------------------
+/* ----------------------------------------------------------------
+ * tuple table slot accessor functions
+ * ----------------------------------------------------------------
*/
/* --------------------------------
- * ExecSetSlotDescriptor
- *
- * This function is used to set the tuple descriptor associated
- * with the slot's tuple.
+ * ExecStoreTuple
+ *
+ * This function is used to store a 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() 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.
* --------------------------------
*/
-TupleDesc /* return: old slot tuple descriptor */
-ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
- TupleDesc tupdesc) /* tuple descriptor */
+TupleTableSlot *
+ExecStoreTuple(HeapTuple tuple,
+ TupleTableSlot *slot,
+ Buffer buffer,
+ bool shouldFree)
{
- TupleDesc old_tupdesc = slot->ttc_tupleDescriptor;
+ /*
+ * sanity checks
+ */
+ Assert(slot != NULL);
+ /* passing shouldFree=true for a tuple on a disk page is not sane */
+ Assert(BufferIsValid(buffer) ? (!shouldFree) : true);
- slot->ttc_tupleDescriptor = tupdesc;
- return old_tupdesc;
-}
+ /* clear out any old contents of the slot */
+ ExecClearTuple(slot);
-/* --------------------------------
- * 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;
-}
+ /*
+ * 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;
-/* --------------------------------
- * 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;
+ /*
+ * 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;
}
/* --------------------------------
- * ExecSlotBuffer
+ * ExecClearTuple
*
- * 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().
+ * This function is used to clear out a slot in the tuple table.
*
- * Now a macro in tuptable.h
+ * NB: only the tuple is cleared, not the tuple descriptor (if any).
* --------------------------------
*/
+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)
+ heap_freetuple(oldtuple);
+
+ 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;
+
+ return slot;
+}
/* --------------------------------
- * ExecSetSlotBuffer
+ * ExecSetSlotDescriptor
*
- * 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().
+ * This function is used to set the tuple descriptor associated
+ * with the slot's tuple.
* --------------------------------
*/
-Buffer /* return: old slot buffer */
-ExecSetSlotBuffer(TupleTableSlot *slot, /* slot to change */
- Buffer b) /* tuple descriptor */
+void
+ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
+ TupleDesc tupdesc, /* new tuple descriptor */
+ bool shouldFree) /* is desc owned by slot? */
{
- Buffer oldb = slot->ttc_buffer;
- slot->ttc_buffer = b;
-
- return oldb;
+ if (slot->ttc_shouldFreeDesc &&
+ slot->ttc_tupleDescriptor != NULL)
+ FreeTupleDesc(slot->ttc_tupleDescriptor);
+
+ slot->ttc_tupleDescriptor = tupdesc;
+ slot->ttc_shouldFreeDesc = shouldFree;
}
/* --------------------------------
- * ExecIncrSlotBufferRefcnt
+ * ExecSetSlotDescriptorIsNew
*
- * 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.
+ * This function is used to change the setting of the "isNew" flag
* --------------------------------
*/
void
-ExecIncrSlotBufferRefcnt(TupleTableSlot *slot) /* slot to bump refcnt */
+ExecSetSlotDescriptorIsNew(TupleTableSlot *slot, /* slot to change */
+ bool isNew) /* "isNew" setting */
{
-/* Buffer b = SlotBuffer((TupleTableSlot*) slot); */
- Buffer b = slot->ttc_buffer;
- if (BufferIsValid(b))
- IncrBufferRefCount(b);
+ slot->ttc_descIsNew = isNew;
}
/* ----------------------------------------------------------------
- * tuple table slot status predicates
+ * 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
+ * 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 \
- TupleTable tupleTable; \
- TupleTableSlot* slot
-
+ 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;
+ tupleTable = (TupleTable) estate->es_tupleTable; \
+ slot = ExecAllocTableSlot(tupleTable);
/* ----------------
- * ExecInitResultTupleSlot
+ * ExecInitResultTupleSlot
* ----------------
*/
void
-ExecInitResultTupleSlot(EState *estate, CommonState *commonstate)
+ExecInitResultTupleSlot(EState *estate, PlanState *planstate)
{
- INIT_SLOT_DEFS;
- INIT_SLOT_ALLOC;
- commonstate->cs_ResultTupleSlot = (TupleTableSlot *) slot;
+ INIT_SLOT_DEFS;
+ INIT_SLOT_ALLOC;
+ planstate->ps_ResultTupleSlot = slot;
}
/* ----------------
- * ExecInitScanTupleSlot
+ * ExecInitScanTupleSlot
* ----------------
*/
void
-ExecInitScanTupleSlot(EState *estate, CommonScanState *commonscanstate)
+ExecInitScanTupleSlot(EState *estate, ScanState *scanstate)
{
- INIT_SLOT_DEFS;
- INIT_SLOT_ALLOC;
- commonscanstate->css_ScanTupleSlot = (TupleTableSlot *)slot;
+ INIT_SLOT_DEFS;
+ INIT_SLOT_ALLOC;
+ scanstate->ss_ScanTupleSlot = slot;
}
/* ----------------
- * ExecInitMarkedTupleSlot
+ * ExecInitExtraTupleSlot
* ----------------
*/
-void
-ExecInitMarkedTupleSlot(EState *estate, MergeJoinState *mergestate)
+TupleTableSlot *
+ExecInitExtraTupleSlot(EState *estate)
{
- INIT_SLOT_DEFS;
- INIT_SLOT_ALLOC;
- mergestate->mj_MarkedTupleSlot = (TupleTableSlot *) slot;
+ INIT_SLOT_DEFS;
+ INIT_SLOT_ALLOC;
+ return slot;
}
/* ----------------
- * ExecInitOuterTupleSlot
+ * 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.
* ----------------
*/
-void
-ExecInitOuterTupleSlot(EState *estate, HashJoinState *hashstate)
+TupleTableSlot *
+ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
{
- INIT_SLOT_DEFS;
- INIT_SLOT_ALLOC;
- hashstate->hj_OuterTupleSlot = slot;
+ TupleTableSlot *slot = ExecInitExtraTupleSlot(estate);
+
+ /*
+ * 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 */
+
+ ExecSetSlotDescriptor(slot, tupType, false);
+
+ nullTuple = heap_formtuple(&NullTupleDesc, values, nulls);
+
+ return ExecStoreTuple(nullTuple, slot, InvalidBuffer, true);
}
-/* ----------------
- * ExecInitHashTupleSlot
- * ----------------
+/* ----------------------------------------------------------------
+ * 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().
+ * ----------------------------------------------------------------
*/
-void
-ExecInitHashTupleSlot(EState *estate, HashJoinState *hashstate)
+TupleDesc
+ExecTypeFromTL(List *targetList, bool hasoid)
{
- INIT_SLOT_DEFS;
- INIT_SLOT_ALLOC;
- hashstate->hj_HashTupleSlot = slot;
+ TupleDesc typeInfo;
+ List *tlitem;
+ int len;
+
+ /*
+ * allocate a new typeInfo
+ */
+ len = ExecTargetListLength(targetList);
+ typeInfo = CreateTemplateTupleDesc(len, hasoid);
+
+ /*
+ * scan list, generate type info for each entry
+ */
+ foreach(tlitem, targetList)
+ {
+ TargetEntry *tle = lfirst(tlitem);
+ Resdom *resdom = tle->resdom;
+
+ TupleDescInitEntry(typeInfo,
+ resdom->resno,
+ resdom->resname,
+ resdom->restype,
+ resdom->restypmod,
+ 0,
+ false);
+ }
+
+ return typeInfo;
}
+/*
+ * TupleDescGetSlot - Initialize a slot based on the supplied tupledesc
+ */
TupleTableSlot *
-NodeGetResultTupleSlot(Plan *node)
+TupleDescGetSlot(TupleDesc tupdesc)
{
- 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:
+ 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
+ */
+ attinfuncinfo = (FmgrInfo *) palloc(natts * sizeof(FmgrInfo));
+ attelems = (Oid *) palloc(natts * sizeof(Oid));
+ atttypmods = (int32 *) palloc(natts * sizeof(int32));
+
+ for (i = 0; i < natts; i++)
{
- MergeJoinState *mergestate = ((MergeJoin *)node)->mergestate;
- slot = mergestate->jstate.cs_ResultTupleSlot;
+ atttypeid = tupdesc->attrs[i]->atttypid;
+ getTypeInputInfo(atttypeid, &attinfuncid, &attelems[i]);
+ fmgr_info(attinfuncid, &attinfuncinfo[i]);
+ atttypmods[i] = tupdesc->attrs[i]->atttypmod;
}
- break;
-
- case T_HashJoin:
+ attinmeta->tupdesc = tupdesc;
+ attinmeta->attinfuncs = attinfuncinfo;
+ attinmeta->attelems = attelems;
+ attinmeta->atttypmods = atttypmods;
+
+ return attinmeta;
+}
+
+/*
+ * 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++)
{
- HashJoinState *hashjoinstate = ((HashJoin *)node)->hashjoinstate;
- slot = hashjoinstate->jstate.cs_ResultTupleSlot;
+ 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
+ {
+ dvalues[i] = (Datum) 0;
+ nulls[i] = 'n';
+ }
}
- break;
-
- case T_Tee:
- {
- TeeState *teestate = ((Tee*)node)->teestate;
- slot = teestate->cstate.cs_ResultTupleSlot;
- }
- break;
-
- default:
- /* ----------------
- * should never get here
- * ----------------
+
+ /*
+ * Form a tuple
*/
- elog(WARN, "NodeGetResultTupleSlot: node not yet supported: %d ",
- nodeTag(node));
-
- return NULL;
- }
- return slot;
+ 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;
}
-/* ----------------------------------------------------------------
- * 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
- * ----------------------------------------------------------------
+/*
+ * 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
*/
-
-TupleDesc
-ExecGetTupType(Plan *node)
+TupOutputState *
+begin_tup_output_tupdesc(CommandDest dest, TupleDesc tupdesc)
{
- TupleTableSlot *slot;
- TupleDesc tupType;
-
- if (node == NULL)
- return NULL;
-
- slot = NodeGetResultTupleSlot(node);
- tupType = slot->ttc_tupleDescriptor;
- return tupType;
+ 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;
}
/*
-TupleDesc
-ExecCopyTupType(TupleDesc td, int natts)
+ * 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)
{
- 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;
+ /* 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);
}
-*/
-/* ----------------------------------------------------------------
- * 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.
- * ----------------------------------------------------------------
+/*
+ * write a chunk of text, breaking at newline characters
+ *
+ * NB: scribbles on its input!
+ *
+ * Should only be used with a single-TEXT-attribute tupdesc.
*/
-TupleDesc
-ExecTypeFromTL(List *targetList)
+void
+do_text_output_multiline(TupOutputState *tstate, char *text)
{
- 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);
+ while (*text)
+ {
+ char *eol;
-/*
- 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;
+ eol = strchr(text, '\n');
+ if (eol)
+ *eol++ = '\0';
+ else
+ eol = text +strlen(text);
- 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));
-*/
- }
+ do_tup_output(tstate, &text);
+ text = eol;
}
-
- tlcdr = lnext(tlcdr);
- }
-
- return typeInfo;
}
-
+void
+end_tup_output(TupOutputState *tstate)
+{
+ (*tstate->destfunc->cleanup) (tstate->destfunc);
+ /* XXX worth cleaning up the attinmetadata? */
+ pfree(tstate);
+}