]> 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 a41c4ff45648f92f176dc2c0bb0ef28a43fe6082..c81dd33d36a2547dc7f83ee5bca36e435ca47504 100644 (file)
  *       This information is needed by routines manipulating tuples
  *       (getattribute, formtuple, etc.).
  *
- * Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.26 1999/07/15 15:18:58 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.64 2003/05/06 00:20:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
  *
  *      TABLE CREATE/DELETE
  *             ExecCreateTupleTable    - create a new tuple table
- *             ExecDestroyTupleTable   - destroy a table
+ *             ExecDropTupleTable              - destroy a table
  *
- *      SLOT RESERVERATION
+ *      SLOT RESERVATION
  *             ExecAllocTableSlot              - find an available slot in the table
  *
  *      SLOT ACCESSORS
  *             ExecStoreTuple                  - store a tuple in the table
  *             ExecFetchTuple                  - fetch a tuple from the table
  *             ExecClearTuple                  - clear contents of a table slot
- *             ExecSlotPolicy                  - return slot's tuple pfree policy
- *             ExecSetSlotPolicy               - diddle the slot policy
- *             ExecSlotDescriptor              - type of tuple in a slot
  *             ExecSetSlotDescriptor   - set a slot's tuple descriptor
  *             ExecSetSlotDescriptorIsNew - diddle the slot-desc-is-new flag
- *             ExecSetNewSlotDescriptor - set a desc and the is-new-flag all at once
- *             ExecSlotBuffer                  - return buffer of tuple in slot
- *             ExecSetSlotBuffer               - set the buffer for tuple in slot
- *             ExecIncrSlotBufferRefcnt - bump the refcnt of the slot buffer(Macro)
  *
  *      SLOT STATUS PREDICATES
  *             TupIsNull                               - true when slot contains no tuple(Macro)
- *             ExecSlotDescriptorIsNew - true if we're now storing a different
- *                                                               type of tuple in a slot
  *
  *      CONVENIENCE INITIALIZATION ROUTINES
- *             ExecInitResultTupleSlot    \    convience routines to initialize
+ *             ExecInitResultTupleSlot    \    convenience routines to initialize
  *             ExecInitScanTupleSlot           \       the various tuple slots for nodes
- *             ExecInitMarkedTupleSlot         /  which store copies of tuples.
- *             ExecInitOuterTupleSlot     /
- *             ExecInitHashTupleSlot     /
+ *             ExecInitExtraTupleSlot          /       which store copies of tuples.
+ *             ExecInitNullTupleSlot      /
  *
- *      old routines:
- *             ExecGetTupType                  - get type of tuple returned by this node
+ *      Routines that probably belong somewhere else:
  *             ExecTypeFromTL                  - form a TupleDesc from a target list
  *
  *      EXAMPLE OF HOW TABLE ROUTINES WORK
@@ -78,8 +68,7 @@
  *               by the access methods into the scan tuple slot.
  *
  *             - ExecSeqScan() calls ExecStoreTuple() to take the result
- *               tuple from ExecTargetList() and place it into the result tuple
- *               slot.
+ *               tuple from ExecProject() and place it into the result tuple slot.
  *
  *             - ExecutePlan() calls ExecRetrieve() which gets the tuple out of
  *               the slot passed to it by calling ExecFetchTuple().  this tuple
@@ -87,7 +76,7 @@
  *
  *             At ExecEnd()
  *             ----------------
- *             - EndPlan() calls ExecDestroyTupleTable() to clean up any remaining
+ *             - EndPlan() calls ExecDropTupleTable() to clean up any remaining
  *               tuples left over from executing the query.
  *
  *             The important thing to watch in the executor code is how pointers
  *             and the TupleTableSlot node in execnodes.h.
  *
  */
-#include <string.h>
-
 #include "postgres.h"
 
-
+#include "funcapi.h"
+#include "access/heapam.h"
 #include "executor/executor.h"
-#undef ExecStoreTuple
-
-#include "access/tupdesc.h"
-#include "catalog/pg_type.h"
-#include "parser/parse_type.h"
-#include "storage/bufmgr.h"
 #include "utils/lsyscache.h"
 
-static TupleTableSlot *NodeGetResultTupleSlot(Plan *node);
-
 
 /* ----------------------------------------------------------------
  *                               tuple table create/delete functions
@@ -155,29 +135,26 @@ ExecCreateTupleTable(int initialSize)     /* initial number of slots in
        TupleTable      newtable;               /* newly allocated table */
        TupleTableSlot *array;          /* newly allocated slot array */
 
-       /* ----------------
-        *      sanity checks
-        * ----------------
+       /*
+        * sanity checks
         */
        Assert(initialSize >= 1);
 
-       /* ----------------
-        *      Now allocate our new table along with space for the pointers
-        *      to the tuples.
+       /*
+        * Now allocate our new table along with space for the pointers to the
+        * tuples.
         */
 
        newtable = (TupleTable) palloc(sizeof(TupleTableData));
        array = (TupleTableSlot *) palloc(initialSize * sizeof(TupleTableSlot));
 
-       /* ----------------
-        *      clean out the slots we just allocated
-        * ----------------
+       /*
+        * clean out the slots we just allocated
         */
        MemSet(array, 0, initialSize * sizeof(TupleTableSlot));
 
-       /* ----------------
-        *      initialize the new table and return it to the caller.
-        * ----------------
+       /*
+        * initialize the new table and return it to the caller.
         */
        newtable->size = initialSize;
        newtable->next = 0;
