]> granicus.if.org Git - postgresql/blobdiff - src/backend/executor/execTuples.c
Implement feature of new FE/BE protocol whereby RowDescription identifies
[postgresql] / src / backend / executor / execTuples.c
index d60c7c4b175db8cbf464943e7e189032963a4def..c81dd33d36a2547dc7f83ee5bca36e435ca47504 100644 (file)
 /*-------------------------------------------------------------------------
  *
- * 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);
+}