From 220b3640f1094899bc62b53be10aad9874b94bf4 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <daniel@yesql.se>
Date: Wed, 22 May 2019 09:59:55 +0200
Subject: [PATCH] WIP: Use heap_multi_insert for catalog relations

Introduce a new function CatalogMultiInsertWithInfo which can replace
multiple calls to CatalogTupleInsertWithInfo by instead taking set of
slots to perform heap_multi_insert on. InsertPgAttributeTuples is also
introduced as a way to insert multiple attributes at once.

For insertions performing recordDependencyOn or InsertPgAttributeTuple
in a loop context, move these to collecting a set of tuples and use the
new InsertPgAttributeTuples or recordMultipleDependencies instead. Also
make recordMultipleDependencies use CatalogMultiInsertWithInfo to ensure
insertion via heap_multi_insert.
---
 src/backend/catalog/dependency.c         |  54 ++++++++----
 src/backend/catalog/heap.c               | 137 ++++++++++++++++++++++++-------
 src/backend/catalog/index.c              |  58 ++++++++-----
 src/backend/catalog/indexing.c           |  37 +++++++++
 src/backend/catalog/pg_aggregate.c       |  69 ++++------------
 src/backend/catalog/pg_constraint.c      |  75 +++++------------
 src/backend/catalog/pg_depend.c          |  84 ++++++++++++++++---
 src/backend/catalog/pg_operator.c        |  58 ++++---------
 src/backend/catalog/pg_proc.c            |  50 ++++-------
 src/backend/catalog/pg_shdepend.c        |  26 ++++--
 src/backend/catalog/pg_type.c            |  51 +++++-------
 src/backend/replication/logical/decode.c |   3 +-
 src/include/catalog/dependency.h         |   5 ++
 src/include/catalog/heap.h               |   4 +
 src/include/catalog/indexing.h           |   3 +
 15 files changed, 414 insertions(+), 300 deletions(-)

diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index dd0a7d8dac..5c6aa0d1dc 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -205,8 +205,6 @@ static bool find_expr_references_walker(Node *node,
 										find_expr_references_context *context);
 static void eliminate_duplicate_dependencies(ObjectAddresses *addrs);
 static int	object_address_comparator(const void *a, const void *b);
-static void add_object_address(ObjectClass oclass, Oid objectId, int32 subId,
-							   ObjectAddresses *addrs);
 static void add_exact_object_address_extra(const ObjectAddress *object,
 										   const ObjectAddressExtra *extra,
 										   ObjectAddresses *addrs);
@@ -1600,10 +1598,16 @@ recordDependencyOnExpr(const ObjectAddress *depender,
 	/* Remove any duplicates */
 	eliminate_duplicate_dependencies(context.addrs);
 
-	/* And record 'em */
-	recordMultipleDependencies(depender,
-							   context.addrs->refs, context.addrs->numrefs,
-							   behavior);
+	/*
+	 * And record 'em. If we know we only have a single dependency then
+	 * avoid the extra cost of setting up a multi insert.
+	 */
+	if (context.addrs->numrefs == 1)
+		recordDependencyOn(depender, &context.addrs->refs[0], behavior);
+	else
+		recordMultipleDependencies(depender,
+								   context.addrs->refs, context.addrs->numrefs,
+								   behavior);
 
 	free_object_addresses(context.addrs);
 }
@@ -1707,10 +1711,16 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
 		free_object_addresses(self_addrs);
 	}
 
-	/* Record the external dependencies */
-	recordMultipleDependencies(depender,
-							   context.addrs->refs, context.addrs->numrefs,
-							   behavior);
+	/*
+	 * Record the external dependencies. If we only have a single dependency
+	 * then avoid the extra cost of setting up a multi insert.
+	 */
+	if (context.addrs->numrefs == 1)
+		recordDependencyOn(depender, &context.addrs->refs[0], behavior);
+	else
+		recordMultipleDependencies(depender,
+								   context.addrs->refs, context.addrs->numrefs,
+								   behavior);
 
 	free_object_addresses(context.addrs);
 }
@@ -2403,7 +2413,7 @@ new_object_addresses(void)
  * It is convenient to specify the class by ObjectClass rather than directly
  * by catalog OID.
  */
-static void
+void
 add_object_address(ObjectClass oclass, Oid objectId, int32 subId,
 				   ObjectAddresses *addrs)
 {
@@ -2651,9 +2661,13 @@ record_object_address_dependencies(const ObjectAddress *depender,
 								   DependencyType behavior)
 {
 	eliminate_duplicate_dependencies(referenced);
-	recordMultipleDependencies(depender,
-							   referenced->refs, referenced->numrefs,
-							   behavior);
+
+	if (referenced->numrefs == 1)
+		recordDependencyOn(depender, &referenced->refs[0], behavior);
+	else
+		recordMultipleDependencies(depender,
+								   referenced->refs, referenced->numrefs,
+								   behavior);
 }
 
 /*
@@ -2673,6 +2687,18 @@ sort_object_addresses(ObjectAddresses *addrs)
 			  object_address_comparator);
 }
 
+void
+reset_object_addresses(ObjectAddresses *addrs)
+{
+	if (addrs->numrefs == 0)
+		return;
+
+	memset(addrs->refs, 0, addrs->maxrefs * sizeof(ObjectAddress));
+	if (addrs->extras)
+		memset(addrs->extras, 0, addrs->maxrefs * sizeof(ObjectAddressExtra));
+	addrs->numrefs = 0;
+}
+
 /*
  * Clean up when done with an ObjectAddresses array.
  */
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index b7bcdd9d0f..098bf5a8da 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -677,6 +677,79 @@ CheckAttributeType(const char *attname,
 				 errhint("Use the COLLATE clause to set the collation explicitly.")));
 }
 