@@ -187,77 +164,55 @@ ExecCreateTupleTable(int initialSize)     /* initial number of slots in
 }
 
 /* --------------------------------
- *             ExecDestroyTupleTable
+ *             ExecDropTupleTable
  *
- *             This pfrees the storage assigned to the tuple table and
- *             optionally pfrees the contents of the table also.
+ *             This frees the storage used by the tuple table itself
+ *             and optionally frees the contents of the table also.
  *             It is expected that this routine be called by EndPlan().
  * --------------------------------
  */
 void
-ExecDestroyTupleTable(TupleTable table, /* tuple table */
-                                         bool shouldFree)      /* true if we should free slot
+ExecDropTupleTable(TupleTable table,   /* tuple table */
+                                  bool shouldFree)             /* true if we should free slot
                                                                                 * contents */
 {
-       int                     next;                   /* next avaliable slot */
+       int                     next;                   /* next available slot */
        TupleTableSlot *array;          /* start of table array */
        int                     i;                              /* counter */
 
-       /* ----------------
-        *      sanity checks
-        * ----------------
+       /*
+        * sanity checks
         */
        Assert(table != NULL);
 
-       /* ----------------
-        *      get information from the table
-        * ----------------
+       /*
+        * get information from the table
         */
        array = table->array;
        next = table->next;
 
-       /* ----------------
-        *      first free all the valid pointers in the tuple array
-        *      if that's what the caller wants..
-        *
-        *      Note: we do nothing about the Buffer and Tuple Descriptor's
-        *      we store in the slots.  This may have to change (ex: we should
-        *      probably worry about pfreeing tuple descs too) -cim 3/14/91
-        * ----------------
+       /*
+        * first free all the valid pointers in the tuple array and drop
+        * refcounts of any referenced buffers, if that's what the caller
+        * wants.  (There is probably no good reason for the caller ever not
+        * to want it!)
         */
        if (shouldFree)
+       {
                for (i = 0; i < next; i++)
                {
-                       TupleTableSlot slot;
-                       HeapTuple       tuple;
-
-                       slot = array[i];
-                       tuple = slot.val;
-
-                       if (tuple != NULL)
-                       {
-                               slot.val = (HeapTuple) NULL;
-                               if (slot.ttc_shouldFree)
-                               {
-                                       /* ----------------
-                                        *      since a tuple may contain a pointer to
-                                        *      lock information allocated along with the
-                                        *      tuple, we have to be careful to free any
-                                        *      rule locks also -cim 1/17/90
-                                        * ----------------
-                                        */
-                                       pfree(tuple);
-                               }
-                       }
+                       ExecClearTuple(&array[i]);
+                       if (array[i].ttc_shouldFreeDesc &&
+                               array[i].ttc_tupleDescriptor != NULL)
+                               FreeTupleDesc(array[i].ttc_tupleDescriptor);
                }
+       }
 
-       /* ----------------
-        *      finally free the tuple array and the table itself.
-        * ----------------
+       /*
+        * finally free the tuple array and the table itself.
         */
        pfree(array);
        pfree(table);
-
 }
 
 
