This is a follow-up of the work done in 
e3931d01.  This case is a bit
 different than pg_attribute and pg_shdepend: the maximum number of items
 to insert is known in advance, but there is no need to handle pinned
 dependencies.  Hence, the base allocation for slots is done based on the
 number of items and the maximum allowed with a cap at 64kB.  Slots are
 initialized once used to minimize the overhead of the operation. 
 The insertions can be done for dependencies of the same type.  More
 could be done by grouping the insertion of multiple dependency types in
 a single batch.  This is left as future work. 
 Some of the multi-insert logic is also simplified for pg_shdepend, as
 per the feedback discussed for this specific patch.  This also moves to
 indexing.h the variable capping the maximum amount of data that can be
 used at once for a multi-insert, instead of having separate definitions
 for pg_attribute, pg_depend and pg_shdepend. 
 Author: Daniel Gustafsson, Michael Paquier 
Reviewed-by: Andres Freund, Álvaro Herrera Discussion: https://postgr.es/m/
20200807061619.GA23955@paquier.xyz  
           }
  }
  
 -/*
 - * Cap the maximum amount of bytes allocated for InsertPgAttributeTuples()
 - * slots.
 - */
 -#define MAX_PGATTRIBUTE_INSERT_BYTES 65535
 -
  /*
   * InsertPgAttributeTuples
   *     Construct and insert a set of tuples in pg_attribute.
   
     /* Initialize the number of slots to use */
     nslots = Min(tupdesc->natts,
 -                (MAX_PGATTRIBUTE_INSERT_BYTES / sizeof(FormData_pg_attribute)));
 +                (MAX_CATALOG_MULTI_INSERT_BYTES / sizeof(FormData_pg_attribute)));
     slot = palloc(sizeof(TupleTableSlot *) * nslots);
     for (int i = 0; i < nslots; i++)
         slot[i] = MakeSingleTupleTableSlot(td, &TTSOpsHeapTuple);
          {
     Relation    dependDesc;
     CatalogIndexState indstate;
 -   HeapTuple   tup;
 -   int         i;
 -   bool        nulls[Natts_pg_depend];
 -   Datum       values[Natts_pg_depend];
 +   TupleTableSlot **slot;
 +   int         i,
 +               max_slots,
 +               slot_init_count,
 +               slot_stored_count;
  
     if (nreferenced <= 0)
         return;                 /* nothing to do */
   
     dependDesc = table_open(DependRelationId, RowExclusiveLock);
  
 +   /*
 +    * Allocate the slots to use, but delay costly initialization until we
 +    * know that they will be used.
 +    */
 +   max_slots = Min(nreferenced,
 +                   MAX_CATALOG_MULTI_INSERT_BYTES / sizeof(FormData_pg_depend));
 +   slot = palloc(sizeof(TupleTableSlot *) * max_slots);
 +
     /* Don't open indexes unless we need to make an update */
     indstate = NULL;
  
 -   memset(nulls, false, sizeof(nulls));
 -
 +   /* number of slots currently storing tuples */
 +   slot_stored_count = 0;
 +   /* number of slots currently initialized */
 +   slot_init_count = 0;
     for (i = 0; i < nreferenced; i++, referenced++)
     {
         /*
           * need to record dependencies on it.  This saves lots of space in
          * pg_depend, so it's worth the time taken to check.
          */
 -       if (!isObjectPinned(referenced, dependDesc))
 -       {
 -           /*
 -            * Record the Dependency.  Note we don't bother to check for
 -            * duplicate dependencies; there's no harm in them.
 -            */
 -           values[Anum_pg_depend_classid - 1] = ObjectIdGetDatum(depender->classId);
 -           values[Anum_pg_depend_objid - 1] = ObjectIdGetDatum(depender->objectId);
 -           values[Anum_pg_depend_objsubid - 1] = Int32GetDatum(depender->objectSubId);
 -
 -           values[Anum_pg_depend_refclassid - 1] = ObjectIdGetDatum(referenced->classId);
 -           values[Anum_pg_depend_refobjid - 1] = ObjectIdGetDatum(referenced->objectId);
 -           values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(referenced->objectSubId);
 +       if (isObjectPinned(referenced, dependDesc))
 +           continue;
  
 -           values[Anum_pg_depend_deptype - 1] = CharGetDatum((char) behavior);
 +       if (slot_init_count < max_slots)
 +       {
 +           slot[slot_stored_count] = MakeSingleTupleTableSlot(RelationGetDescr(dependDesc),
 +                                                              &TTSOpsHeapTuple);
 +           slot_init_count++;
 +       }
  
 -           tup = heap_form_tuple(dependDesc->rd_att, values, nulls);
 +       ExecClearTuple(slot[slot_stored_count]);
  
 +       /*
 +        * Record the dependency.  Note we don't bother to check for duplicate
 +        * dependencies; there's no harm in them.
 +        */
 +       slot[slot_stored_count]->tts_values[Anum_pg_depend_refclassid - 1] = ObjectIdGetDatum(referenced->classId);
 +       slot[slot_stored_count]->tts_values[Anum_pg_depend_refobjid - 1] = ObjectIdGetDatum(referenced->objectId);
 +       slot[slot_stored_count]->tts_values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(referenced->objectSubId);
 +       slot[slot_stored_count]->tts_values[Anum_pg_depend_deptype - 1] = CharGetDatum((char) behavior);
 +       slot[slot_stored_count]->tts_values[Anum_pg_depend_classid - 1] = ObjectIdGetDatum(depender->classId);
 +       slot[slot_stored_count]->tts_values[Anum_pg_depend_objid - 1] = ObjectIdGetDatum(depender->objectId);
 +       slot[slot_stored_count]->tts_values[Anum_pg_depend_objsubid - 1] = Int32GetDatum(depender->objectSubId);
 +
 +       memset(slot[slot_stored_count]->tts_isnull, false,
 +              slot[slot_stored_count]->tts_tupleDescriptor->natts * sizeof(bool));
 +
 +       ExecStoreVirtualTuple(slot[slot_stored_count]);
 +       slot_stored_count++;
 +
 +       /* If slots are full, insert a batch of tuples */
 +       if (slot_stored_count == max_slots)
 +       {
             /* fetch index info only when we know we need it */
             if (indstate == NULL)
                 indstate = CatalogOpenIndexes(dependDesc);
  
 -           CatalogTupleInsertWithInfo(dependDesc, tup, indstate);
 -
 -           heap_freetuple(tup);
 +           CatalogTuplesMultiInsertWithInfo(dependDesc, slot, slot_stored_count,
 +                                            indstate);
 +           slot_stored_count = 0;
         }
     }
  
 +   /* Insert any tuples left in the buffer */
 +   if (slot_stored_count > 0)
 +   {
 +       /* fetch index info only when we know we need it */
 +       if (indstate == NULL)
 +           indstate = CatalogOpenIndexes(dependDesc);
 +
 +       CatalogTuplesMultiInsertWithInfo(dependDesc, slot, slot_stored_count,
 +                                        indstate);
 +   }
 +
     if (indstate != NULL)
         CatalogCloseIndexes(indstate);
  
     table_close(dependDesc, RowExclusiveLock);
 +
 +   /* Drop only the number of slots used */
 +   for (i = 0; i < slot_init_count; i++)
 +       ExecDropSingleTupleTableSlot(slot[i]);
 +   pfree(slot);
  }
  
  /*
          }
  
  
 -/*
 - * Cap the maximum amount of bytes allocated for copyTemplateDependencies()
 - * slots.
 - */
 -#define MAX_PGSHDEPEND_INSERT_BYTES 65535
 -
  /*
   * copyTemplateDependencies
   *
      ScanKeyData key[1];
     SysScanDesc scan;
     HeapTuple   tup;
 -   int         slotCount;
     CatalogIndexState indstate;
     TupleTableSlot **slot;
 -   int         nslots,
 -               max_slots;
 -   bool        slot_init = true;
 +   int         max_slots,
 +               slot_init_count,
 +               slot_stored_count;
  
     sdepRel = table_open(SharedDependRelationId, RowExclusiveLock);
     sdepDesc = RelationGetDescr(sdepRel);
  
     /*
 -    * Allocate the slots to use, but delay initialization until we know that
 -    * they will be used.
 +    * Allocate the slots to use, but delay costly initialization until we
 +    * know that they will be used.
      */
 -   max_slots = MAX_PGSHDEPEND_INSERT_BYTES / sizeof(FormData_pg_shdepend);
 +   max_slots = MAX_CATALOG_MULTI_INSERT_BYTES / sizeof(FormData_pg_shdepend);
     slot = palloc(sizeof(TupleTableSlot *) * max_slots);
  
     indstate = CatalogOpenIndexes(sdepRel);
      scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
                               NULL, 1, key);
  
 +   /* number of slots currently storing tuples */
 +   slot_stored_count = 0;
 +   /* number of slots currently initialized */
 +   slot_init_count = 0;
 +
     /*
      * Copy the entries of the original database, changing the database Id to
      * that of the new database.  Note that because we are not copying rows
       * copy the ownership dependency of the template database itself; this is
      * what we want.
      */
 -   slotCount = 0;
     while (HeapTupleIsValid(tup = systable_getnext(scan)))
     {
         Form_pg_shdepend shdep;
  
 -       if (slot_init)
 -           slot[slotCount] = MakeSingleTupleTableSlot(sdepDesc, &TTSOpsHeapTuple);
 +       if (slot_init_count < max_slots)
 +       {
 +           slot[slot_stored_count] = MakeSingleTupleTableSlot(sdepDesc, &TTSOpsHeapTuple);
 +           slot_init_count++;
 +       }
  
 -       ExecClearTuple(slot[slotCount]);
 +       ExecClearTuple(slot[slot_stored_count]);
  
         shdep = (Form_pg_shdepend) GETSTRUCT(tup);
  
 -       slot[slotCount]->tts_values[Anum_pg_shdepend_dbid] = ObjectIdGetDatum(newDbId);
 -       slot[slotCount]->tts_values[Anum_pg_shdepend_classid] = shdep->classid;
 -       slot[slotCount]->tts_values[Anum_pg_shdepend_objid] = shdep->objid;
 -       slot[slotCount]->tts_values[Anum_pg_shdepend_objsubid] = shdep->objsubid;
 -       slot[slotCount]->tts_values[Anum_pg_shdepend_refclassid] = shdep->refclassid;
 -       slot[slotCount]->tts_values[Anum_pg_shdepend_refobjid] = shdep->refobjid;
 -       slot[slotCount]->tts_values[Anum_pg_shdepend_deptype] = shdep->deptype;
 +       slot[slot_stored_count]->tts_values[Anum_pg_shdepend_dbid] = ObjectIdGetDatum(newDbId);
 +       slot[slot_stored_count]->tts_values[Anum_pg_shdepend_classid] = shdep->classid;
 +       slot[slot_stored_count]->tts_values[Anum_pg_shdepend_objid] = shdep->objid;
 +       slot[slot_stored_count]->tts_values[Anum_pg_shdepend_objsubid] = shdep->objsubid;
 +       slot[slot_stored_count]->tts_values[Anum_pg_shdepend_refclassid] = shdep->refclassid;
 +       slot[slot_stored_count]->tts_values[Anum_pg_shdepend_refobjid] = shdep->refobjid;
 +       slot[slot_stored_count]->tts_values[Anum_pg_shdepend_deptype] = shdep->deptype;
  
 -       ExecStoreVirtualTuple(slot[slotCount]);
 -       slotCount++;
 +       ExecStoreVirtualTuple(slot[slot_stored_count]);
 +       slot_stored_count++;
  
         /* If slots are full, insert a batch of tuples */
 -       if (slotCount == max_slots)
 +       if (slot_stored_count == max_slots)
         {
 -           CatalogTuplesMultiInsertWithInfo(sdepRel, slot, slotCount, indstate);
 -           slotCount = 0;
 -           slot_init = false;
 +           CatalogTuplesMultiInsertWithInfo(sdepRel, slot, slot_stored_count, indstate);
 +           slot_stored_count = 0;
         }
     }
  
     /* Insert any tuples left in the buffer */
 -   if (slotCount > 0)
 -       CatalogTuplesMultiInsertWithInfo(sdepRel, slot, slotCount, indstate);
 +   if (slot_stored_count > 0)
 +       CatalogTuplesMultiInsertWithInfo(sdepRel, slot, slot_stored_count, indstate);
  
     systable_endscan(scan);
  
      table_close(sdepRel, RowExclusiveLock);
  
     /* Drop only the number of slots used */
 -   nslots = slot_init ? slotCount : max_slots;
 -   for (int i = 0; i < nslots; i++)
 +   for (int i = 0; i < slot_init_count; i++)
         ExecDropSingleTupleTableSlot(slot[i]);
     pfree(slot);
  }
           */
  typedef struct ResultRelInfo *CatalogIndexState;
  
 +/*
 + * Cap the maximum amount of bytes allocated for multi-inserts with system
 + * catalogs, limiting the number of slots used.
 + */
 +#define MAX_CATALOG_MULTI_INSERT_BYTES 65535
 +
  /*
   * indexing.c prototypes
   */