+/*
+ * InsertPgAttributeTuples
+ *		Construct and insert multiple tuples in pg_attribute.
+ *
+ * This is a variant of InsertPgAttributeTuple() which dynamically allocates
+ * space for multiple tuples. Having two so similar functions is a kludge, but
+ * for now it's a TODO to make it less terrible.
+ */
+void
+InsertPgAttributeTuples(Relation pg_attribute_rel,
+						FormData_pg_attribute *new_attributes,
+						int natts,
+						CatalogIndexState indstate)
+{
+	Datum		values[Natts_pg_attribute];
+	bool		nulls[Natts_pg_attribute];
+	HeapTuple	tup;
+	int			i;
+	TupleTableSlot **slot;
+
+	/*
+	 * The slots are dropped in CatalogMultiInsertWithInfo(). TODO: natts
+	 * number of slots is not a reasonable assumption as a wide relation
+	 * would be detrimental, figuring a good number is left as a TODO.
+	 */
+	slot = palloc(sizeof(TupleTableSlot *) * natts);
+
+	/* This is a tad tedious, but way cleaner than what we used to do... */
+	memset(values, 0, sizeof(values));
+	memset(nulls, false, sizeof(nulls));
+
+	/* start out with empty permissions and empty options */
+	nulls[Anum_pg_attribute_attacl - 1] = true;
+	nulls[Anum_pg_attribute_attoptions - 1] = true;
+	nulls[Anum_pg_attribute_attfdwoptions - 1] = true;
+	nulls[Anum_pg_attribute_attmissingval - 1] = true;
+
+	/* attcacheoff is always -1 in storage */
+	values[Anum_pg_attribute_attcacheoff - 1] = Int32GetDatum(-1);
+
+	for (i = 0; i < natts; i++)
+	{
+		values[Anum_pg_attribute_attrelid - 1] = ObjectIdGetDatum(new_attributes[i].attrelid);
+		values[Anum_pg_attribute_attname - 1] = NameGetDatum(&new_attributes[i].attname);
+		values[Anum_pg_attribute_atttypid - 1] = ObjectIdGetDatum(new_attributes[i].atttypid);
+		values[Anum_pg_attribute_attstattarget - 1] = Int32GetDatum(new_attributes[i].attstattarget);
+		values[Anum_pg_attribute_attlen - 1] = Int16GetDatum(new_attributes[i].attlen);
+		values[Anum_pg_attribute_attnum - 1] = Int16GetDatum(new_attributes[i].attnum);
+		values[Anum_pg_attribute_attndims - 1] = Int32GetDatum(new_attributes[i].attndims);
+		values[Anum_pg_attribute_atttypmod - 1] = Int32GetDatum(new_attributes[i].atttypmod);
+		values[Anum_pg_attribute_attbyval - 1] = BoolGetDatum(new_attributes[i].attbyval);
+		values[Anum_pg_attribute_attstorage - 1] = CharGetDatum(new_attributes[i].attstorage);
+		values[Anum_pg_attribute_attalign - 1] = CharGetDatum(new_attributes[i].attalign);
+		values[Anum_pg_attribute_attnotnull - 1] = BoolGetDatum(new_attributes[i].attnotnull);
+		values[Anum_pg_attribute_atthasdef - 1] = BoolGetDatum(new_attributes[i].atthasdef);
+		values[Anum_pg_attribute_atthasmissing - 1] = BoolGetDatum(new_attributes[i].atthasmissing);
+		values[Anum_pg_attribute_attidentity - 1] = CharGetDatum(new_attributes[i].attidentity);
+		values[Anum_pg_attribute_attgenerated - 1] = CharGetDatum(new_attributes[i].attgenerated);
+		values[Anum_pg_attribute_attisdropped - 1] = BoolGetDatum(new_attributes[i].attisdropped);
+		values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attributes[i].attislocal);
+		values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attributes[i].attinhcount);
+		values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attributes[i].attcollation);
+
+		slot[i] = MakeSingleTupleTableSlot(RelationGetDescr(pg_attribute_rel),
+										   &TTSOpsHeapTuple);
+		tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls);
+		ExecStoreHeapTuple(heap_copytuple(tup), slot[i], false);
+	}
+
+	/* finally insert the new tuples, update the indexes, and clean up */
+	CatalogMultiInsertWithInfo(pg_attribute_rel, slot, natts, indstate);
+}
+
 /*
  * InsertPgAttributeTuple
  *		Construct and insert a new tuple in pg_attribute.
@@ -754,7 +827,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
 					  TupleDesc tupdesc,
 					  char relkind)
 {
-	Form_pg_attribute attr;
+	Form_pg_attribute *attrs;
 	int			i;
 	Relation	rel;
 	CatalogIndexState indstate;
@@ -769,35 +842,42 @@ AddNewAttributeTuples(Oid new_rel_oid,
 
 	indstate = CatalogOpenIndexes(rel);
 
+	attrs = palloc(sizeof(Form_pg_attribute) * natts);
+
 	/*
-	 * First we add the user attributes.  This is also a convenient place to
-	 * add dependencies on their datatypes and collations.
+	 * First we add the user attributes.
 	 */
 	for (i = 0; i < natts; i++)
 	{
-		attr = TupleDescAttr(tupdesc, i);
+		attrs[i] = TupleDescAttr(tupdesc, i);
 		/* Fill in the correct relation OID */
-		attr->attrelid = new_rel_oid;
+		attrs[i]->attrelid = new_rel_oid;
 		/* Make sure this is OK, too */
-		attr->attstattarget = -1;
+		attrs[i]->attstattarget = -1;
+	}
 
-		InsertPgAttributeTuple(rel, attr, indstate);
+	InsertPgAttributeTuples(rel, *attrs, natts, indstate);
 