@@ -270,64 +225,84 @@ ExecDestroyTupleTable(TupleTable table, /* tuple table */
  *
  *             This routine is used to reserve slots in the table for
  *             use by the various plan nodes.  It is expected to be
- *             called by the node init routines (ex: ExecInitNestLoop).
+ *             called by the node init routines (ex: ExecInitNestLoop)
  *             once per slot needed by the node.  Not all nodes need
  *             slots (some just pass tuples around).
  * --------------------------------
  */
-TupleTableSlot *                               /* return: the slot allocated in the tuple
-                                                                * table */
+TupleTableSlot *
 ExecAllocTableSlot(TupleTable table)
 {
        int                     slotnum;                /* new slot number */
+       TupleTableSlot *slot;
 
-       /* ----------------
-        *      sanity checks
-        * ----------------
+       /*
+        * sanity checks
         */
        Assert(table != NULL);
 
-       /* ----------------
-        *      if our table is full we have to allocate a larger
-        *      size table.  Since ExecAllocTableSlot() is only called
-        *      before the table is ever used to store tuples, we don't
-        *      have to worry about the contents of the old table.
-        *      If this changes, then we will have to preserve the contents.
-        *      -cim 6/23/90
+       /*
+        * if our table is full we have to allocate a larger size table. Since
+        * ExecAllocTableSlot() is only called before the table is ever used
+        * to store tuples, we don't have to worry about the contents of the
+        * old table. If this changes, then we will have to preserve the
+        * contents. -cim 6/23/90
         *
-        *      Unfortunately, we *cannot* do this.  All of the nodes in
-        *      the plan that have already initialized their slots will have
-        *      pointers into _freed_ memory.  This leads to bad ends.  We
-        *      now count the number of slots we will need and create all the
-        *      slots we will need ahead of time.  The if below should never
-        *      happen now.  Give a WARN if it does.  -mer 4 Aug 1992
-        * ----------------
+        * Unfortunately, we *cannot* do this.  All of the nodes in the plan that
+        * have already initialized their slots will have pointers into
+        * _freed_ memory.      This leads to bad ends.  We now count the number
+        * of slots we will need and create all the slots we will need ahead
+        * of time.  The if below should never happen now.      Fail if it does.
+        * -mer 4 Aug 1992
         */
        if (table->next >= table->size)
-       {
+               elog(ERROR, "Plan requires more slots than are available"
+                        "\n\tsend mail to your local executor guru to fix this");
 
-               /*
-                * int newsize = NewTableSize(table->size);
-                *
-                * pfree(table->array); table->array = (Pointer) palloc(newsize *
-                * TableSlotSize); bzero(table->array, newsize * TableSlotSize);
-                * table->size =  newsize;
-                */
-               elog(NOTICE, "Plan requires more slots than are available");
-               elog(ERROR, "send mail to your local executor guru to fix this");
-       }
-
-       /* ----------------
-        *      at this point, space in the table is guaranteed so we
-        *      reserve the next slot, initialize and return it.
-        * ----------------
+       /*
+        * at this point, space in the table is guaranteed so we reserve the
+        * next slot, initialize and return it.
         */
        slotnum = table->next;
        table->next++;
 
-       table->array[slotnum].type = T_TupleTableSlot;
+       slot = &(table->array[slotnum]);
+
+       /* Make sure the allocated slot is valid (and empty) */
+       slot->type = T_TupleTableSlot;
+       slot->val = (HeapTuple) NULL;
+       slot->ttc_shouldFree = true;
+       slot->ttc_descIsNew = true;
+       slot->ttc_shouldFreeDesc = true;
+       slot->ttc_tupleDescriptor = (TupleDesc) NULL;
+       slot->ttc_buffer = InvalidBuffer;
 
-       return &(table->array[slotnum]);
+       return slot;
+}
+
+/* --------------------------------
+ *             MakeTupleTableSlot
+ *
+ *             This routine makes an empty standalone TupleTableSlot.
+ *             It really shouldn't exist, but there are a few places
+ *             that do this, so we may as well centralize the knowledge
+ *             of what's in one ...
+ * --------------------------------
+ */
+TupleTableSlot *
+MakeTupleTableSlot(void)
+{
+       TupleTableSlot *slot = makeNode(TupleTableSlot);
+
+       /* This should match ExecAllocTableSlot() */
+       slot->val = (HeapTuple) NULL;
+       slot->ttc_shouldFree = true;
+       slot->ttc_descIsNew = true;
+       slot->ttc_shouldFreeDesc = true;
+       slot->ttc_tupleDescriptor = (TupleDesc) NULL;
+       slot->ttc_buffer = InvalidBuffer;
+
+       return slot;
 }
 
 /* ----------------------------------------------------------------
@@ -339,37 +314,65 @@ ExecAllocTableSlot(TupleTable table)
  *             ExecStoreTuple
  *
  *             This function is used to store a tuple into a specified
- *             slot in the tuple table.  Note: the only slots which should
- *             be called with shouldFree == false are those slots used to
- *             store tuples not allocated with pfree().  Currently the
- *             seqscan and indexscan nodes use this for the tuples returned
- *             by amgetattr, which are actually pointers onto disk pages.
+ *             slot in the tuple table.
+ *
+ *             tuple:  tuple to store
+ *             slot:   slot to store it in
+ *             buffer: disk buffer if tuple is in a disk page, else InvalidBuffer
+ *             shouldFree: true if ExecClearTuple should pfree() the tuple
+ *                                     when done with it
+ *
+ * If 'buffer' is not InvalidBuffer, the tuple table code acquires a pin
+ * on the buffer which is held until the slot is cleared, so that the tuple
+ * won't go away on us.
+ *
+ * shouldFree is normally set 'true' for tuples constructed on-the-fly.
+ * It must always be 'false' for tuples that are stored in disk pages,
+ * since we don't want to try to pfree those.
+ *
+ * Another case where it is 'false' is when the referenced tuple is held
+ * in a tuple table slot belonging to a lower-level executor Proc node.
+ * In this case the lower-level slot retains ownership and responsibility
+ * for eventually releasing the tuple. When this method is used, we must
+ * be certain that the upper-level Proc node will lose interest in the tuple
+ * sooner than the lower-level one does!  If you're not certain, copy the
+ * lower-level tuple with heap_copytuple and let the upper-level table
+ * slot assume ownership of the copy!
+ *
+ * Return value is just the passed-in slot pointer.
  * --------------------------------
  */
-TupleTableSlot *                               /* return: slot passed */
-ExecStoreTuple(HeapTuple tuple, /* tuple to store */
-                          TupleTableSlot *slot,        /* slot in which to store tuple */
-                          Buffer buffer,       /* buffer associated with tuple */
-                          bool shouldFree) /* true if we call pfree() when we gc. */
+TupleTableSlot *
+ExecStoreTuple(HeapTuple tuple,
+                          TupleTableSlot *slot,
+                          Buffer buffer,
+                          bool shouldFree)
 {
-       /* ----------------
-        *      sanity checks
-        * ----------------
+       /*
+        * sanity checks
         */
        Assert(slot != NULL);
+       /* passing shouldFree=true for a tuple on a disk page is not sane */
+       Assert(BufferIsValid(buffer) ? (!shouldFree) : true);
 
-       /* clear out the slot first */
+       /* clear out any old contents of the slot */
        ExecClearTuple(slot);
 
-       /* ----------------
-        *      store the new tuple into the specified slot and
-        *      return the slot into which we stored the tuple.
-        * ----------------
+       /*
+        * store the new tuple into the specified slot and return the slot
+        * into which we stored the tuple.
         */
        slot->val = tuple;
        slot->ttc_buffer = buffer;
        slot->ttc_shouldFree = shouldFree;
 
+       /*
+        * If tuple is on a disk page, keep the page pinned as long as we hold
+        * a pointer into it.
+        */
+       if (BufferIsValid(buffer))
+               IncrBufferRefCount(buffer);
+
        return slot;
 }
 
@@ -377,6 +380,8 @@ ExecStoreTuple(HeapTuple tuple, /* tuple to store */
  *             ExecClearTuple
  *
  *             This function is used to clear out a slot in the tuple table.
+ *
+ *             NB: only the tuple is cleared, not the tuple descriptor (if any).
  * --------------------------------
  */
 TupleTableSlot *                               /* return: slot passed */
@@ -384,101 +389,37 @@ ExecClearTuple(TupleTableSlot *slot)     /* slot in which to store tuple */
 {
        HeapTuple       oldtuple;               /* prior contents of slot */
 
-       /* ----------------
-        *      sanity checks
-        * ----------------
+       /*
+        * sanity checks
         */
        Assert(slot != NULL);
 
-       /* ----------------
-        *      get information from the tuple table
-        * ----------------
+       /*
+        * get information from the tuple table
         */
        oldtuple = slot->val;
 
-       /* ----------------
-        *      free the old contents of the specified slot if necessary.
-        * ----------------
+       /*
+        * free the old contents of the specified slot if necessary.
         */
        if (slot->ttc_shouldFree && oldtuple != NULL)
-       {
-               /* ----------------
-                *      since a tuple may contain a pointer to
-                *      lock information allocated along with the
-                *      tuple, we have to be careful to free any
-                *      rule locks also -cim 1/17/90
-                * ----------------
-                */
-               pfree(oldtuple);
-       }
+               heap_freetuple(oldtuple);
 
-       /* ----------------
-        *      store NULL into the specified slot and return the slot.
-        *      - also set buffer to InvalidBuffer -cim 3/14/91
-        * ----------------
-        */
        slot->val = (HeapTuple) NULL;
 
+       slot->ttc_shouldFree = true;    /* probably useless code... */
+
+       /*
+        * Drop the pin on the referenced buffer, if there is one.
+        */
        if (BufferIsValid(slot->ttc_buffer))
                ReleaseBuffer(slot->ttc_buffer);
 
        slot->ttc_buffer = InvalidBuffer;
-       slot->ttc_shouldFree = true;
 
        return slot;
 }
 
-
-/* --------------------------------
- *             ExecSlotPolicy
- *
- *             This function is used to get the call/don't call pfree
- *             setting of a slot.      Most executor routines don't need this.
- *             It's only when you do tricky things like marking tuples for
- *             merge joins that you need to diddle the slot policy.
- * --------------------------------
- */
-#ifdef NOT_USED
-bool                                                   /* return: slot policy */
-ExecSlotPolicy(TupleTableSlot *slot)   /* slot to inspect */
-{
-       return slot->ttc_shouldFree;
-}
-
-
-/* --------------------------------
- *             ExecSetSlotPolicy
- *
- *             This function is used to change the call/don't call pfree
- *             setting of a slot.      Most executor routines don't need this.
- *             It's only when you do tricky things like marking tuples for
- *             merge joins that you need to diddle the slot policy.
- * --------------------------------
- */
-bool                                                   /* return: old slot policy */
-ExecSetSlotPolicy(TupleTableSlot *slot, /* slot to change */
-                                 bool shouldFree)              /* true if we call pfree() when we
-                                                                                * gc. */
-{
-       bool            old_shouldFree = slot->ttc_shouldFree;
-
-       slot->ttc_shouldFree = shouldFree;
-
-       return old_shouldFree;
-}
-
-#endif
-
-/* --------------------------------
- *             ExecSlotDescriptor
- *
- *             This function is used to get the tuple descriptor associated
- *             with the slot's tuple.
- *
- * Now a macro in tuptable.h  -mer 5 March 1992
- * --------------------------------
- */
-
 /* --------------------------------
  *             ExecSetSlotDescriptor
  *
@@ -486,14 +427,17 @@ ExecSetSlotPolicy(TupleTableSlot *slot, /* slot to change */
  *             with the slot's tuple.
  * --------------------------------
  */
-TupleDesc                                              /* return: old slot tuple descriptor */
+void
 ExecSetSlotDescriptor(TupleTableSlot *slot,            /* slot to change */
-                                         TupleDesc tupdesc)            /* tuple descriptor */
+                                         TupleDesc tupdesc,            /* new tuple descriptor */
+                                         bool shouldFree)      /* is desc owned by slot? */
 {
-       TupleDesc       old_tupdesc = slot->ttc_tupleDescriptor;
+       if (slot->ttc_shouldFreeDesc &&
+               slot->ttc_tupleDescriptor != NULL)
+               FreeTupleDesc(slot->ttc_tupleDescriptor);
 
        slot->ttc_tupleDescriptor = tupdesc;
-       return old_tupdesc;
+       slot->ttc_shouldFreeDesc = shouldFree;
 }
 
 /* --------------------------------
@@ -509,96 +453,21 @@ ExecSetSlotDescriptorIsNew(TupleTableSlot *slot,          /* slot to change */
        slot->ttc_descIsNew = isNew;
 }
 
