}
 
        /*
-        * Initialize the executor "tuple" table.  We need slots for all the plan
-        * nodes, plus possibly output slots for the junkfilter(s). At this point
-        * we aren't sure if we need junkfilters, so just add slots for them
-        * unconditionally.  Also, if it's not a SELECT, set up a slot for use for
-        * trigger output tuples.  Also, one for RETURNING-list evaluation.
+        * Initialize the executor's tuple table.  Also, if it's not a SELECT,
+        * set up a tuple table slot for use for trigger output tuples.
         */
-       {
-               int                     nSlots;
-
-               /* Slots for the main plan tree */
-               nSlots = ExecCountSlotsNode(plan);
-               /* Add slots for subplans and initplans */
-               foreach(l, plannedstmt->subplans)
-               {
-                       Plan       *subplan = (Plan *) lfirst(l);
-
-                       nSlots += ExecCountSlotsNode(subplan);
-               }
-               /* Add slots for junkfilter(s) */
-               if (plannedstmt->resultRelations != NIL)
-                       nSlots += list_length(plannedstmt->resultRelations);
-               else
-                       nSlots += 1;
-               if (operation != CMD_SELECT)
-                       nSlots++;                       /* for es_trig_tuple_slot */
-               if (plannedstmt->returningLists)
-                       nSlots++;                       /* for RETURNING projection */
-
-               estate->es_tupleTable = ExecCreateTupleTable(nSlots);
-
-               if (operation != CMD_SELECT)
-                       estate->es_trig_tuple_slot =
-                               ExecAllocTableSlot(estate->es_tupleTable);
-       }
+       estate->es_tupleTable = NIL;
+       if (operation != CMD_SELECT)
+               estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate);
 
        /* mark EvalPlanQual not active */
        estate->es_plannedstmt = plannedstmt;
 
                                        j = ExecInitJunkFilter(subplan->plan->targetlist,
                                                        resultRelInfo->ri_RelationDesc->rd_att->tdhasoid,
-                                                                 ExecAllocTableSlot(estate->es_tupleTable));
+                                                                                  ExecInitExtraTupleSlot(estate));
 
                                        /*
                                         * Since it must be UPDATE/DELETE, there had better be a
 
                                j = ExecInitJunkFilter(planstate->plan->targetlist,
                                                                           tupType->tdhasoid,
-                                                                 ExecAllocTableSlot(estate->es_tupleTable));
+                                                                          ExecInitExtraTupleSlot(estate));
                                estate->es_junkFilter = j;
                                if (estate->es_result_relation_info)
                                        estate->es_result_relation_info->ri_junkFilter = j;
                                                                 false);
 
                /* Set up a slot for the output of the RETURNING projection(s) */
-               slot = ExecAllocTableSlot(estate->es_tupleTable);
+               slot = ExecInitExtraTupleSlot(estate);
                ExecSetSlotDescriptor(slot, tupType);
                /* Need an econtext too */
                econtext = CreateExprContext(estate);
        }
 
        /*
-        * destroy the executor "tuple" table.
+        * destroy the executor's tuple table.  Actually we only care about
+        * releasing buffer pins and tupdesc refcounts; there's no need to
+        * pfree the TupleTableSlots, since the containing memory context
+        * is about to go away anyway.
         */
-       ExecDropTupleTable(estate->es_tupleTable, true);
-       estate->es_tupleTable = NULL;
+       ExecResetTupleTable(estate->es_tupleTable, false);
 
        /*
         * close the result relation(s) if any, but hold locks until xact commit.
                epqstate->es_evTuple = priorepq->estate->es_evTuple;
 
        /*
-        * Create sub-tuple-table; we needn't redo the CountSlots work though.
+        * Each epqstate also has its own tuple table.
         */