+	/*
+	 * Now add dependencies on their datatypes and collations.
+	 */
+	for (i = 0; i < natts; i++)
+	{
 		/* Add dependency info */
 		myself.classId = RelationRelationId;
 		myself.objectId = new_rel_oid;
 		myself.objectSubId = i + 1;
 		referenced.classId = TypeRelationId;
-		referenced.objectId = attr->atttypid;
+		referenced.objectId = attrs[i]->atttypid;
 		referenced.objectSubId = 0;
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 		/* The default collation is pinned, so don't bother recording it */
-		if (OidIsValid(attr->attcollation) &&
-			attr->attcollation != DEFAULT_COLLATION_OID)
+		if (OidIsValid(attrs[i]->attcollation) &&
+			attrs[i]->attcollation != DEFAULT_COLLATION_OID)
 		{
 			referenced.classId = CollationRelationId;
-			referenced.objectId = attr->attcollation;
+			referenced.objectId = attrs[i]->attcollation;
 			referenced.objectSubId = 0;
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 		}
@@ -810,22 +890,22 @@ AddNewAttributeTuples(Oid new_rel_oid,
 	 */
 	if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE)
 	{
+		FormData_pg_attribute attStructs[lengthof(SysAtt)];
+
+		/* Fill in the correct relation OID in the copied tuple */
 		for (i = 0; i < (int) lengthof(SysAtt); i++)
 		{
-			FormData_pg_attribute attStruct;
-
-			memcpy(&attStruct, SysAtt[i], sizeof(FormData_pg_attribute));
-
-			/* Fill in the correct relation OID in the copied tuple */
-			attStruct.attrelid = new_rel_oid;
-
-			InsertPgAttributeTuple(rel, &attStruct, indstate);
+			memcpy(&attStructs[i], SysAtt[i], sizeof(FormData_pg_attribute));
+			attStructs[i].attrelid = new_rel_oid;
 		}
+
+		InsertPgAttributeTuples(rel, attStructs, lengthof(SysAtt), indstate);
 	}
 
 	/*
 	 * clean up
 	 */
+	pfree(attrs);
 	CatalogCloseIndexes(indstate);
 
 	table_close(rel, RowExclusiveLock);
@@ -3422,6 +3502,7 @@ StorePartitionKey(Relation rel,
 	bool		nulls[Natts_pg_partitioned_table];
 	ObjectAddress myself;
 	ObjectAddress referenced;
+	ObjectAddresses *refobjs;
 
 	Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
 
@@ -3469,27 +3550,25 @@ StorePartitionKey(Relation rel,
 	myself.objectId = RelationGetRelid(rel);
 	myself.objectSubId = 0;
 
+	refobjs = new_object_addresses();
+
 	/* Operator class and collation per key column */
 	for (i = 0; i < partnatts; i++)
 	{
-		referenced.classId = OperatorClassRelationId;
-		referenced.objectId = partopclass[i];
-		referenced.objectSubId = 0;
-
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+		add_object_address(OCLASS_OPCLASS, partopclass[i], 0, refobjs);
 
 		/* The default collation is pinned, so don't bother recording it */
 		if (OidIsValid(partcollation[i]) &&
 			partcollation[i] != DEFAULT_COLLATION_OID)
 		{
-			referenced.classId = CollationRelationId;
-			referenced.objectId = partcollation[i];
-			referenced.objectSubId = 0;
-
-			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+			add_object_address(OCLASS_COLLATION, partcollation[i], 0, refobjs);
 		}
 	}
 
+	/* Store the dependencies in the catalog */
+	record_object_address_dependencies(&myself, refobjs, DEPENDENCY_NORMAL);
+	free_object_addresses(refobjs);
+
 	/*
 	 * The partitioning columns are made internally dependent on the table,
 	 * because we cannot drop any of them without dropping the whole table.
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 3e1d40662d..6e9e020477 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -505,14 +505,30 @@ AppendAttributeTuples(Relation indexRelation, int numatts)
 	 */
 	indexTupDesc = RelationGetDescr(indexRelation);
 
-	for (i = 0; i < numatts; i++)
+	/*
+	 * If we only have a single attribute we can use the single tuple insert
+	 * function which avoids multi_insert scaffolding costs.
+	 */
+	if (numatts == 1)
 	{
-		Form_pg_attribute attr = TupleDescAttr(indexTupDesc, i);
-
-		Assert(attr->attnum == i + 1);
+		Form_pg_attribute attr = TupleDescAttr(indexTupDesc, 0);
 
 		InsertPgAttributeTuple(pg_attribute, attr, indstate);
 	}
+	else
+	{
+		Form_pg_attribute *attrs = palloc(sizeof(Form_pg_attribute) * numatts);
+
+		for (i = 0; i < numatts; i++)
+		{
+			attrs[i] = TupleDescAttr(indexTupDesc, i);
+
+			Assert(attrs[i]->attnum == i + 1);
+		}
+
+		InsertPgAttributeTuples(pg_attribute, *attrs, numatts, indstate);
+		pfree(attrs);
+	}
 
 	CatalogCloseIndexes(indstate);
 
@@ -982,11 +998,14 @@ index_create(Relation heapRelation,
 	{
 		ObjectAddress myself,
 					referenced;
+		ObjectAddresses *refobjs;
 
 		myself.classId = RelationRelationId;
 		myself.objectId = indexRelationId;
 		myself.objectSubId = 0;
 
+		refobjs = new_object_addresses();
+
 		if ((flags & INDEX_CREATE_ADD_CONSTRAINT) != 0)
 		{
 			char		constraintType;
@@ -1025,12 +1044,9 @@ index_create(Relation heapRelation,
 			{
 				if (indexInfo->ii_IndexAttrNumbers[i] != 0)
 				{
-					referenced.classId = RelationRelationId;
-					referenced.objectId = heapRelationId;
-					referenced.objectSubId = indexInfo->ii_IndexAttrNumbers[i];
-
-					recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
-
+					add_object_address(OCLASS_CLASS, heapRelationId,
+									   indexInfo->ii_IndexAttrNumbers[i],
+									   refobjs);
 					have_simple_col = true;
 				}
 			}
@@ -1049,6 +1065,11 @@ index_create(Relation heapRelation,
 
 				recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 			}
+			else
+			{
+				record_object_address_dependencies(&myself, refobjs, DEPENDENCY_AUTO);
+				reset_object_addresses(refobjs);
+			}
 		}
 
 		/*
@@ -1079,23 +1100,16 @@ index_create(Relation heapRelation,
 			if (OidIsValid(collationObjectId[i]) &&
 				collationObjectId[i] != DEFAULT_COLLATION_OID)
 			{
-				referenced.classId = CollationRelationId;
-				referenced.objectId = collationObjectId[i];
-				referenced.objectSubId = 0;
-
-				recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+				add_object_address(OCLASS_COLLATION, collationObjectId[i], 0,
+								   refobjs);
 			}
 		}
 
 		/* Store dependency on operator classes */
 		for (i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++)
-		{
-			referenced.classId = OperatorClassRelationId;
-			referenced.objectId = classObjectId[i];
-			referenced.objectSubId = 0;
+			add_object_address(OCLASS_OPCLASS, classObjectId[i], 0, refobjs);
 
-			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-		}
+		record_object_address_dependencies(&myself, refobjs, DEPENDENCY_NORMAL);
 
 		/* Store dependencies on anything mentioned in index expressions */
 		if (indexInfo->ii_Expressions)
@@ -1116,6 +1130,8 @@ index_create(Relation heapRelation,
 											DEPENDENCY_NORMAL,
 											DEPENDENCY_AUTO, false);
 		}
+
+		free_object_addresses(refobjs);
 	}
 	else
 	{
diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c
index f237e62bc9..0819a6449c 100644
--- a/src/backend/catalog/indexing.c
+++ b/src/backend/catalog/indexing.c
@@ -18,6 +18,7 @@
 #include "access/genam.h"
 #include "access/heapam.h"
 #include "access/htup_details.h"
+#include "access/xact.h"
 #include "catalog/index.h"
 #include "catalog/indexing.h"
 #include "executor/executor.h"
@@ -209,6 +210,42 @@ CatalogTupleInsertWithInfo(Relation heapRel, HeapTuple tup,
 	CatalogIndexInsert(indstate, tup);
 }
 
+/*
+ * CatalogMultiInsertWithInfo
+ *
+ * Insert multiple tuples into the catalog relation at once, with an amortized
+ * cost of CatalogOpenIndexes.
+ */
+void
+CatalogMultiInsertWithInfo(Relation heapRel, TupleTableSlot **slot, int ntuples,
+						   CatalogIndexState indstate)
+{
+	/* Nothing to do */
+	if (ntuples <= 0)
+		return;
+
+	heap_multi_insert(heapRel, slot, ntuples,
+					  GetCurrentCommandId(true), 0, NULL);
+
+	/*
+	 * There is no equivalent to heap_multi_insert for the catalog indexes, so
+	 * we must loop over and insert individually.
+	 */
+	for (int i = 0; i < ntuples; i++)
+	{
+		bool		should_free;
+		HeapTuple	tuple;
+
+		tuple = ExecFetchSlotHeapTuple(slot[i], true, &should_free);
+		tuple->t_tableOid = slot[i]->tts_tableOid;
+		CatalogIndexInsert(indstate, tuple);
+		ExecDropSingleTupleTableSlot(slot[i]);
+
+		if (should_free)
+			heap_freetuple(tuple);
+	}
+}
+
 /*
  * CatalogTupleUpdate - do heap and indexing work for updating a catalog tuple
  *
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index 201242e796..78550d5d29 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -104,8 +104,8 @@ AggregateCreate(const char *aggName,
 	Oid			procOid;
 	TupleDesc	tupDesc;
 	int			i;
-	ObjectAddress myself,
-				referenced;
+	ObjectAddress myself;
+	ObjectAddresses *refobjs;
 	AclResult	aclresult;
 
 	/* sanity checks (caller should have caught these) */
@@ -740,83 +740,44 @@ AggregateCreate(const char *aggName,
 	 * way.
 	 */
 
+	refobjs = new_object_addresses();
+
 	/* Depends on transition function */
-	referenced.classId = ProcedureRelationId;
-	referenced.objectId = transfn;
-	referenced.objectSubId = 0;
-	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+	add_object_address(OCLASS_PROC, transfn, 0, refobjs);
 
 	/* Depends on final function, if any */
 	if (OidIsValid(finalfn))
-	{
-		referenced.classId = ProcedureRelationId;
-		referenced.objectId = finalfn;
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-	}
+		add_object_address(OCLASS_PROC, finalfn, 0, refobjs);
 
 	/* Depends on combine function, if any */
 	if (OidIsValid(combinefn))
-	{
-		referenced.classId = ProcedureRelationId;
-		referenced.objectId = combinefn;
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-	}
+		add_object_address(OCLASS_PROC, combinefn, 0, refobjs);
 
 	/* Depends on serialization function, if any */
 	if (OidIsValid(serialfn))
-	{
-		referenced.classId = ProcedureRelationId;
-		referenced.objectId = serialfn;
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-	}
+		add_object_address(OCLASS_PROC, serialfn, 0, refobjs);
 
 	/* Depends on deserialization function, if any */
 	if (OidIsValid(deserialfn))
-	{
-		referenced.classId = ProcedureRelationId;
-		referenced.objectId = deserialfn;
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-	}
+		add_object_address(OCLASS_PROC, deserialfn, 0, refobjs);
 
 	/* Depends on forward transition function, if any */
 	if (OidIsValid(mtransfn))
-	{
-		referenced.classId = ProcedureRelationId;
-		referenced.objectId = mtransfn;
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-	}
+		add_object_address(OCLASS_PROC, mtransfn, 0, refobjs);
 
 	/* Depends on inverse transition function, if any */
 	if (OidIsValid(minvtransfn))
-	{
-		referenced.classId = ProcedureRelationId;
-		referenced.objectId = minvtransfn;
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-	}
+		add_object_address(OCLASS_PROC, minvtransfn, 0, refobjs);
 
 	/* Depends on final function, if any */
 	if (OidIsValid(mfinalfn))
-	{
-		referenced.classId = ProcedureRelationId;
-		referenced.objectId = mfinalfn;
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-	}
+		add_object_address(OCLASS_PROC, mfinalfn, 0, refobjs);
 
 	/* Depends on sort operator, if any */
 	if (OidIsValid(sortop))
-	{
-		referenced.classId = OperatorRelationId;
-		referenced.objectId = sortop;
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-	}
+		add_object_address(OCLASS_OPERATOR, sortop, 0, refobjs);
+
+	record_object_address_dependencies(&myself, refobjs, DEPENDENCY_NORMAL);
 
 	return myself;
 }
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index b6145593a3..be7c6b8c12 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -92,6 +92,7 @@ CreateConstraintEntry(const char *constraintName,
 	NameData	cname;
 	int			i;
 	ObjectAddress conobject;
+	ObjectAddresses *refobjs;
 
 	conDesc = table_open(ConstraintRelationId, RowExclusiveLock);
 
@@ -230,30 +231,22 @@ CreateConstraintEntry(const char *constraintName,
 
 	table_close(conDesc, RowExclusiveLock);
 
+	refobjs = new_object_addresses();
+
 	if (OidIsValid(relId))
 	{
 		/*
 		 * Register auto dependency from constraint to owning relation, or to
 		 * specific column(s) if any are mentioned.
 		 */
-		ObjectAddress relobject;
-
-		relobject.classId = RelationRelationId;
-		relobject.objectId = relId;
 		if (constraintNTotalKeys > 0)
 		{
 			for (i = 0; i < constraintNTotalKeys; i++)
-			{
-				relobject.objectSubId = constraintKey[i];
-
-				recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);
-			}
+				add_object_address(OCLASS_CLASS, relId, constraintKey[i], refobjs);
 		}
 		else
 		{
-			relobject.objectSubId = 0;
-
-			recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);
+			add_object_address(OCLASS_CLASS, relId, 0, refobjs);
 		}
 	}
 
@@ -262,39 +255,27 @@ CreateConstraintEntry(const char *constraintName,
 		/*
 		 * Register auto dependency from constraint to owning domain
 		 */
-		ObjectAddress domobject;
-
-		domobject.classId = TypeRelationId;
-		domobject.objectId = domainId;
-		domobject.objectSubId = 0;
-
-		recordDependencyOn(&conobject, &domobject, DEPENDENCY_AUTO);
+		add_object_address(OCLASS_TYPE, domainId, 0, refobjs);
 	}
 
+	/* record the AUTO dependencies we have so far */
+	record_object_address_dependencies(&conobject, refobjs, DEPENDENCY_AUTO);
+	reset_object_addresses(refobjs);
+
 	if (OidIsValid(foreignRelId))
 	{
 		/*
 		 * Register normal dependency from constraint to foreign relation, or
 		 * to specific column(s) if any are mentioned.
 		 */
-		ObjectAddress relobject;
-
-		relobject.classId = RelationRelationId;
-		relobject.objectId = foreignRelId;
 		if (foreignNKeys > 0)
 		{
 			for (i = 0; i < foreignNKeys; i++)
-			{
-				relobject.objectSubId = foreignKey[i];
-
-				recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
-			}
+				add_object_address(OCLASS_CLASS, foreignRelId, foreignKey[i], refobjs);
 		}
 		else
 		{
-			relobject.objectSubId = 0;
-
-			recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
+			add_object_address(OCLASS_CLASS, foreignRelId, 0, refobjs);
 		}
 	}
 
@@ -306,13 +287,7 @@ CreateConstraintEntry(const char *constraintName,
 		 * or primary-key constraints, the dependency runs the other way, and
 		 * is not made here.)
 		 */
-		ObjectAddress relobject;
-
-		relobject.classId = RelationRelationId;
-		relobject.objectId = indexRelId;
-		relobject.objectSubId = 0;
-
-		recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
+		add_object_address(OCLASS_CLASS, indexRelId, 0, refobjs);
 	}
 
 	if (foreignNKeys > 0)
@@ -323,28 +298,22 @@ CreateConstraintEntry(const char *constraintName,
 		 * all three operators for a column are the same; otherwise they are
 		 * different.
 		 */
-		ObjectAddress oprobject;
-
-		oprobject.classId = OperatorRelationId;
-		oprobject.objectSubId = 0;
-
 		for (i = 0; i < foreignNKeys; i++)
 		{
-			oprobject.objectId = pfEqOp[i];
-			recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
+			add_object_address(OCLASS_OPERATOR, pfEqOp[i], 0, refobjs);
+
 			if (ppEqOp[i] != pfEqOp[i])
-			{
-				oprobject.objectId = ppEqOp[i];
-				recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
-			}
+				add_object_address(OCLASS_OPERATOR, ppEqOp[i], 0, refobjs);
+
 			if (ffEqOp[i] != pfEqOp[i])
-			{
-				oprobject.objectId = ffEqOp[i];
-				recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
-			}
+				add_object_address(OCLASS_OPERATOR, ffEqOp[i], 0, refobjs);
 		}
 	}
 
+	record_object_address_dependencies(&conobject, refobjs, DEPENDENCY_NORMAL);
+
+	free_object_addresses(refobjs);
+
 	/*
 	 * We don't bother to register dependencies on the exclusion operators of
 	 * an exclusion constraint.  We assume they are members of the opclass
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
index a060c25d2e..b20004642a 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -35,7 +35,8 @@ static bool isObjectPinned(const ObjectAddress *object, Relation rel);
 /*
  * Record a dependency between 2 objects via their respective objectAddress.
  * The first argument is the dependent object, the second the one it
- * references.
+ * references. This is a simplified version of recordMultipleDependencies()
+ * aiming to avoid some overhead when we know there is only a single tuple.
  *
  * This simply creates an entry in pg_depend, without any other processing.
  */
@@ -44,7 +45,51 @@ recordDependencyOn(const ObjectAddress *depender,
 				   const ObjectAddress *referenced,
 				   DependencyType behavior)
 {
-	recordMultipleDependencies(depender, referenced, 1, behavior);
+	Relation	dependDesc;
+	HeapTuple	tuple;
+	bool		nulls[Natts_pg_depend];
+	Datum		values[Natts_pg_depend];
+
+	/*
+	 * During bootstrap, do nothing since pg_depend may not exist yet. initdb
+	 * will fill in appropriate pg_depend entries after bootstrap.
+	 */
+	if (IsBootstrapProcessingMode())
+		return;
+
+	dependDesc = table_open(DependRelationId, RowExclusiveLock);
+
+	memset(nulls, false, sizeof(nulls));
+
+	/*
+	 * If the referenced object is pinned by the system, there's no real
+	 * 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))
+	{
+		table_close(dependDesc, RowExclusiveLock);
+		return;
+	}
+
+	/*
+	 * 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);
+
+	values[Anum_pg_depend_deptype - 1] = CharGetDatum((char) behavior);
+
+	tuple = heap_form_tuple(dependDesc->rd_att, values, nulls);
+	CatalogTupleInsert(dependDesc, tuple);
+
+	table_close(dependDesc, RowExclusiveLock);
 }
 
 /*
@@ -59,7 +104,9 @@ recordMultipleDependencies(const ObjectAddress *depender,
 {
 	Relation	dependDesc;
 	CatalogIndexState indstate;
-	HeapTuple	tup;
+	HeapTuple	tuple;
+	TupleTableSlot **slot;
+	int			ntuples;
 	int			i;
 	bool		nulls[Natts_pg_depend];
 	Datum		values[Natts_pg_depend];
@@ -81,7 +128,14 @@ recordMultipleDependencies(const ObjectAddress *depender,
 
 	memset(nulls, false, sizeof(nulls));
 
-	for (i = 0; i < nreferenced; i++, referenced++)
+	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);
+
+	/* TODO is nreferenced a reasonable allocation of slots? */
+	slot = palloc(sizeof(TupleTableSlot *) * nreferenced);
+
+	for (i = 0, ntuples = 0; i < nreferenced; i++, referenced++)
 	{
 		/*
 		 * If the referenced object is pinned by the system, there's no real
@@ -94,30 +148,34 @@ recordMultipleDependencies(const ObjectAddress *depender,
 			 * 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);
 
 			values[Anum_pg_depend_deptype - 1] = CharGetDatum((char) behavior);
 
-			tup = heap_form_tuple(dependDesc->rd_att, values, nulls);
+			slot[ntuples] = MakeSingleTupleTableSlot(RelationGetDescr(dependDesc),
+													 &TTSOpsHeapTuple);
+
+			tuple = heap_form_tuple(dependDesc->rd_att, values, nulls);
+			ExecStoreHeapTuple(heap_copytuple(tuple), slot[ntuples], false);
+			ntuples++;
 
 			/* fetch index info only when we know we need it */
 			if (indstate == NULL)
 				indstate = CatalogOpenIndexes(dependDesc);
-
-			CatalogTupleInsertWithInfo(dependDesc, tup, indstate);
-
-			heap_freetuple(tup);
 		}
 	}
 
+	/*
+	 * We will have an indstate in case we found any tuples to insert in the
+	 * catalog, so perform a multi insert and close the index again when done.
+	 */
 	if (indstate != NULL)
+	{
+		CatalogMultiInsertWithInfo(dependDesc, slot, ntuples, indstate);
 		CatalogCloseIndexes(indstate);
+	}
 
 	table_close(dependDesc, RowExclusiveLock);
 }
diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c
index bcaa26c997..b37f01df09 100644
--- a/src/backend/catalog/pg_operator.c
+++ b/src/backend/catalog/pg_operator.c
@@ -773,13 +773,15 @@ ObjectAddress
 makeOperatorDependencies(HeapTuple tuple, bool isUpdate)
 {
 	Form_pg_operator oper = (Form_pg_operator) GETSTRUCT(tuple);
-	ObjectAddress myself,
-				referenced;
+	ObjectAddress myself;
+	ObjectAddresses *refobjs;
 
 	myself.classId = OperatorRelationId;
 	myself.objectId = oper->oid;
 	myself.objectSubId = 0;
 
+	refobjs = new_object_addresses();
+
 	/*
 	 * If we are updating the operator, delete any existing entries, except
 	 * for extension membership which should remain the same.
@@ -792,39 +794,19 @@ makeOperatorDependencies(HeapTuple tuple, bool isUpdate)
 
 	/* Dependency on namespace */
 	if (OidIsValid(oper->oprnamespace))
-	{
-		referenced.classId = NamespaceRelationId;
-		referenced.objectId = oper->oprnamespace;
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-	}
+		add_object_address(OCLASS_SCHEMA, oper->oprnamespace, 0, refobjs);
 
 	/* Dependency on left type */
 	if (OidIsValid(oper->oprleft))
-	{
-		referenced.classId = TypeRelationId;
-		referenced.objectId = oper->oprleft;
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-	}
+		add_object_address(OCLASS_TYPE, oper->oprleft, 0, refobjs);
 
 	/* Dependency on right type */
 	if (OidIsValid(oper->oprright))
-	{
-		referenced.classId = TypeRelationId;
-		referenced.objectId = oper->oprright;
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-	}
+		add_object_address(OCLASS_TYPE, oper->oprright, 0, refobjs);
 
 	/* Dependency on result type */
 	if (OidIsValid(oper->oprresult))
-	{
-		referenced.classId = TypeRelationId;
-		referenced.objectId = oper->oprresult;
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-	}
+		add_object_address(OCLASS_TYPE, oper->oprresult, 0, refobjs);
 
 	/*
 	 * NOTE: we do not consider the operator to depend on the associated
@@ -837,30 +819,18 @@ makeOperatorDependencies(HeapTuple tuple, bool isUpdate)
 
 	/* Dependency on implementation function */
 	if (OidIsValid(oper->oprcode))
-	{
-		referenced.classId = ProcedureRelationId;
-		referenced.objectId = oper->oprcode;
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-	}
+		add_object_address(OCLASS_PROC, oper->oprcode, 0, refobjs);
 
 	/* Dependency on restriction selectivity function */
 	if (OidIsValid(oper->oprrest))
-	{
-		referenced.classId = ProcedureRelationId;
-		referenced.objectId = oper->oprrest;
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-	}
+		add_object_address(OCLASS_PROC, oper->oprrest, 0, refobjs);
 
 	/* Dependency on join selectivity function */
 	if (OidIsValid(oper->oprjoin))
-	{
-		referenced.classId = ProcedureRelationId;
-		referenced.objectId = oper->oprjoin;
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-	}
+		add_object_address(OCLASS_PROC, oper->oprjoin, 0, refobjs);
+
+	record_object_address_dependencies(&myself, refobjs, DEPENDENCY_NORMAL);
+	free_object_addresses(refobjs);
 
 	/* Dependency on owner */
 	recordDependencyOnOwner(OperatorRelationId, oper->oid,
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index ef009ad2bc..e81c78f7d3 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -114,8 +114,8 @@ ProcedureCreate(const char *procedureName,
 	NameData	procname;
 	TupleDesc	tupDesc;
 	bool		is_update;
-	ObjectAddress myself,
-				referenced;
+	ObjectAddress myself;
+	ObjectAddresses *refobjs;
 	int			i;
 	Oid			trfid;
 
@@ -611,49 +611,29 @@ ProcedureCreate(const char *procedureName,
 	myself.objectId = retval;
 	myself.objectSubId = 0;
 
+	refobjs = new_object_addresses();
+
 	/* dependency on namespace */
-	referenced.classId = NamespaceRelationId;
-	referenced.objectId = procNamespace;
-	referenced.objectSubId = 0;
-	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+	add_object_address(OCLASS_SCHEMA, procNamespace, 0, refobjs);
 
 	/* dependency on implementation language */
-	referenced.classId = LanguageRelationId;
-	referenced.objectId = languageObjectId;
-	referenced.objectSubId = 0;
-	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+	add_object_address(OCLASS_LANGUAGE, languageObjectId, 0, refobjs);
 
 	/* dependency on return type */
-	referenced.classId = TypeRelationId;
-	referenced.objectId = returnType;
-	referenced.objectSubId = 0;
-	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+	add_object_address(OCLASS_TYPE, returnType, 0, refobjs);
 
 	/* dependency on transform used by return type, if any */
 	if ((trfid = get_transform_oid(returnType, languageObjectId, true)))
-	{
-		referenced.classId = TransformRelationId;
-		referenced.objectId = trfid;
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-	}
+		add_object_address(OCLASS_TRANSFORM, trfid, 0, refobjs);
 
 	/* dependency on parameter types */
 	for (i = 0; i < allParamCount; i++)
 	{
-		referenced.classId = TypeRelationId;
-		referenced.objectId = allParams[i];
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+		add_object_address(OCLASS_TYPE, allParams[i], 0, refobjs);
 
 		/* dependency on transform used by parameter type, if any */
 		if ((trfid = get_transform_oid(allParams[i], languageObjectId, true)))
-		{
-			referenced.classId = TransformRelationId;
-			referenced.objectId = trfid;
-			referenced.objectSubId = 0;
-			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-		}
+			add_object_address(OCLASS_TRANSFORM, trfid, 0, refobjs);
 	}
 
 	/* dependency on parameter default expressions */
@@ -663,12 +643,10 @@ ProcedureCreate(const char *procedureName,
 
 	/* dependency on support function, if any */
 	if (OidIsValid(prosupport))
-	{
-		referenced.classId = ProcedureRelationId;
-		referenced.objectId = prosupport;
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-	}
+		add_object_address(OCLASS_PROC, prosupport, 0, refobjs);
+
+	record_object_address_dependencies(&myself, refobjs, DEPENDENCY_NORMAL);
+	free_object_addresses(refobjs);
 
 	/* dependency on owner */
 	if (!is_update)
diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c
index fb7f8ddefc..5a24890d8a 100644
--- a/src/backend/catalog/pg_shdepend.c
+++ b/src/backend/catalog/pg_shdepend.c
@@ -66,7 +66,6 @@
 #include "utils/fmgroids.h"
 #include "utils/syscache.h"
 
-
 typedef enum
 {
 	LOCAL_OBJECT,
@@ -800,10 +799,15 @@ copyTemplateDependencies(Oid templateDbId, Oid newDbId)
 	ScanKeyData key[1];
 	SysScanDesc scan;
 	HeapTuple	tup;
+	HeapTuple	newtuple;
+	int			ntuples;
 	CatalogIndexState indstate;
 	Datum		values[Natts_pg_shdepend];
 	bool		nulls[Natts_pg_shdepend];
 	bool		replace[Natts_pg_shdepend];
+	/* TODO figure out a sensible value for the amount of slots */
+#define	DEPEND_TUPLE_BUF 32
+	TupleTableSlot *slot[DEPEND_TUPLE_BUF];
 
 	sdepRel = table_open(SharedDependRelationId, RowExclusiveLock);
 	sdepDesc = RelationGetDescr(sdepRel);
@@ -834,16 +838,26 @@ copyTemplateDependencies(Oid templateDbId, Oid newDbId)
 	 * copy the ownership dependency of the template database itself; this is
 	 * what we want.
 	 */
+	ntuples = 0;
 	while (HeapTupleIsValid(tup = systable_getnext(scan)))
 	{
-		HeapTuple	newtup;
-
-		newtup = heap_modify_tuple(tup, sdepDesc, values, nulls, replace);
-		CatalogTupleInsertWithInfo(sdepRel, newtup, indstate);
+		slot[ntuples] = MakeSingleTupleTableSlot(RelationGetDescr(sdepRel),
+									&TTSOpsHeapTuple);
+		newtuple = heap_modify_tuple(tup, sdepDesc, values, nulls, replace);
+		ExecStoreHeapTuple(heap_copytuple(newtuple), slot[ntuples], false);
+		ntuples++;
 
-		heap_freetuple(newtup);
+		if (ntuples == DEPEND_TUPLE_BUF)
+		{
+			CatalogMultiInsertWithInfo(sdepRel, slot, ntuples, indstate);
+			ntuples = 0;
+		}
 	}
 
+	/* Insert any tuples left in the buffer */
+	if (ntuples)
+		CatalogMultiInsertWithInfo(sdepRel, slot, ntuples, indstate);
+
 	systable_endscan(scan);
 
 	CatalogCloseIndexes(indstate);
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 2a51501d8d..b3707b0f7e 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -544,7 +544,9 @@ GenerateTypeDependencies(Oid typeObjectId,
 						 bool rebuild)
 {
 	ObjectAddress myself,
-				referenced;
+				referenced,
+				refobjs[8];
+	int			nref;
 
 	/* If rebuild, first flush old dependencies, except extension deps */
 	if (rebuild)
@@ -579,63 +581,54 @@ GenerateTypeDependencies(Oid typeObjectId,
 		recordDependencyOnCurrentExtension(&myself, rebuild);
 	}
 
+	nref = 0;
+
 	/* Normal dependencies on the I/O functions */
 	if (OidIsValid(typeForm->typinput))
 	{
-		referenced.classId = ProcedureRelationId;
-		referenced.objectId = typeForm->typinput;
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+		ObjectAddressSet(refobjs[nref], ProcedureRelationId, typeForm->typinput);
+		nref++;
 	}
 
 	if (OidIsValid(typeForm->typoutput))
 	{
-		referenced.classId = ProcedureRelationId;
-		referenced.objectId = typeForm->typoutput;
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+		ObjectAddressSet(refobjs[nref], ProcedureRelationId, typeForm->typoutput);
+		nref++;
 	}
 
 	if (OidIsValid(typeForm->typreceive))
 	{
-		referenced.classId = ProcedureRelationId;
-		referenced.objectId = typeForm->typreceive;
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+		ObjectAddressSet(refobjs[nref], ProcedureRelationId, typeForm->typreceive);
+		nref++;
 	}
 
 	if (OidIsValid(typeForm->typsend))
 	{
-		referenced.classId = ProcedureRelationId;
-		referenced.objectId = typeForm->typsend;
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+		ObjectAddressSet(refobjs[nref], ProcedureRelationId, typeForm->typsend);
+		nref++;
 	}
 
 	if (OidIsValid(typeForm->typmodin))
 	{
-		referenced.classId = ProcedureRelationId;
-		referenced.objectId = typeForm->typmodin;
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+		ObjectAddressSet(refobjs[nref], ProcedureRelationId, typeForm->typmodin);
+		nref++;
 	}
 
 	if (OidIsValid(typeForm->typmodout))
 	{
-		referenced.classId = ProcedureRelationId;
-		referenced.objectId = typeForm->typmodout;
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+		ObjectAddressSet(refobjs[nref], ProcedureRelationId, typeForm->typmodout);
+		nref++;
 	}
 
 	if (OidIsValid(typeForm->typanalyze))
 	{
-		referenced.classId = ProcedureRelationId;
-		referenced.objectId = typeForm->typanalyze;
-		referenced.objectSubId = 0;
-		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+		ObjectAddressSet(refobjs[nref], ProcedureRelationId, typeForm->typanalyze);
+		nref++;
 	}
 
+	if (nref)
+		recordMultipleDependencies(&myself, refobjs, nref, DEPENDENCY_NORMAL);
+
 	/*
 	 * If the type is a rowtype for a relation, mark it as internally
 	 * dependent on the relation, *unless* it is a stand-alone composite type
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index 151c3ef882..5468a6ecb8 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -974,7 +974,8 @@ DecodeMultiInsert(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 		ReorderBufferQueueChange(ctx->reorder, XLogRecGetXid(r),
 								 buf->origptr, change);
 	}
-	Assert(data == tupledata + tuplelen);
+	Assert(xlrec->flags & XLH_INSERT_CONTAINS_NEW_TUPLE &&
+		   data == tupledata + tuplelen);
 }
 
 /*
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index ff50d594f6..7e3bcde2ea 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -162,6 +162,9 @@ extern ObjectClass getObjectClass(const ObjectAddress *object);
 
 extern ObjectAddresses *new_object_addresses(void);
 
+extern void add_object_address(ObjectClass oclass, Oid objectId, int32 subId,
+							   ObjectAddresses *addrs);
+
 extern void add_exact_object_address(const ObjectAddress *object,
 									 ObjectAddresses *addrs);
 
@@ -174,6 +177,8 @@ extern void record_object_address_dependencies(const ObjectAddress *depender,
 
 extern void sort_object_addresses(ObjectAddresses *addrs);
 
+extern void reset_object_addresses(ObjectAddresses *addrs);
+
 extern void free_object_addresses(ObjectAddresses *addrs);
 
 /* in pg_depend.c */
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index eec71c29d5..24405977ff 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -95,6 +95,10 @@ extern List *heap_truncate_find_FKs(List *relationIds);
 extern void InsertPgAttributeTuple(Relation pg_attribute_rel,
 								   Form_pg_attribute new_attribute,
 								   CatalogIndexState indstate);
+extern void InsertPgAttributeTuples(Relation pg_attribute_rel,
+									FormData_pg_attribute *new_attributes,
+									int natts,
+									CatalogIndexState indstate);
 
 extern void InsertPgClassTuple(Relation pg_class_desc,
 							   Relation new_rel_desc,
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index ef4445b017..4ea212ee59 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -19,6 +19,7 @@
 #define INDEXING_H
 
 #include "access/htup.h"
+#include "nodes/execnodes.h"
 #include "utils/relcache.h"
 
 /*
@@ -36,6 +37,8 @@ extern void CatalogCloseIndexes(CatalogIndexState indstate);
 extern void CatalogTupleInsert(Relation heapRel, HeapTuple tup);
 extern void CatalogTupleInsertWithInfo(Relation heapRel, HeapTuple tup,
 									   CatalogIndexState indstate);
+extern void CatalogMultiInsertWithInfo(Relation heapRel, TupleTableSlot **slot,
+						   int ntuples, CatalogIndexState indstate);
 extern void CatalogTupleUpdate(Relation heapRel, ItemPointer otid,
 							   HeapTuple tup);
 extern void CatalogTupleUpdateWithInfo(Relation heapRel,
-- 
2.14.1.145.gb3622a4ee