-/* --------------------------------
- *             ExecSetNewSlotDescriptor
- *
- *             This function is used to set the tuple descriptor associated
- *             with the slot's tuple, and set the "isNew" flag at the same time.
- * --------------------------------
- */
-#ifdef NOT_USED
-TupleDesc                                              /* return: old slot tuple descriptor */
-ExecSetNewSlotDescriptor(TupleTableSlot *slot, /* slot to change */
-                                                TupleDesc tupdesc)             /* tuple descriptor */
-{
-       TupleDesc       old_tupdesc = slot->ttc_tupleDescriptor;
-
-       slot->ttc_tupleDescriptor = tupdesc;
-       slot->ttc_descIsNew = true;
-
-       return old_tupdesc;
-}
-
-#endif
-
-/* --------------------------------
- *             ExecSlotBuffer
- *
- *             This function is used to get the tuple descriptor associated
- *             with the slot's tuple.  Be very careful with this as it does not
- *             balance the reference counts.  If the buffer returned is stored
- *             someplace else, then also use ExecIncrSlotBufferRefcnt().
- *
- * Now a macro in tuptable.h
- * --------------------------------
- */
-
-/* --------------------------------
- *             ExecSetSlotBuffer
- *
- *             This function is used to set the tuple descriptor associated
- *             with the slot's tuple.   Be very careful with this as it does not
- *             balance the reference counts.  If we're using this then we should
- *             also use ExecIncrSlotBufferRefcnt().
- * --------------------------------
- */
-#ifdef NOT_USED
-Buffer                                                 /* return: old slot buffer */
-ExecSetSlotBuffer(TupleTableSlot *slot, /* slot to change */
-                                 Buffer b)             /* tuple descriptor */
-{
-       Buffer          oldb = slot->ttc_buffer;
-
-       slot->ttc_buffer = b;
-
-       return oldb;
-}
-
-#endif
-
 /* ----------------------------------------------------------------
  *                               tuple table slot status predicates
  * ----------------------------------------------------------------
  */
 
