resultRelInfo->ri_BatchSize);
             }
  
 -           resultRelInfo->ri_Slots[resultRelInfo->ri_NumSlots] =
 -               MakeSingleTupleTableSlot(slot->tts_tupleDescriptor,
 -                                        slot->tts_ops);
 -           ExecCopySlot(resultRelInfo->ri_Slots[resultRelInfo->ri_NumSlots],
 -                        slot);
 -           resultRelInfo->ri_PlanSlots[resultRelInfo->ri_NumSlots] =
 -               MakeSingleTupleTableSlot(planSlot->tts_tupleDescriptor,
 -                                        planSlot->tts_ops);
 -           ExecCopySlot(resultRelInfo->ri_PlanSlots[resultRelInfo->ri_NumSlots],
 -                        planSlot);
 +           /*
 +            * Initialize the batch slots. We don't know how many slots will be
 +            * needed, so we initialize them as the batch grows, and we keep
 +            * them across batches. To mitigate an inefficiency in how resource
 +            * owner handles objects with many references (as with many slots
 +            * all referencing the same tuple descriptor) we copy the tuple
 +            * descriptor for each slot.
 +            */
 +           if (resultRelInfo->ri_NumSlots >= resultRelInfo->ri_NumSlotsInitialized)
 +           {
 +               TupleDesc tdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor);
 +
 +               resultRelInfo->ri_Slots[resultRelInfo->ri_NumSlots] =
 +                   MakeSingleTupleTableSlot(tdesc, slot->tts_ops);
 +               ExecCopySlot(resultRelInfo->ri_Slots[resultRelInfo->ri_NumSlots],
 +                            slot);
 +
 +               resultRelInfo->ri_PlanSlots[resultRelInfo->ri_NumSlots] =
 +                   MakeSingleTupleTableSlot(tdesc, planSlot->tts_ops);
 +               ExecCopySlot(resultRelInfo->ri_PlanSlots[resultRelInfo->ri_NumSlots],
 +                            planSlot);
 +
 +               /* remember how many batch slots we initialized */
 +               resultRelInfo->ri_NumSlotsInitialized++;
 +           }
  
             resultRelInfo->ri_NumSlots++;
  
   
     if (canSetTag && numInserted > 0)
         estate->es_processed += numInserted;
 -
 -   for (i = 0; i < numSlots; i++)
 -   {
 -       ExecDropSingleTupleTableSlot(slots[i]);
 -       ExecDropSingleTupleTableSlot(planSlots[i]);
 -   }
  }
  
  /* ----------------------------------------------------------------
       */
     for (i = 0; i < node->mt_nrels; i++)
     {
 +       int j;
         ResultRelInfo *resultRelInfo = node->resultRelInfo + i;
  
         if (!resultRelInfo->ri_usesFdwDirectModify &&
              resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL)
             resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state,
                                                            resultRelInfo);
 +
 +       /*
 +        * Cleanup the initialized batch slots. This only matters for FDWs with
 +        * batching, but the other cases will have ri_NumSlotsInitialized == 0.
 +        */
 +       for (j = 0; j < resultRelInfo->ri_NumSlotsInitialized; j++)
 +       {
 +           ExecDropSingleTupleTableSlot(resultRelInfo->ri_Slots[j]);
 +           ExecDropSingleTupleTableSlot(resultRelInfo->ri_PlanSlots[j]);
 +       }
     }
  
     /*