-       epqstate->es_tupleTable =
-               ExecCreateTupleTable(estate->es_tupleTable->size);
+       epqstate->es_tupleTable = NIL;
 
        /*
         * Initialize private state information for each SubPlan.  We must do this
                ExecEndNode(subplanstate);
        }
 
-       ExecDropTupleTable(epqstate->es_tupleTable, true);
-       epqstate->es_tupleTable = NULL;
+       /* throw away the per-epqstate tuple table completely */
+       ExecResetTupleTable(epqstate->es_tupleTable, true);
+       epqstate->es_tupleTable = NIL;
 
        if (epqstate->es_evTuple[epq->rti - 1] != NULL)
        {
 
 /*-------------------------------------------------------------------------
  *
  * 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 TupleTableSlots.  These are used for resource
+ *       management associated with tuples (eg, releasing buffer pins for
+ *       tuples in disk buffers, or freeing the memory occupied by transient
+ *       tuples).  Slots also provide access abstraction that lets us implement
+ *       "virtual" tuples to reduce data-copying overhead.
  *
  *       Routines dealing with the type information for tuples. Currently,
  *       the type information for a tuple is an array of FormData_pg_attribute.
 /*
  * INTERFACE ROUTINES
  *
- *      TABLE CREATE/DELETE
- *             ExecCreateTupleTable    - create a new tuple table
- *             ExecDropTupleTable              - destroy a table
- *             MakeSingleTupleTableSlot - make a single-slot table
- *             ExecDropSingleTupleTableSlot - destroy same
- *
- *      SLOT RESERVATION
- *             ExecAllocTableSlot              - find an available slot in the table
+ *      SLOT CREATION/DESTRUCTION
+ *             MakeTupleTableSlot              - create an empty slot
+ *             ExecAllocTableSlot              - create a slot within a tuple table
+ *             ExecResetTupleTable             - clear and optionally delete a tuple table
+ *             MakeSingleTupleTableSlot - make a standalone slot, set its descriptor
+ *             ExecDropSingleTupleTableSlot - destroy a standalone slot
  *
  *      SLOT ACCESSORS
  *             ExecSetSlotDescriptor   - set a slot's tuple descriptor
  *
  *             At ExecutorStart()
  *             ----------------
- *             - 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
+ *               ExecInitResultTupleSlot() to construct TupleTableSlots
+ *               for the tuples returned by the access methods and the
  *               tuples resulting from performing target list projections.
  *
  *             During ExecutorRun()
  *
  *             At ExecutorEnd()
  *             ----------------
- *             - EndPlan() calls ExecDropTupleTable() to clean up any remaining
+ *             - EndPlan() calls ExecResetTupleTable() to clean up any remaining
  *               tuples left over from executing the query.
  *
  *             The important thing to watch in the executor code is how pointers
  */
 
 /* --------------------------------
- *             ExecCreateTupleTable
- *
- *             This creates a new tuple table of the specified size.
+ *             MakeTupleTableSlot
  *
- *             This should be used by InitPlan() to allocate the table.
- *             The table's address will be stored in the EState structure.
+ *             Basic routine to make an empty TupleTableSlot.
  * --------------------------------
  */
-TupleTable
-ExecCreateTupleTable(int tableSize)
+TupleTableSlot *
+MakeTupleTableSlot(void)
 {
-       TupleTable      newtable;
-       int                     i;
+       TupleTableSlot *slot = makeNode(TupleTableSlot);
 
-       /*
-        * sanity checks
-        */
-       Assert(tableSize >= 1);
+       slot->tts_isempty = true;
+       slot->tts_shouldFree = false;
+       slot->tts_shouldFreeMin = false;
+       slot->tts_tuple = NULL;
+       slot->tts_tupleDescriptor = NULL;
+       slot->tts_mcxt = CurrentMemoryContext;
+       slot->tts_buffer = InvalidBuffer;
+       slot->tts_nvalid = 0;
+       slot->tts_values = NULL;
+       slot->tts_isnull = NULL;
+       slot->tts_mintuple = NULL;
 
-       /*
-        * allocate the table itself
-        */
-       newtable = (TupleTable) palloc(sizeof(TupleTableData) +
-                                                                  (tableSize - 1) *sizeof(TupleTableSlot));
-       newtable->size = tableSize;
-       newtable->next = 0;
+       return slot;
+}
 
-       /*
-        * initialize all the slots to empty states
-        */
-       for (i = 0; i < tableSize; i++)
-       {
-               TupleTableSlot *slot = &(newtable->array[i]);
-
-               slot->type = T_TupleTableSlot;
-               slot->tts_isempty = true;
-               slot->tts_shouldFree = false;
-               slot->tts_shouldFreeMin = false;
-               slot->tts_tuple = NULL;
-               slot->tts_tupleDescriptor = NULL;
-               slot->tts_mcxt = CurrentMemoryContext;
-               slot->tts_buffer = InvalidBuffer;
-               slot->tts_nvalid = 0;
-               slot->tts_values = NULL;
-               slot->tts_isnull = NULL;
-               slot->tts_mintuple = NULL;
-       }
+/* --------------------------------
+ *             ExecAllocTableSlot
+ *
+ *             Create a tuple table slot within a tuple table (which is just a List).
+ * --------------------------------
+ */
+TupleTableSlot *
+ExecAllocTableSlot(List **tupleTable)
+{
+       TupleTableSlot *slot = MakeTupleTableSlot();
+
+       *tupleTable = lappend(*tupleTable, slot);
 
-       return newtable;
+       return slot;
 }
 
 /* --------------------------------
- *             ExecDropTupleTable
+ *             ExecResetTupleTable
  *
- *             This frees the storage used by the tuple table itself
- *             and optionally frees the contents of the table also.
+ *             This releases any resources (buffer pins, tupdesc refcounts)
+ *             held by the tuple table, and optionally releases the memory
+ *             occupied by the tuple table data structure.
  *             It is expected that this routine be called by EndPlan().
  * --------------------------------
  */
 void
-ExecDropTupleTable(TupleTable table,   /* tuple table */
-                                  bool shouldFree)             /* true if we should free slot
-                                                                                * contents */
+ExecResetTupleTable(List *tupleTable,  /* tuple table */
+                                       bool shouldFree)        /* true if we should free memory */
 {
-       /*
-        * sanity checks
-        */
-       Assert(table != NULL);
+       ListCell   *lc;
 
-       /*
-        * 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)
+       foreach(lc, tupleTable)
        {
-               int                     next = table->next;
-               int                     i;
+               TupleTableSlot *slot = (TupleTableSlot *) lfirst(lc);
 
-               for (i = 0; i < next; i++)
+               /* Sanity checks */
+               Assert(IsA(slot, TupleTableSlot));
+
+               /* Always release resources and reset the slot to empty */
+               ExecClearTuple(slot);
+               if (slot->tts_tupleDescriptor)
                {
-                       TupleTableSlot *slot = &(table->array[i]);
+                       ReleaseTupleDesc(slot->tts_tupleDescriptor);
+                       slot->tts_tupleDescriptor = NULL;
+               }
 
-                       ExecClearTuple(slot);
-                       if (slot->tts_tupleDescriptor)
-                               ReleaseTupleDesc(slot->tts_tupleDescriptor);
+               /* If shouldFree, release memory occupied by the slot itself */
+               if (shouldFree)
+               {
                        if (slot->tts_values)
                                pfree(slot->tts_values);
                        if (slot->tts_isnull)
                                pfree(slot->tts_isnull);
+                       pfree(slot);
                }
        }
 
-       /*
-        * finally free the tuple table itself.
-        */
-       pfree(table);
+       /* If shouldFree, release the list structure */
+       if (shouldFree)
+               list_free(tupleTable);
 }
 
 /* --------------------------------
  *
  *             This is a convenience routine for operations that need a
  *             standalone TupleTableSlot not gotten from the main executor
- *             tuple table.  It makes a single slot and initializes it as
- *             though by ExecSetSlotDescriptor(slot, tupdesc).
+ *             tuple table.  It makes a single slot and initializes it
+ *             to use the given tuple descriptor.
  * --------------------------------
  */
 TupleTableSlot *
 MakeSingleTupleTableSlot(TupleDesc tupdesc)
 {
-       TupleTableSlot *slot = makeNode(TupleTableSlot);
-
-       /* This should match ExecCreateTupleTable() */
-       slot->tts_isempty = true;
-       slot->tts_shouldFree = false;
-       slot->tts_shouldFreeMin = false;
-       slot->tts_tuple = NULL;
-       slot->tts_tupleDescriptor = NULL;
-       slot->tts_mcxt = CurrentMemoryContext;
-       slot->tts_buffer = InvalidBuffer;
-       slot->tts_nvalid = 0;
-       slot->tts_values = NULL;
-       slot->tts_isnull = NULL;
-       slot->tts_mintuple = NULL;
+       TupleTableSlot *slot = MakeTupleTableSlot();
 
        ExecSetSlotDescriptor(slot, tupdesc);
 
  *             ExecDropSingleTupleTableSlot
  *
  *             Release a TupleTableSlot made with MakeSingleTupleTableSlot.
+ *             DON'T use this on a slot that's part of a tuple table list!
  * --------------------------------
  */
 void
 ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
 {
-       /*
-        * sanity checks
-        */
-       Assert(slot != NULL);
-
+       /* This should match ExecResetTupleTable's processing of one slot */
+       Assert(IsA(slot, TupleTableSlot));
        ExecClearTuple(slot);
        if (slot->tts_tupleDescriptor)
                ReleaseTupleDesc(slot->tts_tupleDescriptor);
                pfree(slot->tts_values);
        if (slot->tts_isnull)
                pfree(slot->tts_isnull);
-
        pfree(slot);
 }
 
 
-/* ----------------------------------------------------------------
- *                               tuple table slot reservation functions
- * ----------------------------------------------------------------
- */
-
-/* --------------------------------
- *             ExecAllocTableSlot
- *
- *             This routine is used to reserve slots in the table for
- *             use by the various plan nodes.  It is expected to be
- *             called by the node init routines (ex: ExecInitNestLoop)
- *             once per slot needed by the node.  Not all nodes need
- *             slots (some just pass tuples around).
- * --------------------------------
- */
-TupleTableSlot *
-ExecAllocTableSlot(TupleTable table)
-{
-       int                     slotnum;                /* new slot number */
-
-       /*
-        * sanity checks
-        */
-       Assert(table != NULL);
-
-       /*
-        * We expect that the table was made big enough to begin with. We cannot
-        * reallocate it on the fly since previous plan nodes have already got
-        * pointers to individual entries.
-        */
-       if (table->next >= table->size)
-               elog(ERROR, "plan requires more slots than are available");
-
-       slotnum = table->next;
-       table->next++;
-
-       return &(table->array[slotnum]);
-}
-
 /* ----------------------------------------------------------------
  *                               tuple table slot accessor functions
  * ----------------------------------------------------------------
 void
 ExecInitResultTupleSlot(EState *estate, PlanState *planstate)
 {
-       planstate->ps_ResultTupleSlot = ExecAllocTableSlot(estate->es_tupleTable);
+       planstate->ps_ResultTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable);
 }
 
 /* ----------------
 void
 ExecInitScanTupleSlot(EState *estate, ScanState *scanstate)
 {
-       scanstate->ss_ScanTupleSlot = ExecAllocTableSlot(estate->es_tupleTable);
+       scanstate->ss_ScanTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable);
 }
 
 /* ----------------
 TupleTableSlot *
 ExecInitExtraTupleSlot(EState *estate)
 {
-       return ExecAllocTableSlot(estate->es_tupleTable);
+       return ExecAllocTableSlot(&estate->es_tupleTable);
 }
 
 /* ----------------