-/* --------------------------------
- *             ExecSlotDescriptorIsNew
- *
- *             This function is used to check if the tuple descriptor
- *             associated with this slot has just changed.  ie: we are
- *             now storing a new type of tuple in this slot
- * --------------------------------
- */
-#ifdef NOT_USED
-bool                                                   /* return: descriptor "is new" */
-ExecSlotDescriptorIsNew(TupleTableSlot *slot)  /* slot to inspect */
-{
-/*       bool isNew = SlotTupleDescriptorIsNew((TupleTableSlot*) slot);
-       return isNew; */
-       return slot->ttc_descIsNew;
-}
-
-#endif
-
 /* ----------------------------------------------------------------
  *                             convenience initialization routines
  * ----------------------------------------------------------------
  */
 /* --------------------------------
- *             ExecInit{Result,Scan,Raw,Marked,Outer,Hash}TupleSlot
+ *             ExecInit{Result,Scan,Extra}TupleSlot
  *
- *             These are convenience routines to initialize the specfied slot
- *             in nodes inheriting the appropriate state.
+ *             These are convenience routines to initialize the specified slot
+ *             in nodes inheriting the appropriate state.      ExecInitExtraTupleSlot
+ *             is used for initializing special-purpose slots.
  * --------------------------------
  */
 #define INIT_SLOT_DEFS \
@@ -607,23 +476,18 @@ ExecSlotDescriptorIsNew(TupleTableSlot *slot)     /* slot to inspect */
 
 #define INIT_SLOT_ALLOC \
        tupleTable = (TupleTable) estate->es_tupleTable; \
-       slot =           ExecAllocTableSlot(tupleTable); \
-       slot->val = (HeapTuple)NULL; \
-       slot->ttc_shouldFree = true; \
-       slot->ttc_tupleDescriptor = (TupleDesc)NULL; \
-       slot->ttc_whichplan = -1;\
-       slot->ttc_descIsNew = true;
+       slot =           ExecAllocTableSlot(tupleTable);
 
 /* ----------------
  *             ExecInitResultTupleSlot
  * ----------------
  */
 void
-ExecInitResultTupleSlot(EState *estate, CommonState *commonstate)
+ExecInitResultTupleSlot(EState *estate, PlanState *planstate)
 {
        INIT_SLOT_DEFS;
        INIT_SLOT_ALLOC;
-       commonstate->cs_ResultTupleSlot = (TupleTableSlot *) slot;
+       planstate->ps_ResultTupleSlot = slot;
 }
 
 /* ----------------
@@ -631,385 +495,288 @@ ExecInitResultTupleSlot(EState *estate, CommonState *commonstate)
  * ----------------
  */
 void
-ExecInitScanTupleSlot(EState *estate, CommonScanState *commonscanstate)
+ExecInitScanTupleSlot(EState *estate, ScanState *scanstate)
 {
        INIT_SLOT_DEFS;
        INIT_SLOT_ALLOC;
-       commonscanstate->css_ScanTupleSlot = (TupleTableSlot *) slot;
+       scanstate->ss_ScanTupleSlot = slot;
 }
 
-#ifdef NOT_USED
 /* ----------------
- *             ExecInitMarkedTupleSlot
+ *             ExecInitExtraTupleSlot
  * ----------------
  */
-void
-ExecInitMarkedTupleSlot(EState *estate, MergeJoinState *mergestate)
-{
-       INIT_SLOT_DEFS;
-       INIT_SLOT_ALLOC;
-       mergestate->mj_MarkedTupleSlot = (TupleTableSlot *) slot;
-}
-
-#endif
-
-/* ----------------
- *             ExecInitOuterTupleSlot
- * ----------------
- */
-void
-ExecInitOuterTupleSlot(EState *estate, HashJoinState *hashstate)
-{
-       INIT_SLOT_DEFS;
-       INIT_SLOT_ALLOC;
-       hashstate->hj_OuterTupleSlot = slot;
-}
-
-/* ----------------
- *             ExecInitHashTupleSlot
- * ----------------
- */
-#ifdef NOT_USED
-void
-ExecInitHashTupleSlot(EState *estate, HashJoinState *hashstate)
+TupleTableSlot *
+ExecInitExtraTupleSlot(EState *estate)
 {
        INIT_SLOT_DEFS;
        INIT_SLOT_ALLOC;
-       hashstate->hj_HashTupleSlot = slot;
-}
-
-#endif
-
-static TupleTableSlot *
-NodeGetResultTupleSlot(Plan *node)
-{
-       TupleTableSlot *slot;
-
-       switch (nodeTag(node))
-       {
-
-               case T_Result:
-                       {
-                               ResultState *resstate = ((Result *) node)->resstate;
-
-                               slot = resstate->cstate.cs_ResultTupleSlot;
-                       }
-                       break;
-
-               case T_SeqScan:
-                       {
-                               CommonScanState *scanstate = ((SeqScan *) node)->scanstate;
-
-                               slot = scanstate->cstate.cs_ResultTupleSlot;
-                       }
-                       break;
-
-               case T_NestLoop:
-                       {
-                               NestLoopState *nlstate = ((NestLoop *) node)->nlstate;
-
-                               slot = nlstate->jstate.cs_ResultTupleSlot;
-                       }
-                       break;
-
-               case T_Append:
-                       {
-                               Append     *n = (Append *) node;
-                               AppendState *appendstate;
-                               List       *appendplans;
-                               int                     whichplan;
-                               Plan       *subplan;
-
-                               appendstate = n->appendstate;
-                               appendplans = n->appendplans;
-                               whichplan = appendstate->as_whichplan;
-
-                               subplan = (Plan *) nth(whichplan, appendplans);
-                               slot = NodeGetResultTupleSlot(subplan);
-                               break;
-                       }
-
-               case T_IndexScan:
-                       {
-                               CommonScanState *scanstate = ((IndexScan *) node)->scan.scanstate;
-
-                               slot = scanstate->cstate.cs_ResultTupleSlot;
-                       }
-                       break;
-
-               case T_Material:
-                       {
-                               MaterialState *matstate = ((Material *) node)->matstate;
-
-                               slot = matstate->csstate.css_ScanTupleSlot;
-                       }
-                       break;
-
-               case T_Sort:
-                       {
-                               SortState  *sortstate = ((Sort *) node)->sortstate;
-
-                               slot = sortstate->csstate.css_ScanTupleSlot;
-                       }
-                       break;
-
-               case T_Agg:
-                       {
-                               AggState   *aggstate = ((Agg *) node)->aggstate;
-
-                               slot = aggstate->csstate.cstate.cs_ResultTupleSlot;
-                       }
-                       break;
-
-               case T_Group:
-                       {
-                               GroupState *grpstate = ((Group *) node)->grpstate;
-
-                               slot = grpstate->csstate.cstate.cs_ResultTupleSlot;
-                       }
-                       break;
-
-               case T_Hash:
-                       {
-                               HashState  *hashstate = ((Hash *) node)->hashstate;
-
-                               slot = hashstate->cstate.cs_ResultTupleSlot;
-                       }
-                       break;
-
-               case T_Unique:
-                       {
-                               UniqueState *uniquestate = ((Unique *) node)->uniquestate;
-
-                               slot = uniquestate->cs_ResultTupleSlot;
-                       }
-                       break;
-
-               case T_MergeJoin:
-                       {
-                               MergeJoinState *mergestate = ((MergeJoin *) node)->mergestate;
-
-                               slot = mergestate->jstate.cs_ResultTupleSlot;
-                       }
-                       break;
-
-               case T_HashJoin:
-                       {
-                               HashJoinState *hashjoinstate = ((HashJoin *) node)->hashjoinstate;
-
-                               slot = hashjoinstate->jstate.cs_ResultTupleSlot;
-                       }
-                       break;
-
-               default:
-                       /* ----------------
-                        *        should never get here
-                        * ----------------
-                        */
-                       elog(ERROR, "NodeGetResultTupleSlot: node not yet supported: %d ",
-                                nodeTag(node));
-
-                       return NULL;
-       }
        return slot;
 }
 
-/* ----------------------------------------------------------------
- *             ExecGetTupType
- *
- *             this gives you the tuple descriptor for tuples returned
- *             by this node.  I really wish I could ditch this routine,
- *             but since not all nodes store their type info in the same
- *             place, we have to do something special for each node type.
- *
- *             Soon, the system will have to adapt to deal with changing
- *             tuple descriptors as we deal with dynamic tuple types
- *             being returned from procedure nodes.  Perhaps then this
- *             routine can be retired.  -cim 6/3/91
- *
- * old comments
- *             This routine just gets the type information out of the
- *             node's state.  If you already have a node's state, you
- *             can get this information directly, but this is a useful
- *             routine if you want to get the type information from
- *             the node's inner or outer subplan easily without having
- *             to inspect the subplan.. -cim 10/16/89
+/* ----------------
+ *             ExecInitNullTupleSlot
  *
- * ----------------------------------------------------------------
+ * Build a slot containing an all-nulls tuple of the given type.
+ * This is used as a substitute for an input tuple when performing an
+ * outer join.
+ * ----------------
  */
-
-TupleDesc
-ExecGetTupType(Plan *node)
+TupleTableSlot *
+ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
 {
-       TupleTableSlot *slot;
-       TupleDesc       tupType;
+       TupleTableSlot *slot = ExecInitExtraTupleSlot(estate);
 
-       if (node == NULL)
-               return NULL;
+       /*
+        * Since heap_getattr() will treat attributes beyond a tuple's t_natts
+        * as being NULL, we can make an all-nulls tuple just by making it be
+        * of zero length.      However, the slot descriptor must match the real
+        * tupType.
+        */
+       HeapTuple       nullTuple;
+       Datum           values[1];
+       char            nulls[1];
+       static struct tupleDesc NullTupleDesc;          /* we assume this inits to
+                                                                                                * zeroes */
 
-       slot = NodeGetResultTupleSlot(node);
-       tupType = slot->ttc_tupleDescriptor;
-       return tupType;
-}
+       ExecSetSlotDescriptor(slot, tupType, false);
 
-/*
-TupleDesc
-ExecCopyTupType(TupleDesc td, int natts)
-{
-       TupleDesc newTd;
-       int                             i;
+       nullTuple = heap_formtuple(&NullTupleDesc, values, nulls);
 
-       newTd = CreateTemplateTupleDesc(natts);
-       i = 0;
-       while (i < natts)
-               {
-                       newTd[i] = (Form_pg_attribute)palloc(sizeof(FormData_pg_attribute));
-                       memmove(newTd[i], td[i], sizeof(FormData_pg_attribute));
-                       i++;
-               }
-       return newTd;
+       return ExecStoreTuple(nullTuple, slot, InvalidBuffer, true);
 }
-*/
 
 /* ----------------------------------------------------------------
  *             ExecTypeFromTL
  *
+ *             Generate a tuple descriptor for the result tuple of a targetlist.
+ *             (A parse/plan tlist must be passed, not an ExprState tlist.)
+ *             Note that resjunk columns, if any, are included in the result.
+ *
  *             Currently there are about 4 different places where we create
  *             TupleDescriptors.  They should all be merged, or perhaps
  *             be rewritten to call BuildDesc().
- *
- *     old comments
- *             Forms attribute type info from the target list in the node.
- *             It assumes all domains are individually specified in the target list.
- *             It fails if the target list contains something like Emp.all
- *             which represents all the attributes from EMP relation.
- *
- *             Conditions:
- *                     The inner and outer subtrees should be initialized because it
- *                     might be necessary to know the type infos of the subtrees.
  * ----------------------------------------------------------------
  */
 TupleDesc
-ExecTypeFromTL(List *targetList)
+ExecTypeFromTL(List *targetList, bool hasoid)
 {
-       List       *tlcdr;
        TupleDesc       typeInfo;
-       Resdom     *resdom;
-       Oid                     restype;
+       List       *tlitem;
        int                     len;
 
-       /* ----------------
-        *      examine targetlist - if empty then return NULL
-        * ----------------
+       /*
+        * allocate a new typeInfo
         */
        len = ExecTargetListLength(targetList);
+       typeInfo = CreateTemplateTupleDesc(len, hasoid);
 
-       if (len == 0)
-               return NULL;
-
-       /* ----------------
-        *      allocate a new typeInfo
-        * ----------------
+       /*
+        * scan list, generate type info for each entry
         */
-       typeInfo = CreateTemplateTupleDesc(len);
+       foreach(tlitem, targetList)
+       {
+               TargetEntry *tle = lfirst(tlitem);
+               Resdom     *resdom = tle->resdom;
+
+               TupleDescInitEntry(typeInfo,
+                                                  resdom->resno,
+                                                  resdom->resname,
+                                                  resdom->restype,
+                                                  resdom->restypmod,
+                                                  0,
+                                                  false);
+       }
 
-       /* ----------------
-        * notes: get resdom from (resdom expr)
-        *                get_typbyval comes from src/lib/l-lisp/lsyscache.c
-        * ----------------
+       return typeInfo;
+}
+
+/*
+ * TupleDescGetSlot - Initialize a slot based on the supplied tupledesc
+ */
+TupleTableSlot *
+TupleDescGetSlot(TupleDesc tupdesc)
+{
+       TupleTableSlot *slot;
+
+       /* Make a standalone slot */
+       slot = MakeTupleTableSlot();
+
+       /* Bind the tuple description to the slot */
+       ExecSetSlotDescriptor(slot, tupdesc, true);
+
+       /* Return the slot */
+       return slot;
+}
+
+/*
+ * TupleDescGetAttInMetadata - Build an AttInMetadata structure based on the
+ * supplied TupleDesc. AttInMetadata can be used in conjunction with C strings
+ * to produce a properly formed tuple.
+ */
+AttInMetadata *
+TupleDescGetAttInMetadata(TupleDesc tupdesc)
+{
+       int                     natts = tupdesc->natts;
+       int                     i;
+       Oid                     atttypeid;
+       Oid                     attinfuncid;
+       FmgrInfo   *attinfuncinfo;
+       Oid                *attelems;
+       int32      *atttypmods;
+       AttInMetadata *attinmeta;
+
+       attinmeta = (AttInMetadata *) palloc(sizeof(AttInMetadata));
+
+       /*
+        * Gather info needed later to call the "in" function for each
+        * attribute
         */
-       tlcdr = targetList;
-       while (tlcdr != NIL)
+       attinfuncinfo = (FmgrInfo *) palloc(natts * sizeof(FmgrInfo));
+       attelems = (Oid *) palloc(natts * sizeof(Oid));
+       atttypmods = (int32 *) palloc(natts * sizeof(int32));
+
+       for (i = 0; i < natts; i++)
        {
-               TargetEntry *tle = lfirst(tlcdr);
+               atttypeid = tupdesc->attrs[i]->atttypid;
+               getTypeInputInfo(atttypeid, &attinfuncid, &attelems[i]);
+               fmgr_info(attinfuncid, &attinfuncinfo[i]);
+               atttypmods[i] = tupdesc->attrs[i]->atttypmod;
+       }
+       attinmeta->tupdesc = tupdesc;
+       attinmeta->attinfuncs = attinfuncinfo;
+       attinmeta->attelems = attelems;
+       attinmeta->atttypmods = atttypmods;
 
-               if (tle->resdom != NULL)
-               {
-                       resdom = tle->resdom;
-                       restype = resdom->restype;
-
-                       TupleDescInitEntry(typeInfo,
-                                                          resdom->resno,
-                                                          resdom->resname,
-                       /* fix for SELECT NULL ... */
-                                                          (restype ? restype : UNKNOWNOID),
-                                                          resdom->restypmod,
-                                                          0,
-                                                          false);
+       return attinmeta;
+}
 
 /*
-                       ExecSetTypeInfo(resdom->resno - 1,
-                                                       typeInfo,
-                                                       (Oid) restype,
-                                                       resdom->resno,
-                                                       resdom->reslen,
-                                                       resdom->resname->data,
-                                                       get_typbyval(restype),
-                                                       get_typalign(restype));
-*/
+ * BuildTupleFromCStrings - build a HeapTuple given user data in C string form.
+ * values is an array of C strings, one for each attribute of the return tuple.
+ */
+HeapTuple
+BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
+{
+       TupleDesc       tupdesc = attinmeta->tupdesc;
+       int                     natts = tupdesc->natts;
+       Datum      *dvalues;
+       char       *nulls;
+       int                     i;
+       Oid                     attelem;
+       int32           atttypmod;
+       HeapTuple       tuple;
+
+       dvalues = (Datum *) palloc(natts * sizeof(Datum));
+       nulls = (char *) palloc(natts * sizeof(char));
+
+       /* Call the "in" function for each non-null attribute */
+       for (i = 0; i < natts; i++)
+       {
+               if (values[i] != NULL)
+               {
+                       attelem = attinmeta->attelems[i];
+                       atttypmod = attinmeta->atttypmods[i];
+
+                       dvalues[i] = FunctionCall3(&attinmeta->attinfuncs[i],
+                                                                          CStringGetDatum(values[i]),
+                                                                          ObjectIdGetDatum(attelem),
+                                                                          Int32GetDatum(atttypmod));
+                       nulls[i] = ' ';
                }
                else
                {
-                       Resdom     *fjRes;
-                       List       *fjTlistP;
-                       List       *fjList = lfirst(tlcdr);
-
-#ifdef SETS_FIXED
-                       TargetEntry *tle;
-                       Fjoin      *fjNode = ((TargetEntry *) lfirst(fjList))->fjoin;
-
-                       tle = fjNode->fj_innerNode; /* ??? */
-#endif
-                       fjRes = tle->resdom;
-                       restype = fjRes->restype;
-
-                       TupleDescInitEntry(typeInfo,
-                                                          fjRes->resno,
-                                                          fjRes->resname,
-                                                          restype,
-                                                          fjRes->restypmod,
-                                                          0,
-                                                          false);
+                       dvalues[i] = (Datum) 0;
+                       nulls[i] = 'n';
+               }
+       }
+
+       /*
+        * Form a tuple
+        */
+       tuple = heap_formtuple(tupdesc, dvalues, nulls);
+
+       /*
+        * Release locally palloc'd space.  XXX would probably be good to
+        * pfree values of pass-by-reference datums, as well.
+        */
+       pfree(dvalues);
+       pfree(nulls);
+
+       return tuple;
+}
+
 /*
-                       ExecSetTypeInfo(fjRes->resno - 1,
-                                                       typeInfo,
-                                                       (Oid) restype,
-                                                       fjRes->resno,
-                                                       fjRes->reslen,
-                                                       (char *) fjRes->resname,
-                                                       get_typbyval(restype),
-                                                       get_typalign(restype));
-*/
-
-                       foreach(fjTlistP, lnext(fjList))
-                       {
-                               TargetEntry *fjTle = lfirst(fjTlistP);
-
-                               fjRes = fjTle->resdom;
-
-                               TupleDescInitEntry(typeInfo,
-                                                                  fjRes->resno,
-                                                                  fjRes->resname,
-                                                                  restype,
-                                                                  fjRes->restypmod,
-                                                                  0,
-                                                                  false);
+ * Functions for sending tuples to the frontend (or other specified destination)
+ * as though it is a SELECT result. These are used by utility commands that
+ * need to project directly to the destination and don't need or want full
+ * Table Function capability. Currently used by EXPLAIN and SHOW ALL
+ */
+TupOutputState *
+begin_tup_output_tupdesc(CommandDest dest, TupleDesc tupdesc)
+{
+       TupOutputState *tstate;
+
+       tstate = (TupOutputState *) palloc(sizeof(TupOutputState));
+
+       tstate->metadata = TupleDescGetAttInMetadata(tupdesc);
+       tstate->destfunc = DestToFunction(dest);
+
+       (*tstate->destfunc->setup) (tstate->destfunc, (int) CMD_SELECT,
+                                                               NULL, tupdesc, NIL);
+
+       return tstate;
+}
 
 /*
-                               ExecSetTypeInfo(fjRes->resno - 1,
-                                                               typeInfo,
-                                                               (Oid) fjRes->restype,
-                                                               fjRes->resno,
-                                                               fjRes->reslen,
-                                                               (char *) fjRes->resname,
-                                                               get_typbyval(fjRes->restype),
-                                                               get_typalign(fjRes->restype));
-*/
-                       }
-               }
+ * write a single tuple
+ *
+ * values is a list of the external C string representations of the values
+ * to be projected.
+ */
+void
+do_tup_output(TupOutputState *tstate, char **values)
+{
+       /* build a tuple from the input strings using the tupdesc */
+       HeapTuple       tuple = BuildTupleFromCStrings(tstate->metadata, values);
+
+       /* send the tuple to the receiver */
+       (*tstate->destfunc->receiveTuple) (tuple,
+                                                                          tstate->metadata->tupdesc,
+                                                                          tstate->destfunc);
+       /* clean up */
+       heap_freetuple(tuple);
+}
 
-               tlcdr = lnext(tlcdr);
+/*
+ * write a chunk of text, breaking at newline characters
+ *
+ * NB: scribbles on its input!
+ *
+ * Should only be used with a single-TEXT-attribute tupdesc.
+ */
+void
+do_text_output_multiline(TupOutputState *tstate, char *text)
+{
+       while (*text)
+       {
+               char       *eol;
+
+               eol = strchr(text, '\n');
+               if (eol)
+                       *eol++ = '\0';
+               else
+                       eol = text +strlen(text);
+
+               do_tup_output(tstate, &text);
+               text = eol;
        }
+}
 
-       return typeInfo;
+void
+end_tup_output(TupOutputState *tstate)
+{
+       (*tstate->destfunc->cleanup) (tstate->destfunc);
+       /* XXX worth cleaning up the attinmetadata? */
+       pfree(tstate);
 }