From 1dab865a48a4c3db6217258d55d7e70d23d4774e Mon Sep 17 00:00:00 2001
From: Greg Burd <greg@burd.me>
Date: Fri, 14 Nov 2025 08:43:14 -0500
Subject: [PATCH v4 2/3] Update the remainder of catalog updates using the new
 APIs

NOTE: This is a work-in-progress commit, some of the files (aclchk.c,
heap.c) have been adapted to use the proposed macros, the rest remain
unchanged and use the ealier macros.

Apply all necessary changes to use the new macros for catalog tuples.
This commit finishes the work started in the previous one.  There should
be no behavioral changes resulting from these commits.
---
 src/backend/catalog/aclchk.c                | 159 +++---
 src/backend/catalog/heap.c                  | 359 ++++++------
 src/backend/catalog/index.c                 | 177 +++---
 src/backend/catalog/partition.c             |   5 +-
 src/backend/catalog/pg_attrdef.c            |  38 +-
 src/backend/catalog/pg_cast.c               |  18 +-
 src/backend/catalog/pg_collation.c          |  40 +-
 src/backend/catalog/pg_conversion.c         |  22 +-
 src/backend/catalog/pg_db_role_setting.c    |  54 +-
 src/backend/catalog/pg_depend.c             |  72 +--
 src/backend/catalog/pg_enum.c               |  45 +-
 src/backend/catalog/pg_inherits.c           |  14 +-
 src/backend/catalog/pg_largeobject.c        |  22 +-
 src/backend/catalog/pg_namespace.c          |  24 +-
 src/backend/catalog/pg_operator.c           | 119 ++--
 src/backend/catalog/pg_parameter_acl.c      |  11 +-
 src/backend/catalog/pg_proc.c               | 110 ++--
 src/backend/catalog/pg_publication.c        |  36 +-
 src/backend/catalog/pg_range.c              |  20 +-
 src/backend/catalog/pg_shdepend.c           |  67 +--
 src/backend/catalog/pg_subscription.c       |  86 ++-
 src/backend/catalog/pg_type.c               | 211 ++++----
 src/backend/catalog/toasting.c              |   7 +-
 src/backend/commands/amcmds.c               |  15 +-
 src/backend/commands/cluster.c              |  80 +--
 src/backend/commands/collationcmds.c        |  20 +-
 src/backend/commands/comment.c              |  51 +-
 src/backend/commands/dbcommands.c           | 133 +++--
 src/backend/commands/event_trigger.c        |  42 +-
 src/backend/commands/extension.c            | 107 ++--
 src/backend/commands/foreigncmds.c          | 205 +++----
 src/backend/commands/functioncmds.c         |  69 +--
 src/backend/commands/indexcmds.c            |  16 +-
 src/backend/commands/matview.c              |   7 +-
 src/backend/commands/opclasscmds.c          |  82 +--
 src/backend/commands/operatorcmds.c         |  54 +-
 src/backend/commands/policy.c               |  98 ++--
 src/backend/commands/proclang.c             |  44 +-
 src/backend/commands/publicationcmds.c      |  85 ++-
 src/backend/commands/schemacmds.c           |  25 +-
 src/backend/commands/seclabel.c             |  57 +-
 src/backend/commands/sequence.c             |  32 +-
 src/backend/commands/statscmds.c            |  50 +-
 src/backend/commands/subscriptioncmds.c     | 187 +++----
 src/backend/commands/tablecmds.c            | 569 ++++++++++++--------
 src/backend/commands/tablespace.c           |  47 +-
 src/backend/commands/trigger.c              | 109 ++--
 src/backend/commands/tsearchcmds.c          | 169 +++---
 src/backend/commands/typecmds.c             | 136 ++---
 src/backend/commands/user.c                 | 228 +++-----
 src/backend/replication/logical/origin.c    |  10 +-
 src/backend/replication/logical/tablesync.c |  22 +-
 src/backend/replication/logical/worker.c    |  17 +-
 src/backend/rewrite/rewriteDefine.c         |  60 +--
 src/backend/rewrite/rewriteSupport.c        |   6 +-
 src/backend/statistics/extended_stats.c     |  65 ++-
 src/backend/statistics/relation_stats.c     |  40 +-
 src/backend/storage/large_object/inv_api.c  |  53 +-
 src/backend/utils/cache/relcache.c          |  20 +-
 59 files changed, 2256 insertions(+), 2470 deletions(-)

diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index a431fc0926f..35a38f01a9d 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -1318,8 +1318,7 @@ SetDefaultACL(InternalDefaultACL *iacls)
 	else
 	{
 		Datum		values[Natts_pg_default_acl] = {0};
-		bool		nulls[Natts_pg_default_acl] = {0};
-		bool		replaces[Natts_pg_default_acl] = {0};
+		bool		nulls[Natts_pg_default_acl] = {false};
 		Oid			defAclOid;
 
 		if (isNew)
@@ -1327,26 +1326,28 @@ SetDefaultACL(InternalDefaultACL *iacls)
 			/* insert new entry */
 			defAclOid = GetNewOidWithIndex(rel, DefaultAclOidIndexId,
 										   Anum_pg_default_acl_oid);
-			values[Anum_pg_default_acl_oid - 1] = ObjectIdGetDatum(defAclOid);
-			values[Anum_pg_default_acl_defaclrole - 1] = ObjectIdGetDatum(iacls->roleid);
-			values[Anum_pg_default_acl_defaclnamespace - 1] = ObjectIdGetDatum(iacls->nspid);
-			values[Anum_pg_default_acl_defaclobjtype - 1] = CharGetDatum(objtype);
-			values[Anum_pg_default_acl_defaclacl - 1] = PointerGetDatum(new_acl);
+			HeapTupleSetValue(pg_default_acl, oid, ObjectIdGetDatum(defAclOid), values);
+			HeapTupleSetValue(pg_default_acl, defaclrole, ObjectIdGetDatum(iacls->roleid), values);
+			HeapTupleSetValue(pg_default_acl, defaclnamespace, ObjectIdGetDatum(iacls->nspid), values);
+			HeapTupleSetValue(pg_default_acl, defaclobjtype, CharGetDatum(objtype), values);
+			HeapTupleSetValue(pg_default_acl, defaclacl, PointerGetDatum(new_acl), values);
 
 			newtuple = heap_form_tuple(RelationGetDescr(rel), values, nulls);
-			CatalogTupleInsert(rel, newtuple);
+			CatalogTupleInsert(rel, newtuple, NULL);
 		}
 		else
 		{
+			Bitmapset  *updated = NULL;
+
 			defAclOid = ((Form_pg_default_acl) GETSTRUCT(tuple))->oid;
 
 			/* update existing entry */
-			values[Anum_pg_default_acl_defaclacl - 1] = PointerGetDatum(new_acl);
-			replaces[Anum_pg_default_acl_defaclacl - 1] = true;
+			HeapTupleUpdateValue(pg_default_acl, defaclacl, PointerGetDatum(new_acl), values, nulls, updated);
+
+			newtuple = heap_update_tuple(tuple, RelationGetDescr(rel), values, nulls, updated);
+			CatalogTupleUpdate(rel, &newtuple->t_self, newtuple, updated, NULL);
 
-			newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel),
-										 values, nulls, replaces);
-			CatalogTupleUpdate(rel, &newtuple->t_self, newtuple);
+			bms_free(updated);
 		}
 
 		/* these dependencies don't change in an update */
@@ -1652,12 +1653,12 @@ ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname,
 	bool		need_update;
 	HeapTuple	newtuple;
 	Datum		values[Natts_pg_attribute] = {0};
-	bool		nulls[Natts_pg_attribute] = {0};
-	bool		replaces[Natts_pg_attribute] = {0};
+	bool		nulls[Natts_pg_attribute] = {false};
 	int			noldmembers;
 	int			nnewmembers;
 	Oid		   *oldmembers;
 	Oid		   *newmembers;
+	Bitmapset  *updated = NULL;
 
 	attr_tuple = SearchSysCache2(ATTNUM,
 								 ObjectIdGetDatum(relOid),
@@ -1744,22 +1745,21 @@ ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname,
 	 */
 	if (ACL_NUM(new_acl) > 0)
 	{
-		values[Anum_pg_attribute_attacl - 1] = PointerGetDatum(new_acl);
+		HeapTupleUpdateValue(pg_attribute, attacl, PointerGetDatum(new_acl), values, nulls, updated);
 		need_update = true;
 	}
 	else
 	{
-		nulls[Anum_pg_attribute_attacl - 1] = true;
+		HeapTupleUpdateValueNull(pg_attribute, attacl, values, nulls, updated);
 		need_update = !isNull;
 	}
-	replaces[Anum_pg_attribute_attacl - 1] = true;
 
 	if (need_update)
 	{
-		newtuple = heap_modify_tuple(attr_tuple, RelationGetDescr(attRelation),
-									 values, nulls, replaces);
+		newtuple = heap_update_tuple(attr_tuple, RelationGetDescr(attRelation),
+									 values, nulls, updated);
 
-		CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
+		CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple, updated, NULL);
 
 		/* Update initial privileges for extensions */
 		recordExtensionInitPriv(relOid, RelationRelationId, attnum,
@@ -1775,6 +1775,7 @@ ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname,
 	pfree(new_acl);
 
 	ReleaseSysCache(attr_tuple);
+	bms_free(updated);
 }
 
 /*
@@ -1962,8 +1963,8 @@ ExecGrant_Relation(InternalGrant *istmt)
 			Oid			grantorId;
 			HeapTuple	newtuple;
 			Datum		values[Natts_pg_class] = {0};
-			bool		nulls[Natts_pg_class] = {0};
-			bool		replaces[Natts_pg_class] = {0};
+			bool		nulls[Natts_pg_class] = {false};
+			Bitmapset  *updated = NULL;
 			int			nnewmembers;
 			Oid		   *newmembers;
 			ObjectType	objtype;
@@ -2013,13 +2014,12 @@ ExecGrant_Relation(InternalGrant *istmt)
 			nnewmembers = aclmembers(new_acl, &newmembers);
 
 			/* finished building new ACL value, now insert it */
-			replaces[Anum_pg_class_relacl - 1] = true;
-			values[Anum_pg_class_relacl - 1] = PointerGetDatum(new_acl);
+			HeapTupleUpdateValue(pg_class, relacl, PointerGetDatum(new_acl), values, nulls, updated);
 
-			newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation),
-										 values, nulls, replaces);
+			newtuple = heap_update_tuple(tuple, RelationGetDescr(relation),
+										 values, nulls, updated);
 
-			CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
+			CatalogTupleUpdate(relation, &newtuple->t_self, newtuple, updated, NULL);
 			UnlockTuple(relation, &tuple->t_self, InplaceUpdateTupleLock);
 
 			/* Update initial privileges for extensions */
@@ -2032,6 +2032,7 @@ ExecGrant_Relation(InternalGrant *istmt)
 								  nnewmembers, newmembers);
 
 			pfree(new_acl);
+			bms_free(updated);
 		}
 		else
 			UnlockTuple(relation, &tuple->t_self, InplaceUpdateTupleLock);
@@ -2142,7 +2143,7 @@ ExecGrant_common(InternalGrant *istmt, Oid classid, AclMode default_privs,
 		HeapTuple	newtuple;
 		Datum	   *values = palloc0_array(Datum, RelationGetDescr(relation)->natts);
 		bool	   *nulls = palloc0_array(bool, RelationGetDescr(relation)->natts);
-		bool	   *replaces = palloc0_array(bool, RelationGetDescr(relation)->natts);
+		Bitmapset  *updated = NULL;
 		int			noldmembers;
 		int			nnewmembers;
 		Oid		   *oldmembers;
@@ -2216,14 +2217,17 @@ ExecGrant_common(InternalGrant *istmt, Oid classid, AclMode default_privs,
 		 */
 		nnewmembers = aclmembers(new_acl, &newmembers);
 
-		/* finished building new ACL value, now insert it */
-		replaces[get_object_attnum_acl(classid) - 1] = true;
+		/*
+		 * Finished building new ACL value, now insert it. NOTE: We can't use
+		 * the HeapTupleMarkColumnUpdated() macro here because
+		 * get_object_attnum_acl(classid) provides an index.
+		 */
 		values[get_object_attnum_acl(classid) - 1] = PointerGetDatum(new_acl);
+		updated = bms_add_member(updated, get_object_attnum_acl(classid) - FirstLowInvalidHeapAttributeNumber);
 
-		newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
-									 nulls, replaces);
+		newtuple = heap_update_tuple(tuple, RelationGetDescr(relation), values, nulls, updated);
 
-		CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
+		CatalogTupleUpdate(relation, &newtuple->t_self, newtuple, updated, NULL);
 		UnlockTuple(relation, &tuple->t_self, InplaceUpdateTupleLock);
 
 		/* Update initial privileges for extensions */
@@ -2239,6 +2243,7 @@ ExecGrant_common(InternalGrant *istmt, Oid classid, AclMode default_privs,
 		ReleaseSysCache(tuple);
 
 		pfree(new_acl);
+		bms_free(updated);
 
 		/* prevent error when processing duplicate objects */
 		CommandCounterIncrement();
@@ -2290,8 +2295,8 @@ ExecGrant_Largeobject(InternalGrant *istmt)
 		Oid			ownerId;
 		HeapTuple	newtuple;
 		Datum		values[Natts_pg_largeobject_metadata] = {0};
-		bool		nulls[Natts_pg_largeobject_metadata] = {0};
-		bool		replaces[Natts_pg_largeobject_metadata] = {0};
+		bool		nulls[Natts_pg_largeobject_metadata] = {false};
+		Bitmapset  *updated = NULL;
 		int			noldmembers;
 		int			nnewmembers;
 		Oid		   *oldmembers;
@@ -2369,14 +2374,12 @@ ExecGrant_Largeobject(InternalGrant *istmt)
 		nnewmembers = aclmembers(new_acl, &newmembers);
 
 		/* finished building new ACL value, now insert it */
-		replaces[Anum_pg_largeobject_metadata_lomacl - 1] = true;
-		values[Anum_pg_largeobject_metadata_lomacl - 1]
-			= PointerGetDatum(new_acl);
+		HeapTupleUpdateValue(pg_largeobject_metadata, lomacl, PointerGetDatum(new_acl), values, nulls, updated);
 
-		newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation),
-									 values, nulls, replaces);
+		newtuple = heap_update_tuple(tuple, RelationGetDescr(relation),
+									 values, nulls, updated);
 
-		CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
+		CatalogTupleUpdate(relation, &newtuple->t_self, newtuple, updated, NULL);
 
 		/* Update initial privileges for extensions */
 		recordExtensionInitPriv(loid, LargeObjectRelationId, 0, new_acl);
@@ -2391,6 +2394,7 @@ ExecGrant_Largeobject(InternalGrant *istmt)
 		systable_endscan(scan);
 
 		pfree(new_acl);
+		bms_free(updated);
 
 		/* prevent error when processing duplicate objects */
 		CommandCounterIncrement();
@@ -2529,16 +2533,16 @@ ExecGrant_Parameter(InternalGrant *istmt)
 			/* finished building new ACL value, now insert it */
 			HeapTuple	newtuple;
 			Datum		values[Natts_pg_parameter_acl] = {0};
-			bool		nulls[Natts_pg_parameter_acl] = {0};
-			bool		replaces[Natts_pg_parameter_acl] = {0};
+			bool		nulls[Natts_pg_parameter_acl] = {false};
+			Bitmapset  *updated = NULL;
 
-			replaces[Anum_pg_parameter_acl_paracl - 1] = true;
-			values[Anum_pg_parameter_acl_paracl - 1] = PointerGetDatum(new_acl);
+			HeapTupleUpdateValue(pg_parameter_acl, paracl, PointerGetDatum(new_acl), values, nulls, updated);
 
-			newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation),
-										 values, nulls, replaces);
+			newtuple = heap_update_tuple(tuple, RelationGetDescr(relation),
+										 values, nulls, updated);
 
-			CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
+			CatalogTupleUpdate(relation, &newtuple->t_self, newtuple, updated, NULL);
+			bms_free(updated);
 		}
 
 		/* Update initial privileges for extensions */
@@ -4669,8 +4673,8 @@ recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid,
 	if (HeapTupleIsValid(oldtuple))
 	{
 		Datum		values[Natts_pg_init_privs] = {0};
-		bool		nulls[Natts_pg_init_privs] = {0};
-		bool		replace[Natts_pg_init_privs] = {0};
+		bool		nulls[Natts_pg_init_privs] = {false};
+		Bitmapset  *updated = NULL;
 		Datum		oldAclDatum;
 		bool		isNull;
 		Acl		   *old_acl;
@@ -4689,13 +4693,13 @@ recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid,
 		/* If we have a new ACL to set, then update the row with it. */
 		if (new_acl && ACL_NUM(new_acl) != 0)
 		{
-			values[Anum_pg_init_privs_initprivs - 1] = PointerGetDatum(new_acl);
-			replace[Anum_pg_init_privs_initprivs - 1] = true;
+			HeapTupleUpdateValue(pg_init_privs, initprivs, PointerGetDatum(new_acl), values, nulls, updated);
 
-			oldtuple = heap_modify_tuple(oldtuple, RelationGetDescr(relation),
-										 values, nulls, replace);
+			oldtuple = heap_update_tuple(oldtuple, RelationGetDescr(relation),
+										 values, nulls, updated);
 
-			CatalogTupleUpdate(relation, &oldtuple->t_self, oldtuple);
+			CatalogTupleUpdate(relation, &oldtuple->t_self, oldtuple, updated, NULL);
+			bms_free(updated);
 		}
 		else
 		{
@@ -4706,7 +4710,7 @@ recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid,
 	else
 	{
 		Datum		values[Natts_pg_init_privs] = {0};
-		bool		nulls[Natts_pg_init_privs] = {0};
+		bool		nulls[Natts_pg_init_privs] = {false};
 
 		/*
 		 * Only add a new entry if the new ACL is non-NULL.
@@ -4717,19 +4721,18 @@ recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid,
 		if (new_acl && ACL_NUM(new_acl) != 0)
 		{
 			/* No entry found, so add it. */
-			values[Anum_pg_init_privs_objoid - 1] = ObjectIdGetDatum(objoid);
-			values[Anum_pg_init_privs_classoid - 1] = ObjectIdGetDatum(classoid);
-			values[Anum_pg_init_privs_objsubid - 1] = Int32GetDatum(objsubid);
+			HeapTupleSetValue(pg_init_privs, objoid, ObjectIdGetDatum(objoid), values);
+			HeapTupleSetValue(pg_init_privs, classoid, ObjectIdGetDatum(classoid), values);
+			HeapTupleSetValue(pg_init_privs, objsubid, Int32GetDatum(objsubid), values);
 
 			/* This function only handles initial privileges of extensions */
-			values[Anum_pg_init_privs_privtype - 1] =
-				CharGetDatum(INITPRIVS_EXTENSION);
+			HeapTupleSetValue(pg_init_privs, privtype, CharGetDatum(INITPRIVS_EXTENSION), values);
 
-			values[Anum_pg_init_privs_initprivs - 1] = PointerGetDatum(new_acl);
+			HeapTupleSetValue(pg_init_privs, initprivs, PointerGetDatum(new_acl), values);
 
 			tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
 
-			CatalogTupleInsert(relation, tuple);
+			CatalogTupleInsert(relation, tuple, NULL);
 
 			/* Update pg_shdepend, too. */
 			noldmembers = 0;
@@ -4828,16 +4831,16 @@ ReplaceRoleInInitPriv(Oid oldroleid, Oid newroleid,
 	else
 	{
 		Datum		values[Natts_pg_init_privs] = {0};
-		bool		nulls[Natts_pg_init_privs] = {0};
-		bool		replaces[Natts_pg_init_privs] = {0};
+		bool		nulls[Natts_pg_init_privs] = {false};
+		Bitmapset  *updated = NULL;
 
 		/* Update existing entry. */
-		values[Anum_pg_init_privs_initprivs - 1] = PointerGetDatum(new_acl);
-		replaces[Anum_pg_init_privs_initprivs - 1] = true;
+		HeapTupleUpdateValue(pg_init_privs, initprivs, PointerGetDatum(new_acl), values, nulls, updated);
 
-		newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(rel),
-									 values, nulls, replaces);
-		CatalogTupleUpdate(rel, &newtuple->t_self, newtuple);
+		newtuple = heap_update_tuple(oldtuple, RelationGetDescr(rel),
+									 values, nulls, updated);
+		CatalogTupleUpdate(rel, &newtuple->t_self, newtuple, updated, NULL);
+		bms_free(updated);
 	}
 
 	/*
@@ -4964,16 +4967,16 @@ RemoveRoleFromInitPriv(Oid roleid, Oid classid, Oid objid, int32 objsubid)
 	else
 	{
 		Datum		values[Natts_pg_init_privs] = {0};
-		bool		nulls[Natts_pg_init_privs] = {0};
-		bool		replaces[Natts_pg_init_privs] = {0};
+		bool		nulls[Natts_pg_init_privs] = {false};
+		Bitmapset  *updated2 = NULL;
 
 		/* Update existing entry. */
-		values[Anum_pg_init_privs_initprivs - 1] = PointerGetDatum(new_acl);
-		replaces[Anum_pg_init_privs_initprivs - 1] = true;
+		HeapTupleUpdateValue(pg_init_privs, initprivs, PointerGetDatum(new_acl), values, nulls, updated2);
 
-		newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(rel),
-									 values, nulls, replaces);
-		CatalogTupleUpdate(rel, &newtuple->t_self, newtuple);
+		newtuple = heap_update_tuple(oldtuple, RelationGetDescr(rel),
+									 values, nulls, updated2);
+		CatalogTupleUpdate(rel, &newtuple->t_self, newtuple, updated2, NULL);
+		bms_free(updated2);
 	}
 
 	/*
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 606434823cf..a3ff559ed32 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -30,6 +30,7 @@
 #include "postgres.h"
 
 #include "access/genam.h"
+#include "access/htup.h"
 #include "access/multixact.h"
 #include "access/relation.h"
 #include "access/table.h"
@@ -705,7 +706,7 @@ CheckAttributeType(const char *attname,
  * number of elements as tupdesc or be NULL.  The other variable-length fields
  * of pg_attribute are always initialized to null values.
  *
- * indstate is the index state for CatalogTupleInsertWithInfo.  It can be
+ * indstate is the index state for CatalogTupleInsert.  It can be
  * passed as NULL, in which case we'll fetch the necessary info.  (Don't do
  * this when inserting multiple attributes, because it's a tad more
  * expensive.)
@@ -720,7 +721,7 @@ InsertPgAttributeTuples(Relation pg_attribute_rel,
 						const FormExtraData_pg_attribute tupdesc_extra[],
 						CatalogIndexState indstate)
 {
-	TupleTableSlot **slot;
+	TupleTableSlot **slots;
 	TupleDesc	td;
 	int			nslots;
 	int			natts = 0;
@@ -732,66 +733,70 @@ InsertPgAttributeTuples(Relation pg_attribute_rel,
 	/* Initialize the number of slots to use */
 	nslots = Min(tupdesc->natts,
 				 (MAX_CATALOG_MULTI_INSERT_BYTES / sizeof(FormData_pg_attribute)));
-	slot = palloc_array(TupleTableSlot *, nslots);
+	slots = palloc_array(TupleTableSlot *, nslots);
 	for (int i = 0; i < nslots; i++)
-		slot[i] = MakeSingleTupleTableSlot(td, &TTSOpsHeapTuple);
+		slots[i] = MakeSingleTupleTableSlot(td, &TTSOpsHeapTuple);
 
 	while (natts < tupdesc->natts)
 	{
+		TupleTableSlot *slot = slots[slotCount];
+		Datum	   *values = slot->tts_values;
 		Form_pg_attribute attrs = TupleDescAttr(tupdesc, natts);
 		const FormExtraData_pg_attribute *attrs_extra = tupdesc_extra ? &tupdesc_extra[natts] : NULL;
 
-		ExecClearTuple(slot[slotCount]);
+		ExecClearTuple(slot);
 
-		memset(slot[slotCount]->tts_isnull, false,
-			   slot[slotCount]->tts_tupleDescriptor->natts * sizeof(bool));
+		memset(slot->tts_isnull, false,
+			   slot->tts_tupleDescriptor->natts * sizeof(bool));
 
 		if (new_rel_oid != InvalidOid)
-			slot[slotCount]->tts_values[Anum_pg_attribute_attrelid - 1] = ObjectIdGetDatum(new_rel_oid);
+			HeapTupleSetValue(pg_attribute, attrelid, ObjectIdGetDatum(new_rel_oid), values);
 		else
-			slot[slotCount]->tts_values[Anum_pg_attribute_attrelid - 1] = ObjectIdGetDatum(attrs->attrelid);
-
-		slot[slotCount]->tts_values[Anum_pg_attribute_attname - 1] = NameGetDatum(&attrs->attname);
-		slot[slotCount]->tts_values[Anum_pg_attribute_atttypid - 1] = ObjectIdGetDatum(attrs->atttypid);
-		slot[slotCount]->tts_values[Anum_pg_attribute_attlen - 1] = Int16GetDatum(attrs->attlen);
-		slot[slotCount]->tts_values[Anum_pg_attribute_attnum - 1] = Int16GetDatum(attrs->attnum);
-		slot[slotCount]->tts_values[Anum_pg_attribute_atttypmod - 1] = Int32GetDatum(attrs->atttypmod);
-		slot[slotCount]->tts_values[Anum_pg_attribute_attndims - 1] = Int16GetDatum(attrs->attndims);
-		slot[slotCount]->tts_values[Anum_pg_attribute_attbyval - 1] = BoolGetDatum(attrs->attbyval);
-		slot[slotCount]->tts_values[Anum_pg_attribute_attalign - 1] = CharGetDatum(attrs->attalign);
-		slot[slotCount]->tts_values[Anum_pg_attribute_attstorage - 1] = CharGetDatum(attrs->attstorage);
-		slot[slotCount]->tts_values[Anum_pg_attribute_attcompression - 1] = CharGetDatum(attrs->attcompression);
-		slot[slotCount]->tts_values[Anum_pg_attribute_attnotnull - 1] = BoolGetDatum(attrs->attnotnull);
-		slot[slotCount]->tts_values[Anum_pg_attribute_atthasdef - 1] = BoolGetDatum(attrs->atthasdef);
-		slot[slotCount]->tts_values[Anum_pg_attribute_atthasmissing - 1] = BoolGetDatum(attrs->atthasmissing);
-		slot[slotCount]->tts_values[Anum_pg_attribute_attidentity - 1] = CharGetDatum(attrs->attidentity);
-		slot[slotCount]->tts_values[Anum_pg_attribute_attgenerated - 1] = CharGetDatum(attrs->attgenerated);
-		slot[slotCount]->tts_values[Anum_pg_attribute_attisdropped - 1] = BoolGetDatum(attrs->attisdropped);
-		slot[slotCount]->tts_values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(attrs->attislocal);
-		slot[slotCount]->tts_values[Anum_pg_attribute_attinhcount - 1] = Int16GetDatum(attrs->attinhcount);
-		slot[slotCount]->tts_values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(attrs->attcollation);
+			HeapTupleSetValue(pg_attribute, attrelid, ObjectIdGetDatum(attrs->attrelid), values);
+
+		HeapTupleSetValue(pg_attribute, attname, NameGetDatum(&attrs->attname), values);
+		HeapTupleSetValue(pg_attribute, atttypid, ObjectIdGetDatum(attrs->atttypid), values);
+		HeapTupleSetValue(pg_attribute, attlen, Int16GetDatum(attrs->attlen), values);
+		HeapTupleSetValue(pg_attribute, attnum, Int16GetDatum(attrs->attnum), values);
+		HeapTupleSetValue(pg_attribute, atttypmod, Int32GetDatum(attrs->atttypmod), values);
+		HeapTupleSetValue(pg_attribute, attndims, Int16GetDatum(attrs->attndims), values);
+		HeapTupleSetValue(pg_attribute, attbyval, BoolGetDatum(attrs->attbyval), values);
+		HeapTupleSetValue(pg_attribute, attalign, CharGetDatum(attrs->attalign), values);
+		HeapTupleSetValue(pg_attribute, attstorage, CharGetDatum(attrs->attstorage), values);
+		HeapTupleSetValue(pg_attribute, attcompression, CharGetDatum(attrs->attcompression), values);
+		HeapTupleSetValue(pg_attribute, attnotnull, BoolGetDatum(attrs->attnotnull), values);
+		HeapTupleSetValue(pg_attribute, atthasdef, BoolGetDatum(attrs->atthasdef), values);
+		HeapTupleSetValue(pg_attribute, atthasmissing, BoolGetDatum(attrs->atthasmissing), values);
+		HeapTupleSetValue(pg_attribute, attidentity, CharGetDatum(attrs->attidentity), values);
+		HeapTupleSetValue(pg_attribute, attgenerated, CharGetDatum(attrs->attgenerated), values);
+		HeapTupleSetValue(pg_attribute, attisdropped, BoolGetDatum(attrs->attisdropped), values);
+		HeapTupleSetValue(pg_attribute, attislocal, BoolGetDatum(attrs->attislocal), values);
+		HeapTupleSetValue(pg_attribute, attinhcount, Int16GetDatum(attrs->attinhcount), values);
+		HeapTupleSetValue(pg_attribute, attcollation, ObjectIdGetDatum(attrs->attcollation), values);
 		if (attrs_extra)
 		{
-			slot[slotCount]->tts_values[Anum_pg_attribute_attstattarget - 1] = attrs_extra->attstattarget.value;
-			slot[slotCount]->tts_isnull[Anum_pg_attribute_attstattarget - 1] = attrs_extra->attstattarget.isnull;
+			HeapTupleSetValue(pg_attribute, attstattarget, attrs_extra->attstattarget.value, values);
+			if (attrs_extra->attstattarget.isnull)
+				HeapTupleSetValueNull(pg_attribute, attstattarget, values, slot->tts_isnull);
 
-			slot[slotCount]->tts_values[Anum_pg_attribute_attoptions - 1] = attrs_extra->attoptions.value;
-			slot[slotCount]->tts_isnull[Anum_pg_attribute_attoptions - 1] = attrs_extra->attoptions.isnull;
+			HeapTupleSetValue(pg_attribute, attoptions, attrs_extra->attoptions.value, values);
+			if (attrs_extra->attoptions.isnull)
+				HeapTupleSetValueNull(pg_attribute, attoptions, values, slot->tts_isnull);
 		}
 		else
 		{
-			slot[slotCount]->tts_isnull[Anum_pg_attribute_attstattarget - 1] = true;
-			slot[slotCount]->tts_isnull[Anum_pg_attribute_attoptions - 1] = true;
+			HeapTupleSetValueNull(pg_attribute, attstattarget, values, slot->tts_isnull);
+			HeapTupleSetValueNull(pg_attribute, attoptions, values, slot->tts_isnull);
 		}
 
 		/*
 		 * The remaining fields are not set for new columns.
 		 */
-		slot[slotCount]->tts_isnull[Anum_pg_attribute_attacl - 1] = true;
-		slot[slotCount]->tts_isnull[Anum_pg_attribute_attfdwoptions - 1] = true;
-		slot[slotCount]->tts_isnull[Anum_pg_attribute_attmissingval - 1] = true;
+		HeapTupleSetValueNull(pg_attribute, attacl, values, slot->tts_isnull);
+		HeapTupleSetValueNull(pg_attribute, attfdwoptions, values, slot->tts_isnull);
+		HeapTupleSetValueNull(pg_attribute, attmissingval, values, slot->tts_isnull);
 
-		ExecStoreVirtualTuple(slot[slotCount]);
+		ExecStoreVirtualTuple(slot);
 		slotCount++;
 
 		/*
@@ -808,8 +813,7 @@ InsertPgAttributeTuples(Relation pg_attribute_rel,
 			}
 
 			/* insert the new tuples and update the indexes */
-			CatalogTuplesMultiInsertWithInfo(pg_attribute_rel, slot, slotCount,
-											 indstate);
+			CatalogTuplesMultiInsert(pg_attribute_rel, slots, slotCount, indstate);
 			slotCount = 0;
 		}
 
@@ -819,8 +823,8 @@ InsertPgAttributeTuples(Relation pg_attribute_rel,
 	if (close_index)
 		CatalogCloseIndexes(indstate);
 	for (int i = 0; i < nslots; i++)
-		ExecDropSingleTupleTableSlot(slot[i]);
-	pfree(slot);
+		ExecDropSingleTupleTableSlot(slots[i]);
+	pfree(slots);
 }
 
 /* --------------------------------
@@ -914,61 +918,58 @@ InsertPgClassTuple(Relation pg_class_desc,
 				   Datum reloptions)
 {
 	Form_pg_class rd_rel = new_rel_desc->rd_rel;
-	Datum		values[Natts_pg_class];
-	bool		nulls[Natts_pg_class];
+	Datum		values[Natts_pg_class] = {0};
+	bool		nulls[Natts_pg_class] = {false};
 	HeapTuple	tup;
 
 	/* This is a tad tedious, but way cleaner than what we used to do... */
-	memset(values, 0, sizeof(values));
-	memset(nulls, false, sizeof(nulls));
-
-	values[Anum_pg_class_oid - 1] = ObjectIdGetDatum(new_rel_oid);
-	values[Anum_pg_class_relname - 1] = NameGetDatum(&rd_rel->relname);
-	values[Anum_pg_class_relnamespace - 1] = ObjectIdGetDatum(rd_rel->relnamespace);
-	values[Anum_pg_class_reltype - 1] = ObjectIdGetDatum(rd_rel->reltype);
-	values[Anum_pg_class_reloftype - 1] = ObjectIdGetDatum(rd_rel->reloftype);
-	values[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(rd_rel->relowner);
-	values[Anum_pg_class_relam - 1] = ObjectIdGetDatum(rd_rel->relam);
-	values[Anum_pg_class_relfilenode - 1] = ObjectIdGetDatum(rd_rel->relfilenode);
-	values[Anum_pg_class_reltablespace - 1] = ObjectIdGetDatum(rd_rel->reltablespace);
-	values[Anum_pg_class_relpages - 1] = Int32GetDatum(rd_rel->relpages);
-	values[Anum_pg_class_reltuples - 1] = Float4GetDatum(rd_rel->reltuples);
-	values[Anum_pg_class_relallvisible - 1] = Int32GetDatum(rd_rel->relallvisible);
-	values[Anum_pg_class_relallfrozen - 1] = Int32GetDatum(rd_rel->relallfrozen);
-	values[Anum_pg_class_reltoastrelid - 1] = ObjectIdGetDatum(rd_rel->reltoastrelid);
-	values[Anum_pg_class_relhasindex - 1] = BoolGetDatum(rd_rel->relhasindex);
-	values[Anum_pg_class_relisshared - 1] = BoolGetDatum(rd_rel->relisshared);
-	values[Anum_pg_class_relpersistence - 1] = CharGetDatum(rd_rel->relpersistence);
-	values[Anum_pg_class_relkind - 1] = CharGetDatum(rd_rel->relkind);
-	values[Anum_pg_class_relnatts - 1] = Int16GetDatum(rd_rel->relnatts);
-	values[Anum_pg_class_relchecks - 1] = Int16GetDatum(rd_rel->relchecks);
-	values[Anum_pg_class_relhasrules - 1] = BoolGetDatum(rd_rel->relhasrules);
-	values[Anum_pg_class_relhastriggers - 1] = BoolGetDatum(rd_rel->relhastriggers);
-	values[Anum_pg_class_relrowsecurity - 1] = BoolGetDatum(rd_rel->relrowsecurity);
-	values[Anum_pg_class_relforcerowsecurity - 1] = BoolGetDatum(rd_rel->relforcerowsecurity);
-	values[Anum_pg_class_relhassubclass - 1] = BoolGetDatum(rd_rel->relhassubclass);
-	values[Anum_pg_class_relispopulated - 1] = BoolGetDatum(rd_rel->relispopulated);
-	values[Anum_pg_class_relreplident - 1] = CharGetDatum(rd_rel->relreplident);
-	values[Anum_pg_class_relispartition - 1] = BoolGetDatum(rd_rel->relispartition);
-	values[Anum_pg_class_relrewrite - 1] = ObjectIdGetDatum(rd_rel->relrewrite);
-	values[Anum_pg_class_relfrozenxid - 1] = TransactionIdGetDatum(rd_rel->relfrozenxid);
-	values[Anum_pg_class_relminmxid - 1] = MultiXactIdGetDatum(rd_rel->relminmxid);
+	HeapTupleSetValue(pg_class, oid, ObjectIdGetDatum(new_rel_oid), values);
+	HeapTupleSetValue(pg_class, relname, NameGetDatum(&rd_rel->relname), values);
+	HeapTupleSetValue(pg_class, relnamespace, ObjectIdGetDatum(rd_rel->relnamespace), values);
+	HeapTupleSetValue(pg_class, reltype, ObjectIdGetDatum(rd_rel->reltype), values);
+	HeapTupleSetValue(pg_class, reloftype, ObjectIdGetDatum(rd_rel->reloftype), values);
+	HeapTupleSetValue(pg_class, relowner, ObjectIdGetDatum(rd_rel->relowner), values);
+	HeapTupleSetValue(pg_class, relam, ObjectIdGetDatum(rd_rel->relam), values);
+	HeapTupleSetValue(pg_class, relfilenode, ObjectIdGetDatum(rd_rel->relfilenode), values);
+	HeapTupleSetValue(pg_class, reltablespace, ObjectIdGetDatum(rd_rel->reltablespace), values);
+	HeapTupleSetValue(pg_class, relpages, Int32GetDatum(rd_rel->relpages), values);
+	HeapTupleSetValue(pg_class, reltuples, Float4GetDatum(rd_rel->reltuples), values);
+	HeapTupleSetValue(pg_class, relallvisible, Int32GetDatum(rd_rel->relallvisible), values);
+	HeapTupleSetValue(pg_class, relallfrozen, Int32GetDatum(rd_rel->relallfrozen), values);
+	HeapTupleSetValue(pg_class, reltoastrelid, ObjectIdGetDatum(rd_rel->reltoastrelid), values);
+	HeapTupleSetValue(pg_class, relhasindex, BoolGetDatum(rd_rel->relhasindex), values);
+	HeapTupleSetValue(pg_class, relisshared, BoolGetDatum(rd_rel->relisshared), values);
+	HeapTupleSetValue(pg_class, relpersistence, CharGetDatum(rd_rel->relpersistence), values);
+	HeapTupleSetValue(pg_class, relkind, CharGetDatum(rd_rel->relkind), values);
+	HeapTupleSetValue(pg_class, relnatts, Int16GetDatum(rd_rel->relnatts), values);
+	HeapTupleSetValue(pg_class, relchecks, Int16GetDatum(rd_rel->relchecks), values);
+	HeapTupleSetValue(pg_class, relhasrules, BoolGetDatum(rd_rel->relhasrules), values);
+	HeapTupleSetValue(pg_class, relhastriggers, BoolGetDatum(rd_rel->relhastriggers), values);
+	HeapTupleSetValue(pg_class, relrowsecurity, BoolGetDatum(rd_rel->relrowsecurity), values);
+	HeapTupleSetValue(pg_class, relforcerowsecurity, BoolGetDatum(rd_rel->relforcerowsecurity), values);
+	HeapTupleSetValue(pg_class, relhassubclass, BoolGetDatum(rd_rel->relhassubclass), values);
+	HeapTupleSetValue(pg_class, relispopulated, BoolGetDatum(rd_rel->relispopulated), values);
+	HeapTupleSetValue(pg_class, relreplident, CharGetDatum(rd_rel->relreplident), values);
+	HeapTupleSetValue(pg_class, relispartition, BoolGetDatum(rd_rel->relispartition), values);
+	HeapTupleSetValue(pg_class, relrewrite, ObjectIdGetDatum(rd_rel->relrewrite), values);
+	HeapTupleSetValue(pg_class, relfrozenxid, TransactionIdGetDatum(rd_rel->relfrozenxid), values);
+	HeapTupleSetValue(pg_class, relminmxid, MultiXactIdGetDatum(rd_rel->relminmxid), values);
 	if (relacl != (Datum) 0)
-		values[Anum_pg_class_relacl - 1] = relacl;
+		HeapTupleSetValue(pg_class, relacl, relacl, values);
 	else
-		nulls[Anum_pg_class_relacl - 1] = true;
+		HeapTupleSetValueNull(pg_class, relacl, values, nulls);
 	if (reloptions != (Datum) 0)
-		values[Anum_pg_class_reloptions - 1] = reloptions;
+		HeapTupleSetValue(pg_class, reloptions, reloptions, values);
 	else
-		nulls[Anum_pg_class_reloptions - 1] = true;
+		HeapTupleSetValueNull(pg_class, reloptions, values, nulls);
 
 	/* relpartbound is set by updating this tuple, if necessary */
-	nulls[Anum_pg_class_relpartbound - 1] = true;
+	HeapTupleSetValueNull(pg_class, relpartbound, values, nulls);
 
 	tup = heap_form_tuple(RelationGetDescr(pg_class_desc), values, nulls);
 
 	/* finally insert the new tuple, update the indexes, and clean up */
-	CatalogTupleInsert(pg_class_desc, tup);
+	CatalogTupleInsert(pg_class_desc, tup, NULL);
 
 	heap_freetuple(tup);
 }
@@ -1687,9 +1688,8 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
 	HeapTuple	tuple;
 	Form_pg_attribute attStruct;
 	char		newattname[NAMEDATALEN];
-	Datum		valuesAtt[Natts_pg_attribute] = {0};
-	bool		nullsAtt[Natts_pg_attribute] = {0};
-	bool		replacesAtt[Natts_pg_attribute] = {0};
+	bool		nulls[Natts_pg_attribute] = {false};
+	Bitmapset  *updated = NULL;
 
 	/*
 	 * Grab an exclusive lock on the target table, which we will NOT release
@@ -1710,7 +1710,7 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
 	attStruct = (Form_pg_attribute) GETSTRUCT(tuple);
 
 	/* Mark the attribute as dropped */
-	attStruct->attisdropped = true;
+	HeapTupleUpdateField(pg_attribute, attisdropped, true, attStruct, updated);
 
 	/*
 	 * Set the type OID to invalid.  A dropped attribute's type link cannot be
@@ -1720,13 +1720,13 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
 	 * the attribute's attlen and attalign.  We set atttypid to zero here as a
 	 * means of catching code that incorrectly expects it to be valid.
 	 */
-	attStruct->atttypid = InvalidOid;
+	HeapTupleUpdateField(pg_attribute, atttypid, InvalidOid, attStruct, updated);
 
 	/* Remove any not-null constraint the column may have */
-	attStruct->attnotnull = false;
+	HeapTupleUpdateField(pg_attribute, attnotnull, false, attStruct, updated);
 
 	/* Unset this so no one tries to look up the generation expression */
-	attStruct->attgenerated = '\0';
+	HeapTupleUpdateField(pg_attribute, attgenerated, '\0', attStruct, updated);
 
 	/*
 	 * Change the column name to something that isn't likely to conflict
@@ -1734,29 +1734,33 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
 	snprintf(newattname, sizeof(newattname),
 			 "........pg.dropped.%d........", attnum);
 	namestrcpy(&(attStruct->attname), newattname);
+	HeapTupleMarkColumnUpdated(pg_attribute, attname, updated);
 
 	/* Clear the missing value */
-	attStruct->atthasmissing = false;
-	nullsAtt[Anum_pg_attribute_attmissingval - 1] = true;
-	replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
+	HeapTupleUpdateField(pg_attribute, atthasmissing, false, attStruct, updated);
+	HeapTupleSetFieldNull(pg_attribute, attmissingval, nulls);
+	HeapTupleMarkColumnUpdated(pg_attribute, attmissingval, updated);
 
 	/*
 	 * Clear the other nullable fields.  This saves some space in pg_attribute
 	 * and removes no longer useful information.
 	 */
-	nullsAtt[Anum_pg_attribute_attstattarget - 1] = true;
-	replacesAtt[Anum_pg_attribute_attstattarget - 1] = true;
-	nullsAtt[Anum_pg_attribute_attacl - 1] = true;
-	replacesAtt[Anum_pg_attribute_attacl - 1] = true;
-	nullsAtt[Anum_pg_attribute_attoptions - 1] = true;
-	replacesAtt[Anum_pg_attribute_attoptions - 1] = true;
-	nullsAtt[Anum_pg_attribute_attfdwoptions - 1] = true;
-	replacesAtt[Anum_pg_attribute_attfdwoptions - 1] = true;
+	HeapTupleSetFieldNull(pg_attribute, attstattarget, nulls);
+	HeapTupleMarkColumnUpdated(pg_attribute, attstattarget, updated);
 
-	tuple = heap_modify_tuple(tuple, RelationGetDescr(attr_rel),
-							  valuesAtt, nullsAtt, replacesAtt);
+	HeapTupleSetFieldNull(pg_attribute, attacl, nulls);
+	HeapTupleMarkColumnUpdated(pg_attribute, attacl, updated);
 
-	CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
+	HeapTupleSetFieldNull(pg_attribute, attoptions, nulls);
+	HeapTupleMarkColumnUpdated(pg_attribute, attoptions, updated);
+
+	HeapTupleSetFieldNull(pg_attribute, attfdwoptions, nulls);
+	HeapTupleMarkColumnUpdated(pg_attribute, attfdwoptions, updated);
+
+	tuple = heap_update_tuple(tuple, RelationGetDescr(attr_rel),
+							  NULL, nulls, updated);
+
+	CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple, updated, NULL);
 
 	/*
 	 * Because updating the pg_attribute row will trigger a relcache flush for
@@ -1765,10 +1769,9 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
 	 */
 
 	table_close(attr_rel, RowExclusiveLock);
-
 	RemoveStatistics(relid, attnum);
-
 	relation_close(rel, NoLock);
+	bms_free(updated);
 }
 
 /*
@@ -1967,23 +1970,15 @@ RelationClearMissing(Relation rel)
 	Oid			relid = RelationGetRelid(rel);
 	int			natts = RelationGetNumberOfAttributes(rel);
 	int			attnum;
-	Datum		repl_val[Natts_pg_attribute];
-	bool		repl_null[Natts_pg_attribute];
-	bool		repl_repl[Natts_pg_attribute];
+	Datum		values[Natts_pg_attribute] = {0};
+	bool		nulls[Natts_pg_attribute] = {false};
+	Bitmapset  *updated = NULL;
 	Form_pg_attribute attrtuple;
 	HeapTuple	tuple,
 				newtuple;
 
-	memset(repl_val, 0, sizeof(repl_val));
-	memset(repl_null, false, sizeof(repl_null));
-	memset(repl_repl, false, sizeof(repl_repl));
-
-	repl_val[Anum_pg_attribute_atthasmissing - 1] = BoolGetDatum(false);
-	repl_null[Anum_pg_attribute_attmissingval - 1] = true;
-
-	repl_repl[Anum_pg_attribute_atthasmissing - 1] = true;
-	repl_repl[Anum_pg_attribute_attmissingval - 1] = true;
-
+	HeapTupleUpdateValue(pg_attribute, atthasmissing, BoolGetDatum(false), values, nulls, updated);
+	HeapTupleUpdateValueNull(pg_attribute, attmissingval, values, nulls, updated);
 
 	/* Get a lock on pg_attribute */
 	attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
@@ -2003,10 +1998,10 @@ RelationClearMissing(Relation rel)
 		/* ignore any where atthasmissing is not true */
 		if (attrtuple->atthasmissing)
 		{
-			newtuple = heap_modify_tuple(tuple, RelationGetDescr(attr_rel),
-										 repl_val, repl_null, repl_repl);
+			newtuple = heap_update_tuple(tuple, RelationGetDescr(attr_rel),
+										 values, nulls, updated);
 
-			CatalogTupleUpdate(attr_rel, &newtuple->t_self, newtuple);
+			CatalogTupleUpdate(attr_rel, &newtuple->t_self, newtuple, updated, NULL);
 
 			heap_freetuple(newtuple);
 		}
@@ -2019,6 +2014,7 @@ RelationClearMissing(Relation rel)
 	 * there's nothing else to do here.
 	 */
 	table_close(attr_rel, RowExclusiveLock);
+	bms_free(updated);
 }
 
 /*
@@ -2029,9 +2025,9 @@ RelationClearMissing(Relation rel)
 void
 StoreAttrMissingVal(Relation rel, AttrNumber attnum, Datum missingval)
 {
-	Datum		valuesAtt[Natts_pg_attribute] = {0};
-	bool		nullsAtt[Natts_pg_attribute] = {0};
-	bool		replacesAtt[Natts_pg_attribute] = {0};
+	Datum		values[Natts_pg_attribute] = {0};
+	bool		nulls[Natts_pg_attribute] = {false};
+	Bitmapset  *updated = NULL;
 	Relation	attrrel;
 	Form_pg_attribute attStruct;
 	HeapTuple	atttup,
@@ -2060,17 +2056,15 @@ StoreAttrMissingVal(Relation rel, AttrNumber attnum, Datum missingval)
 												 attStruct->attalign));
 
 	/* Update the pg_attribute row */
-	valuesAtt[Anum_pg_attribute_atthasmissing - 1] = BoolGetDatum(true);
-	replacesAtt[Anum_pg_attribute_atthasmissing - 1] = true;
-
-	valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
-	replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
+	HeapTupleUpdateValue(pg_attribute, atthasmissing, BoolGetDatum(true), values, nulls, updated);
+	HeapTupleUpdateValue(pg_attribute, attmissingval, missingval, values, nulls, updated);
 
-	newtup = heap_modify_tuple(atttup, RelationGetDescr(attrrel),
-							   valuesAtt, nullsAtt, replacesAtt);
-	CatalogTupleUpdate(attrrel, &newtup->t_self, newtup);
+	newtup = heap_update_tuple(atttup, RelationGetDescr(attrrel),
+							   values, nulls, updated);
+	CatalogTupleUpdate(attrrel, &newtup->t_self, newtup, updated, NULL);
 
 	/* clean up */
+	bms_free(updated);
 	ReleaseSysCache(atttup);
 	table_close(attrrel, RowExclusiveLock);
 }
@@ -2085,9 +2079,9 @@ StoreAttrMissingVal(Relation rel, AttrNumber attnum, Datum missingval)
 void
 SetAttrMissing(Oid relid, char *attname, char *value)
 {
-	Datum		valuesAtt[Natts_pg_attribute] = {0};
-	bool		nullsAtt[Natts_pg_attribute] = {0};
-	bool		replacesAtt[Natts_pg_attribute] = {0};
+	Datum		values[Natts_pg_attribute] = {0};
+	bool		nulls[Natts_pg_attribute] = {false};
+	Bitmapset  *updated = NULL;
 	Datum		missingval;
 	Form_pg_attribute attStruct;
 	Relation	attrrel,
@@ -2120,16 +2114,15 @@ SetAttrMissing(Oid relid, char *attname, char *value)
 								  Int32GetDatum(attStruct->atttypmod));
 
 	/* update the tuple - set atthasmissing and attmissingval */
-	valuesAtt[Anum_pg_attribute_atthasmissing - 1] = BoolGetDatum(true);
-	replacesAtt[Anum_pg_attribute_atthasmissing - 1] = true;
-	valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
-	replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
+	HeapTupleUpdateValue(pg_attribute, atthasmissing, BoolGetDatum(true), values, nulls, updated);
+	HeapTupleUpdateValue(pg_attribute, attmissingval, missingval, values, nulls, updated);
 
-	newtup = heap_modify_tuple(atttup, RelationGetDescr(attrrel),
-							   valuesAtt, nullsAtt, replacesAtt);
-	CatalogTupleUpdate(attrrel, &newtup->t_self, newtup);
+	newtup = heap_update_tuple(atttup, RelationGetDescr(attrrel),
+							   values, nulls, updated);
+	CatalogTupleUpdate(attrrel, &newtup->t_self, newtup, updated, NULL);
 
 	/* clean up */
+	bms_free(updated);
 	ReleaseSysCache(atttup);
 	table_close(attrrel, RowExclusiveLock);
 	table_close(tablerel, AccessExclusiveLock);
@@ -2719,6 +2712,7 @@ MergeWithExistingConstraint(Relation rel, const char *ccname, Node *expr,
 	SysScanDesc conscan;
 	ScanKeyData skey[3];
 	HeapTuple	tup;
+	Bitmapset  *updated = NULL;
 
 	/* Search for a pg_constraint entry with same name and relation */
 	conDesc = table_open(ConstraintRelationId, RowExclusiveLock);
@@ -2834,24 +2828,25 @@ MergeWithExistingConstraint(Relation rel, const char *ccname, Node *expr,
 		 */
 		if (rel->rd_rel->relispartition)
 		{
-			con->coninhcount = 1;
-			con->conislocal = false;
+			HeapTupleUpdateField(pg_constraint, coninhcount, 1, con, updated);
+			HeapTupleUpdateField(pg_constraint, conislocal, false, con, updated);
 		}
 		else
 		{
 			if (is_local)
-				con->conislocal = true;
+				HeapTupleUpdateField(pg_constraint, conislocal, true, con, updated);
 			else if (pg_add_s16_overflow(con->coninhcount, 1,
 										 &con->coninhcount))
 				ereport(ERROR,
 						errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 						errmsg("too many inheritance parents"));
+			HeapTupleUpdateField(pg_constraint, coninhcount, con->coninhcount, con, updated);
 		}
 
 		if (is_no_inherit)
 		{
 			Assert(is_local);
-			con->connoinherit = true;
+			HeapTupleUpdateField(pg_constraint, connoinherit, true, con, updated);
 		}
 
 		/*
@@ -2863,11 +2858,12 @@ MergeWithExistingConstraint(Relation rel, const char *ccname, Node *expr,
 		if (is_enforced && !con->conenforced)
 		{
 			Assert(is_local);
-			con->conenforced = true;
-			con->convalidated = true;
+			HeapTupleUpdateField(pg_constraint, conenforced, true, con, updated);
+			HeapTupleUpdateField(pg_constraint, convalidated, true, con, updated);
 		}
 
-		CatalogTupleUpdate(conDesc, &tup->t_self, tup);
+		CatalogTupleUpdate(conDesc, &tup->t_self, tup, updated, NULL);
+		bms_free(updated);
 	}
 
 	systable_endscan(conscan);
@@ -3148,6 +3144,7 @@ SetRelationNumChecks(Relation rel, int numchecks)
 	Relation	relrel;
 	HeapTuple	reltup;
 	Form_pg_class relStruct;
+	Bitmapset  *updated = NULL;
 
 	relrel = table_open(RelationRelationId, RowExclusiveLock);
 	reltup = SearchSysCacheCopy1(RELOID,
@@ -3159,9 +3156,10 @@ SetRelationNumChecks(Relation rel, int numchecks)
 
 	if (relStruct->relchecks != numchecks)
 	{
-		relStruct->relchecks = numchecks;
+		HeapTupleUpdateField(pg_class, relchecks, numchecks, relStruct, updated);
 
-		CatalogTupleUpdate(relrel, &reltup->t_self, reltup);
+		CatalogTupleUpdate(relrel, &reltup->t_self, reltup, updated, NULL);
+		bms_free(updated);
 	}
 	else
 	{
@@ -3470,7 +3468,7 @@ CopyStatistics(Oid fromrelid, Oid torelid)
 		if (indstate == NULL)
 			indstate = CatalogOpenIndexes(statrel);
 
-		CatalogTupleInsertWithInfo(statrel, tup, indstate);
+		CatalogTupleInsert(statrel, tup, indstate);
 
 		heap_freetuple(tup);
 	}
@@ -3906,8 +3904,8 @@ StorePartitionKey(Relation rel,
 	Datum		partexprDatum;
 	Relation	pg_partitioned_table;
 	HeapTuple	tuple;
-	Datum		values[Natts_pg_partitioned_table];
-	bool		nulls[Natts_pg_partitioned_table] = {0};
+	Datum		values[Natts_pg_partitioned_table] = {0};
+	bool		nulls[Natts_pg_partitioned_table] = {false};
 	ObjectAddress myself;
 	ObjectAddress referenced;
 	ObjectAddresses *addrs;
@@ -3935,20 +3933,20 @@ StorePartitionKey(Relation rel,
 
 	/* Only this can ever be NULL */
 	if (!partexprDatum)
-		nulls[Anum_pg_partitioned_table_partexprs - 1] = true;
+		HeapTupleSetValueNull(pg_partitioned_table, partexprs, values, nulls);
 
-	values[Anum_pg_partitioned_table_partrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
-	values[Anum_pg_partitioned_table_partstrat - 1] = CharGetDatum(strategy);
-	values[Anum_pg_partitioned_table_partnatts - 1] = Int16GetDatum(partnatts);
-	values[Anum_pg_partitioned_table_partdefid - 1] = ObjectIdGetDatum(InvalidOid);
-	values[Anum_pg_partitioned_table_partattrs - 1] = PointerGetDatum(partattrs_vec);
-	values[Anum_pg_partitioned_table_partclass - 1] = PointerGetDatum(partopclass_vec);
-	values[Anum_pg_partitioned_table_partcollation - 1] = PointerGetDatum(partcollation_vec);
-	values[Anum_pg_partitioned_table_partexprs - 1] = partexprDatum;
+	HeapTupleSetValue(pg_partitioned_table, partrelid, ObjectIdGetDatum(RelationGetRelid(rel)), values);
+	HeapTupleSetValue(pg_partitioned_table, partstrat, CharGetDatum(strategy), values);
+	HeapTupleSetValue(pg_partitioned_table, partnatts, Int16GetDatum(partnatts), values);
+	HeapTupleSetValue(pg_partitioned_table, partdefid, ObjectIdGetDatum(InvalidOid), values);
+	HeapTupleSetValue(pg_partitioned_table, partattrs, PointerGetDatum(partattrs_vec), values);
+	HeapTupleSetValue(pg_partitioned_table, partclass, PointerGetDatum(partopclass_vec), values);
+	HeapTupleSetValue(pg_partitioned_table, partcollation, PointerGetDatum(partcollation_vec), values);
+	HeapTupleSetValue(pg_partitioned_table, partexprs, partexprDatum, values);
 
 	tuple = heap_form_tuple(RelationGetDescr(pg_partitioned_table), values, nulls);
 
-	CatalogTupleInsert(pg_partitioned_table, tuple);
+	CatalogTupleInsert(pg_partitioned_table, tuple, NULL);
 	table_close(pg_partitioned_table, RowExclusiveLock);
 
 	/* Mark this relation as dependent on a few things as follows */
@@ -4052,9 +4050,9 @@ StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
 	Relation	classRel;
 	HeapTuple	tuple,
 				newtuple;
-	Datum		new_val[Natts_pg_class];
-	bool		new_null[Natts_pg_class],
-				new_repl[Natts_pg_class];
+	Datum		values[Natts_pg_class] = {0};
+	bool		nulls[Natts_pg_class] = {false};
+	Bitmapset  *updated = NULL;
 	Oid			defaultPartOid;
 
 	/* Update pg_class tuple */
@@ -4065,12 +4063,14 @@ StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
 		elog(ERROR, "cache lookup failed for relation %u",
 			 RelationGetRelid(rel));
 
+
 #ifdef USE_ASSERT_CHECKING
 	{
 		Form_pg_class classForm;
 		bool		isnull;
 
 		classForm = (Form_pg_class) GETSTRUCT(tuple);
+
 		Assert(!classForm->relispartition);
 		(void) SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relpartbound,
 							   &isnull);
@@ -4079,26 +4079,23 @@ StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
 #endif
 
 	/* Fill in relpartbound value */
-	memset(new_val, 0, sizeof(new_val));
-	memset(new_null, false, sizeof(new_null));
-	memset(new_repl, false, sizeof(new_repl));
-	new_val[Anum_pg_class_relpartbound - 1] = CStringGetTextDatum(nodeToString(bound));
-	new_null[Anum_pg_class_relpartbound - 1] = false;
-	new_repl[Anum_pg_class_relpartbound - 1] = true;
-	newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
-								 new_val, new_null, new_repl);
+	HeapTupleUpdateValue(pg_class, relpartbound, CStringGetTextDatum(nodeToString(bound)), values, nulls, updated);
+	newtuple = heap_update_tuple(tuple, RelationGetDescr(classRel),
+								 values, nulls, updated);
+
 	/* Also set the flag */
-	((Form_pg_class) GETSTRUCT(newtuple))->relispartition = true;
+	HeapTupleUpdateField(pg_class, relispartition, true, (Form_pg_class) GETSTRUCT(newtuple), updated);
 
 	/*
 	 * We already checked for no inheritance children, but reset
 	 * relhassubclass in case it was left over.
 	 */
 	if (rel->rd_rel->relkind == RELKIND_RELATION && rel->rd_rel->relhassubclass)
-		((Form_pg_class) GETSTRUCT(newtuple))->relhassubclass = false;
+		HeapTupleUpdateField(pg_class, relhassubclass, false, (Form_pg_class) GETSTRUCT(newtuple), updated);
 
-	CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
+	CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple, updated, NULL);
 	heap_freetuple(newtuple);
+	bms_free(updated);
 	table_close(classRel, RowExclusiveLock);
 
 	/*
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 43de42ce39e..7a3ad712975 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -577,8 +577,8 @@ UpdateIndexRelation(Oid indexoid,
 	int2vector *indoption;
 	Datum		exprsDatum;
 	Datum		predDatum;
-	Datum		values[Natts_pg_index];
-	bool		nulls[Natts_pg_index] = {0};
+	Datum		values[Natts_pg_index] = {0};
+	bool		nulls[Natts_pg_index] = {false};
 	Relation	pg_index;
 	HeapTuple	tuple;
 	int			i;
@@ -632,38 +632,38 @@ UpdateIndexRelation(Oid indexoid,
 	/*
 	 * Build a pg_index tuple
 	 */
-	values[Anum_pg_index_indexrelid - 1] = ObjectIdGetDatum(indexoid);
-	values[Anum_pg_index_indrelid - 1] = ObjectIdGetDatum(heapoid);
-	values[Anum_pg_index_indnatts - 1] = Int16GetDatum(indexInfo->ii_NumIndexAttrs);
-	values[Anum_pg_index_indnkeyatts - 1] = Int16GetDatum(indexInfo->ii_NumIndexKeyAttrs);
-	values[Anum_pg_index_indisunique - 1] = BoolGetDatum(indexInfo->ii_Unique);
-	values[Anum_pg_index_indnullsnotdistinct - 1] = BoolGetDatum(indexInfo->ii_NullsNotDistinct);
-	values[Anum_pg_index_indisprimary - 1] = BoolGetDatum(primary);
-	values[Anum_pg_index_indisexclusion - 1] = BoolGetDatum(isexclusion);
-	values[Anum_pg_index_indimmediate - 1] = BoolGetDatum(immediate);
-	values[Anum_pg_index_indisclustered - 1] = BoolGetDatum(false);
-	values[Anum_pg_index_indisvalid - 1] = BoolGetDatum(isvalid);
-	values[Anum_pg_index_indcheckxmin - 1] = BoolGetDatum(false);
-	values[Anum_pg_index_indisready - 1] = BoolGetDatum(isready);
-	values[Anum_pg_index_indislive - 1] = BoolGetDatum(true);
-	values[Anum_pg_index_indisreplident - 1] = BoolGetDatum(false);
-	values[Anum_pg_index_indkey - 1] = PointerGetDatum(indkey);
-	values[Anum_pg_index_indcollation - 1] = PointerGetDatum(indcollation);
-	values[Anum_pg_index_indclass - 1] = PointerGetDatum(indclass);
-	values[Anum_pg_index_indoption - 1] = PointerGetDatum(indoption);
-	values[Anum_pg_index_indexprs - 1] = exprsDatum;
+	HeapTupleSetValue(pg_index, indexrelid, ObjectIdGetDatum(indexoid), values);
+	HeapTupleSetValue(pg_index, indrelid, ObjectIdGetDatum(heapoid), values);
+	HeapTupleSetValue(pg_index, indnatts, Int16GetDatum(indexInfo->ii_NumIndexAttrs), values);
+	HeapTupleSetValue(pg_index, indnkeyatts, Int16GetDatum(indexInfo->ii_NumIndexKeyAttrs), values);
+	HeapTupleSetValue(pg_index, indisunique, BoolGetDatum(indexInfo->ii_Unique), values);
+	HeapTupleSetValue(pg_index, indnullsnotdistinct, BoolGetDatum(indexInfo->ii_NullsNotDistinct), values);
+	HeapTupleSetValue(pg_index, indisprimary, BoolGetDatum(primary), values);
+	HeapTupleSetValue(pg_index, indisexclusion, BoolGetDatum(isexclusion), values);
+	HeapTupleSetValue(pg_index, indimmediate, BoolGetDatum(immediate), values);
+	HeapTupleSetValue(pg_index, indisclustered, BoolGetDatum(false), values);
+	HeapTupleSetValue(pg_index, indisvalid, BoolGetDatum(isvalid), values);
+	HeapTupleSetValue(pg_index, indcheckxmin, BoolGetDatum(false), values);
+	HeapTupleSetValue(pg_index, indisready, BoolGetDatum(isready), values);
+	HeapTupleSetValue(pg_index, indislive, BoolGetDatum(true), values);
+	HeapTupleSetValue(pg_index, indisreplident, BoolGetDatum(false), values);
+	HeapTupleSetValue(pg_index, indkey, PointerGetDatum(indkey), values);
+	HeapTupleSetValue(pg_index, indcollation, PointerGetDatum(indcollation), values);
+	HeapTupleSetValue(pg_index, indclass, PointerGetDatum(indclass), values);
+	HeapTupleSetValue(pg_index, indoption, PointerGetDatum(indoption), values);
+	HeapTupleSetValue(pg_index, indexprs, exprsDatum, values);
 	if (exprsDatum == (Datum) 0)
-		nulls[Anum_pg_index_indexprs - 1] = true;
-	values[Anum_pg_index_indpred - 1] = predDatum;
+		HeapTupleSetValueNull(pg_index, indexprs, values, nulls);
+	HeapTupleSetValue(pg_index, indpred, predDatum, values);
 	if (predDatum == (Datum) 0)
-		nulls[Anum_pg_index_indpred - 1] = true;
+		HeapTupleSetValueNull(pg_index, indpred, values, nulls);
 
 	tuple = heap_form_tuple(RelationGetDescr(pg_index), values, nulls);
 
 	/*
 	 * insert the tuple into the pg_index catalog
 	 */
-	CatalogTupleInsert(pg_index, tuple);
+	CatalogTupleInsert(pg_index, tuple, NULL);
 
 	/*
 	 * close the relation and free the tuple
@@ -1567,6 +1567,8 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName)
 	Oid			indexConstraintOid;
 	List	   *constraintOids = NIL;
 	ListCell   *lc;
+	Bitmapset  *oldUpdated = NULL;
+	Bitmapset  *newUpdated = NULL;
 
 	/*
 	 * Take a necessary lock on the old and new index before swapping them.
@@ -1592,14 +1594,21 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName)
 	/* Swap the names */
 	namestrcpy(&newClassForm->relname, NameStr(oldClassForm->relname));
 	namestrcpy(&oldClassForm->relname, oldName);
+	HeapTupleUpdateField(pg_class, relname, newClassForm->relname, newClassForm, newUpdated);
+	HeapTupleUpdateField(pg_class, relname, oldClassForm->relname, oldClassForm, oldUpdated);
 
 	/* Swap the partition flags to track inheritance properly */
 	isPartition = newClassForm->relispartition;
-	newClassForm->relispartition = oldClassForm->relispartition;
-	oldClassForm->relispartition = isPartition;
+	HeapTupleUpdateField(pg_class, relispartition, oldClassForm->relispartition, newClassForm, newUpdated);
+	HeapTupleUpdateField(pg_class, relispartition, isPartition, oldClassForm, oldUpdated);
 
-	CatalogTupleUpdate(pg_class, &oldClassTuple->t_self, oldClassTuple);
-	CatalogTupleUpdate(pg_class, &newClassTuple->t_self, newClassTuple);
+	CatalogTupleUpdate(pg_class, &oldClassTuple->t_self, oldClassTuple, oldUpdated, NULL);
+	CatalogTupleUpdate(pg_class, &newClassTuple->t_self, newClassTuple, newUpdated, NULL);
+
+	bms_free(oldUpdated);
+	oldUpdated = NULL;
+	bms_free(newUpdated);
+	newUpdated = NULL;
 
 	heap_freetuple(oldClassTuple);
 	heap_freetuple(newClassTuple);
@@ -1623,34 +1632,39 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName)
 	 * Copy constraint flags from the old index. This is safe because the old
 	 * index guaranteed uniqueness.
 	 */
-	newIndexForm->indisprimary = oldIndexForm->indisprimary;
-	oldIndexForm->indisprimary = false;
-	newIndexForm->indisexclusion = oldIndexForm->indisexclusion;
-	oldIndexForm->indisexclusion = false;
-	newIndexForm->indimmediate = oldIndexForm->indimmediate;
-	oldIndexForm->indimmediate = true;
+	HeapTupleUpdateField(pg_index, indisprimary, oldIndexForm->indisprimary, newIndexForm, newUpdated);
+	HeapTupleUpdateField(pg_index, indisprimary, false, oldIndexForm, oldUpdated);
+	HeapTupleUpdateField(pg_index, indisexclusion, oldIndexForm->indisexclusion, newIndexForm, newUpdated);
+	HeapTupleUpdateField(pg_index, indisexclusion, false, oldIndexForm, oldUpdated);
+	HeapTupleUpdateField(pg_index, indimmediate, oldIndexForm->indimmediate, newIndexForm, newUpdated);
+	HeapTupleUpdateField(pg_index, indimmediate, true, oldIndexForm, oldUpdated);
 
 	/* Preserve indisreplident in the new index */
-	newIndexForm->indisreplident = oldIndexForm->indisreplident;
+	HeapTupleUpdateField(pg_index, indisreplident, oldIndexForm->indisreplident, newIndexForm, newUpdated);
 
 	/* Preserve indisclustered in the new index */
-	newIndexForm->indisclustered = oldIndexForm->indisclustered;
+	HeapTupleUpdateField(pg_index, indisclustered, oldIndexForm->indisclustered, newIndexForm, newUpdated);
 
 	/*
 	 * Mark the new index as valid, and the old index as invalid similarly to
 	 * what index_set_state_flags() does.
 	 */
-	newIndexForm->indisvalid = true;
-	oldIndexForm->indisvalid = false;
-	oldIndexForm->indisclustered = false;
-	oldIndexForm->indisreplident = false;
+	HeapTupleUpdateField(pg_index, indisvalid, true, newIndexForm, newUpdated);
+	HeapTupleUpdateField(pg_index, indisvalid, false, oldIndexForm, oldUpdated);
+	HeapTupleUpdateField(pg_index, indisclustered, false, oldIndexForm, oldUpdated);
+	HeapTupleUpdateField(pg_index, indisreplident, false, oldIndexForm, oldUpdated);
 
-	CatalogTupleUpdate(pg_index, &oldIndexTuple->t_self, oldIndexTuple);
-	CatalogTupleUpdate(pg_index, &newIndexTuple->t_self, newIndexTuple);
+	CatalogTupleUpdate(pg_index, &oldIndexTuple->t_self, oldIndexTuple, oldUpdated, NULL);
+	CatalogTupleUpdate(pg_index, &newIndexTuple->t_self, newIndexTuple, newUpdated, NULL);
 
 	heap_freetuple(oldIndexTuple);
 	heap_freetuple(newIndexTuple);
 
+	bms_free(oldUpdated);
+	oldUpdated = NULL;
+	bms_free(newUpdated);
+	newUpdated = NULL;
+
 	/*
 	 * Move constraints and triggers over to the new index
 	 */
@@ -1684,9 +1698,11 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName)
 
 		if (conForm->conindid == oldIndexId)
 		{
-			conForm->conindid = newIndexId;
+			Bitmapset  *updated = NULL;
 
-			CatalogTupleUpdate(pg_constraint, &constraintTuple->t_self, constraintTuple);
+			HeapTupleUpdateField(pg_constraint, conindid, newIndexId, conForm, updated);
+			CatalogTupleUpdate(pg_constraint, &constraintTuple->t_self, constraintTuple, updated, NULL);
+			bms_free(updated);
 		}
 
 		heap_freetuple(constraintTuple);
@@ -1702,6 +1718,8 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName)
 
 		while (HeapTupleIsValid((triggerTuple = systable_getnext(scan))))
 		{
+			Bitmapset  *updated = NULL;
+
 			Form_pg_trigger tgForm = (Form_pg_trigger) GETSTRUCT(triggerTuple);
 
 			if (tgForm->tgconstrindid != oldIndexId)
@@ -1711,10 +1729,9 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName)
 			triggerTuple = heap_copytuple(triggerTuple);
 			tgForm = (Form_pg_trigger) GETSTRUCT(triggerTuple);
 
-			tgForm->tgconstrindid = newIndexId;
-
-			CatalogTupleUpdate(pg_trigger, &triggerTuple->t_self, triggerTuple);
-
+			HeapTupleUpdateField(pg_trigger, tgconstrindid, newIndexId, tgForm, updated);
+			CatalogTupleUpdate(pg_trigger, &triggerTuple->t_self, triggerTuple, updated, NULL);
+			bms_free(updated);
 			heap_freetuple(triggerTuple);
 		}
 
@@ -1730,11 +1747,10 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName)
 		SysScanDesc sd;
 		HeapTuple	tuple;
 		Datum		values[Natts_pg_description] = {0};
-		bool		nulls[Natts_pg_description] = {0};
-		bool		replaces[Natts_pg_description] = {0};
+		bool		nulls[Natts_pg_description] = {false};
+		Bitmapset  *updated = NULL;
 
-		values[Anum_pg_description_objoid - 1] = ObjectIdGetDatum(newIndexId);
-		replaces[Anum_pg_description_objoid - 1] = true;
+		HeapTupleUpdateValue(pg_description, objoid, ObjectIdGetDatum(newIndexId), values, nulls, updated);
 
 		ScanKeyInit(&skey[0],
 					Anum_pg_description_objoid,
@@ -1756,15 +1772,16 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName)
 
 		while ((tuple = systable_getnext(sd)) != NULL)
 		{
-			tuple = heap_modify_tuple(tuple, RelationGetDescr(description),
-									  values, nulls, replaces);
-			CatalogTupleUpdate(description, &tuple->t_self, tuple);
+			tuple = heap_update_tuple(tuple, RelationGetDescr(description),
+									  values, nulls, updated);
+			CatalogTupleUpdate(description, &tuple->t_self, tuple, updated, NULL);
 
 			break;				/* Assume there can be only one match */
 		}
 
 		systable_endscan(sd);
 		table_close(description, NoLock);
+		bms_free(updated);
 	}
 
 	/*
@@ -2059,6 +2076,7 @@ index_constraint_create(Relation heapRelation,
 		Form_pg_index indexForm;
 		bool		dirty = false;
 		bool		marked_as_primary = false;
+		Bitmapset  *updated = NULL;
 
 		pg_index = table_open(IndexRelationId, RowExclusiveLock);
 
@@ -2070,20 +2088,20 @@ index_constraint_create(Relation heapRelation,
 
 		if (mark_as_primary && !indexForm->indisprimary)
 		{
-			indexForm->indisprimary = true;
+			HeapTupleUpdateField(pg_index, indisprimary, true, indexForm, updated);
 			dirty = true;
 			marked_as_primary = true;
 		}
 
 		if (deferrable && indexForm->indimmediate)
 		{
-			indexForm->indimmediate = false;
+			HeapTupleUpdateField(pg_index, indimmediate, false, indexForm, updated);
 			dirty = true;
 		}
 
 		if (dirty)
 		{
-			CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple);
+			CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple, updated, NULL);
 
 			/*
 			 * When we mark an existing index as primary, force a relcache
@@ -2100,6 +2118,7 @@ index_constraint_create(Relation heapRelation,
 
 		heap_freetuple(indexTuple);
 		table_close(pg_index, RowExclusiveLock);
+		bms_free(updated);
 	}
 
 	return myself;
@@ -3128,6 +3147,7 @@ index_build(Relation heapRelation,
 		Relation	pg_index;
 		HeapTuple	indexTuple;
 		Form_pg_index indexForm;
+		Bitmapset  *updated = NULL;
 
 		pg_index = table_open(IndexRelationId, RowExclusiveLock);
 
@@ -3140,11 +3160,12 @@ index_build(Relation heapRelation,
 		/* If it's a new index, indcheckxmin shouldn't be set ... */
 		Assert(!indexForm->indcheckxmin);
 
-		indexForm->indcheckxmin = true;
-		CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple);
+		HeapTupleUpdateField(pg_index, indcheckxmin, true, indexForm, updated);
+		CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple, updated, NULL);
 
 		heap_freetuple(indexTuple);
 		table_close(pg_index, RowExclusiveLock);
+		bms_free(updated);
 	}
 
 	/*
@@ -3503,6 +3524,7 @@ index_set_state_flags(Oid indexId, IndexStateFlagsAction action)
 	Relation	pg_index;
 	HeapTuple	indexTuple;
 	Form_pg_index indexForm;
+	Bitmapset  *updated = NULL;
 
 	/* Open pg_index and fetch a writable copy of the index's tuple */
 	pg_index = table_open(IndexRelationId, RowExclusiveLock);
@@ -3521,14 +3543,14 @@ index_set_state_flags(Oid indexId, IndexStateFlagsAction action)
 			Assert(indexForm->indislive);
 			Assert(!indexForm->indisready);
 			Assert(!indexForm->indisvalid);
-			indexForm->indisready = true;
+			HeapTupleUpdateField(pg_index, indisready, true, indexForm, updated);
 			break;
 		case INDEX_CREATE_SET_VALID:
 			/* Set indisvalid during a CREATE INDEX CONCURRENTLY sequence */
 			Assert(indexForm->indislive);
 			Assert(indexForm->indisready);
 			Assert(!indexForm->indisvalid);
-			indexForm->indisvalid = true;
+			HeapTupleUpdateField(pg_index, indisvalid, true, indexForm, updated);
 			break;
 		case INDEX_DROP_CLEAR_VALID:
 
@@ -3545,9 +3567,9 @@ index_set_state_flags(Oid indexId, IndexStateFlagsAction action)
 			 * set on any invalid index, so clear that flag too.  For
 			 * cleanliness, also clear indisreplident.
 			 */
-			indexForm->indisvalid = false;
-			indexForm->indisclustered = false;
-			indexForm->indisreplident = false;
+			HeapTupleUpdateField(pg_index, indisvalid, false, indexForm, updated);
+			HeapTupleUpdateField(pg_index, indisclustered, false, indexForm, updated);
+			HeapTupleUpdateField(pg_index, indisreplident, false, indexForm, updated);
 			break;
 		case INDEX_DROP_SET_DEAD:
 
@@ -3561,15 +3583,16 @@ index_set_state_flags(Oid indexId, IndexStateFlagsAction action)
 			Assert(!indexForm->indisvalid);
 			Assert(!indexForm->indisclustered);
 			Assert(!indexForm->indisreplident);
-			indexForm->indisready = false;
-			indexForm->indislive = false;
+			HeapTupleUpdateField(pg_index, indisready, false, indexForm, updated);
+			HeapTupleUpdateField(pg_index, indislive, false, indexForm, updated);
 			break;
 	}
 
 	/* ... and update it */
-	CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple);
+	CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple, updated, NULL);
 
 	table_close(pg_index, RowExclusiveLock);
+	bms_free(updated);
 }
 
 
@@ -3848,6 +3871,7 @@ reindex_index(const ReindexStmt *stmt, Oid indexId,
 		HeapTuple	indexTuple;
 		Form_pg_index indexForm;
 		bool		index_bad;
+		Bitmapset  *updated = NULL;
 
 		pg_index = table_open(IndexRelationId, RowExclusiveLock);
 
@@ -3864,13 +3888,13 @@ reindex_index(const ReindexStmt *stmt, Oid indexId,
 			(indexForm->indcheckxmin && !indexInfo->ii_BrokenHotChain))
 		{
 			if (!indexInfo->ii_BrokenHotChain)
-				indexForm->indcheckxmin = false;
+				HeapTupleUpdateField(pg_index, indcheckxmin, false, indexForm, updated);
 			else if (index_bad)
-				indexForm->indcheckxmin = true;
-			indexForm->indisvalid = true;
-			indexForm->indisready = true;
-			indexForm->indislive = true;
-			CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple);
+				HeapTupleUpdateField(pg_index, indcheckxmin, true, indexForm, updated);
+			HeapTupleUpdateField(pg_index, indisvalid, true, indexForm, updated);
+			HeapTupleUpdateField(pg_index, indisready, true, indexForm, updated);
+			HeapTupleUpdateField(pg_index, indislive, true, indexForm, updated);
+			CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple, updated, NULL);
 
 			/*
 			 * Invalidate the relcache for the table, so that after we commit
@@ -3883,6 +3907,7 @@ reindex_index(const ReindexStmt *stmt, Oid indexId,
 		}
 
 		table_close(pg_index, RowExclusiveLock);
+		bms_free(updated);
 	}
 
 	/* Log what we did */
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 28f3cade6ff..3e6cc90af9e 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -342,6 +342,7 @@ update_default_partition_oid(Oid parentId, Oid defaultPartId)
 	HeapTuple	tuple;
 	Relation	pg_partitioned_table;
 	Form_pg_partitioned_table part_table_form;
+	Bitmapset  *updated = NULL;
 
 	pg_partitioned_table = table_open(PartitionedRelationId, RowExclusiveLock);
 
@@ -353,10 +354,12 @@ update_default_partition_oid(Oid parentId, Oid defaultPartId)
 
 	part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
 	part_table_form->partdefid = defaultPartId;
-	CatalogTupleUpdate(pg_partitioned_table, &tuple->t_self, tuple);
+	HeapTupleMarkColumnUpdated(pg_partitioned_table, partdefid, updated);
+	CatalogTupleUpdate(pg_partitioned_table, &tuple->t_self, tuple, updated, NULL);
 
 	heap_freetuple(tuple);
 	table_close(pg_partitioned_table, RowExclusiveLock);
+	bms_free(updated);
 }
 
 /*
diff --git a/src/backend/catalog/pg_attrdef.c b/src/backend/catalog/pg_attrdef.c
index 24815090d3d..8232ddd188c 100644
--- a/src/backend/catalog/pg_attrdef.c
+++ b/src/backend/catalog/pg_attrdef.c
@@ -40,14 +40,12 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
 	char	   *adbin;
 	Relation	adrel;
 	HeapTuple	tuple;
-	Datum		values[Natts_pg_attrdef];
-	static bool nulls[Natts_pg_attrdef] = {false, false, false, false};
+	Datum		values[Natts_pg_attrdef] = {0};
+	bool		nulls[Natts_pg_attrdef] = {false};
 	Relation	attrrel;
 	HeapTuple	atttup;
 	Form_pg_attribute attStruct;
-	Datum		valuesAtt[Natts_pg_attribute] = {0};
-	bool		nullsAtt[Natts_pg_attribute] = {0};
-	bool		replacesAtt[Natts_pg_attribute] = {0};
+	Bitmapset  *updated = NULL;
 	char		attgenerated;
 	Oid			attrdefOid;
 	ObjectAddress colobject,
@@ -65,13 +63,13 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
 	 */
 	attrdefOid = GetNewOidWithIndex(adrel, AttrDefaultOidIndexId,
 									Anum_pg_attrdef_oid);
-	values[Anum_pg_attrdef_oid - 1] = ObjectIdGetDatum(attrdefOid);
-	values[Anum_pg_attrdef_adrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
-	values[Anum_pg_attrdef_adnum - 1] = Int16GetDatum(attnum);
-	values[Anum_pg_attrdef_adbin - 1] = CStringGetTextDatum(adbin);
+	HeapTupleSetValue(pg_attrdef, oid, ObjectIdGetDatum(attrdefOid), values);
+	HeapTupleSetValue(pg_attrdef, adrelid, ObjectIdGetDatum(RelationGetRelid(rel)), values);
+	HeapTupleSetValue(pg_attrdef, adnum, Int16GetDatum(attnum), values);
+	HeapTupleSetValue(pg_attrdef, adbin, CStringGetTextDatum(adbin), values);
 
 	tuple = heap_form_tuple(adrel->rd_att, values, nulls);
-	CatalogTupleInsert(adrel, tuple);
+	CatalogTupleInsert(adrel, tuple, NULL);
 
 	defobject.classId = AttrDefaultRelationId;
 	defobject.objectId = attrdefOid;
@@ -80,7 +78,7 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
 	table_close(adrel, RowExclusiveLock);
 
 	/* now can free some of the stuff allocated above */
-	pfree(DatumGetPointer(values[Anum_pg_attrdef_adbin - 1]));
+	pfree(DatumGetPointer(HeapTupleValue(pg_attrdef, adbin, values)));
 	heap_freetuple(tuple);
 	pfree(adbin);
 
@@ -92,22 +90,21 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
 	atttup = SearchSysCacheCopy2(ATTNUM,
 								 ObjectIdGetDatum(RelationGetRelid(rel)),
 								 Int16GetDatum(attnum));
+
 	if (!HeapTupleIsValid(atttup))
 		elog(ERROR, "cache lookup failed for attribute %d of relation %u",
 			 attnum, RelationGetRelid(rel));
+
 	attStruct = (Form_pg_attribute) GETSTRUCT(atttup);
 	attgenerated = attStruct->attgenerated;
 
-	valuesAtt[Anum_pg_attribute_atthasdef - 1] = BoolGetDatum(true);
-	replacesAtt[Anum_pg_attribute_atthasdef - 1] = true;
-
-	atttup = heap_modify_tuple(atttup, RelationGetDescr(attrrel),
-							   valuesAtt, nullsAtt, replacesAtt);
-
-	CatalogTupleUpdate(attrrel, &atttup->t_self, atttup);
+	Assert(bms_is_empty(updated));
+	HeapTupleUpdateField(pg_attribute, atthasdef, BoolGetDatum(true), attStruct, updated);
+	CatalogTupleUpdate(attrrel, &atttup->t_self, atttup, updated, NULL);
 
 	table_close(attrrel, RowExclusiveLock);
 	heap_freetuple(atttup);
+	bms_free(updated);
 
 	/*
 	 * Make a dependency so that the pg_attrdef entry goes away if the column
@@ -216,6 +213,7 @@ RemoveAttrDefaultById(Oid attrdefId)
 	HeapTuple	tuple;
 	Oid			myrelid;
 	AttrNumber	myattnum;
+	Bitmapset  *updated = NULL;
 
 	/* Grab an appropriate lock on the pg_attrdef relation */
 	attrdef_rel = table_open(AttrDefaultRelationId, RowExclusiveLock);
@@ -256,8 +254,8 @@ RemoveAttrDefaultById(Oid attrdefId)
 			 myattnum, myrelid);
 
 	((Form_pg_attribute) GETSTRUCT(tuple))->atthasdef = false;
-
-	CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
+	HeapTupleMarkColumnUpdated(pg_attribute, atthasdef, updated);
+	CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple, updated, NULL);
 
 	/*
 	 * Our update of the pg_attribute row will force a relcache rebuild, so
diff --git a/src/backend/catalog/pg_cast.c b/src/backend/catalog/pg_cast.c
index 5119c2acda2..f472f9cb354 100644
--- a/src/backend/catalog/pg_cast.c
+++ b/src/backend/catalog/pg_cast.c
@@ -53,8 +53,8 @@ CastCreate(Oid sourcetypeid, Oid targettypeid,
 	Relation	relation;
 	HeapTuple	tuple;
 	Oid			castid;
-	Datum		values[Natts_pg_cast];
-	bool		nulls[Natts_pg_cast] = {0};
+	Datum		values[Natts_pg_cast] = {0};
+	bool		nulls[Natts_pg_cast] = {false};
 	ObjectAddress myself,
 				referenced;
 	ObjectAddresses *addrs;
@@ -78,16 +78,16 @@ CastCreate(Oid sourcetypeid, Oid targettypeid,
 
 	/* ready to go */
 	castid = GetNewOidWithIndex(relation, CastOidIndexId, Anum_pg_cast_oid);
-	values[Anum_pg_cast_oid - 1] = ObjectIdGetDatum(castid);
-	values[Anum_pg_cast_castsource - 1] = ObjectIdGetDatum(sourcetypeid);
-	values[Anum_pg_cast_casttarget - 1] = ObjectIdGetDatum(targettypeid);
-	values[Anum_pg_cast_castfunc - 1] = ObjectIdGetDatum(funcid);
-	values[Anum_pg_cast_castcontext - 1] = CharGetDatum(castcontext);
-	values[Anum_pg_cast_castmethod - 1] = CharGetDatum(castmethod);
+	HeapTupleSetValue(pg_cast, oid, ObjectIdGetDatum(castid), values);
+	HeapTupleSetValue(pg_cast, castsource, ObjectIdGetDatum(sourcetypeid), values);
+	HeapTupleSetValue(pg_cast, casttarget, ObjectIdGetDatum(targettypeid), values);
+	HeapTupleSetValue(pg_cast, castfunc, ObjectIdGetDatum(funcid), values);
+	HeapTupleSetValue(pg_cast, castcontext, CharGetDatum(castcontext), values);
+	HeapTupleSetValue(pg_cast, castmethod, CharGetDatum(castmethod), values);
 
 	tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
 
-	CatalogTupleInsert(relation, tuple);
+	CatalogTupleInsert(relation, tuple, NULL);
 
 	addrs = new_object_addresses();
 
diff --git a/src/backend/catalog/pg_collation.c b/src/backend/catalog/pg_collation.c
index 26d42c3e51a..33228ee7442 100644
--- a/src/backend/catalog/pg_collation.c
+++ b/src/backend/catalog/pg_collation.c
@@ -54,8 +54,8 @@ CollationCreate(const char *collname, Oid collnamespace,
 	Relation	rel;
 	TupleDesc	tupDesc;
 	HeapTuple	tup;
-	Datum		values[Natts_pg_collation];
-	bool		nulls[Natts_pg_collation];
+	Datum		values[Natts_pg_collation] = {0};
+	bool		nulls[Natts_pg_collation] = {false};
 	NameData	name_name;
 	Oid			oid;
 	ObjectAddress myself,
@@ -175,38 +175,38 @@ CollationCreate(const char *collname, Oid collnamespace,
 	namestrcpy(&name_name, collname);
 	oid = GetNewOidWithIndex(rel, CollationOidIndexId,
 							 Anum_pg_collation_oid);
-	values[Anum_pg_collation_oid - 1] = ObjectIdGetDatum(oid);
-	values[Anum_pg_collation_collname - 1] = NameGetDatum(&name_name);
-	values[Anum_pg_collation_collnamespace - 1] = ObjectIdGetDatum(collnamespace);
-	values[Anum_pg_collation_collowner - 1] = ObjectIdGetDatum(collowner);
-	values[Anum_pg_collation_collprovider - 1] = CharGetDatum(collprovider);
-	values[Anum_pg_collation_collisdeterministic - 1] = BoolGetDatum(collisdeterministic);
-	values[Anum_pg_collation_collencoding - 1] = Int32GetDatum(collencoding);
+	HeapTupleSetValue(pg_collation, oid, ObjectIdGetDatum(oid), values);
+	HeapTupleSetValue(pg_collation, collname, NameGetDatum(&name_name), values);
+	HeapTupleSetValue(pg_collation, collnamespace, ObjectIdGetDatum(collnamespace), values);
+	HeapTupleSetValue(pg_collation, collowner, ObjectIdGetDatum(collowner), values);
+	HeapTupleSetValue(pg_collation, collprovider, CharGetDatum(collprovider), values);
+	HeapTupleSetValue(pg_collation, collisdeterministic, BoolGetDatum(collisdeterministic), values);
+	HeapTupleSetValue(pg_collation, collencoding, Int32GetDatum(collencoding), values);
 	if (collcollate)
-		values[Anum_pg_collation_collcollate - 1] = CStringGetTextDatum(collcollate);
+		HeapTupleSetValue(pg_collation, collcollate, CStringGetTextDatum(collcollate), values);
 	else
-		nulls[Anum_pg_collation_collcollate - 1] = true;
+		HeapTupleSetValueNull(pg_collation, collcollate, values, nulls);
 	if (collctype)
-		values[Anum_pg_collation_collctype - 1] = CStringGetTextDatum(collctype);
+		HeapTupleSetValue(pg_collation, collctype, CStringGetTextDatum(collctype), values);
 	else
-		nulls[Anum_pg_collation_collctype - 1] = true;
+		HeapTupleSetValueNull(pg_collation, collctype, values, nulls);
 	if (colllocale)
-		values[Anum_pg_collation_colllocale - 1] = CStringGetTextDatum(colllocale);
+		HeapTupleSetValue(pg_collation, colllocale, CStringGetTextDatum(colllocale), values);
 	else
-		nulls[Anum_pg_collation_colllocale - 1] = true;
+		HeapTupleSetValueNull(pg_collation, colllocale, values, nulls);
 	if (collicurules)
-		values[Anum_pg_collation_collicurules - 1] = CStringGetTextDatum(collicurules);
+		HeapTupleSetValue(pg_collation, collicurules, CStringGetTextDatum(collicurules), values);
 	else
-		nulls[Anum_pg_collation_collicurules - 1] = true;
+		HeapTupleSetValueNull(pg_collation, collicurules, values, nulls);
 	if (collversion)
-		values[Anum_pg_collation_collversion - 1] = CStringGetTextDatum(collversion);
+		HeapTupleSetValue(pg_collation, collversion, CStringGetTextDatum(collversion), values);
 	else
-		nulls[Anum_pg_collation_collversion - 1] = true;
+		HeapTupleSetValueNull(pg_collation, collversion, values, nulls);
 
 	tup = heap_form_tuple(tupDesc, values, nulls);
 
 	/* insert a new tuple */
-	CatalogTupleInsert(rel, tup);
+	CatalogTupleInsert(rel, tup, NULL);
 	Assert(OidIsValid(oid));
 
 	/* set up dependencies for the new collation */
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 0accc83cb1e..a0147dfddc0 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -45,8 +45,8 @@ ConversionCreate(const char *conname, Oid connamespace,
 	TupleDesc	tupDesc;
 	HeapTuple	tup;
 	Oid			oid;
-	bool		nulls[Natts_pg_conversion];
-	Datum		values[Natts_pg_conversion];
+	Datum		values[Natts_pg_conversion] = {0};
+	bool		nulls[Natts_pg_conversion] = {false};
 	NameData	cname;
 	ObjectAddress myself,
 				referenced;
@@ -94,19 +94,19 @@ ConversionCreate(const char *conname, Oid connamespace,
 	namestrcpy(&cname, conname);
 	oid = GetNewOidWithIndex(rel, ConversionOidIndexId,
 							 Anum_pg_conversion_oid);
-	values[Anum_pg_conversion_oid - 1] = ObjectIdGetDatum(oid);
-	values[Anum_pg_conversion_conname - 1] = NameGetDatum(&cname);
-	values[Anum_pg_conversion_connamespace - 1] = ObjectIdGetDatum(connamespace);
-	values[Anum_pg_conversion_conowner - 1] = ObjectIdGetDatum(conowner);
-	values[Anum_pg_conversion_conforencoding - 1] = Int32GetDatum(conforencoding);
-	values[Anum_pg_conversion_contoencoding - 1] = Int32GetDatum(contoencoding);
-	values[Anum_pg_conversion_conproc - 1] = ObjectIdGetDatum(conproc);
-	values[Anum_pg_conversion_condefault - 1] = BoolGetDatum(def);
+	HeapTupleSetValue(pg_conversion, oid, ObjectIdGetDatum(oid), values);
+	HeapTupleSetValue(pg_conversion, conname, NameGetDatum(&cname), values);
+	HeapTupleSetValue(pg_conversion, connamespace, ObjectIdGetDatum(connamespace), values);
+	HeapTupleSetValue(pg_conversion, conowner, ObjectIdGetDatum(conowner), values);
+	HeapTupleSetValue(pg_conversion, conforencoding, Int32GetDatum(conforencoding), values);
+	HeapTupleSetValue(pg_conversion, contoencoding, Int32GetDatum(contoencoding), values);
+	HeapTupleSetValue(pg_conversion, conproc, ObjectIdGetDatum(conproc), values);
+	HeapTupleSetValue(pg_conversion, condefault, BoolGetDatum(def), values);
 
 	tup = heap_form_tuple(tupDesc, values, nulls);
 
 	/* insert a new tuple */
-	CatalogTupleInsert(rel, tup);
+	CatalogTupleInsert(rel, tup, NULL);
 
 	myself.classId = ConversionRelationId;
 	myself.objectId = oid;
diff --git a/src/backend/catalog/pg_db_role_setting.c b/src/backend/catalog/pg_db_role_setting.c
index 57d5f5f391b..ee58c21922b 100644
--- a/src/backend/catalog/pg_db_role_setting.c
+++ b/src/backend/catalog/pg_db_role_setting.c
@@ -74,21 +74,18 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt)
 
 			if (new)
 			{
-				Datum		repl_val[Natts_pg_db_role_setting];
-				bool		repl_null[Natts_pg_db_role_setting];
-				bool		repl_repl[Natts_pg_db_role_setting];
+				Datum		values[Natts_pg_db_role_setting] = {0};
+				bool		nulls[Natts_pg_db_role_setting] = {false};
 				HeapTuple	newtuple;
+				Bitmapset  *updated = NULL;
 
-				memset(repl_repl, false, sizeof(repl_repl));
+				HeapTupleUpdateValue(pg_db_role_setting, setconfig, PointerGetDatum(new), values, nulls, updated);
 
-				repl_val[Anum_pg_db_role_setting_setconfig - 1] =
-					PointerGetDatum(new);
-				repl_repl[Anum_pg_db_role_setting_setconfig - 1] = true;
-				repl_null[Anum_pg_db_role_setting_setconfig - 1] = false;
+				newtuple = heap_update_tuple(tuple, RelationGetDescr(rel),
+											 values, nulls, updated);
+				CatalogTupleUpdate(rel, &tuple->t_self, newtuple, updated, NULL);
 
-				newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel),
-											 repl_val, repl_null, repl_repl);
-				CatalogTupleUpdate(rel, &tuple->t_self, newtuple);
+				bms_free(updated);
 			}
 			else
 				CatalogTupleDelete(rel, &tuple->t_self);
@@ -96,18 +93,14 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt)
 	}
 	else if (HeapTupleIsValid(tuple))
 	{
-		Datum		repl_val[Natts_pg_db_role_setting];
-		bool		repl_null[Natts_pg_db_role_setting];
-		bool		repl_repl[Natts_pg_db_role_setting];
+		Datum		values[Natts_pg_db_role_setting] = {0};
+		bool		nulls[Natts_pg_db_role_setting] = {false};
+		Bitmapset  *updated = NULL;
 		HeapTuple	newtuple;
 		Datum		datum;
 		bool		isnull;
 		ArrayType  *a;
 
-		memset(repl_repl, false, sizeof(repl_repl));
-		repl_repl[Anum_pg_db_role_setting_setconfig - 1] = true;
-		repl_null[Anum_pg_db_role_setting_setconfig - 1] = false;
-
 		/* Extract old value of setconfig */
 		datum = heap_getattr(tuple, Anum_pg_db_role_setting_setconfig,
 							 RelationGetDescr(rel), &isnull);
@@ -121,12 +114,12 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt)
 
 		if (a)
 		{
-			repl_val[Anum_pg_db_role_setting_setconfig - 1] =
-				PointerGetDatum(a);
+			HeapTupleUpdateValue(pg_db_role_setting, setconfig, PointerGetDatum(a), values, nulls, updated);
 
-			newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel),
-										 repl_val, repl_null, repl_repl);
-			CatalogTupleUpdate(rel, &tuple->t_self, newtuple);
+			newtuple = heap_update_tuple(tuple, RelationGetDescr(rel),
+										 values, nulls, updated);
+			CatalogTupleUpdate(rel, &tuple->t_self, newtuple, updated, NULL);
+			bms_free(updated);
 		}
 		else
 			CatalogTupleDelete(rel, &tuple->t_self);
@@ -135,21 +128,18 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt)
 	{
 		/* non-null valuestr means it's not RESET, so insert a new tuple */
 		HeapTuple	newtuple;
-		Datum		values[Natts_pg_db_role_setting];
-		bool		nulls[Natts_pg_db_role_setting];
+		Datum		values[Natts_pg_db_role_setting] = {0};
+		bool		nulls[Natts_pg_db_role_setting] = {false};
 		ArrayType  *a;
 
-		memset(nulls, false, sizeof(nulls));
-
 		a = GUCArrayAdd(NULL, setstmt->name, valuestr);
 
-		values[Anum_pg_db_role_setting_setdatabase - 1] =
-			ObjectIdGetDatum(databaseid);
-		values[Anum_pg_db_role_setting_setrole - 1] = ObjectIdGetDatum(roleid);
-		values[Anum_pg_db_role_setting_setconfig - 1] = PointerGetDatum(a);
+		HeapTupleSetValue(pg_db_role_setting, setdatabase, ObjectIdGetDatum(databaseid), values);
+		HeapTupleSetValue(pg_db_role_setting, setrole, ObjectIdGetDatum(roleid), values);
+		HeapTupleSetValue(pg_db_role_setting, setconfig, PointerGetDatum(a), values);
 		newtuple = heap_form_tuple(RelationGetDescr(rel), values, nulls);
 
-		CatalogTupleInsert(rel, newtuple);
+		CatalogTupleInsert(rel, newtuple, NULL);
 	}
 	else
 	{
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
index 55309d16f15..433e574f4dc 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -61,7 +61,7 @@ recordMultipleDependencies(const ObjectAddress *depender,
 {
 	Relation	dependDesc;
 	CatalogIndexState indstate;
-	TupleTableSlot **slot;
+	TupleTableSlot **slots;
 	int			i,
 				max_slots,
 				slot_init_count,
@@ -88,7 +88,7 @@ recordMultipleDependencies(const ObjectAddress *depender,
 	 */
 	max_slots = Min(nreferenced,
 					MAX_CATALOG_MULTI_INSERT_BYTES / sizeof(FormData_pg_depend));
-	slot = palloc_array(TupleTableSlot *, max_slots);
+	slots = palloc_array(TupleTableSlot *, max_slots);
 
 	/* Don't open indexes unless we need to make an update */
 	indstate = NULL;
@@ -99,6 +99,9 @@ recordMultipleDependencies(const ObjectAddress *depender,
 	slot_init_count = 0;
 	for (i = 0; i < nreferenced; i++, referenced++)
 	{
+		TupleTableSlot *slot;
+		Datum	   *values;
+
 		/*
 		 * 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
@@ -109,29 +112,32 @@ recordMultipleDependencies(const ObjectAddress *depender,
 
 		if (slot_init_count < max_slots)
 		{
-			slot[slot_stored_count] = MakeSingleTupleTableSlot(RelationGetDescr(dependDesc),
-															   &TTSOpsHeapTuple);
+			slots[slot_stored_count] = MakeSingleTupleTableSlot(RelationGetDescr(dependDesc),
+																&TTSOpsHeapTuple);
 			slot_init_count++;
 		}
 
-		ExecClearTuple(slot[slot_stored_count]);
+		ExecClearTuple(slots[slot_stored_count]);
+
+		slot = slots[slot_stored_count];
+		values = slot->tts_values;
 
 		/*
 		 * 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]);
+		HeapTupleSetValue(pg_depend, refclassid, ObjectIdGetDatum(referenced->classId), values);
+		HeapTupleSetValue(pg_depend, refobjid, ObjectIdGetDatum(referenced->objectId), values);
+		HeapTupleSetValue(pg_depend, refobjsubid, Int32GetDatum(referenced->objectSubId), values);
+		HeapTupleSetValue(pg_depend, deptype, CharGetDatum((char) behavior), values);
+		HeapTupleSetValue(pg_depend, classid, ObjectIdGetDatum(depender->classId), values);
+		HeapTupleSetValue(pg_depend, objid, ObjectIdGetDatum(depender->objectId), values);
+		HeapTupleSetValue(pg_depend, objsubid, Int32GetDatum(depender->objectSubId), values);
+
+		memset(slot->tts_isnull, false,
+			   slot->tts_tupleDescriptor->natts * sizeof(bool));
+
+		ExecStoreVirtualTuple(slot);
 		slot_stored_count++;
 
 		/* If slots are full, insert a batch of tuples */
@@ -141,8 +147,7 @@ recordMultipleDependencies(const ObjectAddress *depender,
 			if (indstate == NULL)
 				indstate = CatalogOpenIndexes(dependDesc);
 
-			CatalogTuplesMultiInsertWithInfo(dependDesc, slot, slot_stored_count,
-											 indstate);
+			CatalogTuplesMultiInsert(dependDesc, slots, slot_stored_count, indstate);
 			slot_stored_count = 0;
 		}
 	}
@@ -154,8 +159,7 @@ recordMultipleDependencies(const ObjectAddress *depender,
 		if (indstate == NULL)
 			indstate = CatalogOpenIndexes(dependDesc);
 
-		CatalogTuplesMultiInsertWithInfo(dependDesc, slot, slot_stored_count,
-										 indstate);
+		CatalogTuplesMultiInsert(dependDesc, slots, slot_stored_count, indstate);
 	}
 
 	if (indstate != NULL)
@@ -165,8 +169,8 @@ recordMultipleDependencies(const ObjectAddress *depender,
 
 	/* Drop only the number of slots used */
 	for (i = 0; i < slot_init_count; i++)
-		ExecDropSingleTupleTableSlot(slot[i]);
-	pfree(slot);
+		ExecDropSingleTupleTableSlot(slots[i]);
+	pfree(slots);
 }
 
 /*
@@ -531,14 +535,16 @@ changeDependencyFor(Oid classId, Oid objectId,
 				CatalogTupleDelete(depRel, &tup->t_self);
 			else
 			{
+				Bitmapset  *updated = NULL;
+
 				/* make a modifiable copy */
 				tup = heap_copytuple(tup);
 				depform = (Form_pg_depend) GETSTRUCT(tup);
 
-				depform->refobjid = newRefObjectId;
-
-				CatalogTupleUpdate(depRel, &tup->t_self, tup);
+				HeapTupleUpdateField(pg_depend, refobjid, newRefObjectId, depform, updated);
+				CatalogTupleUpdate(depRel, &tup->t_self, tup, updated, NULL);
 
+				bms_free(updated);
 				heap_freetuple(tup);
 			}
 
@@ -588,15 +594,16 @@ changeDependenciesOf(Oid classId, Oid oldObjectId,
 	while (HeapTupleIsValid((tup = systable_getnext(scan))))
 	{
 		Form_pg_depend depform;
+		Bitmapset  *updated = NULL;
 
 		/* make a modifiable copy */
 		tup = heap_copytuple(tup);
 		depform = (Form_pg_depend) GETSTRUCT(tup);
 
-		depform->objid = newObjectId;
-
-		CatalogTupleUpdate(depRel, &tup->t_self, tup);
+		HeapTupleUpdateField(pg_depend, objid, newObjectId, depform, updated);
+		CatalogTupleUpdate(depRel, &tup->t_self, tup, updated, NULL);
 
+		bms_free(updated);
 		heap_freetuple(tup);
 
 		count++;
@@ -675,15 +682,16 @@ changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
 		else
 		{
 			Form_pg_depend depform;
+			Bitmapset  *updated = NULL;
 
 			/* make a modifiable copy */
 			tup = heap_copytuple(tup);
 			depform = (Form_pg_depend) GETSTRUCT(tup);
 
-			depform->refobjid = newRefObjectId;
-
-			CatalogTupleUpdate(depRel, &tup->t_self, tup);
+			HeapTupleUpdateField(pg_depend, refobjid, newRefObjectId, depform, updated);
+			CatalogTupleUpdate(depRel, &tup->t_self, tup, updated, NULL);
 
+			bms_free(updated);
 			heap_freetuple(tup);
 		}
 
diff --git a/src/backend/catalog/pg_enum.c b/src/backend/catalog/pg_enum.c
index 33a461484d4..33818f9d242 100644
--- a/src/backend/catalog/pg_enum.c
+++ b/src/backend/catalog/pg_enum.c
@@ -194,12 +194,12 @@ EnumValuesCreate(Oid enumTypeOid, List *vals)
 		memset(slot[slotCount]->tts_isnull, false,
 			   slot[slotCount]->tts_tupleDescriptor->natts * sizeof(bool));
 
-		slot[slotCount]->tts_values[Anum_pg_enum_oid - 1] = ObjectIdGetDatum(oids[elemno]);
-		slot[slotCount]->tts_values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
-		slot[slotCount]->tts_values[Anum_pg_enum_enumsortorder - 1] = Float4GetDatum(elemno + 1);
+		HeapTupleSetValue(pg_enum, oid, ObjectIdGetDatum(oids[elemno]), slot[slotCount]->tts_values);
+		HeapTupleSetValue(pg_enum, enumtypid, ObjectIdGetDatum(enumTypeOid), slot[slotCount]->tts_values);
+		HeapTupleSetValue(pg_enum, enumsortorder, Float4GetDatum(elemno + 1), slot[slotCount]->tts_values);
 
 		namestrcpy(enumlabel, lab);
-		slot[slotCount]->tts_values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(enumlabel);
+		HeapTupleSetValue(pg_enum, enumlabel, NameGetDatum(enumlabel), slot[slotCount]->tts_values);
 
 		ExecStoreVirtualTuple(slot[slotCount]);
 		slotCount++;
@@ -207,8 +207,7 @@ EnumValuesCreate(Oid enumTypeOid, List *vals)
 		/* if slots are full, insert a batch of tuples */
 		if (slotCount == nslots)
 		{
-			CatalogTuplesMultiInsertWithInfo(pg_enum, slot, slotCount,
-											 indstate);
+			CatalogTuplesMultiInsert(pg_enum, slot, slotCount, indstate);
 			slotCount = 0;
 		}
 
@@ -217,10 +216,9 @@ EnumValuesCreate(Oid enumTypeOid, List *vals)
 
 	/* Insert any tuples left in the buffer */
 	if (slotCount > 0)
-		CatalogTuplesMultiInsertWithInfo(pg_enum, slot, slotCount,
-										 indstate);
+		CatalogTuplesMultiInsert(pg_enum, slot, slotCount, indstate);
 
-	/* clean up */
+	/* Clean up */
 	pfree(oids);
 	for (int i = 0; i < nslots; i++)
 		ExecDropSingleTupleTableSlot(slot[i]);
@@ -310,8 +308,8 @@ AddEnumLabel(Oid enumTypeOid,
 {
 	Relation	pg_enum;
 	Oid			newOid;
-	Datum		values[Natts_pg_enum];
-	bool		nulls[Natts_pg_enum];
+	Datum		values[Natts_pg_enum] = {0};
+	bool		nulls[Natts_pg_enum] = {false};
 	NameData	enumlabel;
 	HeapTuple	enum_tup;
 	float4		newelemorder;
@@ -577,13 +575,13 @@ restart:
 
 	/* Create the new pg_enum entry */
 	memset(nulls, false, sizeof(nulls));
-	values[Anum_pg_enum_oid - 1] = ObjectIdGetDatum(newOid);
-	values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
-	values[Anum_pg_enum_enumsortorder - 1] = Float4GetDatum(newelemorder);
+	HeapTupleSetValue(pg_enum, oid, ObjectIdGetDatum(newOid), values);
+	HeapTupleSetValue(pg_enum, enumtypid, ObjectIdGetDatum(enumTypeOid), values);
+	HeapTupleSetValue(pg_enum, enumsortorder, Float4GetDatum(newelemorder), values);
 	namestrcpy(&enumlabel, newVal);
-	values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
+	HeapTupleSetValue(pg_enum, enumlabel, NameGetDatum(&enumlabel), values);
 	enum_tup = heap_form_tuple(RelationGetDescr(pg_enum), values, nulls);
-	CatalogTupleInsert(pg_enum, enum_tup);
+	CatalogTupleInsert(pg_enum, enum_tup, NULL);
 	heap_freetuple(enum_tup);
 
 	table_close(pg_enum, RowExclusiveLock);
@@ -629,6 +627,7 @@ RenameEnumLabel(Oid enumTypeOid,
 	HeapTuple	old_tup;
 	bool		found_new;
 	int			i;
+	Bitmapset  *updated = NULL;
 
 	/* check length of new label is ok */
 	if (strlen(newVal) > (NAMEDATALEN - 1))
@@ -689,9 +688,12 @@ RenameEnumLabel(Oid enumTypeOid,
 
 	/* Update the pg_enum entry */
 	namestrcpy(&en->enumlabel, newVal);
-	CatalogTupleUpdate(pg_enum, &enum_tup->t_self, enum_tup);
-	heap_freetuple(enum_tup);
+	HeapTupleMarkColumnUpdated(pg_enum, enumlabel, updated);
+
+	CatalogTupleUpdate(pg_enum, &enum_tup->t_self, enum_tup, updated, NULL);
 
+	bms_free(updated);
+	heap_freetuple(enum_tup);
 	table_close(pg_enum, RowExclusiveLock);
 }
 
@@ -792,9 +794,12 @@ RenumberEnumType(Relation pg_enum, HeapTuple *existing, int nelems)
 		newsortorder = i + 1;
 		if (en->enumsortorder != newsortorder)
 		{
-			en->enumsortorder = newsortorder;
+			Bitmapset  *updated = NULL;
+
+			HeapTupleUpdateField(pg_enum, enumsortorder, newsortorder, en, updated);
 
-			CatalogTupleUpdate(pg_enum, &newtup->t_self, newtup);
+			CatalogTupleUpdate(pg_enum, &newtup->t_self, newtup, updated, NULL);
+			bms_free(updated);
 		}
 
 		heap_freetuple(newtup);
diff --git a/src/backend/catalog/pg_inherits.c b/src/backend/catalog/pg_inherits.c
index cef80633157..710cbcfee51 100644
--- a/src/backend/catalog/pg_inherits.c
+++ b/src/backend/catalog/pg_inherits.c
@@ -507,8 +507,8 @@ typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId)
 void
 StoreSingleInheritance(Oid relationId, Oid parentOid, int32 seqNumber)
 {
-	Datum		values[Natts_pg_inherits];
-	bool		nulls[Natts_pg_inherits];
+	Datum		values[Natts_pg_inherits] = {0};
+	bool		nulls[Natts_pg_inherits] = {false};
 	HeapTuple	tuple;
 	Relation	inhRelation;
 
@@ -517,16 +517,16 @@ StoreSingleInheritance(Oid relationId, Oid parentOid, int32 seqNumber)
 	/*
 	 * Make the pg_inherits entry
 	 */
-	values[Anum_pg_inherits_inhrelid - 1] = ObjectIdGetDatum(relationId);
-	values[Anum_pg_inherits_inhparent - 1] = ObjectIdGetDatum(parentOid);
-	values[Anum_pg_inherits_inhseqno - 1] = Int32GetDatum(seqNumber);
-	values[Anum_pg_inherits_inhdetachpending - 1] = BoolGetDatum(false);
+	HeapTupleSetValue(pg_inherits, inhrelid, ObjectIdGetDatum(relationId), values);
+	HeapTupleSetValue(pg_inherits, inhparent, ObjectIdGetDatum(parentOid), values);
+	HeapTupleSetValue(pg_inherits, inhseqno, Int32GetDatum(seqNumber), values);
+	HeapTupleSetValue(pg_inherits, inhdetachpending, BoolGetDatum(false), values);
 
 	memset(nulls, 0, sizeof(nulls));
 
 	tuple = heap_form_tuple(RelationGetDescr(inhRelation), values, nulls);
 
-	CatalogTupleInsert(inhRelation, tuple);
+	CatalogTupleInsert(inhRelation, tuple, NULL);
 
 	heap_freetuple(tuple);
 
diff --git a/src/backend/catalog/pg_largeobject.c b/src/backend/catalog/pg_largeobject.c
index 0e1dd22d908..fb954d5ad24 100644
--- a/src/backend/catalog/pg_largeobject.c
+++ b/src/backend/catalog/pg_largeobject.c
@@ -40,8 +40,8 @@ LargeObjectCreate(Oid loid)
 	Relation	pg_lo_meta;
 	HeapTuple	ntup;
 	Oid			loid_new;
-	Datum		values[Natts_pg_largeobject_metadata];
-	bool		nulls[Natts_pg_largeobject_metadata];
+	Datum		values[Natts_pg_largeobject_metadata] = {0};
+	bool		nulls[Natts_pg_largeobject_metadata] = {false};
 	Oid			ownerId;
 	Acl		   *lomacl;
 
@@ -51,9 +51,6 @@ LargeObjectCreate(Oid loid)
 	/*
 	 * Insert metadata of the largeobject
 	 */
-	memset(values, 0, sizeof(values));
-	memset(nulls, false, sizeof(nulls));
-
 	if (OidIsValid(loid))
 		loid_new = loid;
 	else
@@ -63,20 +60,17 @@ LargeObjectCreate(Oid loid)
 	ownerId = GetUserId();
 	lomacl = get_user_default_acl(OBJECT_LARGEOBJECT, ownerId, InvalidOid);
 
-	values[Anum_pg_largeobject_metadata_oid - 1] = ObjectIdGetDatum(loid_new);
-	values[Anum_pg_largeobject_metadata_lomowner - 1]
-		= ObjectIdGetDatum(ownerId);
+	HeapTupleSetValue(pg_largeobject_metadata, oid, ObjectIdGetDatum(loid_new), values);
+	HeapTupleSetValue(pg_largeobject_metadata, lomowner, ObjectIdGetDatum(ownerId), values);
 
 	if (lomacl != NULL)
-		values[Anum_pg_largeobject_metadata_lomacl - 1]
-			= PointerGetDatum(lomacl);
+		HeapTupleSetValue(pg_largeobject_metadata, lomacl, PointerGetDatum(lomacl), values);
 	else
-		nulls[Anum_pg_largeobject_metadata_lomacl - 1] = true;
+		HeapTupleSetValueNull(pg_largeobject_metadata, lomacl, values, nulls);
 
-	ntup = heap_form_tuple(RelationGetDescr(pg_lo_meta),
-						   values, nulls);
+	ntup = heap_form_tuple(RelationGetDescr(pg_lo_meta), values, nulls);
 
-	CatalogTupleInsert(pg_lo_meta, ntup);
+	CatalogTupleInsert(pg_lo_meta, ntup, NULL);
 
 	heap_freetuple(ntup);
 
diff --git a/src/backend/catalog/pg_namespace.c b/src/backend/catalog/pg_namespace.c
index f35ef5a344e..9aca3cbe637 100644
--- a/src/backend/catalog/pg_namespace.c
+++ b/src/backend/catalog/pg_namespace.c
@@ -45,12 +45,11 @@ NamespaceCreate(const char *nspName, Oid ownerId, bool isTemp)
 	Relation	nspdesc;
 	HeapTuple	tup;
 	Oid			nspoid;
-	bool		nulls[Natts_pg_namespace];
-	Datum		values[Natts_pg_namespace];
+	bool		nulls[Natts_pg_namespace] = {false};
+	Datum		values[Natts_pg_namespace] = {0};
 	NameData	nname;
 	TupleDesc	tupDesc;
 	ObjectAddress myself;
-	int			i;
 	Acl		   *nspacl;
 
 	/* sanity checks */
@@ -72,28 +71,21 @@ NamespaceCreate(const char *nspName, Oid ownerId, bool isTemp)
 	nspdesc = table_open(NamespaceRelationId, RowExclusiveLock);
 	tupDesc = nspdesc->rd_att;
 
-	/* initialize nulls and values */
-	for (i = 0; i < Natts_pg_namespace; i++)
-	{
-		nulls[i] = false;
-		values[i] = (Datum) 0;
-	}
-
 	nspoid = GetNewOidWithIndex(nspdesc, NamespaceOidIndexId,
 								Anum_pg_namespace_oid);
-	values[Anum_pg_namespace_oid - 1] = ObjectIdGetDatum(nspoid);
+	HeapTupleSetValue(pg_namespace, oid, ObjectIdGetDatum(nspoid), values);
 	namestrcpy(&nname, nspName);
-	values[Anum_pg_namespace_nspname - 1] = NameGetDatum(&nname);
-	values[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(ownerId);
+	HeapTupleSetValue(pg_namespace, nspname, NameGetDatum(&nname), values);
+	HeapTupleSetValue(pg_namespace, nspowner, ObjectIdGetDatum(ownerId), values);
 	if (nspacl != NULL)
-		values[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(nspacl);
+		HeapTupleSetValue(pg_namespace, nspacl, PointerGetDatum(nspacl), values);
 	else
-		nulls[Anum_pg_namespace_nspacl - 1] = true;
+		HeapTupleSetValueNull(pg_namespace, nspacl, values, nulls);
 
 
 	tup = heap_form_tuple(tupDesc, values, nulls);
 
-	CatalogTupleInsert(nspdesc, tup);
+	CatalogTupleInsert(nspdesc, tup, NULL);
 	Assert(OidIsValid(nspoid));
 
 	table_close(nspdesc, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c
index 6b90c774c18..bec79133715 100644
--- a/src/backend/catalog/pg_operator.c
+++ b/src/backend/catalog/pg_operator.c
@@ -17,6 +17,7 @@
  */
 #include "postgres.h"
 
+#include "access/htup.h"
 #include "access/htup_details.h"
 #include "access/table.h"
 #include "access/xact.h"
@@ -199,10 +200,11 @@ OperatorShellMake(const char *operatorName,
 	Oid			operatorObjectId;
 	int			i;
 	HeapTuple	tup;
-	Datum		values[Natts_pg_operator];
-	bool		nulls[Natts_pg_operator];
+	Datum		values[Natts_pg_operator] = {0};
+	bool		nulls[Natts_pg_operator] = {false};
 	NameData	oname;
 	TupleDesc	tupDesc;
+	Bitmapset  *updated = NULL;
 
 	/*
 	 * validate operator name
@@ -234,22 +236,23 @@ OperatorShellMake(const char *operatorName,
 	 */
 	operatorObjectId = GetNewOidWithIndex(pg_operator_desc, OperatorOidIndexId,
 										  Anum_pg_operator_oid);
-	values[Anum_pg_operator_oid - 1] = ObjectIdGetDatum(operatorObjectId);
+
+	HeapTupleUpdateValue(pg_operator, oid, ObjectIdGetDatum(operatorObjectId), values, nulls, updated);
 	namestrcpy(&oname, operatorName);
-	values[Anum_pg_operator_oprname - 1] = NameGetDatum(&oname);
-	values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(operatorNamespace);
-	values[Anum_pg_operator_oprowner - 1] = ObjectIdGetDatum(GetUserId());
-	values[Anum_pg_operator_oprkind - 1] = CharGetDatum(leftTypeId ? 'b' : 'l');
-	values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(false);
-	values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(false);
-	values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(leftTypeId);
-	values[Anum_pg_operator_oprright - 1] = ObjectIdGetDatum(rightTypeId);
-	values[Anum_pg_operator_oprresult - 1] = ObjectIdGetDatum(InvalidOid);
-	values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(InvalidOid);
-	values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(InvalidOid);
-	values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(InvalidOid);
-	values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(InvalidOid);
-	values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(InvalidOid);
+	HeapTupleUpdateValue(pg_operator, oprname, NameGetDatum(&oname), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprnamespace, ObjectIdGetDatum(operatorNamespace), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprowner, ObjectIdGetDatum(GetUserId()), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprkind, CharGetDatum(leftTypeId ? 'b' : 'l'), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprcanmerge, BoolGetDatum(false), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprcanhash, BoolGetDatum(false), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprleft, ObjectIdGetDatum(leftTypeId), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprright, ObjectIdGetDatum(rightTypeId), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprresult, ObjectIdGetDatum(InvalidOid), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprcom, ObjectIdGetDatum(InvalidOid), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprnegate, ObjectIdGetDatum(InvalidOid), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprcode, ObjectIdGetDatum(InvalidOid), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprrest, ObjectIdGetDatum(InvalidOid), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprjoin, ObjectIdGetDatum(InvalidOid), values, nulls, updated);
 
 	/*
 	 * create a new operator tuple
@@ -259,7 +262,7 @@ OperatorShellMake(const char *operatorName,
 	/*
 	 * insert our "shell" operator tuple
 	 */
-	CatalogTupleInsert(pg_operator_desc, tup);
+	CatalogTupleInsert(pg_operator_desc, tup, NULL);
 
 	/* Add dependencies for the entry */
 	makeOperatorDependencies(tup, true, false);
@@ -333,9 +336,9 @@ OperatorCreate(const char *operatorName,
 	Relation	pg_operator_desc;
 	HeapTuple	tup;
 	bool		isUpdate;
-	bool		nulls[Natts_pg_operator];
-	bool		replaces[Natts_pg_operator];
-	Datum		values[Natts_pg_operator];
+	Datum		values[Natts_pg_operator] = {0};
+	bool		nulls[Natts_pg_operator] = {false};
+	Bitmapset  *updated = NULL;
 	Oid			operatorObjectId;
 	bool		operatorAlreadyDefined;
 	Oid			operResultType;
@@ -343,7 +346,6 @@ OperatorCreate(const char *operatorName,
 				negatorId;
 	bool		selfCommutator = false;
 	NameData	oname;
-	int			i;
 	ObjectAddress address;
 
 	/*
@@ -447,32 +449,25 @@ OperatorCreate(const char *operatorName,
 	else
 		negatorId = InvalidOid;
 
-	/*
-	 * set up values in the operator tuple
-	 */
-
-	for (i = 0; i < Natts_pg_operator; ++i)
-	{
-		values[i] = (Datum) 0;
-		replaces[i] = true;
-		nulls[i] = false;
-	}
+	/* Set up values in the operator tuple */
+	HeapTupleUpdateSetAllColumnsUpdated(pg_operator, updated);
+	HeapTupleSetColumnNotUpdated(pg_operator, oid, updated);
 
 	namestrcpy(&oname, operatorName);
-	values[Anum_pg_operator_oprname - 1] = NameGetDatum(&oname);
-	values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(operatorNamespace);
-	values[Anum_pg_operator_oprowner - 1] = ObjectIdGetDatum(GetUserId());
-	values[Anum_pg_operator_oprkind - 1] = CharGetDatum(leftTypeId ? 'b' : 'l');
-	values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(canMerge);
-	values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(canHash);
-	values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(leftTypeId);
-	values[Anum_pg_operator_oprright - 1] = ObjectIdGetDatum(rightTypeId);
-	values[Anum_pg_operator_oprresult - 1] = ObjectIdGetDatum(operResultType);
-	values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(commutatorId);
-	values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(negatorId);
-	values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(procedureId);
-	values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(restrictionId);
-	values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(joinId);
+	HeapTupleUpdateValue(pg_operator, oprname, NameGetDatum(&oname), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprnamespace, ObjectIdGetDatum(operatorNamespace), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprowner, ObjectIdGetDatum(GetUserId()), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprkind, CharGetDatum(leftTypeId ? 'b' : 'l'), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprcanmerge, BoolGetDatum(canMerge), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprcanhash, BoolGetDatum(canHash), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprleft, ObjectIdGetDatum(leftTypeId), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprright, ObjectIdGetDatum(rightTypeId), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprresult, ObjectIdGetDatum(operResultType), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprcom, ObjectIdGetDatum(commutatorId), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprnegate, ObjectIdGetDatum(negatorId), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprcode, ObjectIdGetDatum(procedureId), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprrest, ObjectIdGetDatum(restrictionId), values, nulls, updated);
+	HeapTupleUpdateValue(pg_operator, oprjoin, ObjectIdGetDatum(joinId), values, nulls, updated);
 
 	pg_operator_desc = table_open(OperatorRelationId, RowExclusiveLock);
 
@@ -489,14 +484,9 @@ OperatorCreate(const char *operatorName,
 			elog(ERROR, "cache lookup failed for operator %u",
 				 operatorObjectId);
 
-		replaces[Anum_pg_operator_oid - 1] = false;
-		tup = heap_modify_tuple(tup,
-								RelationGetDescr(pg_operator_desc),
-								values,
-								nulls,
-								replaces);
+		tup = heap_update_tuple(tup, RelationGetDescr(pg_operator_desc), values, nulls, updated);
 
-		CatalogTupleUpdate(pg_operator_desc, &tup->t_self, tup);
+		CatalogTupleUpdate(pg_operator_desc, &tup->t_self, tup, updated, NULL);
 	}
 	else
 	{
@@ -505,12 +495,12 @@ OperatorCreate(const char *operatorName,
 		operatorObjectId = GetNewOidWithIndex(pg_operator_desc,
 											  OperatorOidIndexId,
 											  Anum_pg_operator_oid);
-		values[Anum_pg_operator_oid - 1] = ObjectIdGetDatum(operatorObjectId);
+		HeapTupleUpdateValue(pg_operator, oid, ObjectIdGetDatum(operatorObjectId), values, nulls, updated);
 
 		tup = heap_form_tuple(RelationGetDescr(pg_operator_desc),
 							  values, nulls);
 
-		CatalogTupleInsert(pg_operator_desc, tup);
+		CatalogTupleInsert(pg_operator_desc, tup, NULL);
 	}
 
 	/* Add dependencies for the entry */
@@ -536,6 +526,7 @@ OperatorCreate(const char *operatorName,
 	/* Post creation hook for new operator */
 	InvokeObjectPostCreateHook(OperatorRelationId, operatorObjectId, 0);
 
+	bms_free(updated);
 	table_close(pg_operator_desc, RowExclusiveLock);
 
 	return address;
@@ -708,6 +699,7 @@ OperatorUpd(Oid baseId, Oid commId, Oid negId, bool isDelete)
 	if (HeapTupleIsValid(tup))
 	{
 		Form_pg_operator t = (Form_pg_operator) GETSTRUCT(tup);
+		Bitmapset  *updated = NULL;
 		bool		update_commutator = false;
 
 		/*
@@ -717,7 +709,7 @@ OperatorUpd(Oid baseId, Oid commId, Oid negId, bool isDelete)
 		 */
 		if (isDelete && OidIsValid(t->oprcom))
 		{
-			t->oprcom = InvalidOid;
+			HeapTupleUpdateField(pg_operator, oprcom, InvalidOid, t, updated);
 			update_commutator = true;
 		}
 		else if (!isDelete && t->oprcom != baseId)
@@ -745,14 +737,14 @@ OperatorUpd(Oid baseId, Oid commId, Oid negId, bool isDelete)
 									NameStr(t->oprname), t->oprcom)));
 			}
 
-			t->oprcom = baseId;
+			HeapTupleUpdateField(pg_operator, oprcom, baseId, t, updated);
 			update_commutator = true;
 		}
 
 		/* If any columns were found to need modification, update tuple. */
 		if (update_commutator)
 		{
-			CatalogTupleUpdate(pg_operator_desc, &tup->t_self, tup);
+			CatalogTupleUpdate(pg_operator_desc, &tup->t_self, tup, updated, NULL);
 
 			/*
 			 * Do CCI to make the updated tuple visible.  We must do this in
@@ -763,6 +755,8 @@ OperatorUpd(Oid baseId, Oid commId, Oid negId, bool isDelete)
 			 */
 			CommandCounterIncrement();
 		}
+
+		bms_free(updated);
 	}
 
 	/*
@@ -777,6 +771,7 @@ OperatorUpd(Oid baseId, Oid commId, Oid negId, bool isDelete)
 	{
 		Form_pg_operator t = (Form_pg_operator) GETSTRUCT(tup);
 		bool		update_negator = false;
+		Bitmapset  *updated = NULL;
 
 		/*
 		 * We can skip doing anything if the negator's oprnegate field is
@@ -785,7 +780,7 @@ OperatorUpd(Oid baseId, Oid commId, Oid negId, bool isDelete)
 		 */
 		if (isDelete && OidIsValid(t->oprnegate))
 		{
-			t->oprnegate = InvalidOid;
+			HeapTupleUpdateField(pg_operator, oprnegate, InvalidOid, t, updated);
 			update_negator = true;
 		}
 		else if (!isDelete && t->oprnegate != baseId)
@@ -813,14 +808,14 @@ OperatorUpd(Oid baseId, Oid commId, Oid negId, bool isDelete)
 									NameStr(t->oprname), t->oprnegate)));
 			}
 
-			t->oprnegate = baseId;
+			HeapTupleUpdateField(pg_operator, oprnegate, baseId, t, updated);
 			update_negator = true;
 		}
 
 		/* If any columns were found to need modification, update tuple. */
 		if (update_negator)
 		{
-			CatalogTupleUpdate(pg_operator_desc, &tup->t_self, tup);
+			CatalogTupleUpdate(pg_operator_desc, &tup->t_self, tup, updated, NULL);
 
 			/*
 			 * In the deletion case, do CCI to make the updated tuple visible.
@@ -831,6 +826,8 @@ OperatorUpd(Oid baseId, Oid commId, Oid negId, bool isDelete)
 			if (isDelete)
 				CommandCounterIncrement();
 		}
+
+		bms_free(updated);
 	}
 
 	/* Close relation and release catalog lock. */
diff --git a/src/backend/catalog/pg_parameter_acl.c b/src/backend/catalog/pg_parameter_acl.c
index 4d2fc158a74..b61d0b315d8 100644
--- a/src/backend/catalog/pg_parameter_acl.c
+++ b/src/backend/catalog/pg_parameter_acl.c
@@ -74,7 +74,7 @@ ParameterAclCreate(const char *parameter)
 	TupleDesc	tupDesc;
 	HeapTuple	tuple;
 	Datum		values[Natts_pg_parameter_acl] = {0};
-	bool		nulls[Natts_pg_parameter_acl] = {0};
+	bool		nulls[Natts_pg_parameter_acl] = {false};
 
 	/*
 	 * To prevent cluttering pg_parameter_acl with useless entries, insist
@@ -96,12 +96,11 @@ ParameterAclCreate(const char *parameter)
 	parameterId = GetNewOidWithIndex(rel,
 									 ParameterAclOidIndexId,
 									 Anum_pg_parameter_acl_oid);
-	values[Anum_pg_parameter_acl_oid - 1] = ObjectIdGetDatum(parameterId);
-	values[Anum_pg_parameter_acl_parname - 1] =
-		PointerGetDatum(cstring_to_text(parname));
-	nulls[Anum_pg_parameter_acl_paracl - 1] = true;
+	HeapTupleSetValue(pg_parameter_acl, oid, ObjectIdGetDatum(parameterId), values);
+	HeapTupleSetValue(pg_parameter_acl, parname, PointerGetDatum(cstring_to_text(parname)), values);
+	HeapTupleSetValueNull(pg_parameter_acl, paracl, values, nulls);
 	tuple = heap_form_tuple(tupDesc, values, nulls);
-	CatalogTupleInsert(rel, tuple);
+	CatalogTupleInsert(rel, tuple, NULL);
 
 	/* Close pg_parameter_acl, but keep lock till commit. */
 	heap_freetuple(tuple);
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index acff7a0096d..c5b4a99b7ae 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -14,6 +14,7 @@
  */
 #include "postgres.h"
 
+#include "access/htup.h"
 #include "access/htup_details.h"
 #include "access/table.h"
 #include "access/xact.h"
@@ -135,9 +136,9 @@ ProcedureCreate(const char *procedureName,
 	Relation	rel;
 	HeapTuple	tup;
 	HeapTuple	oldtup;
-	bool		nulls[Natts_pg_proc];
-	Datum		values[Natts_pg_proc];
-	bool		replaces[Natts_pg_proc];
+	Datum		values[Natts_pg_proc] = {0};
+	bool		nulls[Natts_pg_proc] = {false};
+	Bitmapset  *updated = NULL;
 	NameData	procname;
 	TupleDesc	tupDesc;
 	bool		is_update;
@@ -318,67 +319,61 @@ ProcedureCreate(const char *procedureName,
 	/*
 	 * All seems OK; prepare the data to be inserted into pg_proc.
 	 */
-
-	for (i = 0; i < Natts_pg_proc; ++i)
-	{
-		nulls[i] = false;
-		values[i] = (Datum) 0;
-		replaces[i] = true;
-	}
+	HeapTupleUpdateSetAllColumnsUpdated(pg_proc, updated);
 
 	namestrcpy(&procname, procedureName);
-	values[Anum_pg_proc_proname - 1] = NameGetDatum(&procname);
-	values[Anum_pg_proc_pronamespace - 1] = ObjectIdGetDatum(procNamespace);
-	values[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(proowner);
-	values[Anum_pg_proc_prolang - 1] = ObjectIdGetDatum(languageObjectId);
-	values[Anum_pg_proc_procost - 1] = Float4GetDatum(procost);
-	values[Anum_pg_proc_prorows - 1] = Float4GetDatum(prorows);
-	values[Anum_pg_proc_provariadic - 1] = ObjectIdGetDatum(variadicType);
-	values[Anum_pg_proc_prosupport - 1] = ObjectIdGetDatum(prosupport);
-	values[Anum_pg_proc_prokind - 1] = CharGetDatum(prokind);
-	values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer);
-	values[Anum_pg_proc_proleakproof - 1] = BoolGetDatum(isLeakProof);
-	values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict);
-	values[Anum_pg_proc_proretset - 1] = BoolGetDatum(returnsSet);
-	values[Anum_pg_proc_provolatile - 1] = CharGetDatum(volatility);
-	values[Anum_pg_proc_proparallel - 1] = CharGetDatum(parallel);
-	values[Anum_pg_proc_pronargs - 1] = UInt16GetDatum(parameterCount);
-	values[Anum_pg_proc_pronargdefaults - 1] = UInt16GetDatum(list_length(parameterDefaults));
-	values[Anum_pg_proc_prorettype - 1] = ObjectIdGetDatum(returnType);
-	values[Anum_pg_proc_proargtypes - 1] = PointerGetDatum(parameterTypes);
+	HeapTupleUpdateValue(pg_proc, proname, NameGetDatum(&procname), values, nulls, updated);
+	HeapTupleUpdateValue(pg_proc, pronamespace, ObjectIdGetDatum(procNamespace), values, nulls, updated);
+	HeapTupleUpdateValue(pg_proc, proowner, ObjectIdGetDatum(proowner), values, nulls, updated);
+	HeapTupleUpdateValue(pg_proc, prolang, ObjectIdGetDatum(languageObjectId), values, nulls, updated);
+	HeapTupleUpdateValue(pg_proc, procost, Float4GetDatum(procost), values, nulls, updated);
+	HeapTupleUpdateValue(pg_proc, prorows, Float4GetDatum(prorows), values, nulls, updated);
+	HeapTupleUpdateValue(pg_proc, provariadic, ObjectIdGetDatum(variadicType), values, nulls, updated);
+	HeapTupleUpdateValue(pg_proc, prosupport, ObjectIdGetDatum(prosupport), values, nulls, updated);
+	HeapTupleUpdateValue(pg_proc, prokind, CharGetDatum(prokind), values, nulls, updated);
+	HeapTupleUpdateValue(pg_proc, prosecdef, BoolGetDatum(security_definer), values, nulls, updated);
+	HeapTupleUpdateValue(pg_proc, proleakproof, BoolGetDatum(isLeakProof), values, nulls, updated);
+	HeapTupleUpdateValue(pg_proc, proisstrict, BoolGetDatum(isStrict), values, nulls, updated);
+	HeapTupleUpdateValue(pg_proc, proretset, BoolGetDatum(returnsSet), values, nulls, updated);
+	HeapTupleUpdateValue(pg_proc, provolatile, CharGetDatum(volatility), values, nulls, updated);
+	HeapTupleUpdateValue(pg_proc, proparallel, CharGetDatum(parallel), values, nulls, updated);
+	HeapTupleUpdateValue(pg_proc, pronargs, UInt16GetDatum(parameterCount), values, nulls, updated);
+	HeapTupleUpdateValue(pg_proc, pronargdefaults, UInt16GetDatum(list_length(parameterDefaults)), values, nulls, updated);
+	HeapTupleUpdateValue(pg_proc, prorettype, ObjectIdGetDatum(returnType), values, nulls, updated);
+	HeapTupleUpdateValue(pg_proc, proargtypes, PointerGetDatum(parameterTypes), values, nulls, updated);
 	if (allParameterTypes != PointerGetDatum(NULL))
-		values[Anum_pg_proc_proallargtypes - 1] = allParameterTypes;
+		HeapTupleUpdateValue(pg_proc, proallargtypes, allParameterTypes, values, nulls, updated);
 	else
-		nulls[Anum_pg_proc_proallargtypes - 1] = true;
+		HeapTupleUpdateValueNull(pg_proc, proallargtypes, values, nulls, updated);
 	if (parameterModes != PointerGetDatum(NULL))
-		values[Anum_pg_proc_proargmodes - 1] = parameterModes;
+		HeapTupleUpdateValue(pg_proc, proargmodes, parameterModes, values, nulls, updated);
 	else
-		nulls[Anum_pg_proc_proargmodes - 1] = true;
+		HeapTupleUpdateValueNull(pg_proc, proargmodes, values, nulls, updated);
 	if (parameterNames != PointerGetDatum(NULL))
-		values[Anum_pg_proc_proargnames - 1] = parameterNames;
+		HeapTupleUpdateValue(pg_proc, proargnames, parameterNames, values, nulls, updated);
 	else
-		nulls[Anum_pg_proc_proargnames - 1] = true;
+		HeapTupleUpdateValueNull(pg_proc, proargnames, values, nulls, updated);
 	if (parameterDefaults != NIL)
-		values[Anum_pg_proc_proargdefaults - 1] = CStringGetTextDatum(nodeToString(parameterDefaults));
+		HeapTupleUpdateValue(pg_proc, proargdefaults, CStringGetTextDatum(nodeToString(parameterDefaults)), values, nulls, updated);
 	else
-		nulls[Anum_pg_proc_proargdefaults - 1] = true;
+		HeapTupleUpdateValueNull(pg_proc, proargdefaults, values, nulls, updated);
 	if (trftypes != PointerGetDatum(NULL))
-		values[Anum_pg_proc_protrftypes - 1] = trftypes;
+		HeapTupleUpdateValue(pg_proc, protrftypes, trftypes, values, nulls, updated);
 	else
-		nulls[Anum_pg_proc_protrftypes - 1] = true;
-	values[Anum_pg_proc_prosrc - 1] = CStringGetTextDatum(prosrc);
+		HeapTupleUpdateValueNull(pg_proc, protrftypes, values, nulls, updated);
+	HeapTupleUpdateValue(pg_proc, prosrc, CStringGetTextDatum(prosrc), values, nulls, updated);
 	if (probin)
-		values[Anum_pg_proc_probin - 1] = CStringGetTextDatum(probin);
+		HeapTupleUpdateValue(pg_proc, probin, CStringGetTextDatum(probin), values, nulls, updated);
 	else
-		nulls[Anum_pg_proc_probin - 1] = true;
+		HeapTupleUpdateValueNull(pg_proc, probin, values, nulls, updated);
 	if (prosqlbody)
-		values[Anum_pg_proc_prosqlbody - 1] = CStringGetTextDatum(nodeToString(prosqlbody));
+		HeapTupleUpdateValue(pg_proc, prosqlbody, CStringGetTextDatum(nodeToString(prosqlbody)), values, nulls, updated);
 	else
-		nulls[Anum_pg_proc_prosqlbody - 1] = true;
+		HeapTupleUpdateValueNull(pg_proc, prosqlbody, values, nulls, updated);
 	if (proconfig != PointerGetDatum(NULL))
-		values[Anum_pg_proc_proconfig - 1] = proconfig;
+		HeapTupleUpdateValue(pg_proc, proconfig, proconfig, values, nulls, updated);
 	else
-		nulls[Anum_pg_proc_proconfig - 1] = true;
+		HeapTupleUpdateValueNull(pg_proc, proconfig, values, nulls, updated);
 	/* proacl will be determined later */
 
 	rel = table_open(ProcedureRelationId, RowExclusiveLock);
@@ -579,13 +574,13 @@ ProcedureCreate(const char *procedureName,
 		 * Do not change existing oid, ownership or permissions, either.  Note
 		 * dependency-update code below has to agree with this decision.
 		 */
-		replaces[Anum_pg_proc_oid - 1] = false;
-		replaces[Anum_pg_proc_proowner - 1] = false;
-		replaces[Anum_pg_proc_proacl - 1] = false;
+		HeapTupleSetColumnNotUpdated(pg_proc, oid, updated);
+		HeapTupleSetColumnNotUpdated(pg_proc, proowner, updated);
+		HeapTupleSetColumnNotUpdated(pg_proc, proacl, updated);
 
 		/* Okay, do it... */
-		tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces);
-		CatalogTupleUpdate(rel, &tup->t_self, tup);
+		tup = heap_update_tuple(oldtup, tupDesc, values, nulls, updated);
+		CatalogTupleUpdate(rel, &tup->t_self, tup, updated, NULL);
 
 		ReleaseSysCache(oldtup);
 		is_update = true;
@@ -599,18 +594,23 @@ ProcedureCreate(const char *procedureName,
 		proacl = get_user_default_acl(OBJECT_FUNCTION, proowner,
 									  procNamespace);
 		if (proacl != NULL)
-			values[Anum_pg_proc_proacl - 1] = PointerGetDatum(proacl);
+			HeapTupleUpdateValue(pg_proc, proacl, PointerGetDatum(proacl), values, nulls, updated);
 		else
-			nulls[Anum_pg_proc_proacl - 1] = true;
+			HeapTupleUpdateValueNull(pg_proc, proacl, values, nulls, updated);
 
 		newOid = GetNewOidWithIndex(rel, ProcedureOidIndexId,
 									Anum_pg_proc_oid);
-		values[Anum_pg_proc_oid - 1] = ObjectIdGetDatum(newOid);
+		HeapTupleUpdateValue(pg_proc, oid, ObjectIdGetDatum(newOid), values, nulls, updated);
+
+		/* Okay, do it... */
 		tup = heap_form_tuple(tupDesc, values, nulls);
-		CatalogTupleInsert(rel, tup);
+		CatalogTupleInsert(rel, tup, NULL);
+
 		is_update = false;
 	}
 
+	bms_free(updated);
+	updated = NULL;
 
 	retval = ((Form_pg_proc) GETSTRUCT(tup))->oid;
 
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 9a4791c573e..08cfee44f8f 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -433,8 +433,8 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,
 {
 	Relation	rel;
 	HeapTuple	tup;
-	Datum		values[Natts_pg_publication_rel];
-	bool		nulls[Natts_pg_publication_rel];
+	Datum		values[Natts_pg_publication_rel] = {0};
+	bool		nulls[Natts_pg_publication_rel] = {false};
 	Relation	targetrel = pri->relation;
 	Oid			relid = RelationGetRelid(targetrel);
 	Oid			pubreloid;
@@ -477,28 +477,26 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,
 
 	pubreloid = GetNewOidWithIndex(rel, PublicationRelObjectIndexId,
 								   Anum_pg_publication_rel_oid);
-	values[Anum_pg_publication_rel_oid - 1] = ObjectIdGetDatum(pubreloid);
-	values[Anum_pg_publication_rel_prpubid - 1] =
-		ObjectIdGetDatum(pubid);
-	values[Anum_pg_publication_rel_prrelid - 1] =
-		ObjectIdGetDatum(relid);
+	HeapTupleSetValue(pg_publication_rel, oid, ObjectIdGetDatum(pubreloid), values);
+	HeapTupleSetValue(pg_publication_rel, prpubid, ObjectIdGetDatum(pubid), values);
+	HeapTupleSetValue(pg_publication_rel, prrelid, ObjectIdGetDatum(relid), values);
 
 	/* Add qualifications, if available */
 	if (pri->whereClause != NULL)
-		values[Anum_pg_publication_rel_prqual - 1] = CStringGetTextDatum(nodeToString(pri->whereClause));
+		HeapTupleSetValue(pg_publication_rel, prqual, CStringGetTextDatum(nodeToString(pri->whereClause)), values);
 	else
-		nulls[Anum_pg_publication_rel_prqual - 1] = true;
+		HeapTupleSetValueNull(pg_publication_rel, prqual, values, nulls);
 
 	/* Add column list, if available */
 	if (pri->columns)
-		values[Anum_pg_publication_rel_prattrs - 1] = PointerGetDatum(attnumstoint2vector(attnums));
+		HeapTupleSetValue(pg_publication_rel, prattrs, PointerGetDatum(attnumstoint2vector(attnums)), values);
 	else
-		nulls[Anum_pg_publication_rel_prattrs - 1] = true;
+		HeapTupleSetValueNull(pg_publication_rel, prattrs, values, nulls);
 
 	tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
 
 	/* Insert tuple into catalog. */
-	CatalogTupleInsert(rel, tup);
+	CatalogTupleInsert(rel, tup, NULL);
 	heap_freetuple(tup);
 
 	/* Register dependencies as needed */
@@ -674,8 +672,8 @@ publication_add_schema(Oid pubid, Oid schemaid, bool if_not_exists)
 {
 	Relation	rel;
 	HeapTuple	tup;
-	Datum		values[Natts_pg_publication_namespace];
-	bool		nulls[Natts_pg_publication_namespace];
+	Datum		values[Natts_pg_publication_namespace] = {0};
+	bool		nulls[Natts_pg_publication_namespace] = {false};
 	Oid			psschid;
 	Publication *pub = GetPublication(pubid);
 	List	   *schemaRels = NIL;
@@ -712,16 +710,14 @@ publication_add_schema(Oid pubid, Oid schemaid, bool if_not_exists)
 
 	psschid = GetNewOidWithIndex(rel, PublicationNamespaceObjectIndexId,
 								 Anum_pg_publication_namespace_oid);
-	values[Anum_pg_publication_namespace_oid - 1] = ObjectIdGetDatum(psschid);
-	values[Anum_pg_publication_namespace_pnpubid - 1] =
-		ObjectIdGetDatum(pubid);
-	values[Anum_pg_publication_namespace_pnnspid - 1] =
-		ObjectIdGetDatum(schemaid);
+	HeapTupleSetValue(pg_publication_namespace, oid, ObjectIdGetDatum(psschid), values);
+	HeapTupleSetValue(pg_publication_namespace, pnpubid, ObjectIdGetDatum(pubid), values);
+	HeapTupleSetValue(pg_publication_namespace, pnnspid, ObjectIdGetDatum(schemaid), values);
 
 	tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
 
 	/* Insert tuple into catalog */
-	CatalogTupleInsert(rel, tup);
+	CatalogTupleInsert(rel, tup, NULL);
 	heap_freetuple(tup);
 
 	ObjectAddressSet(myself, PublicationNamespaceRelationId, psschid);
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
index cd21c84c8fd..0e6a5b27501 100644
--- a/src/backend/catalog/pg_range.c
+++ b/src/backend/catalog/pg_range.c
@@ -38,8 +38,8 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
 			RegProcedure rangeSubDiff, Oid multirangeTypeOid)
 {
 	Relation	pg_range;
-	Datum		values[Natts_pg_range];
-	bool		nulls[Natts_pg_range];
+	Datum		values[Natts_pg_range] = {0};
+	bool		nulls[Natts_pg_range] = {false};
 	HeapTuple	tup;
 	ObjectAddress myself;
 	ObjectAddress referenced;
@@ -50,17 +50,17 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
 
 	memset(nulls, 0, sizeof(nulls));
 
-	values[Anum_pg_range_rngtypid - 1] = ObjectIdGetDatum(rangeTypeOid);
-	values[Anum_pg_range_rngsubtype - 1] = ObjectIdGetDatum(rangeSubType);
-	values[Anum_pg_range_rngcollation - 1] = ObjectIdGetDatum(rangeCollation);
-	values[Anum_pg_range_rngsubopc - 1] = ObjectIdGetDatum(rangeSubOpclass);
-	values[Anum_pg_range_rngcanonical - 1] = ObjectIdGetDatum(rangeCanonical);
-	values[Anum_pg_range_rngsubdiff - 1] = ObjectIdGetDatum(rangeSubDiff);
-	values[Anum_pg_range_rngmultitypid - 1] = ObjectIdGetDatum(multirangeTypeOid);
+	HeapTupleSetValue(pg_range, rngtypid, ObjectIdGetDatum(rangeTypeOid), values);
+	HeapTupleSetValue(pg_range, rngsubtype, ObjectIdGetDatum(rangeSubType), values);
+	HeapTupleSetValue(pg_range, rngcollation, ObjectIdGetDatum(rangeCollation), values);
+	HeapTupleSetValue(pg_range, rngsubopc, ObjectIdGetDatum(rangeSubOpclass), values);
+	HeapTupleSetValue(pg_range, rngcanonical, ObjectIdGetDatum(rangeCanonical), values);
+	HeapTupleSetValue(pg_range, rngsubdiff, ObjectIdGetDatum(rangeSubDiff), values);
+	HeapTupleSetValue(pg_range, rngmultitypid, ObjectIdGetDatum(multirangeTypeOid), values);
 
 	tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls);
 
-	CatalogTupleInsert(pg_range, tup);
+	CatalogTupleInsert(pg_range, tup, NULL);
 	heap_freetuple(tup);
 
 	/* record type's dependencies on range-related items */
diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c
index 3db41ecd74f..e926d0fcbd7 100644
--- a/src/backend/catalog/pg_shdepend.c
+++ b/src/backend/catalog/pg_shdepend.c
@@ -266,38 +266,41 @@ shdepChangeDep(Relation sdepRel,
 	}
 	else if (oldtup)
 	{
+		Bitmapset  *updated = NULL;
+
 		/* Need to update existing entry */
 		Form_pg_shdepend shForm = (Form_pg_shdepend) GETSTRUCT(oldtup);
 
 		/* Since oldtup is a copy, we can just modify it in-memory */
 		shForm->refclassid = refclassid;
+		HeapTupleMarkColumnUpdated(pg_shdepend, refclassid, updated);
 		shForm->refobjid = refobjid;
+		HeapTupleMarkColumnUpdated(pg_shdepend, refobjid, updated);
 
-		CatalogTupleUpdate(sdepRel, &oldtup->t_self, oldtup);
+		CatalogTupleUpdate(sdepRel, &oldtup->t_self, oldtup, updated, NULL);
+		bms_free(updated);
 	}
 	else
 	{
 		/* Need to insert new entry */
-		Datum		values[Natts_pg_shdepend];
-		bool		nulls[Natts_pg_shdepend];
-
-		memset(nulls, false, sizeof(nulls));
+		Datum		values[Natts_pg_shdepend] = {0};
+		bool		nulls[Natts_pg_shdepend] = {false};
 
-		values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(dbid);
-		values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classid);
-		values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objid);
-		values[Anum_pg_shdepend_objsubid - 1] = Int32GetDatum(objsubid);
+		HeapTupleSetValue(pg_shdepend, dbid, ObjectIdGetDatum(dbid), values);
+		HeapTupleSetValue(pg_shdepend, classid, ObjectIdGetDatum(classid), values);
+		HeapTupleSetValue(pg_shdepend, objid, ObjectIdGetDatum(objid), values);
+		HeapTupleSetValue(pg_shdepend, objsubid, Int32GetDatum(objsubid), values);
 
-		values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassid);
-		values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjid);
-		values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(deptype);
+		HeapTupleSetValue(pg_shdepend, refclassid, ObjectIdGetDatum(refclassid), values);
+		HeapTupleSetValue(pg_shdepend, refobjid, ObjectIdGetDatum(refobjid), values);
+		HeapTupleSetValue(pg_shdepend, deptype, CharGetDatum(deptype), values);
 
 		/*
 		 * we are reusing oldtup just to avoid declaring a new variable, but
 		 * it's certainly a new tuple
 		 */
 		oldtup = heap_form_tuple(RelationGetDescr(sdepRel), values, nulls);
-		CatalogTupleInsert(sdepRel, oldtup);
+		CatalogTupleInsert(sdepRel, oldtup, NULL);
 	}
 
 	if (oldtup)
@@ -955,13 +958,13 @@ copyTemplateDependencies(Oid templateDbId, Oid newDbId)
 
 		shdep = (Form_pg_shdepend) GETSTRUCT(tup);
 
-		slot[slot_stored_count]->tts_values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(newDbId);
-		slot[slot_stored_count]->tts_values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(shdep->classid);
-		slot[slot_stored_count]->tts_values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(shdep->objid);
-		slot[slot_stored_count]->tts_values[Anum_pg_shdepend_objsubid - 1] = Int32GetDatum(shdep->objsubid);
-		slot[slot_stored_count]->tts_values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(shdep->refclassid);
-		slot[slot_stored_count]->tts_values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(shdep->refobjid);
-		slot[slot_stored_count]->tts_values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(shdep->deptype);
+		HeapTupleSetValue(pg_shdepend, dbid, ObjectIdGetDatum(newDbId), slot[slot_stored_count]->tts_values);
+		HeapTupleSetValue(pg_shdepend, classid, ObjectIdGetDatum(shdep->classid), slot[slot_stored_count]->tts_values);
+		HeapTupleSetValue(pg_shdepend, objid, ObjectIdGetDatum(shdep->objid), slot[slot_stored_count]->tts_values);
+		HeapTupleSetValue(pg_shdepend, objsubid, Int32GetDatum(shdep->objsubid), slot[slot_stored_count]->tts_values);
+		HeapTupleSetValue(pg_shdepend, refclassid, ObjectIdGetDatum(shdep->refclassid), slot[slot_stored_count]->tts_values);
+		HeapTupleSetValue(pg_shdepend, refobjid, ObjectIdGetDatum(shdep->refobjid), slot[slot_stored_count]->tts_values);
+		HeapTupleSetValue(pg_shdepend, deptype, CharGetDatum(shdep->deptype), slot[slot_stored_count]->tts_values);
 
 		ExecStoreVirtualTuple(slot[slot_stored_count]);
 		slot_stored_count++;
@@ -969,14 +972,14 @@ copyTemplateDependencies(Oid templateDbId, Oid newDbId)
 		/* If slots are full, insert a batch of tuples */
 		if (slot_stored_count == max_slots)
 		{
-			CatalogTuplesMultiInsertWithInfo(sdepRel, slot, slot_stored_count, indstate);
+			CatalogTuplesMultiInsert(sdepRel, slot, slot_stored_count, indstate);
 			slot_stored_count = 0;
 		}
 	}
 
 	/* Insert any tuples left in the buffer */
 	if (slot_stored_count > 0)
-		CatalogTuplesMultiInsertWithInfo(sdepRel, slot, slot_stored_count, indstate);
+		CatalogTuplesMultiInsert(sdepRel, slot, slot_stored_count, indstate);
 
 	systable_endscan(scan);
 
@@ -1072,8 +1075,8 @@ shdepAddDependency(Relation sdepRel,
 				   SharedDependencyType deptype)
 {
 	HeapTuple	tup;
-	Datum		values[Natts_pg_shdepend];
-	bool		nulls[Natts_pg_shdepend];
+	Datum		values[Natts_pg_shdepend] = {0};
+	bool		nulls[Natts_pg_shdepend] = {false};
 
 	/*
 	 * Make sure the object doesn't go away while we record the dependency on
@@ -1087,18 +1090,18 @@ shdepAddDependency(Relation sdepRel,
 	/*
 	 * Form the new tuple and record the dependency.
 	 */
-	values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(classIdGetDbId(classId));
-	values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classId);
-	values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objectId);
-	values[Anum_pg_shdepend_objsubid - 1] = Int32GetDatum(objsubId);
+	HeapTupleSetValue(pg_shdepend, dbid, ObjectIdGetDatum(classIdGetDbId(classId)), values);
+	HeapTupleSetValue(pg_shdepend, classid, ObjectIdGetDatum(classId), values);
+	HeapTupleSetValue(pg_shdepend, objid, ObjectIdGetDatum(objectId), values);
+	HeapTupleSetValue(pg_shdepend, objsubid, Int32GetDatum(objsubId), values);
 
-	values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassId);
-	values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjId);
-	values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(deptype);
+	HeapTupleSetValue(pg_shdepend, refclassid, ObjectIdGetDatum(refclassId), values);
+	HeapTupleSetValue(pg_shdepend, refobjid, ObjectIdGetDatum(refobjId), values);
+	HeapTupleSetValue(pg_shdepend, deptype, CharGetDatum(deptype), values);
 
 	tup = heap_form_tuple(sdepRel->rd_att, values, nulls);
 
-	CatalogTupleInsert(sdepRel, tup);
+	CatalogTupleInsert(sdepRel, tup, NULL);
 
 	/* clean up */
 	heap_freetuple(tup);
diff --git a/src/backend/catalog/pg_subscription.c b/src/backend/catalog/pg_subscription.c
index 2b103245290..ffb0e422f7d 100644
--- a/src/backend/catalog/pg_subscription.c
+++ b/src/backend/catalog/pg_subscription.c
@@ -203,9 +203,9 @@ void
 DisableSubscription(Oid subid)
 {
 	Relation	rel;
-	bool		nulls[Natts_pg_subscription];
-	bool		replaces[Natts_pg_subscription];
-	Datum		values[Natts_pg_subscription];
+	Datum		values[Natts_pg_subscription] = {0};
+	bool		nulls[Natts_pg_subscription] = {false};
+	Bitmapset  *updated = NULL;
 	HeapTuple	tup;
 
 	/* Look up the subscription in the catalog */
@@ -217,19 +217,13 @@ DisableSubscription(Oid subid)
 
 	LockSharedObject(SubscriptionRelationId, subid, 0, AccessShareLock);
 
-	/* Form a new tuple. */
-	memset(values, 0, sizeof(values));
-	memset(nulls, false, sizeof(nulls));
-	memset(replaces, false, sizeof(replaces));
-
 	/* Set the subscription to disabled. */
-	values[Anum_pg_subscription_subenabled - 1] = BoolGetDatum(false);
-	replaces[Anum_pg_subscription_subenabled - 1] = true;
+	HeapTupleUpdateValue(pg_subscription, subenabled, BoolGetDatum(false), values, nulls, updated);
 
 	/* Update the catalog */
-	tup = heap_modify_tuple(tup, RelationGetDescr(rel), values, nulls,
-							replaces);
-	CatalogTupleUpdate(rel, &tup->t_self, tup);
+	tup = heap_update_tuple(tup, RelationGetDescr(rel), values, nulls, updated);
+	CatalogTupleUpdate(rel, &tup->t_self, tup, updated, NULL);
+	bms_free(updated);
 	heap_freetuple(tup);
 
 	table_close(rel, NoLock);
@@ -272,8 +266,8 @@ AddSubscriptionRelState(Oid subid, Oid relid, char state,
 {
 	Relation	rel;
 	HeapTuple	tup;
-	bool		nulls[Natts_pg_subscription_rel];
-	Datum		values[Natts_pg_subscription_rel];
+	Datum		values[Natts_pg_subscription_rel] = {0};
+	bool		nulls[Natts_pg_subscription_rel] = {false};
 
 	LockSharedObject(SubscriptionRelationId, subid, 0, AccessShareLock);
 
@@ -288,20 +282,18 @@ AddSubscriptionRelState(Oid subid, Oid relid, char state,
 			 relid, subid);
 
 	/* Form the tuple. */
-	memset(values, 0, sizeof(values));
-	memset(nulls, false, sizeof(nulls));
-	values[Anum_pg_subscription_rel_srsubid - 1] = ObjectIdGetDatum(subid);
-	values[Anum_pg_subscription_rel_srrelid - 1] = ObjectIdGetDatum(relid);
-	values[Anum_pg_subscription_rel_srsubstate - 1] = CharGetDatum(state);
+	HeapTupleSetValue(pg_subscription_rel, srsubid, ObjectIdGetDatum(subid), values);
+	HeapTupleSetValue(pg_subscription_rel, srrelid, ObjectIdGetDatum(relid), values);
+	HeapTupleSetValue(pg_subscription_rel, srsubstate, CharGetDatum(state), values);
 	if (XLogRecPtrIsValid(sublsn))
-		values[Anum_pg_subscription_rel_srsublsn - 1] = LSNGetDatum(sublsn);
+		HeapTupleSetValue(pg_subscription_rel, srsublsn, LSNGetDatum(sublsn), values);
 	else
-		nulls[Anum_pg_subscription_rel_srsublsn - 1] = true;
+		HeapTupleSetValueNull(pg_subscription_rel, srsublsn, values, nulls);
 
 	tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
 
 	/* Insert tuple into catalog. */
-	CatalogTupleInsert(rel, tup);
+	CatalogTupleInsert(rel, tup, NULL);
 
 	heap_freetuple(tup);
 
@@ -326,9 +318,9 @@ UpdateSubscriptionRelState(Oid subid, Oid relid, char state,
 {
 	Relation	rel;
 	HeapTuple	tup;
-	bool		nulls[Natts_pg_subscription_rel];
-	Datum		values[Natts_pg_subscription_rel];
-	bool		replaces[Natts_pg_subscription_rel];
+	Datum		values[Natts_pg_subscription_rel] = {0};
+	bool		nulls[Natts_pg_subscription_rel] = {false};
+	Bitmapset  *updated = NULL;
 
 	if (already_locked)
 	{
@@ -358,27 +350,21 @@ UpdateSubscriptionRelState(Oid subid, Oid relid, char state,
 			 relid, subid);
 
 	/* Update the tuple. */
-	memset(values, 0, sizeof(values));
-	memset(nulls, false, sizeof(nulls));
-	memset(replaces, false, sizeof(replaces));
-
-	replaces[Anum_pg_subscription_rel_srsubstate - 1] = true;
-	values[Anum_pg_subscription_rel_srsubstate - 1] = CharGetDatum(state);
+	HeapTupleUpdateValue(pg_subscription_rel, srsubstate, CharGetDatum(state), values, nulls, updated);
 
-	replaces[Anum_pg_subscription_rel_srsublsn - 1] = true;
 	if (XLogRecPtrIsValid(sublsn))
-		values[Anum_pg_subscription_rel_srsublsn - 1] = LSNGetDatum(sublsn);
+		HeapTupleUpdateValue(pg_subscription_rel, srsublsn, LSNGetDatum(sublsn), values, nulls, updated);
 	else
-		nulls[Anum_pg_subscription_rel_srsublsn - 1] = true;
+		HeapTupleUpdateValueNull(pg_subscription_rel, srsublsn, values, nulls, updated);
 
-	tup = heap_modify_tuple(tup, RelationGetDescr(rel), values, nulls,
-							replaces);
+	tup = heap_update_tuple(tup, RelationGetDescr(rel), values, nulls, updated);
 
 	/* Update the catalog. */
-	CatalogTupleUpdate(rel, &tup->t_self, tup);
+	CatalogTupleUpdate(rel, &tup->t_self, tup, updated, NULL);
 
 	/* Cleanup. */
 	table_close(rel, NoLock);
+	bms_free(updated);
 }
 
 /*
@@ -645,9 +631,9 @@ void
 UpdateDeadTupleRetentionStatus(Oid subid, bool active)
 {
 	Relation	rel;
-	bool		nulls[Natts_pg_subscription];
-	bool		replaces[Natts_pg_subscription];
-	Datum		values[Natts_pg_subscription];
+	Datum		values[Natts_pg_subscription] = {0};
+	bool		nulls[Natts_pg_subscription] = {false};
+	Bitmapset  *updated = NULL;
 	HeapTuple	tup;
 
 	/* Look up the subscription in the catalog */
@@ -659,20 +645,16 @@ UpdateDeadTupleRetentionStatus(Oid subid, bool active)
 
 	LockSharedObject(SubscriptionRelationId, subid, 0, AccessShareLock);
 
-	/* Form a new tuple. */
-	memset(values, 0, sizeof(values));
-	memset(nulls, false, sizeof(nulls));
-	memset(replaces, false, sizeof(replaces));
-
 	/* Set the subscription to disabled. */
-	values[Anum_pg_subscription_subretentionactive - 1] = active;
-	replaces[Anum_pg_subscription_subretentionactive - 1] = true;
+	HeapTupleUpdateValue(pg_subscription, subretentionactive, active, values, nulls, updated);
 
 	/* Update the catalog */
-	tup = heap_modify_tuple(tup, RelationGetDescr(rel), values, nulls,
-							replaces);
-	CatalogTupleUpdate(rel, &tup->t_self, tup);
-	heap_freetuple(tup);
+	tup = heap_update_tuple(tup, RelationGetDescr(rel), values, nulls, updated);
 
+	CatalogTupleUpdate(rel, &tup->t_self, tup, updated, NULL);
+
+	/* Cleanup */
+	bms_free(updated);
+	heap_freetuple(tup);
 	table_close(rel, NoLock);
 }
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index fc369c35aa6..0270a2e3d32 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -60,8 +60,8 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
 	TupleDesc	tupDesc;
 	int			i;
 	HeapTuple	tup;
-	Datum		values[Natts_pg_type];
-	bool		nulls[Natts_pg_type];
+	Datum		values[Natts_pg_type] = {0};
+	bool		nulls[Natts_pg_type] = {false};
 	Oid			typoid;
 	NameData	name;
 	ObjectAddress address;
@@ -92,37 +92,37 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
 	 * mistaken for a usable type.
 	 */
 	namestrcpy(&name, typeName);
-	values[Anum_pg_type_typname - 1] = NameGetDatum(&name);
-	values[Anum_pg_type_typnamespace - 1] = ObjectIdGetDatum(typeNamespace);
-	values[Anum_pg_type_typowner - 1] = ObjectIdGetDatum(ownerId);
-	values[Anum_pg_type_typlen - 1] = Int16GetDatum(sizeof(int32));
-	values[Anum_pg_type_typbyval - 1] = BoolGetDatum(true);
-	values[Anum_pg_type_typtype - 1] = CharGetDatum(TYPTYPE_PSEUDO);
-	values[Anum_pg_type_typcategory - 1] = CharGetDatum(TYPCATEGORY_PSEUDOTYPE);
-	values[Anum_pg_type_typispreferred - 1] = BoolGetDatum(false);
-	values[Anum_pg_type_typisdefined - 1] = BoolGetDatum(false);
-	values[Anum_pg_type_typdelim - 1] = CharGetDatum(DEFAULT_TYPDELIM);
-	values[Anum_pg_type_typrelid - 1] = ObjectIdGetDatum(InvalidOid);
-	values[Anum_pg_type_typsubscript - 1] = ObjectIdGetDatum(InvalidOid);
-	values[Anum_pg_type_typelem - 1] = ObjectIdGetDatum(InvalidOid);
-	values[Anum_pg_type_typarray - 1] = ObjectIdGetDatum(InvalidOid);
-	values[Anum_pg_type_typinput - 1] = ObjectIdGetDatum(F_SHELL_IN);
-	values[Anum_pg_type_typoutput - 1] = ObjectIdGetDatum(F_SHELL_OUT);
-	values[Anum_pg_type_typreceive - 1] = ObjectIdGetDatum(InvalidOid);
-	values[Anum_pg_type_typsend - 1] = ObjectIdGetDatum(InvalidOid);
-	values[Anum_pg_type_typmodin - 1] = ObjectIdGetDatum(InvalidOid);
-	values[Anum_pg_type_typmodout - 1] = ObjectIdGetDatum(InvalidOid);
-	values[Anum_pg_type_typanalyze - 1] = ObjectIdGetDatum(InvalidOid);
-	values[Anum_pg_type_typalign - 1] = CharGetDatum(TYPALIGN_INT);
-	values[Anum_pg_type_typstorage - 1] = CharGetDatum(TYPSTORAGE_PLAIN);
-	values[Anum_pg_type_typnotnull - 1] = BoolGetDatum(false);
-	values[Anum_pg_type_typbasetype - 1] = ObjectIdGetDatum(InvalidOid);
-	values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(-1);
-	values[Anum_pg_type_typndims - 1] = Int32GetDatum(0);
-	values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(InvalidOid);
-	nulls[Anum_pg_type_typdefaultbin - 1] = true;
-	nulls[Anum_pg_type_typdefault - 1] = true;
-	nulls[Anum_pg_type_typacl - 1] = true;
+	HeapTupleSetValue(pg_type, typname, NameGetDatum(&name), values);
+	HeapTupleSetValue(pg_type, typnamespace, ObjectIdGetDatum(typeNamespace), values);
+	HeapTupleSetValue(pg_type, typowner, ObjectIdGetDatum(ownerId), values);
+	HeapTupleSetValue(pg_type, typlen, Int16GetDatum(sizeof(int32)), values);
+	HeapTupleSetValue(pg_type, typbyval, BoolGetDatum(true), values);
+	HeapTupleSetValue(pg_type, typtype, CharGetDatum(TYPTYPE_PSEUDO), values);
+	HeapTupleSetValue(pg_type, typcategory, CharGetDatum(TYPCATEGORY_PSEUDOTYPE), values);
+	HeapTupleSetValue(pg_type, typispreferred, BoolGetDatum(false), values);
+	HeapTupleSetValue(pg_type, typisdefined, BoolGetDatum(false), values);
+	HeapTupleSetValue(pg_type, typdelim, CharGetDatum(DEFAULT_TYPDELIM), values);
+	HeapTupleSetValue(pg_type, typrelid, ObjectIdGetDatum(InvalidOid), values);
+	HeapTupleSetValue(pg_type, typsubscript, ObjectIdGetDatum(InvalidOid), values);
+	HeapTupleSetValue(pg_type, typelem, ObjectIdGetDatum(InvalidOid), values);
+	HeapTupleSetValue(pg_type, typarray, ObjectIdGetDatum(InvalidOid), values);
+	HeapTupleSetValue(pg_type, typinput, ObjectIdGetDatum(F_SHELL_IN), values);
+	HeapTupleSetValue(pg_type, typoutput, ObjectIdGetDatum(F_SHELL_OUT), values);
+	HeapTupleSetValue(pg_type, typreceive, ObjectIdGetDatum(InvalidOid), values);
+	HeapTupleSetValue(pg_type, typsend, ObjectIdGetDatum(InvalidOid), values);
+	HeapTupleSetValue(pg_type, typmodin, ObjectIdGetDatum(InvalidOid), values);
+	HeapTupleSetValue(pg_type, typmodout, ObjectIdGetDatum(InvalidOid), values);
+	HeapTupleSetValue(pg_type, typanalyze, ObjectIdGetDatum(InvalidOid), values);
+	HeapTupleSetValue(pg_type, typalign, CharGetDatum(TYPALIGN_INT), values);
+	HeapTupleSetValue(pg_type, typstorage, CharGetDatum(TYPSTORAGE_PLAIN), values);
+	HeapTupleSetValue(pg_type, typnotnull, BoolGetDatum(false), values);
+	HeapTupleSetValue(pg_type, typbasetype, ObjectIdGetDatum(InvalidOid), values);
+	HeapTupleSetValue(pg_type, typtypmod, Int32GetDatum(-1), values);
+	HeapTupleSetValue(pg_type, typndims, Int32GetDatum(0), values);
+	HeapTupleSetValue(pg_type, typcollation, ObjectIdGetDatum(InvalidOid), values);
+	HeapTupleSetValueNull(pg_type, typdefaultbin, values, nulls);
+	HeapTupleSetValueNull(pg_type, typdefault, values, nulls);
+	HeapTupleSetValueNull(pg_type, typacl, values, nulls);
 
 	/* Use binary-upgrade override for pg_type.oid? */
 	if (IsBinaryUpgrade)
@@ -141,7 +141,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
 									Anum_pg_type_oid);
 	}
 
-	values[Anum_pg_type_oid - 1] = ObjectIdGetDatum(typoid);
+	HeapTupleSetValue(pg_type, oid, ObjectIdGetDatum(typoid), values);
 
 	/*
 	 * create a new type tuple
@@ -151,7 +151,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
 	/*
 	 * insert the tuple in the relation and get the tuple's oid.
 	 */
-	CatalogTupleInsert(pg_type_desc, tup);
+	CatalogTupleInsert(pg_type_desc, tup, NULL);
 
 	/*
 	 * Create dependencies.  We can/must skip this in bootstrap mode.
@@ -229,13 +229,12 @@ TypeCreate(Oid newTypeOid,
 	Oid			typeObjectId;
 	bool		isDependentType;
 	bool		rebuildDeps = false;
-	Acl		   *typacl;
+	Acl		   *typacl = NULL;
 	HeapTuple	tup;
-	bool		nulls[Natts_pg_type];
-	bool		replaces[Natts_pg_type];
-	Datum		values[Natts_pg_type];
+	Datum		values[Natts_pg_type] = {0};
+	bool		nulls[Natts_pg_type] = {false};
+	Bitmapset  *updated = NULL;
 	NameData	name;
-	int			i;
 	ObjectAddress address;
 
 	/*
@@ -334,78 +333,65 @@ TypeCreate(Oid newTypeOid,
 		typeType == TYPTYPE_MULTIRANGE ||
 		(OidIsValid(relationOid) && relationKind != RELKIND_COMPOSITE_TYPE);
 
-	/*
-	 * initialize arrays needed for heap_form_tuple or heap_modify_tuple
-	 */
-	for (i = 0; i < Natts_pg_type; ++i)
-	{
-		nulls[i] = false;
-		replaces[i] = true;
-		values[i] = (Datum) 0;
-	}
+	HeapTupleUpdateSetAllColumnsUpdated(pg_type, updated);
 
-	/*
-	 * insert data values
-	 */
+	/* Insert data values */
 	namestrcpy(&name, typeName);
-	values[Anum_pg_type_typname - 1] = NameGetDatum(&name);
-	values[Anum_pg_type_typnamespace - 1] = ObjectIdGetDatum(typeNamespace);
-	values[Anum_pg_type_typowner - 1] = ObjectIdGetDatum(ownerId);
-	values[Anum_pg_type_typlen - 1] = Int16GetDatum(internalSize);
-	values[Anum_pg_type_typbyval - 1] = BoolGetDatum(passedByValue);
-	values[Anum_pg_type_typtype - 1] = CharGetDatum(typeType);
-	values[Anum_pg_type_typcategory - 1] = CharGetDatum(typeCategory);
-	values[Anum_pg_type_typispreferred - 1] = BoolGetDatum(typePreferred);
-	values[Anum_pg_type_typisdefined - 1] = BoolGetDatum(true);
-	values[Anum_pg_type_typdelim - 1] = CharGetDatum(typDelim);
-	values[Anum_pg_type_typrelid - 1] = ObjectIdGetDatum(relationOid);
-	values[Anum_pg_type_typsubscript - 1] = ObjectIdGetDatum(subscriptProcedure);
-	values[Anum_pg_type_typelem - 1] = ObjectIdGetDatum(elementType);
-	values[Anum_pg_type_typarray - 1] = ObjectIdGetDatum(arrayType);
-	values[Anum_pg_type_typinput - 1] = ObjectIdGetDatum(inputProcedure);
-	values[Anum_pg_type_typoutput - 1] = ObjectIdGetDatum(outputProcedure);
-	values[Anum_pg_type_typreceive - 1] = ObjectIdGetDatum(receiveProcedure);
-	values[Anum_pg_type_typsend - 1] = ObjectIdGetDatum(sendProcedure);
-	values[Anum_pg_type_typmodin - 1] = ObjectIdGetDatum(typmodinProcedure);
-	values[Anum_pg_type_typmodout - 1] = ObjectIdGetDatum(typmodoutProcedure);
-	values[Anum_pg_type_typanalyze - 1] = ObjectIdGetDatum(analyzeProcedure);
-	values[Anum_pg_type_typalign - 1] = CharGetDatum(alignment);
-	values[Anum_pg_type_typstorage - 1] = CharGetDatum(storage);
-	values[Anum_pg_type_typnotnull - 1] = BoolGetDatum(typeNotNull);
-	values[Anum_pg_type_typbasetype - 1] = ObjectIdGetDatum(baseType);
-	values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(typeMod);
-	values[Anum_pg_type_typndims - 1] = Int32GetDatum(typNDims);
-	values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(typeCollation);
+	HeapTupleUpdateValue(pg_type, typname, NameGetDatum(&name), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typnamespace, ObjectIdGetDatum(typeNamespace), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typowner, ObjectIdGetDatum(ownerId), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typlen, Int16GetDatum(internalSize), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typbyval, BoolGetDatum(passedByValue), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typtype, CharGetDatum(typeType), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typcategory, CharGetDatum(typeCategory), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typispreferred, BoolGetDatum(typePreferred), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typisdefined, BoolGetDatum(true), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typdelim, CharGetDatum(typDelim), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typrelid, ObjectIdGetDatum(relationOid), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typsubscript, ObjectIdGetDatum(subscriptProcedure), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typelem, ObjectIdGetDatum(elementType), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typarray, ObjectIdGetDatum(arrayType), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typinput, ObjectIdGetDatum(inputProcedure), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typoutput, ObjectIdGetDatum(outputProcedure), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typreceive, ObjectIdGetDatum(receiveProcedure), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typsend, ObjectIdGetDatum(sendProcedure), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typmodin, ObjectIdGetDatum(typmodinProcedure), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typmodout, ObjectIdGetDatum(typmodoutProcedure), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typanalyze, ObjectIdGetDatum(analyzeProcedure), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typalign, CharGetDatum(alignment), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typstorage, CharGetDatum(storage), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typnotnull, BoolGetDatum(typeNotNull), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typbasetype, ObjectIdGetDatum(baseType), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typtypmod, Int32GetDatum(typeMod), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typndims, Int32GetDatum(typNDims), values, nulls, updated);
+	HeapTupleUpdateValue(pg_type, typcollation, ObjectIdGetDatum(typeCollation), values, nulls, updated);
 
 	/*
-	 * initialize the default binary value for this type.  Check for nulls of
-	 * course.
+	 * Initialize the default binary value for this type.
 	 */
 	if (defaultTypeBin)
-		values[Anum_pg_type_typdefaultbin - 1] = CStringGetTextDatum(defaultTypeBin);
+		HeapTupleUpdateValue(pg_type, typdefaultbin, CStringGetTextDatum(defaultTypeBin), values, nulls, updated);
 	else
-		nulls[Anum_pg_type_typdefaultbin - 1] = true;
+		HeapTupleUpdateValueNull(pg_type, typdefaultbin, values, nulls, updated);
 
 	/*
-	 * initialize the default value for this type.
+	 * Initialize the default value for this type.
 	 */
 	if (defaultTypeValue)
-		values[Anum_pg_type_typdefault - 1] = CStringGetTextDatum(defaultTypeValue);
+		HeapTupleUpdateValue(pg_type, typdefault, CStringGetTextDatum(defaultTypeValue), values, nulls, updated);
 	else
-		nulls[Anum_pg_type_typdefault - 1] = true;
+		HeapTupleUpdateValueNull(pg_type, typdefault, values, nulls, updated);
 
 	/*
-	 * Initialize the type's ACL, too.  But dependent types don't get one.
+	 * Initialize the type's ACL too, but dependent types don't get one.
 	 */
-	if (isDependentType)
-		typacl = NULL;
-	else
-		typacl = get_user_default_acl(OBJECT_TYPE, ownerId,
-									  typeNamespace);
+	if (!isDependentType)
+		typacl = get_user_default_acl(OBJECT_TYPE, ownerId, typeNamespace);
+
 	if (typacl != NULL)
-		values[Anum_pg_type_typacl - 1] = PointerGetDatum(typacl);
+		HeapTupleUpdateValue(pg_type, typacl, PointerGetDatum(typacl), values, nulls, updated);
 	else
-		nulls[Anum_pg_type_typacl - 1] = true;
+		HeapTupleUpdateValueNull(pg_type, typacl, values, nulls, updated);
 
 	/*
 	 * open pg_type and prepare to insert or update a row.
@@ -431,9 +417,7 @@ TypeCreate(Oid newTypeOid,
 					(errcode(ERRCODE_DUPLICATE_OBJECT),
 					 errmsg("type \"%s\" already exists", typeName)));
 
-		/*
-		 * shell type must have been created by same owner
-		 */
+		/* Shell type must have been created by same owner */
 		if (typform->typowner != ownerId)
 			aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_TYPE, typeName);
 
@@ -441,18 +425,16 @@ TypeCreate(Oid newTypeOid,
 		if (OidIsValid(newTypeOid))
 			elog(ERROR, "cannot assign new OID to existing shell type");
 
-		replaces[Anum_pg_type_oid - 1] = false;
+		HeapTupleSetColumnNotUpdated(pg_type, oid, updated);
 
-		/*
-		 * Okay to update existing shell type tuple
-		 */
-		tup = heap_modify_tuple(tup,
+		/* Okay to update existing shell type tuple */
+		tup = heap_update_tuple(tup,
 								RelationGetDescr(pg_type_desc),
 								values,
 								nulls,
-								replaces);
+								updated);
 
-		CatalogTupleUpdate(pg_type_desc, &tup->t_self, tup);
+		CatalogTupleUpdate(pg_type_desc, &tup->t_self, tup, updated, NULL);
 
 		typeObjectId = typform->oid;
 
@@ -480,12 +462,11 @@ TypeCreate(Oid newTypeOid,
 											  Anum_pg_type_oid);
 		}
 
-		values[Anum_pg_type_oid - 1] = ObjectIdGetDatum(typeObjectId);
+		HeapTupleUpdateValue(pg_type, oid, ObjectIdGetDatum(typeObjectId), values, nulls, updated);
 
-		tup = heap_form_tuple(RelationGetDescr(pg_type_desc),
-							  values, nulls);
+		tup = heap_form_tuple(RelationGetDescr(pg_type_desc), values, nulls);
 
-		CatalogTupleInsert(pg_type_desc, tup);
+		CatalogTupleInsert(pg_type_desc, tup, NULL);
 	}
 
 	/*
@@ -509,9 +490,8 @@ TypeCreate(Oid newTypeOid,
 
 	ObjectAddressSet(address, TypeRelationId, typeObjectId);
 
-	/*
-	 * finish up
-	 */
+	/* Clean up */
+	bms_free(updated);
 	table_close(pg_type_desc, RowExclusiveLock);
 
 	return address;
@@ -765,6 +745,7 @@ RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
 	Relation	pg_type_desc;
 	HeapTuple	tuple;
 	Form_pg_type typ;
+	Bitmapset  *updated = NULL;
 	Oid			arrayOid;
 	Oid			oldTypeOid;
 
@@ -804,12 +785,14 @@ RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
 	}
 
 	/* OK, do the rename --- tuple is a copy, so OK to scribble on it */
-	namestrcpy(&(typ->typname), newTypeName);
+	namestrcpy(&typ->typname, newTypeName);
+	HeapTupleMarkColumnUpdated(pg_type, typname, updated);
 
-	CatalogTupleUpdate(pg_type_desc, &tuple->t_self, tuple);
+	CatalogTupleUpdate(pg_type_desc, &tuple->t_self, tuple, updated, NULL);
 
 	InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0);
 
+	bms_free(updated);
 	heap_freetuple(tuple);
 	table_close(pg_type_desc, RowExclusiveLock);
 
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index c78dcea98c1..8cdfcdb3d68 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -336,14 +336,17 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 
 	if (!IsBootstrapProcessingMode())
 	{
+		Bitmapset  *updated = NULL;
+
 		/* normal case, use a transactional update */
 		reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
 		if (!HeapTupleIsValid(reltup))
 			elog(ERROR, "cache lookup failed for relation %u", relOid);
 
-		((Form_pg_class) GETSTRUCT(reltup))->reltoastrelid = toast_relid;
+		HeapTupleUpdateField(pg_class, reltoastrelid, toast_relid, (Form_pg_class) GETSTRUCT(reltup), updated);
+		CatalogTupleUpdate(class_rel, &reltup->t_self, reltup, updated, NULL);
 
-		CatalogTupleUpdate(class_rel, &reltup->t_self, reltup);
+		bms_free(updated);
 	}
 	else
 	{
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index 21825e8c3f5..efeedc1c3c8 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -47,8 +47,8 @@ CreateAccessMethod(CreateAmStmt *stmt)
 	ObjectAddress referenced;
 	Oid			amoid;
 	Oid			amhandler;
-	bool		nulls[Natts_pg_am];
-	Datum		values[Natts_pg_am];
+	Datum		values[Natts_pg_am] = {0};
+	bool		nulls[Natts_pg_am] = {false};
 	HeapTuple	tup;
 
 	rel = table_open(AccessMethodRelationId, RowExclusiveLock);
@@ -84,15 +84,14 @@ CreateAccessMethod(CreateAmStmt *stmt)
 	memset(nulls, false, sizeof(nulls));
 
 	amoid = GetNewOidWithIndex(rel, AmOidIndexId, Anum_pg_am_oid);
-	values[Anum_pg_am_oid - 1] = ObjectIdGetDatum(amoid);
-	values[Anum_pg_am_amname - 1] =
-		DirectFunctionCall1(namein, CStringGetDatum(stmt->amname));
-	values[Anum_pg_am_amhandler - 1] = ObjectIdGetDatum(amhandler);
-	values[Anum_pg_am_amtype - 1] = CharGetDatum(stmt->amtype);
+	HeapTupleSetValue(pg_am, oid, ObjectIdGetDatum(amoid), values);
+	HeapTupleSetValue(pg_am, amname, DirectFunctionCall1(namein, CStringGetDatum(stmt->amname)), values);
+	HeapTupleSetValue(pg_am, amhandler, ObjectIdGetDatum(amhandler), values);
+	HeapTupleSetValue(pg_am, amtype, CharGetDatum(stmt->amtype), values);
 
 	tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
 
-	CatalogTupleInsert(rel, tup);
+	CatalogTupleInsert(rel, tup, NULL);
 	heap_freetuple(tup);
 
 	myself.classId = AccessMethodRelationId;
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 60a4617a585..83b9528fff2 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -557,6 +557,7 @@ mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
 	Form_pg_index indexForm;
 	Relation	pg_index;
 	ListCell   *index;
+	Bitmapset  *updated = NULL;
 
 	/* Disallow applying to a partitioned table */
 	if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
@@ -594,16 +595,16 @@ mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
 		 */
 		if (indexForm->indisclustered)
 		{
-			indexForm->indisclustered = false;
-			CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple);
+			HeapTupleUpdateField(pg_index, indisclustered, false, indexForm, updated);
+			CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple, updated, NULL);
 		}
 		else if (thisIndexOid == indexOid)
 		{
 			/* this was checked earlier, but let's be real sure */
 			if (!indexForm->indisvalid)
 				elog(ERROR, "cannot cluster on invalid index %u", indexOid);
-			indexForm->indisclustered = true;
-			CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple);
+			HeapTupleUpdateField(pg_index, indisclustered, true, indexForm, updated);
+			CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple, updated, NULL);
 		}
 
 		InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
@@ -613,6 +614,7 @@ mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
 	}
 
 	table_close(pg_index, RowExclusiveLock);
+	bms_free(updated);
 }
 
 /*
@@ -847,6 +849,7 @@ copy_table_data(Relation NewHeap, Relation OldHeap, Relation OldIndex, bool verb
 	int			elevel = verbose ? INFO : DEBUG2;
 	PGRUsage	ru0;
 	char	   *nspname;
+	Bitmapset  *updated = NULL;
 
 	pg_rusage_init(&ru0);
 
@@ -1016,18 +1019,19 @@ copy_table_data(Relation NewHeap, Relation OldHeap, Relation OldIndex, bool verb
 			 RelationGetRelid(NewHeap));
 	relform = (Form_pg_class) GETSTRUCT(reltup);
 
-	relform->relpages = num_pages;
-	relform->reltuples = num_tuples;
+	HeapTupleUpdateField(pg_class, relpages, num_pages, relform, updated);
+	HeapTupleUpdateField(pg_class, reltuples, num_tuples, relform, updated);
 
 	/* Don't update the stats for pg_class.  See swap_relation_files. */
 	if (RelationGetRelid(OldHeap) != RelationRelationId)
-		CatalogTupleUpdate(relRelation, &reltup->t_self, reltup);
+		CatalogTupleUpdate(relRelation, &reltup->t_self, reltup, updated, NULL);
 	else
 		CacheInvalidateRelcacheByTuple(reltup);
 
 	/* Clean up. */
 	heap_freetuple(reltup);
 	table_close(relRelation, RowExclusiveLock);
+	bms_free(updated);
 
 	/* Make the update visible */
 	CommandCounterIncrement();
@@ -1078,6 +1082,8 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
 	char		swptmpchr;
 	Oid			relam1,
 				relam2;
+	Bitmapset  *updated1 = NULL;
+	Bitmapset  *updated2 = NULL;
 
 	/* We need writable copies of both pg_class tuples. */
 	relRelation = table_open(RelationRelationId, RowExclusiveLock);
@@ -1107,27 +1113,27 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
 		Assert(!target_is_pg_class);
 
 		swaptemp = relform1->relfilenode;
-		relform1->relfilenode = relform2->relfilenode;
-		relform2->relfilenode = swaptemp;
+		HeapTupleUpdateField(pg_class, relfilenode, relform2->relfilenode, relform1, updated1);
+		HeapTupleUpdateField(pg_class, relfilenode, swaptemp, relform2, updated2);
 
 		swaptemp = relform1->reltablespace;
-		relform1->reltablespace = relform2->reltablespace;
-		relform2->reltablespace = swaptemp;
+		HeapTupleUpdateField(pg_class, reltablespace, relform2->reltablespace, relform1, updated1);
+		HeapTupleUpdateField(pg_class, reltablespace, swaptemp, relform2, updated2);
 
 		swaptemp = relform1->relam;
-		relform1->relam = relform2->relam;
-		relform2->relam = swaptemp;
+		HeapTupleUpdateField(pg_class, relam, relform2->relam, relform1, updated1);
+		HeapTupleUpdateField(pg_class, relam, swaptemp, relform2, updated2);
 
 		swptmpchr = relform1->relpersistence;
-		relform1->relpersistence = relform2->relpersistence;
-		relform2->relpersistence = swptmpchr;
+		HeapTupleUpdateField(pg_class, relpersistence, relform2->relpersistence, relform1, updated1);
+		HeapTupleUpdateField(pg_class, relpersistence, swptmpchr, relform2, updated2);
 
 		/* Also swap toast links, if we're swapping by links */
 		if (!swap_toast_by_content)
 		{
 			swaptemp = relform1->reltoastrelid;
-			relform1->reltoastrelid = relform2->reltoastrelid;
-			relform2->reltoastrelid = swaptemp;
+			HeapTupleUpdateField(pg_class, reltoastrelid, relform2->reltoastrelid, relform1, updated1);
+			HeapTupleUpdateField(pg_class, reltoastrelid, swaptemp, relform2, updated2);
 		}
 	}
 	else
@@ -1217,8 +1223,8 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
 	{
 		Assert(!TransactionIdIsValid(frozenXid) ||
 			   TransactionIdIsNormal(frozenXid));
-		relform1->relfrozenxid = frozenXid;
-		relform1->relminmxid = cutoffMulti;
+		HeapTupleUpdateField(pg_class, relfrozenxid, frozenXid, relform1, updated1);
+		HeapTupleUpdateField(pg_class, relminmxid, cutoffMulti, relform1, updated1);
 	}
 
 	/* swap size statistics too, since new rel has freshly-updated stats */
@@ -1229,20 +1235,20 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
 		int32		swap_allfrozen;
 
 		swap_pages = relform1->relpages;
-		relform1->relpages = relform2->relpages;
-		relform2->relpages = swap_pages;
+		HeapTupleUpdateField(pg_class, relpages, relform2->relpages, relform1, updated1);
+		HeapTupleUpdateField(pg_class, relpages, swap_pages, relform2, updated2);
 
 		swap_tuples = relform1->reltuples;
-		relform1->reltuples = relform2->reltuples;
-		relform2->reltuples = swap_tuples;
+		HeapTupleUpdateField(pg_class, reltuples, relform2->reltuples, relform1, updated1);
+		HeapTupleUpdateField(pg_class, reltuples, swap_tuples, relform2, updated2);
 
 		swap_allvisible = relform1->relallvisible;
-		relform1->relallvisible = relform2->relallvisible;
-		relform2->relallvisible = swap_allvisible;
+		HeapTupleUpdateField(pg_class, relallvisible, relform2->relallvisible, relform1, updated1);
+		HeapTupleUpdateField(pg_class, relallvisible, swap_allvisible, relform2, updated2);
 
 		swap_allfrozen = relform1->relallfrozen;
-		relform1->relallfrozen = relform2->relallfrozen;
-		relform2->relallfrozen = swap_allfrozen;
+		HeapTupleUpdateField(pg_class, relallfrozen, relform2->relallfrozen, relform1, updated1);
+		HeapTupleUpdateField(pg_class, relallfrozen, swap_allfrozen, relform2, updated2);
 	}
 
 	/*
@@ -1256,13 +1262,11 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
 	 */
 	if (!target_is_pg_class)
 	{
-		CatalogIndexState indstate;
+		CatalogIndexState indstate = CatalogOpenIndexes(relRelation);
+
+		CatalogTupleUpdate(relRelation, &reltup1->t_self, reltup1, updated1, indstate);
+		CatalogTupleUpdate(relRelation, &reltup2->t_self, reltup2, updated2, indstate);
 
-		indstate = CatalogOpenIndexes(relRelation);
-		CatalogTupleUpdateWithInfo(relRelation, &reltup1->t_self, reltup1,
-								   indstate);
-		CatalogTupleUpdateWithInfo(relRelation, &reltup2->t_self, reltup2,
-								   indstate);
 		CatalogCloseIndexes(indstate);
 	}
 	else
@@ -1435,6 +1439,8 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
 	heap_freetuple(reltup2);
 
 	table_close(relRelation, RowExclusiveLock);
+	bms_free(updated1);
+	bms_free(updated2);
 }
 
 /*
@@ -1535,6 +1541,7 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
 		Relation	relRelation;
 		HeapTuple	reltup;
 		Form_pg_class relform;
+		Bitmapset  *updated = NULL;
 
 		relRelation = table_open(RelationRelationId, RowExclusiveLock);
 
@@ -1543,12 +1550,13 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
 			elog(ERROR, "cache lookup failed for relation %u", OIDOldHeap);
 		relform = (Form_pg_class) GETSTRUCT(reltup);
 
-		relform->relfrozenxid = frozenXid;
-		relform->relminmxid = cutoffMulti;
+		HeapTupleUpdateField(pg_class, relfrozenxid, frozenXid, relform, updated);
+		HeapTupleUpdateField(pg_class, relminmxid, cutoffMulti, relform, updated);
 
-		CatalogTupleUpdate(relRelation, &reltup->t_self, reltup);
+		CatalogTupleUpdate(relRelation, &reltup->t_self, reltup, updated, NULL);
 
 		table_close(relRelation, RowExclusiveLock);
+		bms_free(updated);
 	}
 
 	/* Destroy new heap with old filenumber */
diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c
index 0bc31ec2b6f..0fe19f2ef87 100644
--- a/src/backend/commands/collationcmds.c
+++ b/src/backend/commands/collationcmds.c
@@ -432,6 +432,7 @@ AlterCollation(AlterCollationStmt *stmt)
 	Oid			collOid;
 	HeapTuple	tup;
 	Form_pg_collation collForm;
+	Bitmapset  *updated = NULL;
 	Datum		datum;
 	bool		isnull;
 	char	   *oldversion;
@@ -464,6 +465,7 @@ AlterCollation(AlterCollationStmt *stmt)
 		datum = SysCacheGetAttrNotNull(COLLOID, tup, Anum_pg_collation_collcollate);
 	else
 		datum = SysCacheGetAttrNotNull(COLLOID, tup, Anum_pg_collation_colllocale);
+	HeapTupleMarkColumnUpdated(pg_collation, collcollate, updated);
 
 	newversion = get_collation_actual_version(collForm->collprovider,
 											  TextDatumGetCString(datum));
@@ -473,29 +475,22 @@ AlterCollation(AlterCollationStmt *stmt)
 		elog(ERROR, "invalid collation version change");
 	else if (oldversion && newversion && strcmp(newversion, oldversion) != 0)
 	{
-		bool		nulls[Natts_pg_collation];
-		bool		replaces[Natts_pg_collation];
-		Datum		values[Natts_pg_collation];
+		bool		nulls[Natts_pg_collation] = {false};
+		Datum		values[Natts_pg_collation] = {0};
 
 		ereport(NOTICE,
 				(errmsg("changing version from %s to %s",
 						oldversion, newversion)));
 
-		memset(values, 0, sizeof(values));
-		memset(nulls, false, sizeof(nulls));
-		memset(replaces, false, sizeof(replaces));
+		HeapTupleUpdateValue(pg_collation, collversion, CStringGetTextDatum(newversion), values, nulls, updated);
 
-		values[Anum_pg_collation_collversion - 1] = CStringGetTextDatum(newversion);
-		replaces[Anum_pg_collation_collversion - 1] = true;
-
-		tup = heap_modify_tuple(tup, RelationGetDescr(rel),
-								values, nulls, replaces);
+		tup = heap_update_tuple(tup, RelationGetDescr(rel), values, nulls, updated);
 	}
 	else
 		ereport(NOTICE,
 				(errmsg("version has not changed")));
 
-	CatalogTupleUpdate(rel, &tup->t_self, tup);
+	CatalogTupleUpdate(rel, &tup->t_self, tup, updated, NULL);
 
 	InvokeObjectPostAlterHook(CollationRelationId, collOid, 0);
 
@@ -503,6 +498,7 @@ AlterCollation(AlterCollationStmt *stmt)
 
 	heap_freetuple(tup);
 	table_close(rel, NoLock);
+	bms_free(updated);
 
 	return address;
 }
diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c
index caacb17e5d7..678cf3320ed 100644
--- a/src/backend/commands/comment.c
+++ b/src/backend/commands/comment.c
@@ -147,10 +147,9 @@ CreateComments(Oid oid, Oid classoid, int32 subid, const char *comment)
 	SysScanDesc sd;
 	HeapTuple	oldtuple;
 	HeapTuple	newtuple = NULL;
-	Datum		values[Natts_pg_description];
-	bool		nulls[Natts_pg_description];
-	bool		replaces[Natts_pg_description];
-	int			i;
+	Datum		values[Natts_pg_description] = {0};
+	bool		nulls[Natts_pg_description] = {false};
+	Bitmapset  *updated = NULL;
 
 	/* Reduce empty-string to NULL case */
 	if (comment != NULL && strlen(comment) == 0)
@@ -159,15 +158,10 @@ CreateComments(Oid oid, Oid classoid, int32 subid, const char *comment)
 	/* Prepare to form or update a tuple, if necessary */
 	if (comment != NULL)
 	{
-		for (i = 0; i < Natts_pg_description; i++)
-		{
-			nulls[i] = false;
-			replaces[i] = true;
-		}
-		values[Anum_pg_description_objoid - 1] = ObjectIdGetDatum(oid);
-		values[Anum_pg_description_classoid - 1] = ObjectIdGetDatum(classoid);
-		values[Anum_pg_description_objsubid - 1] = Int32GetDatum(subid);
-		values[Anum_pg_description_description - 1] = CStringGetTextDatum(comment);
+		HeapTupleUpdateValue(pg_description, objoid, ObjectIdGetDatum(oid), values, nulls, updated);
+		HeapTupleUpdateValue(pg_description, classoid, ObjectIdGetDatum(classoid), values, nulls, updated);
+		HeapTupleUpdateValue(pg_description, objsubid, Int32GetDatum(subid), values, nulls, updated);
+		HeapTupleUpdateValue(pg_description, description, CStringGetTextDatum(comment), values, nulls, updated);
 	}
 
 	/* Use the index to search for a matching old tuple */
@@ -198,9 +192,9 @@ CreateComments(Oid oid, Oid classoid, int32 subid, const char *comment)
 			CatalogTupleDelete(description, &oldtuple->t_self);
 		else
 		{
-			newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(description), values,
-										 nulls, replaces);
-			CatalogTupleUpdate(description, &oldtuple->t_self, newtuple);
+			HeapTupleUpdateValue(pg_description, description, CStringGetTextDatum(comment), values, nulls, updated);
+			newtuple = heap_update_tuple(oldtuple, RelationGetDescr(description), values, nulls, updated);
+			CatalogTupleUpdate(description, &oldtuple->t_self, newtuple, updated, NULL);
 		}
 
 		break;					/* Assume there can be only one match */
@@ -214,7 +208,7 @@ CreateComments(Oid oid, Oid classoid, int32 subid, const char *comment)
 	{
 		newtuple = heap_form_tuple(RelationGetDescr(description),
 								   values, nulls);
-		CatalogTupleInsert(description, newtuple);
+		CatalogTupleInsert(description, newtuple, NULL);
 	}
 
 	if (newtuple != NULL)
@@ -223,6 +217,7 @@ CreateComments(Oid oid, Oid classoid, int32 subid, const char *comment)
 	/* Done */
 
 	table_close(description, NoLock);
+	bms_free(updated);
 }
 
 /*
@@ -242,9 +237,9 @@ CreateSharedComments(Oid oid, Oid classoid, const char *comment)
 	SysScanDesc sd;
 	HeapTuple	oldtuple;
 	HeapTuple	newtuple = NULL;
-	Datum		values[Natts_pg_shdescription];
-	bool		nulls[Natts_pg_shdescription];
-	bool		replaces[Natts_pg_shdescription];
+	Datum		values[Natts_pg_shdescription] = {0};
+	bool		nulls[Natts_pg_shdescription] = {false};
+	Bitmapset  *updated = NULL;
 	int			i;
 
 	/* Reduce empty-string to NULL case */
@@ -257,11 +252,10 @@ CreateSharedComments(Oid oid, Oid classoid, const char *comment)
 		for (i = 0; i < Natts_pg_shdescription; i++)
 		{
 			nulls[i] = false;
-			replaces[i] = true;
 		}
-		values[Anum_pg_shdescription_objoid - 1] = ObjectIdGetDatum(oid);
-		values[Anum_pg_shdescription_classoid - 1] = ObjectIdGetDatum(classoid);
-		values[Anum_pg_shdescription_description - 1] = CStringGetTextDatum(comment);
+		HeapTupleUpdateValue(pg_shdescription, objoid, ObjectIdGetDatum(oid), values, nulls, updated);
+		HeapTupleUpdateValue(pg_shdescription, classoid, ObjectIdGetDatum(classoid), values, nulls, updated);
+		HeapTupleUpdateValue(pg_shdescription, description, CStringGetTextDatum(comment), values, nulls, updated);
 	}
 
 	/* Use the index to search for a matching old tuple */
@@ -288,9 +282,9 @@ CreateSharedComments(Oid oid, Oid classoid, const char *comment)
 			CatalogTupleDelete(shdescription, &oldtuple->t_self);
 		else
 		{
-			newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(shdescription),
-										 values, nulls, replaces);
-			CatalogTupleUpdate(shdescription, &oldtuple->t_self, newtuple);
+			HeapTupleUpdateValue(pg_shdescription, description, CStringGetTextDatum(comment), values, nulls, updated);
+			newtuple = heap_update_tuple(oldtuple, RelationGetDescr(shdescription), values, nulls, updated);
+			CatalogTupleUpdate(shdescription, &oldtuple->t_self, newtuple, updated, NULL);
 		}
 
 		break;					/* Assume there can be only one match */
@@ -304,7 +298,7 @@ CreateSharedComments(Oid oid, Oid classoid, const char *comment)
 	{
 		newtuple = heap_form_tuple(RelationGetDescr(shdescription),
 								   values, nulls);
-		CatalogTupleInsert(shdescription, newtuple);
+		CatalogTupleInsert(shdescription, newtuple, NULL);
 	}
 
 	if (newtuple != NULL)
@@ -313,6 +307,7 @@ CreateSharedComments(Oid oid, Oid classoid, const char *comment)
 	/* Done */
 
 	table_close(shdescription, NoLock);
+	bms_free(updated);
 }
 
 /*
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 87949054f26..2d18d1fad22 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -702,8 +702,8 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
 	volatile Oid dst_deftablespace;
 	Relation	pg_database_rel;
 	HeapTuple	tuple;
-	Datum		new_record[Natts_pg_database] = {0};
-	bool		new_record_nulls[Natts_pg_database] = {0};
+	Datum		values[Natts_pg_database] = {0};
+	bool		nulls[Natts_pg_database] = {false};
 	Oid			dboid = InvalidOid;
 	Oid			datdba;
 	ListCell   *option;
@@ -1457,45 +1457,44 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
 		   (dblocprovider == COLLPROVIDER_LIBC && !dblocale));
 
 	/* Form tuple */
-	new_record[Anum_pg_database_oid - 1] = ObjectIdGetDatum(dboid);
-	new_record[Anum_pg_database_datname - 1] =
-		DirectFunctionCall1(namein, CStringGetDatum(dbname));
-	new_record[Anum_pg_database_datdba - 1] = ObjectIdGetDatum(datdba);
-	new_record[Anum_pg_database_encoding - 1] = Int32GetDatum(encoding);
-	new_record[Anum_pg_database_datlocprovider - 1] = CharGetDatum(dblocprovider);
-	new_record[Anum_pg_database_datistemplate - 1] = BoolGetDatum(dbistemplate);
-	new_record[Anum_pg_database_datallowconn - 1] = BoolGetDatum(dballowconnections);
-	new_record[Anum_pg_database_dathasloginevt - 1] = BoolGetDatum(src_hasloginevt);
-	new_record[Anum_pg_database_datconnlimit - 1] = Int32GetDatum(dbconnlimit);
-	new_record[Anum_pg_database_datfrozenxid - 1] = TransactionIdGetDatum(src_frozenxid);
-	new_record[Anum_pg_database_datminmxid - 1] = TransactionIdGetDatum(src_minmxid);
-	new_record[Anum_pg_database_dattablespace - 1] = ObjectIdGetDatum(dst_deftablespace);
-	new_record[Anum_pg_database_datcollate - 1] = CStringGetTextDatum(dbcollate);
-	new_record[Anum_pg_database_datctype - 1] = CStringGetTextDatum(dbctype);
+	HeapTupleSetValue(pg_database, oid, ObjectIdGetDatum(dboid), values);
+	HeapTupleSetValue(pg_database, datname, DirectFunctionCall1(namein, CStringGetDatum(dbname)), values);
+	HeapTupleSetValue(pg_database, datdba, ObjectIdGetDatum(datdba), values);
+	HeapTupleSetValue(pg_database, encoding, Int32GetDatum(encoding), values);
+	HeapTupleSetValue(pg_database, datlocprovider, CharGetDatum(dblocprovider), values);
+	HeapTupleSetValue(pg_database, datistemplate, BoolGetDatum(dbistemplate), values);
+	HeapTupleSetValue(pg_database, datallowconn, BoolGetDatum(dballowconnections), values);
+	HeapTupleSetValue(pg_database, dathasloginevt, BoolGetDatum(src_hasloginevt), values);
+	HeapTupleSetValue(pg_database, datconnlimit, Int32GetDatum(dbconnlimit), values);
+	HeapTupleSetValue(pg_database, datfrozenxid, TransactionIdGetDatum(src_frozenxid), values);
+	HeapTupleSetValue(pg_database, datminmxid, TransactionIdGetDatum(src_minmxid), values);
+	HeapTupleSetValue(pg_database, dattablespace, ObjectIdGetDatum(dst_deftablespace), values);
+	HeapTupleSetValue(pg_database, datcollate, CStringGetTextDatum(dbcollate), values);
+	HeapTupleSetValue(pg_database, datctype, CStringGetTextDatum(dbctype), values);
 	if (dblocale)
-		new_record[Anum_pg_database_datlocale - 1] = CStringGetTextDatum(dblocale);
+		HeapTupleSetValue(pg_database, datlocale, CStringGetTextDatum(dblocale), values);
 	else
-		new_record_nulls[Anum_pg_database_datlocale - 1] = true;
+		HeapTupleSetValueNull(pg_database, datlocale, values, nulls);
 	if (dbicurules)
-		new_record[Anum_pg_database_daticurules - 1] = CStringGetTextDatum(dbicurules);
+		HeapTupleSetValue(pg_database, daticurules, CStringGetTextDatum(dbicurules), values);
 	else
-		new_record_nulls[Anum_pg_database_daticurules - 1] = true;
+		HeapTupleSetValueNull(pg_database, daticurules, values, nulls);
 	if (dbcollversion)
-		new_record[Anum_pg_database_datcollversion - 1] = CStringGetTextDatum(dbcollversion);
+		HeapTupleSetValue(pg_database, datcollversion, CStringGetTextDatum(dbcollversion), values);
 	else
-		new_record_nulls[Anum_pg_database_datcollversion - 1] = true;
+		HeapTupleSetValueNull(pg_database, datcollversion, values, nulls);
 
 	/*
 	 * We deliberately set datacl to default (NULL), rather than copying it
 	 * from the template database.  Copying it would be a bad idea when the
 	 * owner is not the same as the template's owner.
 	 */
-	new_record_nulls[Anum_pg_database_datacl - 1] = true;
+	HeapTupleSetValueNull(pg_database, datacl, values, nulls);
 
 	tuple = heap_form_tuple(RelationGetDescr(pg_database_rel),
-							new_record, new_record_nulls);
+							values, nulls);
 
-	CatalogTupleInsert(pg_database_rel, tuple);
+	CatalogTupleInsert(pg_database_rel, tuple, NULL);
 
 	/*
 	 * Now generate additional catalog entries associated with the new DB
@@ -1909,6 +1908,7 @@ RenameDatabase(const char *oldname, const char *newname)
 	int			notherbackends;
 	int			npreparedxacts;
 	ObjectAddress address;
+	Bitmapset  *updated = NULL;
 
 	/*
 	 * Look up the target database's OID, and get exclusive lock on it. We
@@ -1980,8 +1980,9 @@ RenameDatabase(const char *oldname, const char *newname)
 	if (!HeapTupleIsValid(newtup))
 		elog(ERROR, "cache lookup failed for database %u", db_id);
 	otid = newtup->t_self;
-	namestrcpy(&(((Form_pg_database) GETSTRUCT(newtup))->datname), newname);
-	CatalogTupleUpdate(rel, &otid, newtup);
+	namestrcpy(&((Form_pg_database) GETSTRUCT(newtup))->datname, newname);
+	HeapTupleMarkColumnUpdated(pg_database, datname, updated);
+	CatalogTupleUpdate(rel, &otid, newtup, updated, NULL);
 	UnlockTuple(rel, &otid, InplaceUpdateTupleLock);
 
 	InvokeObjectPostAlterHook(DatabaseRelationId, db_id, 0);
@@ -1992,6 +1993,7 @@ RenameDatabase(const char *oldname, const char *newname)
 	 * Close pg_database, but keep lock till commit.
 	 */
 	table_close(rel, NoLock);
+	bms_free(updated);
 
 	return address;
 }
@@ -2189,9 +2191,9 @@ movedb(const char *dbname, const char *tblspcname)
 	PG_ENSURE_ERROR_CLEANUP(movedb_failure_callback,
 							PointerGetDatum(&fparms));
 	{
-		Datum		new_record[Natts_pg_database] = {0};
-		bool		new_record_nulls[Natts_pg_database] = {0};
-		bool		new_record_repl[Natts_pg_database] = {0};
+		Datum		values[Natts_pg_database] = {0};
+		bool		nulls[Natts_pg_database] = {false};
+		Bitmapset  *updated = NULL;
 
 		/*
 		 * Copy files from the old tablespace to the new one
@@ -2233,13 +2235,10 @@ movedb(const char *dbname, const char *tblspcname)
 					 errmsg("database \"%s\" does not exist", dbname)));
 		LockTuple(pgdbrel, &oldtuple->t_self, InplaceUpdateTupleLock);
 
-		new_record[Anum_pg_database_dattablespace - 1] = ObjectIdGetDatum(dst_tblspcoid);
-		new_record_repl[Anum_pg_database_dattablespace - 1] = true;
+		HeapTupleUpdateValue(pg_database, dattablespace, ObjectIdGetDatum(dst_tblspcoid), values, nulls, updated);
 
-		newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(pgdbrel),
-									 new_record,
-									 new_record_nulls, new_record_repl);
-		CatalogTupleUpdate(pgdbrel, &oldtuple->t_self, newtuple);
+		newtuple = heap_update_tuple(oldtuple, RelationGetDescr(pgdbrel), values, nulls, updated);
+		CatalogTupleUpdate(pgdbrel, &oldtuple->t_self, newtuple, updated, NULL);
 		UnlockTuple(pgdbrel, &oldtuple->t_self, InplaceUpdateTupleLock);
 
 		InvokeObjectPostAlterHook(DatabaseRelationId, db_id, 0);
@@ -2267,6 +2266,7 @@ movedb(const char *dbname, const char *tblspcname)
 		 * Close pg_database, but keep lock till commit.
 		 */
 		table_close(pgdbrel, NoLock);
+		bms_free(updated);
 	}
 	PG_END_ENSURE_ERROR_CLEANUP(movedb_failure_callback,
 								PointerGetDatum(&fparms));
@@ -2383,9 +2383,9 @@ AlterDatabase(ParseState *pstate, AlterDatabaseStmt *stmt, bool isTopLevel)
 	DefElem    *dallowconnections = NULL;
 	DefElem    *dconnlimit = NULL;
 	DefElem    *dtablespace = NULL;
-	Datum		new_record[Natts_pg_database] = {0};
-	bool		new_record_nulls[Natts_pg_database] = {0};
-	bool		new_record_repl[Natts_pg_database] = {0};
+	Datum		values[Natts_pg_database] = {0};
+	bool		nulls[Natts_pg_database] = {false};
+	Bitmapset  *updated = NULL;
 
 	/* Extract options from the statement node tree */
 	foreach(option, stmt->options)
@@ -2504,24 +2504,16 @@ AlterDatabase(ParseState *pstate, AlterDatabaseStmt *stmt, bool isTopLevel)
 	 * Build an updated tuple, perusing the information just obtained
 	 */
 	if (distemplate)
-	{
-		new_record[Anum_pg_database_datistemplate - 1] = BoolGetDatum(dbistemplate);
-		new_record_repl[Anum_pg_database_datistemplate - 1] = true;
-	}
+		HeapTupleUpdateValue(pg_database, datistemplate, BoolGetDatum(dbistemplate), values, nulls, updated);
+
 	if (dallowconnections)
-	{
-		new_record[Anum_pg_database_datallowconn - 1] = BoolGetDatum(dballowconnections);
-		new_record_repl[Anum_pg_database_datallowconn - 1] = true;
-	}
+		HeapTupleUpdateValue(pg_database, datallowconn, BoolGetDatum(dballowconnections), values, nulls, updated);
+
 	if (dconnlimit)
-	{
-		new_record[Anum_pg_database_datconnlimit - 1] = Int32GetDatum(dbconnlimit);
-		new_record_repl[Anum_pg_database_datconnlimit - 1] = true;
-	}
+		HeapTupleUpdateValue(pg_database, datconnlimit, Int32GetDatum(dbconnlimit), values, nulls, updated);
 
-	newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel), new_record,
-								 new_record_nulls, new_record_repl);
-	CatalogTupleUpdate(rel, &tuple->t_self, newtuple);
+	newtuple = heap_update_tuple(tuple, RelationGetDescr(rel), values, nulls, updated);
+	CatalogTupleUpdate(rel, &tuple->t_self, newtuple, updated, NULL);
 	UnlockTuple(rel, &tuple->t_self, InplaceUpdateTupleLock);
 
 	InvokeObjectPostAlterHook(DatabaseRelationId, dboid, 0);
@@ -2530,6 +2522,7 @@ AlterDatabase(ParseState *pstate, AlterDatabaseStmt *stmt, bool isTopLevel)
 
 	/* Close pg_database, but keep lock till commit */
 	table_close(rel, NoLock);
+	bms_free(updated);
 
 	return dboid;
 }
@@ -2598,22 +2591,21 @@ AlterDatabaseRefreshColl(AlterDatabaseRefreshCollStmt *stmt)
 		elog(ERROR, "invalid collation version change");
 	else if (oldversion && newversion && strcmp(newversion, oldversion) != 0)
 	{
-		bool		nulls[Natts_pg_database] = {0};
-		bool		replaces[Natts_pg_database] = {0};
 		Datum		values[Natts_pg_database] = {0};
+		bool		nulls[Natts_pg_database] = {false};
+		Bitmapset  *updated = NULL;
 		HeapTuple	newtuple;
 
 		ereport(NOTICE,
 				(errmsg("changing version from %s to %s",
 						oldversion, newversion)));
 
-		values[Anum_pg_database_datcollversion - 1] = CStringGetTextDatum(newversion);
-		replaces[Anum_pg_database_datcollversion - 1] = true;
+		HeapTupleUpdateValue(pg_database, datcollversion, CStringGetTextDatum(newversion), values, nulls, updated);
 
-		newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel),
-									 values, nulls, replaces);
-		CatalogTupleUpdate(rel, &tuple->t_self, newtuple);
+		newtuple = heap_update_tuple(tuple, RelationGetDescr(rel), values, nulls, updated);
+		CatalogTupleUpdate(rel, &tuple->t_self, newtuple, updated, NULL);
 		heap_freetuple(newtuple);
+		bms_free(updated);
 	}
 	else
 		ereport(NOTICE,
@@ -2700,9 +2692,9 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId)
 	 */
 	if (datForm->datdba != newOwnerId)
 	{
-		Datum		repl_val[Natts_pg_database];
-		bool		repl_null[Natts_pg_database] = {0};
-		bool		repl_repl[Natts_pg_database] = {0};
+		Datum		values[Natts_pg_database] = {0};
+		bool		nulls[Natts_pg_database] = {false};
+		Bitmapset  *updated = NULL;
 		Acl		   *newAcl;
 		Datum		aclDatum;
 		bool		isNull;
@@ -2732,8 +2724,7 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId)
 
 		LockTuple(rel, &tuple->t_self, InplaceUpdateTupleLock);
 
-		repl_repl[Anum_pg_database_datdba - 1] = true;
-		repl_val[Anum_pg_database_datdba - 1] = ObjectIdGetDatum(newOwnerId);
+		HeapTupleUpdateValue(pg_database, datdba, ObjectIdGetDatum(newOwnerId), values, nulls, updated);
 
 		/*
 		 * Determine the modified ACL for the new owner.  This is only
@@ -2747,18 +2738,18 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId)
 		{
 			newAcl = aclnewowner(DatumGetAclP(aclDatum),
 								 datForm->datdba, newOwnerId);
-			repl_repl[Anum_pg_database_datacl - 1] = true;
-			repl_val[Anum_pg_database_datacl - 1] = PointerGetDatum(newAcl);
+			HeapTupleUpdateValue(pg_database, datacl, PointerGetDatum(newAcl), values, nulls, updated);
 		}
 
-		newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel), repl_val, repl_null, repl_repl);
-		CatalogTupleUpdate(rel, &newtuple->t_self, newtuple);
+		newtuple = heap_update_tuple(tuple, RelationGetDescr(rel), values, nulls, updated);
+		CatalogTupleUpdate(rel, &newtuple->t_self, newtuple, updated, NULL);
 		UnlockTuple(rel, &tuple->t_self, InplaceUpdateTupleLock);
 
 		heap_freetuple(newtuple);
 
 		/* Update owner dependency reference */
 		changeDependencyOnOwner(DatabaseRelationId, db_id, newOwnerId);
+		bms_free(updated);
 	}
 
 	InvokeObjectPostAlterHook(DatabaseRelationId, db_id, 0);
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 028f9e2de90..e7d69176ce7 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -280,8 +280,8 @@ insert_event_trigger_tuple(const char *trigname, const char *eventname, Oid evtO
 	Relation	tgrel;
 	Oid			trigoid;
 	HeapTuple	tuple;
-	Datum		values[Natts_pg_event_trigger];
-	bool		nulls[Natts_pg_event_trigger];
+	Datum		values[Natts_pg_event_trigger] = {0};
+	bool		nulls[Natts_pg_event_trigger] = {false};
 	NameData	evtnamedata,
 				evteventdata;
 	ObjectAddress myself,
@@ -293,25 +293,23 @@ insert_event_trigger_tuple(const char *trigname, const char *eventname, Oid evtO
 	/* Build the new pg_trigger tuple. */
 	trigoid = GetNewOidWithIndex(tgrel, EventTriggerOidIndexId,
 								 Anum_pg_event_trigger_oid);
-	values[Anum_pg_event_trigger_oid - 1] = ObjectIdGetDatum(trigoid);
+	HeapTupleSetValue(pg_event_trigger, oid, ObjectIdGetDatum(trigoid), values);
 	memset(nulls, false, sizeof(nulls));
 	namestrcpy(&evtnamedata, trigname);
-	values[Anum_pg_event_trigger_evtname - 1] = NameGetDatum(&evtnamedata);
+	HeapTupleSetValue(pg_event_trigger, evtname, NameGetDatum(&evtnamedata), values);
 	namestrcpy(&evteventdata, eventname);
-	values[Anum_pg_event_trigger_evtevent - 1] = NameGetDatum(&evteventdata);
-	values[Anum_pg_event_trigger_evtowner - 1] = ObjectIdGetDatum(evtOwner);
-	values[Anum_pg_event_trigger_evtfoid - 1] = ObjectIdGetDatum(funcoid);
-	values[Anum_pg_event_trigger_evtenabled - 1] =
-		CharGetDatum(TRIGGER_FIRES_ON_ORIGIN);
+	HeapTupleSetValue(pg_event_trigger, evtevent, NameGetDatum(&evteventdata), values);
+	HeapTupleSetValue(pg_event_trigger, evtowner, ObjectIdGetDatum(evtOwner), values);
+	HeapTupleSetValue(pg_event_trigger, evtfoid, ObjectIdGetDatum(funcoid), values);
+	HeapTupleSetValue(pg_event_trigger, evtenabled, CharGetDatum(TRIGGER_FIRES_ON_ORIGIN), values);
 	if (taglist == NIL)
-		nulls[Anum_pg_event_trigger_evttags - 1] = true;
+		HeapTupleSetValueNull(pg_event_trigger, evttags, values, nulls);
 	else
-		values[Anum_pg_event_trigger_evttags - 1] =
-			filter_list_to_array(taglist);
+		HeapTupleSetValue(pg_event_trigger, evttags, filter_list_to_array(taglist), values);
 
 	/* Insert heap tuple. */
 	tuple = heap_form_tuple(tgrel->rd_att, values, nulls);
-	CatalogTupleInsert(tgrel, tuple);
+	CatalogTupleInsert(tgrel, tuple, NULL);
 	heap_freetuple(tuple);
 
 	/*
@@ -394,6 +392,7 @@ SetDatabaseHasLoginEventTriggers(void)
 	Relation	pg_db = table_open(DatabaseRelationId, RowExclusiveLock);
 	ItemPointerData otid;
 	HeapTuple	tuple;
+	Bitmapset  *updated = NULL;
 
 	/*
 	 * Use shared lock to prevent a conflict with EventTriggerOnLogin() trying
@@ -411,8 +410,9 @@ SetDatabaseHasLoginEventTriggers(void)
 	db = (Form_pg_database) GETSTRUCT(tuple);
 	if (!db->dathasloginevt)
 	{
-		db->dathasloginevt = true;
-		CatalogTupleUpdate(pg_db, &otid, tuple);
+		HeapTupleUpdateField(pg_database, dathasloginevt, true, db, updated);
+		CatalogTupleUpdate(pg_db, &otid, tuple, updated, NULL);
+		bms_free(updated);
 		CommandCounterIncrement();
 	}
 	UnlockTuple(pg_db, &otid, InplaceUpdateTupleLock);
@@ -431,6 +431,7 @@ AlterEventTrigger(AlterEventTrigStmt *stmt)
 	Oid			trigoid;
 	Form_pg_event_trigger evtForm;
 	char		tgenabled = stmt->tgenabled;
+	Bitmapset  *updated = NULL;
 
 	tgrel = table_open(EventTriggerRelationId, RowExclusiveLock);
 
@@ -450,9 +451,10 @@ AlterEventTrigger(AlterEventTrigStmt *stmt)
 					   stmt->trigname);
 
 	/* tuple is a copy, so we can modify it below */
-	evtForm->evtenabled = tgenabled;
+	HeapTupleUpdateField(pg_event_trigger, evtenabled, tgenabled, evtForm, updated);
 
-	CatalogTupleUpdate(tgrel, &tup->t_self, tup);
+	CatalogTupleUpdate(tgrel, &tup->t_self, tup, updated, NULL);
+	bms_free(updated);
 
 	/*
 	 * Login event triggers have an additional flag in pg_database to enable
@@ -539,6 +541,7 @@ static void
 AlterEventTriggerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
 {
 	Form_pg_event_trigger form;
+	Bitmapset  *updated = NULL;
 
 	form = (Form_pg_event_trigger) GETSTRUCT(tup);
 
@@ -557,8 +560,9 @@ AlterEventTriggerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
 						NameStr(form->evtname)),
 				 errhint("The owner of an event trigger must be a superuser.")));
 
-	form->evtowner = newOwnerId;
-	CatalogTupleUpdate(rel, &tup->t_self, tup);
+	HeapTupleUpdateField(pg_event_trigger, evtowner, newOwnerId, form, updated);
+	CatalogTupleUpdate(rel, &tup->t_self, tup, updated, NULL);
+	bms_free(updated);
 
 	/* Update owner dependency reference */
 	changeDependencyOnOwner(EventTriggerRelationId,
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 596105ee078..1ddb14c53b4 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -2115,8 +2115,8 @@ InsertExtensionTuple(const char *extName, Oid extOwner,
 {
 	Oid			extensionOid;
 	Relation	rel;
-	Datum		values[Natts_pg_extension];
-	bool		nulls[Natts_pg_extension];
+	Datum		values[Natts_pg_extension] = {0};
+	bool		nulls[Natts_pg_extension] = {false};
 	HeapTuple	tuple;
 	ObjectAddress myself;
 	ObjectAddress nsp;
@@ -2133,27 +2133,26 @@ InsertExtensionTuple(const char *extName, Oid extOwner,
 
 	extensionOid = GetNewOidWithIndex(rel, ExtensionOidIndexId,
 									  Anum_pg_extension_oid);
-	values[Anum_pg_extension_oid - 1] = ObjectIdGetDatum(extensionOid);
-	values[Anum_pg_extension_extname - 1] =
-		DirectFunctionCall1(namein, CStringGetDatum(extName));
-	values[Anum_pg_extension_extowner - 1] = ObjectIdGetDatum(extOwner);
-	values[Anum_pg_extension_extnamespace - 1] = ObjectIdGetDatum(schemaOid);
-	values[Anum_pg_extension_extrelocatable - 1] = BoolGetDatum(relocatable);
-	values[Anum_pg_extension_extversion - 1] = CStringGetTextDatum(extVersion);
+	HeapTupleSetValue(pg_extension, oid, ObjectIdGetDatum(extensionOid), values);
+	HeapTupleSetValue(pg_extension, extname, DirectFunctionCall1(namein, CStringGetDatum(extName)), values);
+	HeapTupleSetValue(pg_extension, extowner, ObjectIdGetDatum(extOwner), values);
+	HeapTupleSetValue(pg_extension, extnamespace, ObjectIdGetDatum(schemaOid), values);
+	HeapTupleSetValue(pg_extension, extrelocatable, BoolGetDatum(relocatable), values);
+	HeapTupleSetValue(pg_extension, extversion, CStringGetTextDatum(extVersion), values);
 
 	if (extConfig == PointerGetDatum(NULL))
-		nulls[Anum_pg_extension_extconfig - 1] = true;
+		HeapTupleSetValueNull(pg_extension, extconfig, values, nulls);
 	else
-		values[Anum_pg_extension_extconfig - 1] = extConfig;
+		HeapTupleSetValue(pg_extension, extconfig, extConfig, values);
 
 	if (extCondition == PointerGetDatum(NULL))
-		nulls[Anum_pg_extension_extcondition - 1] = true;
+		HeapTupleSetValueNull(pg_extension, extcondition, values, nulls);
 	else
-		values[Anum_pg_extension_extcondition - 1] = extCondition;
+		HeapTupleSetValue(pg_extension, extcondition, extCondition, values);
 
 	tuple = heap_form_tuple(rel->rd_att, values, nulls);
 
-	CatalogTupleInsert(rel, tuple);
+	CatalogTupleInsert(rel, tuple, NULL);
 
 	heap_freetuple(tuple);
 	table_close(rel, RowExclusiveLock);
@@ -2732,9 +2731,9 @@ pg_extension_config_dump(PG_FUNCTION_ARGS)
 	int			arrayLength;
 	int			arrayIndex;
 	bool		isnull;
-	Datum		repl_val[Natts_pg_extension];
-	bool		repl_null[Natts_pg_extension];
-	bool		repl_repl[Natts_pg_extension];
+	Datum		values[Natts_pg_extension] = {0};
+	bool		nulls[Natts_pg_extension] = {false};
+	Bitmapset  *updated = NULL;
 	ArrayType  *a;
 
 	/*
@@ -2789,10 +2788,6 @@ pg_extension_config_dump(PG_FUNCTION_ARGS)
 		elog(ERROR, "could not find tuple for extension %u",
 			 CurrentExtensionObject);
 
-	memset(repl_val, 0, sizeof(repl_val));
-	memset(repl_null, false, sizeof(repl_null));
-	memset(repl_repl, false, sizeof(repl_repl));
-
 	/* Build or modify the extconfig value */
 	elementDatum = ObjectIdGetDatum(tableoid);
 
@@ -2842,8 +2837,7 @@ pg_extension_config_dump(PG_FUNCTION_ARGS)
 					  true /* OID's typbyval */ ,
 					  TYPALIGN_INT /* OID's typalign */ );
 	}
-	repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a);
-	repl_repl[Anum_pg_extension_extconfig - 1] = true;
+	HeapTupleUpdateValue(pg_extension, extconfig, PointerGetDatum(a), values, nulls, updated);
 
 	/* Build or modify the extcondition value */
 	elementDatum = PointerGetDatum(wherecond);
@@ -2878,17 +2872,16 @@ pg_extension_config_dump(PG_FUNCTION_ARGS)
 					  false /* TEXT's typbyval */ ,
 					  TYPALIGN_INT /* TEXT's typalign */ );
 	}
-	repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a);
-	repl_repl[Anum_pg_extension_extcondition - 1] = true;
+	HeapTupleUpdateValue(pg_extension, extcondition, PointerGetDatum(a), values, nulls, updated);
 
-	extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
-							   repl_val, repl_null, repl_repl);
+	extTup = heap_update_tuple(extTup, RelationGetDescr(extRel), values, nulls, updated);
 
-	CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
+	CatalogTupleUpdate(extRel, &extTup->t_self, extTup, updated, NULL);
 
 	systable_endscan(extScan);
 
 	table_close(extRel, RowExclusiveLock);
+	bms_free(updated);
 
 	PG_RETURN_VOID();
 }
@@ -2964,9 +2957,9 @@ extension_config_remove(Oid extensionoid, Oid tableoid)
 	int			arrayLength;
 	int			arrayIndex;
 	bool		isnull;
-	Datum		repl_val[Natts_pg_extension];
-	bool		repl_null[Natts_pg_extension];
-	bool		repl_repl[Natts_pg_extension];
+	Datum		values[Natts_pg_extension] = {0};
+	bool		nulls[Natts_pg_extension] = {false};
+	Bitmapset  *updated = NULL;
 	ArrayType  *a;
 
 	/* Find the pg_extension tuple */
@@ -3033,14 +3026,10 @@ extension_config_remove(Oid extensionoid, Oid tableoid)
 	}
 
 	/* Modify or delete the extconfig value */
-	memset(repl_val, 0, sizeof(repl_val));
-	memset(repl_null, false, sizeof(repl_null));
-	memset(repl_repl, false, sizeof(repl_repl));
-
 	if (arrayLength <= 1)
 	{
 		/* removing only element, just set array to null */
-		repl_null[Anum_pg_extension_extconfig - 1] = true;
+		HeapTupleUpdateValueNull(pg_extension, extconfig, values, nulls, updated);
 	}
 	else
 	{
@@ -3057,9 +3046,8 @@ extension_config_remove(Oid extensionoid, Oid tableoid)
 
 		a = construct_array_builtin(dvalues, arrayLength - 1, OIDOID);
 
-		repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a);
+		HeapTupleUpdateValue(pg_extension, extconfig, PointerGetDatum(a), values, nulls, updated);
 	}
-	repl_repl[Anum_pg_extension_extconfig - 1] = true;
 
 	/* Modify or delete the extcondition value */
 	arrayDatum = heap_getattr(extTup, Anum_pg_extension_extcondition,
@@ -3084,7 +3072,7 @@ extension_config_remove(Oid extensionoid, Oid tableoid)
 	if (arrayLength <= 1)
 	{
 		/* removing only element, just set array to null */
-		repl_null[Anum_pg_extension_extcondition - 1] = true;
+		HeapTupleUpdateValueNull(pg_extension, extcondition, values, nulls, updated);
 	}
 	else
 	{
@@ -3100,19 +3088,17 @@ extension_config_remove(Oid extensionoid, Oid tableoid)
 			dvalues[i] = dvalues[i + 1];
 
 		a = construct_array_builtin(dvalues, arrayLength - 1, TEXTOID);
-
-		repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a);
+		HeapTupleUpdateValue(pg_extension, extcondition, PointerGetDatum(a), values, nulls, updated);
 	}
-	repl_repl[Anum_pg_extension_extcondition - 1] = true;
 
-	extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
-							   repl_val, repl_null, repl_repl);
+	extTup = heap_update_tuple(extTup, RelationGetDescr(extRel), values, nulls, updated);
 
-	CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
+	CatalogTupleUpdate(extRel, &extTup->t_self, extTup, updated, NULL);
 
 	systable_endscan(extScan);
 
 	table_close(extRel, RowExclusiveLock);
+	bms_free(updated);
 }
 
 /*
@@ -3130,6 +3116,7 @@ AlterExtensionNamespace(const char *extensionName, const char *newschema, Oid *o
 	SysScanDesc extScan;
 	HeapTuple	extTup;
 	Form_pg_extension extForm;
+	Bitmapset  *updated = NULL;
 	Relation	depRel;
 	SysScanDesc depScan;
 	HeapTuple	depTup;
@@ -3311,11 +3298,12 @@ AlterExtensionNamespace(const char *extensionName, const char *newschema, Oid *o
 	relation_close(depRel, AccessShareLock);
 
 	/* Now adjust pg_extension.extnamespace */
-	extForm->extnamespace = nspOid;
+	HeapTupleUpdateField(pg_extension, extnamespace, nspOid, extForm, updated);
 
-	CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
+	CatalogTupleUpdate(extRel, &extTup->t_self, extTup, updated, NULL);
 
 	table_close(extRel, RowExclusiveLock);
+	bms_free(updated);
 
 	/* update dependency to point to the new schema */
 	if (changeDependencyFor(ExtensionRelationId, extensionOid,
@@ -3505,9 +3493,9 @@ ApplyExtensionUpdates(Oid extensionOid,
 		SysScanDesc extScan;
 		HeapTuple	extTup;
 		Form_pg_extension extForm;
-		Datum		values[Natts_pg_extension];
-		bool		nulls[Natts_pg_extension];
-		bool		repl[Natts_pg_extension];
+		Datum		values[Natts_pg_extension] = {0};
+		bool		nulls[Natts_pg_extension] = {false};
+		Bitmapset  *updated = NULL;
 		ObjectAddress myself;
 		ListCell   *lc;
 
@@ -3544,21 +3532,14 @@ ApplyExtensionUpdates(Oid extensionOid,
 		/*
 		 * Modify extrelocatable and extversion in the pg_extension tuple
 		 */
-		memset(values, 0, sizeof(values));
-		memset(nulls, 0, sizeof(nulls));
-		memset(repl, 0, sizeof(repl));
-
-		values[Anum_pg_extension_extrelocatable - 1] =
-			BoolGetDatum(control->relocatable);
-		repl[Anum_pg_extension_extrelocatable - 1] = true;
-		values[Anum_pg_extension_extversion - 1] =
-			CStringGetTextDatum(versionName);
-		repl[Anum_pg_extension_extversion - 1] = true;
+		HeapTupleUpdateValue(pg_extension, extrelocatable, BoolGetDatum(control->relocatable), values, nulls, updated);
+		HeapTupleUpdateValue(pg_extension, extversion, CStringGetTextDatum(versionName), values, nulls, updated);
 
-		extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
-								   values, nulls, repl);
+		extTup = heap_update_tuple(extTup, RelationGetDescr(extRel),
+								   values, nulls, updated);
 
-		CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
+		CatalogTupleUpdate(extRel, &extTup->t_self, extTup, updated, NULL);
+		bms_free(updated);
 
 		systable_endscan(extScan);
 
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index b56d1ad6785..d381090e56a 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -216,9 +216,9 @@ static void
 AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
 {
 	Form_pg_foreign_data_wrapper form;
-	Datum		repl_val[Natts_pg_foreign_data_wrapper];
-	bool		repl_null[Natts_pg_foreign_data_wrapper];
-	bool		repl_repl[Natts_pg_foreign_data_wrapper];
+	Datum		values[Natts_pg_foreign_data_wrapper] = {0};
+	bool		nulls[Natts_pg_foreign_data_wrapper] = {false};
+	Bitmapset  *updated = NULL;
 	Acl		   *newAcl;
 	Datum		aclDatum;
 	bool		isNull;
@@ -243,11 +243,7 @@ AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerI
 
 	if (form->fdwowner != newOwnerId)
 	{
-		memset(repl_null, false, sizeof(repl_null));
-		memset(repl_repl, false, sizeof(repl_repl));
-
-		repl_repl[Anum_pg_foreign_data_wrapper_fdwowner - 1] = true;
-		repl_val[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(newOwnerId);
+		HeapTupleUpdateValue(pg_foreign_data_wrapper, fdwowner, ObjectIdGetDatum(newOwnerId), values, nulls, updated);
 
 		aclDatum = heap_getattr(tup,
 								Anum_pg_foreign_data_wrapper_fdwacl,
@@ -258,19 +254,18 @@ AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerI
 		{
 			newAcl = aclnewowner(DatumGetAclP(aclDatum),
 								 form->fdwowner, newOwnerId);
-			repl_repl[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
-			repl_val[Anum_pg_foreign_data_wrapper_fdwacl - 1] = PointerGetDatum(newAcl);
+			HeapTupleUpdateValue(pg_foreign_data_wrapper, fdwacl, PointerGetDatum(newAcl), values, nulls, updated);
 		}
 
-		tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
-								repl_repl);
+		tup = heap_update_tuple(tup, RelationGetDescr(rel), values, nulls, updated);
 
-		CatalogTupleUpdate(rel, &tup->t_self, tup);
+		CatalogTupleUpdate(rel, &tup->t_self, tup, updated, NULL);
 
 		/* Update owner dependency reference */
 		changeDependencyOnOwner(ForeignDataWrapperRelationId,
 								form->oid,
 								newOwnerId);
+		bms_free(updated);
 	}
 
 	InvokeObjectPostAlterHook(ForeignDataWrapperRelationId,
@@ -349,9 +344,9 @@ static void
 AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
 {
 	Form_pg_foreign_server form;
-	Datum		repl_val[Natts_pg_foreign_server];
-	bool		repl_null[Natts_pg_foreign_server];
-	bool		repl_repl[Natts_pg_foreign_server];
+	Datum		values[Natts_pg_foreign_server] = {0};
+	bool		nulls[Natts_pg_foreign_server] = {false};
+	Bitmapset  *updated = NULL;
 	Acl		   *newAcl;
 	Datum		aclDatum;
 	bool		isNull;
@@ -386,11 +381,7 @@ AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
 			}
 		}
 
-		memset(repl_null, false, sizeof(repl_null));
-		memset(repl_repl, false, sizeof(repl_repl));
-
-		repl_repl[Anum_pg_foreign_server_srvowner - 1] = true;
-		repl_val[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(newOwnerId);
+		HeapTupleUpdateValue(pg_foreign_server, srvowner, ObjectIdGetDatum(newOwnerId), values, nulls, updated);
 
 		aclDatum = heap_getattr(tup,
 								Anum_pg_foreign_server_srvacl,
@@ -401,18 +392,17 @@ AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
 		{
 			newAcl = aclnewowner(DatumGetAclP(aclDatum),
 								 form->srvowner, newOwnerId);
-			repl_repl[Anum_pg_foreign_server_srvacl - 1] = true;
-			repl_val[Anum_pg_foreign_server_srvacl - 1] = PointerGetDatum(newAcl);
+			HeapTupleUpdateValue(pg_foreign_server, srvacl, PointerGetDatum(newAcl), values, nulls, updated);
 		}
 
-		tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
-								repl_repl);
+		tup = heap_update_tuple(tup, RelationGetDescr(rel), values, nulls, updated);
 
-		CatalogTupleUpdate(rel, &tup->t_self, tup);
+		CatalogTupleUpdate(rel, &tup->t_self, tup, updated, NULL);
 
 		/* Update owner dependency reference */
 		changeDependencyOnOwner(ForeignServerRelationId, form->oid,
 								newOwnerId);
+		bms_free(updated);
 	}
 
 	InvokeObjectPostAlterHook(ForeignServerRelationId,
@@ -569,8 +559,8 @@ ObjectAddress
 CreateForeignDataWrapper(ParseState *pstate, CreateFdwStmt *stmt)
 {
 	Relation	rel;
-	Datum		values[Natts_pg_foreign_data_wrapper];
-	bool		nulls[Natts_pg_foreign_data_wrapper];
+	Datum		values[Natts_pg_foreign_data_wrapper] = {0};
+	bool		nulls[Natts_pg_foreign_data_wrapper] = {false};
 	HeapTuple	tuple;
 	Oid			fdwId;
 	bool		handler_given;
@@ -612,20 +602,19 @@ CreateForeignDataWrapper(ParseState *pstate, CreateFdwStmt *stmt)
 
 	fdwId = GetNewOidWithIndex(rel, ForeignDataWrapperOidIndexId,
 							   Anum_pg_foreign_data_wrapper_oid);
-	values[Anum_pg_foreign_data_wrapper_oid - 1] = ObjectIdGetDatum(fdwId);
-	values[Anum_pg_foreign_data_wrapper_fdwname - 1] =
-		DirectFunctionCall1(namein, CStringGetDatum(stmt->fdwname));
-	values[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(ownerId);
+	HeapTupleSetValue(pg_foreign_data_wrapper, oid, ObjectIdGetDatum(fdwId), values);
+	HeapTupleSetValue(pg_foreign_data_wrapper, fdwname, DirectFunctionCall1(namein, CStringGetDatum(stmt->fdwname)), values);
+	HeapTupleSetValue(pg_foreign_data_wrapper, fdwowner, ObjectIdGetDatum(ownerId), values);
 
 	/* Lookup handler and validator functions, if given */
 	parse_func_options(pstate, stmt->func_options,
 					   &handler_given, &fdwhandler,
 					   &validator_given, &fdwvalidator);
 
-	values[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
-	values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
+	HeapTupleSetValue(pg_foreign_data_wrapper, fdwhandler, ObjectIdGetDatum(fdwhandler), values);
+	HeapTupleSetValue(pg_foreign_data_wrapper, fdwvalidator, ObjectIdGetDatum(fdwvalidator), values);
 
-	nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
+	HeapTupleSetValueNull(pg_foreign_data_wrapper, fdwacl, values, nulls);
 
 	fdwoptions = transformGenericOptions(ForeignDataWrapperRelationId,
 										 PointerGetDatum(NULL),
@@ -633,13 +622,13 @@ CreateForeignDataWrapper(ParseState *pstate, CreateFdwStmt *stmt)
 										 fdwvalidator);
 
 	if (DatumGetPointer(fdwoptions) != NULL)
-		values[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = fdwoptions;
+		HeapTupleSetValue(pg_foreign_data_wrapper, fdwoptions, fdwoptions, values);
 	else
-		nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
+		HeapTupleSetValueNull(pg_foreign_data_wrapper, fdwoptions, values, nulls);
 
 	tuple = heap_form_tuple(rel->rd_att, values, nulls);
 
-	CatalogTupleInsert(rel, tuple);
+	CatalogTupleInsert(rel, tuple, NULL);
 
 	heap_freetuple(tuple);
 
@@ -687,9 +676,9 @@ AlterForeignDataWrapper(ParseState *pstate, AlterFdwStmt *stmt)
 	Relation	rel;
 	HeapTuple	tp;
 	Form_pg_foreign_data_wrapper fdwForm;
-	Datum		repl_val[Natts_pg_foreign_data_wrapper];
-	bool		repl_null[Natts_pg_foreign_data_wrapper];
-	bool		repl_repl[Natts_pg_foreign_data_wrapper];
+	Datum		values[Natts_pg_foreign_data_wrapper] = {0};
+	bool		nulls[Natts_pg_foreign_data_wrapper] = {false};
+	Bitmapset  *updated = NULL;
 	Oid			fdwId;
 	bool		isnull;
 	Datum		datum;
@@ -720,18 +709,13 @@ AlterForeignDataWrapper(ParseState *pstate, AlterFdwStmt *stmt)
 	fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
 	fdwId = fdwForm->oid;
 
-	memset(repl_val, 0, sizeof(repl_val));
-	memset(repl_null, false, sizeof(repl_null));
-	memset(repl_repl, false, sizeof(repl_repl));
-
 	parse_func_options(pstate, stmt->func_options,
 					   &handler_given, &fdwhandler,
 					   &validator_given, &fdwvalidator);
 
 	if (handler_given)
 	{
-		repl_val[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
-		repl_repl[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = true;
+		HeapTupleUpdateValue(pg_foreign_data_wrapper, fdwhandler, ObjectIdGetDatum(fdwhandler), values, nulls, updated);
 
 		/*
 		 * It could be that the behavior of accessing foreign table changes
@@ -743,8 +727,7 @@ AlterForeignDataWrapper(ParseState *pstate, AlterFdwStmt *stmt)
 
 	if (validator_given)
 	{
-		repl_val[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
-		repl_repl[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = true;
+		HeapTupleUpdateValue(pg_foreign_data_wrapper, fdwvalidator, ObjectIdGetDatum(fdwvalidator), values, nulls, updated);
 
 		/*
 		 * It could be that existing options for the FDW or dependent SERVER,
@@ -784,18 +767,15 @@ AlterForeignDataWrapper(ParseState *pstate, AlterFdwStmt *stmt)
 										fdwvalidator);
 
 		if (DatumGetPointer(datum) != NULL)
-			repl_val[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = datum;
+			HeapTupleUpdateValue(pg_foreign_data_wrapper, fdwoptions, datum, values, nulls, updated);
 		else
-			repl_null[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
-
-		repl_repl[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
+			HeapTupleUpdateValueNull(pg_foreign_data_wrapper, fdwoptions, values, nulls, updated);
 	}
 
 	/* Everything looks good - update the tuple */
-	tp = heap_modify_tuple(tp, RelationGetDescr(rel),
-						   repl_val, repl_null, repl_repl);
+	tp = heap_update_tuple(tp, RelationGetDescr(rel), values, nulls, updated);
 
-	CatalogTupleUpdate(rel, &tp->t_self, tp);
+	CatalogTupleUpdate(rel, &tp->t_self, tp, updated, NULL);
 
 	heap_freetuple(tp);
 
@@ -837,6 +817,7 @@ AlterForeignDataWrapper(ParseState *pstate, AlterFdwStmt *stmt)
 	InvokeObjectPostAlterHook(ForeignDataWrapperRelationId, fdwId, 0);
 
 	table_close(rel, RowExclusiveLock);
+	bms_free(updated);
 
 	return myself;
 }
@@ -850,8 +831,8 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
 {
 	Relation	rel;
 	Datum		srvoptions;
-	Datum		values[Natts_pg_foreign_server];
-	bool		nulls[Natts_pg_foreign_server];
+	Datum		values[Natts_pg_foreign_server] = {0};
+	bool		nulls[Natts_pg_foreign_server] = {false};
 	HeapTuple	tuple;
 	Oid			srvId;
 	Oid			ownerId;
@@ -914,28 +895,25 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
 
 	srvId = GetNewOidWithIndex(rel, ForeignServerOidIndexId,
 							   Anum_pg_foreign_server_oid);
-	values[Anum_pg_foreign_server_oid - 1] = ObjectIdGetDatum(srvId);
-	values[Anum_pg_foreign_server_srvname - 1] =
-		DirectFunctionCall1(namein, CStringGetDatum(stmt->servername));
-	values[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(ownerId);
-	values[Anum_pg_foreign_server_srvfdw - 1] = ObjectIdGetDatum(fdw->fdwid);
+	HeapTupleSetValue(pg_foreign_server, oid, ObjectIdGetDatum(srvId), values);
+	HeapTupleSetValue(pg_foreign_server, srvname, DirectFunctionCall1(namein, CStringGetDatum(stmt->servername)), values);
+	HeapTupleSetValue(pg_foreign_server, srvowner, ObjectIdGetDatum(ownerId), values);
+	HeapTupleSetValue(pg_foreign_server, srvfdw, ObjectIdGetDatum(fdw->fdwid), values);
 
 	/* Add server type if supplied */
 	if (stmt->servertype)
-		values[Anum_pg_foreign_server_srvtype - 1] =
-			CStringGetTextDatum(stmt->servertype);
+		HeapTupleSetValue(pg_foreign_server, srvtype, CStringGetTextDatum(stmt->servertype), values);
 	else
-		nulls[Anum_pg_foreign_server_srvtype - 1] = true;
+		HeapTupleSetValueNull(pg_foreign_server, srvtype, values, nulls);
 
 	/* Add server version if supplied */
 	if (stmt->version)
-		values[Anum_pg_foreign_server_srvversion - 1] =
-			CStringGetTextDatum(stmt->version);
+		HeapTupleSetValue(pg_foreign_server, srvversion, CStringGetTextDatum(stmt->version), values);
 	else
-		nulls[Anum_pg_foreign_server_srvversion - 1] = true;
+		HeapTupleSetValueNull(pg_foreign_server, srvversion, values, nulls);
 
 	/* Start with a blank acl */
-	nulls[Anum_pg_foreign_server_srvacl - 1] = true;
+	HeapTupleSetValueNull(pg_foreign_server, srvacl, values, nulls);
 
 	/* Add server options */
 	srvoptions = transformGenericOptions(ForeignServerRelationId,
@@ -944,13 +922,13 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
 										 fdw->fdwvalidator);
 
 	if (DatumGetPointer(srvoptions) != NULL)
-		values[Anum_pg_foreign_server_srvoptions - 1] = srvoptions;
+		HeapTupleSetValue(pg_foreign_server, srvoptions, srvoptions, values);
 	else
-		nulls[Anum_pg_foreign_server_srvoptions - 1] = true;
+		HeapTupleSetValueNull(pg_foreign_server, srvoptions, values, nulls);
 
 	tuple = heap_form_tuple(rel->rd_att, values, nulls);
 
-	CatalogTupleInsert(rel, tuple);
+	CatalogTupleInsert(rel, tuple, NULL);
 
 	heap_freetuple(tuple);
 
@@ -986,9 +964,9 @@ AlterForeignServer(AlterForeignServerStmt *stmt)
 {
 	Relation	rel;
 	HeapTuple	tp;
-	Datum		repl_val[Natts_pg_foreign_server];
-	bool		repl_null[Natts_pg_foreign_server];
-	bool		repl_repl[Natts_pg_foreign_server];
+	Datum		values[Natts_pg_foreign_server] = {0};
+	bool		nulls[Natts_pg_foreign_server] = {false};
+	Bitmapset  *updated = NULL;
 	Oid			srvId;
 	Form_pg_foreign_server srvForm;
 	ObjectAddress address;
@@ -1013,22 +991,15 @@ AlterForeignServer(AlterForeignServerStmt *stmt)
 		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FOREIGN_SERVER,
 					   stmt->servername);
 
-	memset(repl_val, 0, sizeof(repl_val));
-	memset(repl_null, false, sizeof(repl_null));
-	memset(repl_repl, false, sizeof(repl_repl));
-
 	if (stmt->has_version)
 	{
 		/*
 		 * Change the server VERSION string.
 		 */
 		if (stmt->version)
-			repl_val[Anum_pg_foreign_server_srvversion - 1] =
-				CStringGetTextDatum(stmt->version);
+			HeapTupleUpdateValue(pg_foreign_server, srvversion, CStringGetTextDatum(stmt->version), values, nulls, updated);
 		else
-			repl_null[Anum_pg_foreign_server_srvversion - 1] = true;
-
-		repl_repl[Anum_pg_foreign_server_srvversion - 1] = true;
+			HeapTupleUpdateValueNull(pg_foreign_server, srvversion, values, nulls, updated);
 	}
 
 	if (stmt->options)
@@ -1052,18 +1023,15 @@ AlterForeignServer(AlterForeignServerStmt *stmt)
 										fdw->fdwvalidator);
 
 		if (DatumGetPointer(datum) != NULL)
-			repl_val[Anum_pg_foreign_server_srvoptions - 1] = datum;
+			HeapTupleUpdateValue(pg_foreign_server, srvoptions, datum, values, nulls, updated);
 		else
-			repl_null[Anum_pg_foreign_server_srvoptions - 1] = true;
-
-		repl_repl[Anum_pg_foreign_server_srvoptions - 1] = true;
+			HeapTupleUpdateValueNull(pg_foreign_server, srvoptions, values, nulls, updated);
 	}
 
 	/* Everything looks good - update the tuple */
-	tp = heap_modify_tuple(tp, RelationGetDescr(rel),
-						   repl_val, repl_null, repl_repl);
+	tp = heap_update_tuple(tp, RelationGetDescr(rel), values, nulls, updated);
 
-	CatalogTupleUpdate(rel, &tp->t_self, tp);
+	CatalogTupleUpdate(rel, &tp->t_self, tp, updated, NULL);
 
 	InvokeObjectPostAlterHook(ForeignServerRelationId, srvId, 0);
 
@@ -1072,6 +1040,7 @@ AlterForeignServer(AlterForeignServerStmt *stmt)
 	heap_freetuple(tp);
 
 	table_close(rel, RowExclusiveLock);
+	bms_free(updated);
 
 	return address;
 }
@@ -1112,8 +1081,8 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
 {
 	Relation	rel;
 	Datum		useoptions;
-	Datum		values[Natts_pg_user_mapping];
-	bool		nulls[Natts_pg_user_mapping];
+	Datum		values[Natts_pg_user_mapping] = {0};
+	bool		nulls[Natts_pg_user_mapping] = {false};
 	HeapTuple	tuple;
 	Oid			useId;
 	Oid			umId;
@@ -1177,9 +1146,9 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
 
 	umId = GetNewOidWithIndex(rel, UserMappingOidIndexId,
 							  Anum_pg_user_mapping_oid);
-	values[Anum_pg_user_mapping_oid - 1] = ObjectIdGetDatum(umId);
-	values[Anum_pg_user_mapping_umuser - 1] = ObjectIdGetDatum(useId);
-	values[Anum_pg_user_mapping_umserver - 1] = ObjectIdGetDatum(srv->serverid);
+	HeapTupleSetValue(pg_user_mapping, oid, ObjectIdGetDatum(umId), values);
+	HeapTupleSetValue(pg_user_mapping, umuser, ObjectIdGetDatum(useId), values);
+	HeapTupleSetValue(pg_user_mapping, umserver, ObjectIdGetDatum(srv->serverid), values);
 
 	/* Add user options */
 	useoptions = transformGenericOptions(UserMappingRelationId,
@@ -1188,13 +1157,13 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
 										 fdw->fdwvalidator);
 
 	if (DatumGetPointer(useoptions) != NULL)
-		values[Anum_pg_user_mapping_umoptions - 1] = useoptions;
+		HeapTupleSetValue(pg_user_mapping, umoptions, useoptions, values);
 	else
-		nulls[Anum_pg_user_mapping_umoptions - 1] = true;
+		HeapTupleSetValueNull(pg_user_mapping, umoptions, values, nulls);
 
 	tuple = heap_form_tuple(rel->rd_att, values, nulls);
 
-	CatalogTupleInsert(rel, tuple);
+	CatalogTupleInsert(rel, tuple, NULL);
 
 	heap_freetuple(tuple);
 
@@ -1238,9 +1207,9 @@ AlterUserMapping(AlterUserMappingStmt *stmt)
 {
 	Relation	rel;
 	HeapTuple	tp;
-	Datum		repl_val[Natts_pg_user_mapping];
-	bool		repl_null[Natts_pg_user_mapping];
-	bool		repl_repl[Natts_pg_user_mapping];
+	Datum		values[Natts_pg_user_mapping] = {0};
+	bool		nulls[Natts_pg_user_mapping] = {false};
+	Bitmapset  *updated = NULL;
 	Oid			useId;
 	Oid			umId;
 	ForeignServer *srv;
@@ -1272,10 +1241,6 @@ AlterUserMapping(AlterUserMappingStmt *stmt)
 	if (!HeapTupleIsValid(tp))
 		elog(ERROR, "cache lookup failed for user mapping %u", umId);
 
-	memset(repl_val, 0, sizeof(repl_val));
-	memset(repl_null, false, sizeof(repl_null));
-	memset(repl_repl, false, sizeof(repl_repl));
-
 	if (stmt->options)
 	{
 		ForeignDataWrapper *fdw;
@@ -1302,18 +1267,15 @@ AlterUserMapping(AlterUserMappingStmt *stmt)
 										fdw->fdwvalidator);
 
 		if (DatumGetPointer(datum) != NULL)
-			repl_val[Anum_pg_user_mapping_umoptions - 1] = datum;
+			HeapTupleUpdateValue(pg_user_mapping, umoptions, datum, values, nulls, updated);
 		else
-			repl_null[Anum_pg_user_mapping_umoptions - 1] = true;
-
-		repl_repl[Anum_pg_user_mapping_umoptions - 1] = true;
+			HeapTupleUpdateValueNull(pg_user_mapping, umoptions, values, nulls, updated);
 	}
 
 	/* Everything looks good - update the tuple */
-	tp = heap_modify_tuple(tp, RelationGetDescr(rel),
-						   repl_val, repl_null, repl_repl);
+	tp = heap_update_tuple(tp, RelationGetDescr(rel), values, nulls, updated);
 
-	CatalogTupleUpdate(rel, &tp->t_self, tp);
+	CatalogTupleUpdate(rel, &tp->t_self, tp, updated, NULL);
 
 	InvokeObjectPostAlterHook(UserMappingRelationId,
 							  umId, 0);
@@ -1323,6 +1285,7 @@ AlterUserMapping(AlterUserMappingStmt *stmt)
 	heap_freetuple(tp);
 
 	table_close(rel, RowExclusiveLock);
+	bms_free(updated);
 
 	return address;
 }
@@ -1416,8 +1379,8 @@ CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
 {
 	Relation	ftrel;
 	Datum		ftoptions;
-	Datum		values[Natts_pg_foreign_table];
-	bool		nulls[Natts_pg_foreign_table];
+	Datum		values[Natts_pg_foreign_table] = {0};
+	bool		nulls[Natts_pg_foreign_table] = {false};
 	HeapTuple	tuple;
 	AclResult	aclresult;
 	ObjectAddress myself;
@@ -1456,8 +1419,8 @@ CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
 	memset(values, 0, sizeof(values));
 	memset(nulls, false, sizeof(nulls));
 
-	values[Anum_pg_foreign_table_ftrelid - 1] = ObjectIdGetDatum(relid);
-	values[Anum_pg_foreign_table_ftserver - 1] = ObjectIdGetDatum(server->serverid);
+	HeapTupleSetValue(pg_foreign_table, ftrelid, ObjectIdGetDatum(relid), values);
+	HeapTupleSetValue(pg_foreign_table, ftserver, ObjectIdGetDatum(server->serverid), values);
 	/* Add table generic options */
 	ftoptions = transformGenericOptions(ForeignTableRelationId,
 										PointerGetDatum(NULL),
@@ -1465,13 +1428,13 @@ CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
 										fdw->fdwvalidator);
 
 	if (DatumGetPointer(ftoptions) != NULL)
-		values[Anum_pg_foreign_table_ftoptions - 1] = ftoptions;
+		HeapTupleSetValue(pg_foreign_table, ftoptions, ftoptions, values);
 	else
-		nulls[Anum_pg_foreign_table_ftoptions - 1] = true;
+		HeapTupleSetValueNull(pg_foreign_table, ftoptions, values, nulls);
 
 	tuple = heap_form_tuple(ftrel->rd_att, values, nulls);
 
-	CatalogTupleInsert(ftrel, tuple);
+	CatalogTupleInsert(ftrel, tuple, NULL);
 
 	heap_freetuple(tuple);
 
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index a516b037dea..809cdf9a29f 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -1377,6 +1377,7 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
 	DefElem    *rows_item = NULL;
 	DefElem    *support_item = NULL;
 	DefElem    *parallel_item = NULL;
+	Bitmapset  *updated = NULL;
 	ObjectAddress address;
 
 	rel = table_open(ProcedureRelationId, RowExclusiveLock);
@@ -1425,14 +1426,25 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
 	}
 
 	if (volatility_item)
+	{
 		procForm->provolatile = interpret_func_volatility(volatility_item);
+		HeapTupleMarkColumnUpdated(pg_proc, provolatile, updated);
+	}
+
 	if (strict_item)
+	{
 		procForm->proisstrict = boolVal(strict_item->arg);
+		HeapTupleMarkColumnUpdated(pg_proc, proisstrict, updated);
+	}
 	if (security_def_item)
+	{
 		procForm->prosecdef = boolVal(security_def_item->arg);
+		HeapTupleMarkColumnUpdated(pg_proc, prosecdef, updated);
+	}
 	if (leakproof_item)
 	{
 		procForm->proleakproof = boolVal(leakproof_item->arg);
+		HeapTupleMarkColumnUpdated(pg_proc, proleakproof, updated);
 		if (procForm->proleakproof && !superuser())
 			ereport(ERROR,
 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
@@ -1441,6 +1453,7 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
 	if (cost_item)
 	{
 		procForm->procost = defGetNumeric(cost_item);
+		HeapTupleMarkColumnUpdated(pg_proc, procost, updated);
 		if (procForm->procost <= 0)
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -1449,6 +1462,7 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
 	if (rows_item)
 	{
 		procForm->prorows = defGetNumeric(rows_item);
+		HeapTupleMarkColumnUpdated(pg_proc, prorows, updated);
 		if (procForm->prorows <= 0)
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -1483,17 +1497,20 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
 		}
 
 		procForm->prosupport = newsupport;
+		HeapTupleMarkColumnUpdated(pg_proc, prosupport, updated);
 	}
 	if (parallel_item)
+	{
 		procForm->proparallel = interpret_func_parallel(parallel_item);
+		HeapTupleMarkColumnUpdated(pg_proc, proparallel, updated);
+	}
 	if (set_items)
 	{
 		Datum		datum;
 		bool		isnull;
 		ArrayType  *a;
-		Datum		repl_val[Natts_pg_proc];
-		bool		repl_null[Natts_pg_proc];
-		bool		repl_repl[Natts_pg_proc];
+		Datum		values[Natts_pg_proc] = {0};
+		bool		nulls[Natts_pg_proc] = {false};
 
 		/* extract existing proconfig setting */
 		datum = SysCacheGetAttr(PROCOID, tup, Anum_pg_proc_proconfig, &isnull);
@@ -1503,27 +1520,19 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
 		a = update_proconfig_value(a, set_items);
 
 		/* update the tuple */
-		memset(repl_repl, false, sizeof(repl_repl));
-		repl_repl[Anum_pg_proc_proconfig - 1] = true;
-
 		if (a == NULL)
-		{
-			repl_val[Anum_pg_proc_proconfig - 1] = (Datum) 0;
-			repl_null[Anum_pg_proc_proconfig - 1] = true;
-		}
+			HeapTupleUpdateValueNull(pg_proc, proconfig, values, nulls, updated);
 		else
-		{
-			repl_val[Anum_pg_proc_proconfig - 1] = PointerGetDatum(a);
-			repl_null[Anum_pg_proc_proconfig - 1] = false;
-		}
+			HeapTupleUpdateValue(pg_proc, proconfig, PointerGetDatum(a), values, nulls, updated);
 
-		tup = heap_modify_tuple(tup, RelationGetDescr(rel),
-								repl_val, repl_null, repl_repl);
+		tup = heap_update_tuple(tup, RelationGetDescr(rel),
+								values, nulls, updated);
 	}
 	/* DO NOT put more touches of procForm below here; it's now dangling. */
 
 	/* Do the update */
-	CatalogTupleUpdate(rel, &tup->t_self, tup);
+	CatalogTupleUpdate(rel, &tup->t_self, tup, updated, NULL);
+	bms_free(updated);
 
 	InvokeObjectPostAlterHook(ProcedureRelationId, funcOid, 0);
 
@@ -1840,9 +1849,9 @@ CreateTransform(CreateTransformStmt *stmt)
 	Oid			tosqlfuncid;
 	AclResult	aclresult;
 	Form_pg_proc procstruct;
-	Datum		values[Natts_pg_transform];
-	bool		nulls[Natts_pg_transform] = {0};
-	bool		replaces[Natts_pg_transform] = {0};
+	Datum		values[Natts_pg_transform] = {0};
+	bool		nulls[Natts_pg_transform] = {false};
+	Bitmapset  *updated = NULL;
 	Oid			transformid;
 	HeapTuple	tuple;
 	HeapTuple	newtuple;
@@ -1943,10 +1952,10 @@ CreateTransform(CreateTransformStmt *stmt)
 	/*
 	 * Ready to go
 	 */
-	values[Anum_pg_transform_trftype - 1] = ObjectIdGetDatum(typeid);
-	values[Anum_pg_transform_trflang - 1] = ObjectIdGetDatum(langid);
-	values[Anum_pg_transform_trffromsql - 1] = ObjectIdGetDatum(fromsqlfuncid);
-	values[Anum_pg_transform_trftosql - 1] = ObjectIdGetDatum(tosqlfuncid);
+	HeapTupleUpdateValue(pg_transform, trftype, ObjectIdGetDatum(typeid), values, nulls, updated);
+	HeapTupleUpdateValue(pg_transform, trflang, ObjectIdGetDatum(langid), values, nulls, updated);
+	HeapTupleUpdateValue(pg_transform, trffromsql, ObjectIdGetDatum(fromsqlfuncid), values, nulls, updated);
+	HeapTupleUpdateValue(pg_transform, trftosql, ObjectIdGetDatum(tosqlfuncid), values, nulls, updated);
 
 	relation = table_open(TransformRelationId, RowExclusiveLock);
 
@@ -1964,11 +1973,8 @@ CreateTransform(CreateTransformStmt *stmt)
 							format_type_be(typeid),
 							stmt->lang)));
 
-		replaces[Anum_pg_transform_trffromsql - 1] = true;
-		replaces[Anum_pg_transform_trftosql - 1] = true;
-
-		newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, nulls, replaces);
-		CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
+		newtuple = heap_update_tuple(tuple, RelationGetDescr(relation), values, nulls, updated);
+		CatalogTupleUpdate(relation, &newtuple->t_self, newtuple, updated, NULL);
 
 		transformid = form->oid;
 		ReleaseSysCache(tuple);
@@ -1978,9 +1984,9 @@ CreateTransform(CreateTransformStmt *stmt)
 	{
 		transformid = GetNewOidWithIndex(relation, TransformOidIndexId,
 										 Anum_pg_transform_oid);
-		values[Anum_pg_transform_oid - 1] = ObjectIdGetDatum(transformid);
+		HeapTupleUpdateValue(pg_transform, oid, ObjectIdGetDatum(transformid), values, nulls, updated);
 		newtuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
-		CatalogTupleInsert(relation, newtuple);
+		CatalogTupleInsert(relation, newtuple, NULL);
 		is_replace = false;
 	}
 
@@ -2022,6 +2028,7 @@ CreateTransform(CreateTransformStmt *stmt)
 	InvokeObjectPostCreateHook(TransformRelationId, transformid, 0);
 
 	heap_freetuple(newtuple);
+	bms_free(updated);
 
 	table_close(relation, RowExclusiveLock);
 
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 635679cc1f2..2b70b0f9d08 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -1561,6 +1561,8 @@ DefineIndex(ParseState *pstate,
 				Relation	pg_index = table_open(IndexRelationId, RowExclusiveLock);
 				HeapTuple	tup,
 							newtup;
+				Form_pg_index indexForm;
+				Bitmapset  *updated = NULL;
 
 				tup = SearchSysCache1(INDEXRELID,
 									  ObjectIdGetDatum(indexRelationId));
@@ -1568,8 +1570,10 @@ DefineIndex(ParseState *pstate,
 					elog(ERROR, "cache lookup failed for index %u",
 						 indexRelationId);
 				newtup = heap_copytuple(tup);
-				((Form_pg_index) GETSTRUCT(newtup))->indisvalid = false;
-				CatalogTupleUpdate(pg_index, &tup->t_self, newtup);
+				indexForm = (Form_pg_index) GETSTRUCT(newtup);
+				HeapTupleUpdateField(pg_index, indisvalid, false, indexForm, updated);
+				CatalogTupleUpdate(pg_index, &tup->t_self, newtup, updated, NULL);
+				bms_free(updated);
 				ReleaseSysCache(tup);
 				table_close(pg_index, RowExclusiveLock);
 				heap_freetuple(newtup);
@@ -4604,6 +4608,8 @@ update_relispartition(Oid relationId, bool newval)
 	HeapTuple	tup;
 	Relation	classRel;
 	ItemPointerData otid;
+	Form_pg_class classForm;
+	Bitmapset  *updated = NULL;
 
 	classRel = table_open(RelationRelationId, RowExclusiveLock);
 	tup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relationId));
@@ -4611,8 +4617,10 @@ update_relispartition(Oid relationId, bool newval)
 		elog(ERROR, "cache lookup failed for relation %u", relationId);
 	otid = tup->t_self;
 	Assert(((Form_pg_class) GETSTRUCT(tup))->relispartition != newval);
-	((Form_pg_class) GETSTRUCT(tup))->relispartition = newval;
-	CatalogTupleUpdate(classRel, &otid, tup);
+	classForm = (Form_pg_class) GETSTRUCT(tup);
+	HeapTupleUpdateField(pg_class, relispartition, newval, classForm, updated);
+	CatalogTupleUpdate(classRel, &otid, tup, updated, NULL);
+	bms_free(updated);
 	UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
 	heap_freetuple(tup);
 	table_close(classRel, RowExclusiveLock);
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index 81a55a33ef2..d09245b65b5 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -79,6 +79,7 @@ SetMatViewPopulatedState(Relation relation, bool newstate)
 {
 	Relation	pgrel;
 	HeapTuple	tuple;
+	Bitmapset  *updated = NULL;
 
 	Assert(relation->rd_rel->relkind == RELKIND_MATVIEW);
 
@@ -94,12 +95,12 @@ SetMatViewPopulatedState(Relation relation, bool newstate)
 		elog(ERROR, "cache lookup failed for relation %u",
 			 RelationGetRelid(relation));
 
-	((Form_pg_class) GETSTRUCT(tuple))->relispopulated = newstate;
-
-	CatalogTupleUpdate(pgrel, &tuple->t_self, tuple);
+	HeapTupleUpdateField(pg_class, relispopulated, newstate, (Form_pg_class) GETSTRUCT(tuple), updated);
+	CatalogTupleUpdate(pgrel, &tuple->t_self, tuple, updated, NULL);
 
 	heap_freetuple(tuple);
 	table_close(pgrel, RowExclusiveLock);
+	bms_free(updated);
 
 	/*
 	 * Advance command counter to make the updated pg_class row locally
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 7493a9ccc06..30513a69eab 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -246,8 +246,8 @@ CreateOpFamily(CreateOpFamilyStmt *stmt, const char *opfname,
 	Oid			opfamilyoid;
 	Relation	rel;
 	HeapTuple	tup;
-	Datum		values[Natts_pg_opfamily];
-	bool		nulls[Natts_pg_opfamily];
+	Datum		values[Natts_pg_opfamily] = {0};
+	bool		nulls[Natts_pg_opfamily] = {false};
 	NameData	opfName;
 	ObjectAddress myself,
 				referenced;
@@ -275,16 +275,16 @@ CreateOpFamily(CreateOpFamilyStmt *stmt, const char *opfname,
 
 	opfamilyoid = GetNewOidWithIndex(rel, OpfamilyOidIndexId,
 									 Anum_pg_opfamily_oid);
-	values[Anum_pg_opfamily_oid - 1] = ObjectIdGetDatum(opfamilyoid);
-	values[Anum_pg_opfamily_opfmethod - 1] = ObjectIdGetDatum(amoid);
+	HeapTupleSetValue(pg_opfamily, oid, ObjectIdGetDatum(opfamilyoid), values);
+	HeapTupleSetValue(pg_opfamily, opfmethod, ObjectIdGetDatum(amoid), values);
 	namestrcpy(&opfName, opfname);
-	values[Anum_pg_opfamily_opfname - 1] = NameGetDatum(&opfName);
-	values[Anum_pg_opfamily_opfnamespace - 1] = ObjectIdGetDatum(namespaceoid);
-	values[Anum_pg_opfamily_opfowner - 1] = ObjectIdGetDatum(GetUserId());
+	HeapTupleSetValue(pg_opfamily, opfname, NameGetDatum(&opfName), values);
+	HeapTupleSetValue(pg_opfamily, opfnamespace, ObjectIdGetDatum(namespaceoid), values);
+	HeapTupleSetValue(pg_opfamily, opfowner, ObjectIdGetDatum(GetUserId()), values);
 
 	tup = heap_form_tuple(rel->rd_att, values, nulls);
 
-	CatalogTupleInsert(rel, tup);
+	CatalogTupleInsert(rel, tup, NULL);
 
 	heap_freetuple(tup);
 
@@ -351,8 +351,8 @@ DefineOpClass(CreateOpClassStmt *stmt)
 	HeapTuple	tup;
 	Form_pg_am	amform;
 	const IndexAmRoutine *amroutine;
-	Datum		values[Natts_pg_opclass];
-	bool		nulls[Natts_pg_opclass];
+	Datum		values[Natts_pg_opclass] = {0};
+	bool		nulls[Natts_pg_opclass] = {false};
 	AclResult	aclresult;
 	NameData	opcName;
 	ObjectAddress myself,
@@ -673,20 +673,20 @@ DefineOpClass(CreateOpClassStmt *stmt)
 
 	opclassoid = GetNewOidWithIndex(rel, OpclassOidIndexId,
 									Anum_pg_opclass_oid);
-	values[Anum_pg_opclass_oid - 1] = ObjectIdGetDatum(opclassoid);
-	values[Anum_pg_opclass_opcmethod - 1] = ObjectIdGetDatum(amoid);
+	HeapTupleSetValue(pg_opclass, oid, ObjectIdGetDatum(opclassoid), values);
+	HeapTupleSetValue(pg_opclass, opcmethod, ObjectIdGetDatum(amoid), values);
 	namestrcpy(&opcName, opcname);
-	values[Anum_pg_opclass_opcname - 1] = NameGetDatum(&opcName);
-	values[Anum_pg_opclass_opcnamespace - 1] = ObjectIdGetDatum(namespaceoid);
-	values[Anum_pg_opclass_opcowner - 1] = ObjectIdGetDatum(GetUserId());
-	values[Anum_pg_opclass_opcfamily - 1] = ObjectIdGetDatum(opfamilyoid);
-	values[Anum_pg_opclass_opcintype - 1] = ObjectIdGetDatum(typeoid);
-	values[Anum_pg_opclass_opcdefault - 1] = BoolGetDatum(isDefault);
-	values[Anum_pg_opclass_opckeytype - 1] = ObjectIdGetDatum(storageoid);
+	HeapTupleSetValue(pg_opclass, opcname, NameGetDatum(&opcName), values);
+	HeapTupleSetValue(pg_opclass, opcnamespace, ObjectIdGetDatum(namespaceoid), values);
+	HeapTupleSetValue(pg_opclass, opcowner, ObjectIdGetDatum(GetUserId()), values);
+	HeapTupleSetValue(pg_opclass, opcfamily, ObjectIdGetDatum(opfamilyoid), values);
+	HeapTupleSetValue(pg_opclass, opcintype, ObjectIdGetDatum(typeoid), values);
+	HeapTupleSetValue(pg_opclass, opcdefault, BoolGetDatum(isDefault), values);
+	HeapTupleSetValue(pg_opclass, opckeytype, ObjectIdGetDatum(storageoid), values);
 
 	tup = heap_form_tuple(rel->rd_att, values, nulls);
 
-	CatalogTupleInsert(rel, tup);
+	CatalogTupleInsert(rel, tup, NULL);
 
 	heap_freetuple(tup);
 
@@ -1473,8 +1473,8 @@ storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
 			   List *operators, bool isAdd)
 {
 	Relation	rel;
-	Datum		values[Natts_pg_amop];
-	bool		nulls[Natts_pg_amop];
+	Datum		values[Natts_pg_amop] = {0};
+	bool		nulls[Natts_pg_amop] = {false};
 	HeapTuple	tup;
 	Oid			entryoid;
 	ObjectAddress myself,
@@ -1514,19 +1514,19 @@ storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
 
 		entryoid = GetNewOidWithIndex(rel, AccessMethodOperatorOidIndexId,
 									  Anum_pg_amop_oid);
-		values[Anum_pg_amop_oid - 1] = ObjectIdGetDatum(entryoid);
-		values[Anum_pg_amop_amopfamily - 1] = ObjectIdGetDatum(opfamilyoid);
-		values[Anum_pg_amop_amoplefttype - 1] = ObjectIdGetDatum(op->lefttype);
-		values[Anum_pg_amop_amoprighttype - 1] = ObjectIdGetDatum(op->righttype);
-		values[Anum_pg_amop_amopstrategy - 1] = Int16GetDatum(op->number);
-		values[Anum_pg_amop_amoppurpose - 1] = CharGetDatum(oppurpose);
-		values[Anum_pg_amop_amopopr - 1] = ObjectIdGetDatum(op->object);
-		values[Anum_pg_amop_amopmethod - 1] = ObjectIdGetDatum(amoid);
-		values[Anum_pg_amop_amopsortfamily - 1] = ObjectIdGetDatum(op->sortfamily);
+		HeapTupleSetValue(pg_amop, oid, ObjectIdGetDatum(entryoid), values);
+		HeapTupleSetValue(pg_amop, amopfamily, ObjectIdGetDatum(opfamilyoid), values);
+		HeapTupleSetValue(pg_amop, amoplefttype, ObjectIdGetDatum(op->lefttype), values);
+		HeapTupleSetValue(pg_amop, amoprighttype, ObjectIdGetDatum(op->righttype), values);
+		HeapTupleSetValue(pg_amop, amopstrategy, Int16GetDatum(op->number), values);
+		HeapTupleSetValue(pg_amop, amoppurpose, CharGetDatum(oppurpose), values);
+		HeapTupleSetValue(pg_amop, amopopr, ObjectIdGetDatum(op->object), values);
+		HeapTupleSetValue(pg_amop, amopmethod, ObjectIdGetDatum(amoid), values);
+		HeapTupleSetValue(pg_amop, amopsortfamily, ObjectIdGetDatum(op->sortfamily), values);
 
 		tup = heap_form_tuple(rel->rd_att, values, nulls);
 
-		CatalogTupleInsert(rel, tup);
+		CatalogTupleInsert(rel, tup, NULL);
 
 		heap_freetuple(tup);
 
@@ -1603,8 +1603,8 @@ storeProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
 				List *procedures, bool isAdd)
 {
 	Relation	rel;
-	Datum		values[Natts_pg_amproc];
-	bool		nulls[Natts_pg_amproc];
+	Datum		values[Natts_pg_amproc] = {0};
+	bool		nulls[Natts_pg_amproc] = {false};
 	HeapTuple	tup;
 	Oid			entryoid;
 	ObjectAddress myself,
@@ -1641,16 +1641,16 @@ storeProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
 
 		entryoid = GetNewOidWithIndex(rel, AccessMethodProcedureOidIndexId,
 									  Anum_pg_amproc_oid);
-		values[Anum_pg_amproc_oid - 1] = ObjectIdGetDatum(entryoid);
-		values[Anum_pg_amproc_amprocfamily - 1] = ObjectIdGetDatum(opfamilyoid);
-		values[Anum_pg_amproc_amproclefttype - 1] = ObjectIdGetDatum(proc->lefttype);
-		values[Anum_pg_amproc_amprocrighttype - 1] = ObjectIdGetDatum(proc->righttype);
-		values[Anum_pg_amproc_amprocnum - 1] = Int16GetDatum(proc->number);
-		values[Anum_pg_amproc_amproc - 1] = ObjectIdGetDatum(proc->object);
+		HeapTupleSetValue(pg_amproc, oid, ObjectIdGetDatum(entryoid), values);
+		HeapTupleSetValue(pg_amproc, amprocfamily, ObjectIdGetDatum(opfamilyoid), values);
+		HeapTupleSetValue(pg_amproc, amproclefttype, ObjectIdGetDatum(proc->lefttype), values);
+		HeapTupleSetValue(pg_amproc, amprocrighttype, ObjectIdGetDatum(proc->righttype), values);
+		HeapTupleSetValue(pg_amproc, amprocnum, Int16GetDatum(proc->number), values);
+		HeapTupleSetValue(pg_amproc, amproc, ObjectIdGetDatum(proc->object), values);
 
 		tup = heap_form_tuple(rel->rd_att, values, nulls);
 
-		CatalogTupleInsert(rel, tup);
+		CatalogTupleInsert(rel, tup, NULL);
 
 		heap_freetuple(tup);
 
diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c
index 9f7e0ed17ce..cfaa6af9371 100644
--- a/src/backend/commands/operatorcmds.c
+++ b/src/backend/commands/operatorcmds.c
@@ -466,11 +466,9 @@ AlterOperator(AlterOperatorStmt *stmt)
 	Relation	catalog;
 	HeapTuple	tup;
 	Form_pg_operator oprForm;
-	int			i;
 	ListCell   *pl;
-	Datum		values[Natts_pg_operator];
-	bool		nulls[Natts_pg_operator];
-	bool		replaces[Natts_pg_operator];
+	Datum		values[Natts_pg_operator] = {0};
+	bool		nulls[Natts_pg_operator] = {false};
 	List	   *restrictionName = NIL;	/* optional restrict. sel. function */
 	bool		updateRestriction = false;
 	Oid			restrictionOid;
@@ -485,6 +483,7 @@ AlterOperator(AlterOperatorStmt *stmt)
 	bool		updateMerges = false;
 	bool		canHash = false;
 	bool		updateHashes = false;
+	Bitmapset  *updated = NULL;
 
 	/* Look up the operator */
 	oprId = LookupOperWithArgs(stmt->opername, false);
@@ -646,47 +645,27 @@ AlterOperator(AlterOperatorStmt *stmt)
 						   canHash);
 
 	/* Update the tuple */
-	for (i = 0; i < Natts_pg_operator; ++i)
-	{
-		values[i] = (Datum) 0;
-		replaces[i] = false;
-		nulls[i] = false;
-	}
 	if (updateRestriction)
-	{
-		replaces[Anum_pg_operator_oprrest - 1] = true;
-		values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(restrictionOid);
-	}
+		HeapTupleUpdateValue(pg_operator, oprrest, ObjectIdGetDatum(restrictionOid), values, nulls, updated);
+
 	if (updateJoin)
-	{
-		replaces[Anum_pg_operator_oprjoin - 1] = true;
-		values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(joinOid);
-	}
+		HeapTupleUpdateValue(pg_operator, oprjoin, ObjectIdGetDatum(joinOid), values, nulls, updated);
+
 	if (OidIsValid(commutatorOid))
-	{
-		replaces[Anum_pg_operator_oprcom - 1] = true;
-		values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(commutatorOid);
-	}
+		HeapTupleUpdateValue(pg_operator, oprcom, ObjectIdGetDatum(commutatorOid), values, nulls, updated);
+
 	if (OidIsValid(negatorOid))
-	{
-		replaces[Anum_pg_operator_oprnegate - 1] = true;
-		values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(negatorOid);
-	}
+		HeapTupleUpdateValue(pg_operator, oprnegate, ObjectIdGetDatum(negatorOid), values, nulls, updated);
+
 	if (updateMerges)
-	{
-		replaces[Anum_pg_operator_oprcanmerge - 1] = true;
-		values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(canMerge);
-	}
+		HeapTupleUpdateValue(pg_operator, oprcanmerge, BoolGetDatum(canMerge), values, nulls, updated);
+
 	if (updateHashes)
-	{
-		replaces[Anum_pg_operator_oprcanhash - 1] = true;
-		values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(canHash);
-	}
+		HeapTupleUpdateValue(pg_operator, oprcanhash, BoolGetDatum(canHash), values, nulls, updated);
 
-	tup = heap_modify_tuple(tup, RelationGetDescr(catalog),
-							values, nulls, replaces);
+	tup = heap_update_tuple(tup, RelationGetDescr(catalog), values, nulls, updated);
 
-	CatalogTupleUpdate(catalog, &tup->t_self, tup);
+	CatalogTupleUpdate(catalog, &tup->t_self, tup, updated, NULL);
 
 	address = makeOperatorDependencies(tup, false, true);
 
@@ -696,6 +675,7 @@ AlterOperator(AlterOperatorStmt *stmt)
 	InvokeObjectPostAlterHook(OperatorRelationId, oprId, 0);
 
 	table_close(catalog, NoLock);
+	bms_free(updated);
 
 	return address;
 }
diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index 21b8eebe32d..336e8f38bf5 100644
--- a/src/backend/commands/policy.c
+++ b/src/backend/commands/policy.c
@@ -427,6 +427,7 @@ RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id)
 	Datum	   *role_oids;
 	bool		attr_isnull;
 	bool		keep_policy = true;
+	Bitmapset  *updated = NULL;
 	int			i,
 				j;
 
@@ -483,29 +484,19 @@ RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id)
 	if (num_roles > 0)
 	{
 		ArrayType  *role_ids;
-		Datum		values[Natts_pg_policy];
-		bool		isnull[Natts_pg_policy];
-		bool		replaces[Natts_pg_policy];
+		Datum		values[Natts_pg_policy] = {0};
+		bool		nulls[Natts_pg_policy] = {false};
 		HeapTuple	new_tuple;
 		HeapTuple	reltup;
 		ObjectAddress target;
 		ObjectAddress myself;
 
-		/* zero-clear */
-		memset(values, 0, sizeof(values));
-		memset(replaces, 0, sizeof(replaces));
-		memset(isnull, 0, sizeof(isnull));
-
 		/* This is the array for the new tuple */
 		role_ids = construct_array_builtin(role_oids, num_roles, OIDOID);
 
-		replaces[Anum_pg_policy_polroles - 1] = true;
-		values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
-
-		new_tuple = heap_modify_tuple(tuple,
-									  RelationGetDescr(pg_policy_rel),
-									  values, isnull, replaces);
-		CatalogTupleUpdate(pg_policy_rel, &new_tuple->t_self, new_tuple);
+		HeapTupleUpdateValue(pg_policy, polroles, PointerGetDatum(role_ids), values, nulls, updated);
+		new_tuple = heap_update_tuple(tuple, RelationGetDescr(pg_policy_rel), values, nulls, updated);
+		CatalogTupleUpdate(pg_policy_rel, &new_tuple->t_self, new_tuple, updated, NULL);
 
 		/* Remove all the old shared dependencies (roles) */
 		deleteSharedDependencyRecordsFor(PolicyRelationId, policy_id, 0);
@@ -552,8 +543,8 @@ RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id)
 	}
 
 	/* Clean up. */
+	bms_free(updated);
 	systable_endscan(sscan);
-
 	table_close(pg_policy_rel, RowExclusiveLock);
 
 	return keep_policy;
@@ -584,8 +575,8 @@ CreatePolicy(CreatePolicyStmt *stmt)
 	ScanKeyData skey[2];
 	SysScanDesc sscan;
 	HeapTuple	policy_tuple;
-	Datum		values[Natts_pg_policy];
-	bool		isnull[Natts_pg_policy];
+	Datum		values[Natts_pg_policy] = {0};
+	bool		nulls[Natts_pg_policy] = {false};
 	ObjectAddress target;
 	ObjectAddress myself;
 	int			i;
@@ -619,10 +610,6 @@ CreatePolicy(CreatePolicyStmt *stmt)
 	qual_pstate = make_parsestate(NULL);
 	with_check_pstate = make_parsestate(NULL);
 
-	/* zero-clear */
-	memset(values, 0, sizeof(values));
-	memset(isnull, 0, sizeof(isnull));
-
 	/* Get id of table.  Also handles permissions checks. */
 	table_id = RangeVarGetRelidExtended(stmt->table, AccessExclusiveLock,
 										0,
@@ -688,30 +675,29 @@ CreatePolicy(CreatePolicyStmt *stmt)
 
 	policy_id = GetNewOidWithIndex(pg_policy_rel, PolicyOidIndexId,
 								   Anum_pg_policy_oid);
-	values[Anum_pg_policy_oid - 1] = ObjectIdGetDatum(policy_id);
-	values[Anum_pg_policy_polrelid - 1] = ObjectIdGetDatum(table_id);
-	values[Anum_pg_policy_polname - 1] = DirectFunctionCall1(namein,
-															 CStringGetDatum(stmt->policy_name));
-	values[Anum_pg_policy_polcmd - 1] = CharGetDatum(polcmd);
-	values[Anum_pg_policy_polpermissive - 1] = BoolGetDatum(stmt->permissive);
-	values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
+	HeapTupleSetValue(pg_policy, oid, ObjectIdGetDatum(policy_id), values);
+	HeapTupleSetValue(pg_policy, polrelid, ObjectIdGetDatum(table_id), values);
+	HeapTupleSetValue(pg_policy, polname, DirectFunctionCall1(namein,
+															  CStringGetDatum(stmt->policy_name)), values);
+	HeapTupleSetValue(pg_policy, polcmd, CharGetDatum(polcmd), values);
+	HeapTupleSetValue(pg_policy, polpermissive, BoolGetDatum(stmt->permissive), values);
+	HeapTupleSetValue(pg_policy, polroles, PointerGetDatum(role_ids), values);
 
 	/* Add qual if present. */
 	if (qual)
-		values[Anum_pg_policy_polqual - 1] = CStringGetTextDatum(nodeToString(qual));
+		HeapTupleSetValue(pg_policy, polqual, CStringGetTextDatum(nodeToString(qual)), values);
 	else
-		isnull[Anum_pg_policy_polqual - 1] = true;
+		HeapTupleSetValueNull(pg_policy, polqual, values, nulls);
 
 	/* Add WITH CHECK qual if present */
 	if (with_check_qual)
-		values[Anum_pg_policy_polwithcheck - 1] = CStringGetTextDatum(nodeToString(with_check_qual));
+		HeapTupleSetValue(pg_policy, polwithcheck, CStringGetTextDatum(nodeToString(with_check_qual)), values);
 	else
-		isnull[Anum_pg_policy_polwithcheck - 1] = true;
+		HeapTupleSetValueNull(pg_policy, polwithcheck, values, nulls);
 
-	policy_tuple = heap_form_tuple(RelationGetDescr(pg_policy_rel), values,
-								   isnull);
+	policy_tuple = heap_form_tuple(RelationGetDescr(pg_policy_rel), values, nulls);
 
-	CatalogTupleInsert(pg_policy_rel, policy_tuple);
+	CatalogTupleInsert(pg_policy_rel, policy_tuple, NULL);
 
 	/* Record Dependencies */
 	target.classId = RelationRelationId;
@@ -782,9 +768,9 @@ AlterPolicy(AlterPolicyStmt *stmt)
 	SysScanDesc sscan;
 	HeapTuple	policy_tuple;
 	HeapTuple	new_tuple;
-	Datum		values[Natts_pg_policy];
-	bool		isnull[Natts_pg_policy];
-	bool		replaces[Natts_pg_policy];
+	Datum		values[Natts_pg_policy] = {0};
+	bool		nulls[Natts_pg_policy] = {false};
+	Bitmapset  *updated = NULL;
 	ObjectAddress target;
 	ObjectAddress myself;
 	Datum		polcmd_datum;
@@ -854,11 +840,6 @@ AlterPolicy(AlterPolicyStmt *stmt)
 		free_parsestate(with_check_pstate);
 	}
 
-	/* zero-clear */
-	memset(values, 0, sizeof(values));
-	memset(replaces, 0, sizeof(replaces));
-	memset(isnull, 0, sizeof(isnull));
-
 	/* Find policy to update. */
 	pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);
 
@@ -918,8 +899,7 @@ AlterPolicy(AlterPolicyStmt *stmt)
 
 	if (role_ids != NULL)
 	{
-		replaces[Anum_pg_policy_polroles - 1] = true;
-		values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
+		HeapTupleUpdateValue(pg_policy, polroles, PointerGetDatum(role_ids), values, nulls, updated);
 	}
 	else
 	{
@@ -953,9 +933,7 @@ AlterPolicy(AlterPolicyStmt *stmt)
 
 	if (qual != NULL)
 	{
-		replaces[Anum_pg_policy_polqual - 1] = true;
-		values[Anum_pg_policy_polqual - 1]
-			= CStringGetTextDatum(nodeToString(qual));
+		HeapTupleUpdateValue(pg_policy, polqual, CStringGetTextDatum(nodeToString(qual)), values, nulls, updated);
 	}
 	else
 	{
@@ -995,9 +973,7 @@ AlterPolicy(AlterPolicyStmt *stmt)
 
 	if (with_check_qual != NULL)
 	{
-		replaces[Anum_pg_policy_polwithcheck - 1] = true;
-		values[Anum_pg_policy_polwithcheck - 1]
-			= CStringGetTextDatum(nodeToString(with_check_qual));
+		HeapTupleUpdateValue(pg_policy, polwithcheck, CStringGetTextDatum(nodeToString(with_check_qual)), values, nulls, updated);
 	}
 	else
 	{
@@ -1036,10 +1012,10 @@ AlterPolicy(AlterPolicyStmt *stmt)
 		}
 	}
 
-	new_tuple = heap_modify_tuple(policy_tuple,
+	new_tuple = heap_update_tuple(policy_tuple,
 								  RelationGetDescr(pg_policy_rel),
-								  values, isnull, replaces);
-	CatalogTupleUpdate(pg_policy_rel, &new_tuple->t_self, new_tuple);
+								  values, nulls, updated);
+	CatalogTupleUpdate(pg_policy_rel, &new_tuple->t_self, new_tuple, updated, NULL);
 
 	/* Update Dependencies. */
 	deleteDependencyRecordsFor(PolicyRelationId, policy_id, false);
@@ -1081,6 +1057,7 @@ AlterPolicy(AlterPolicyStmt *stmt)
 	CacheInvalidateRelcache(target_table);
 
 	/* Clean up. */
+	bms_free(updated);
 	systable_endscan(sscan);
 	relation_close(target_table, NoLock);
 	table_close(pg_policy_rel, RowExclusiveLock);
@@ -1102,6 +1079,8 @@ rename_policy(RenameStmt *stmt)
 	ScanKeyData skey[2];
 	SysScanDesc sscan;
 	HeapTuple	policy_tuple;
+	Form_pg_policy polform;
+	Bitmapset  *updated = NULL;
 	ObjectAddress address;
 
 	/* Get id of table.  Also handles permissions checks. */
@@ -1169,11 +1148,12 @@ rename_policy(RenameStmt *stmt)
 	opoloid = ((Form_pg_policy) GETSTRUCT(policy_tuple))->oid;
 
 	policy_tuple = heap_copytuple(policy_tuple);
+	polform = (Form_pg_policy) GETSTRUCT(policy_tuple);
+	namestrcpy(&polform->polname, stmt->newname);
+	HeapTupleMarkColumnUpdated(pg_policy, polname, updated);
 
-	namestrcpy(&((Form_pg_policy) GETSTRUCT(policy_tuple))->polname,
-			   stmt->newname);
-
-	CatalogTupleUpdate(pg_policy_rel, &policy_tuple->t_self, policy_tuple);
+	CatalogTupleUpdate(pg_policy_rel, &policy_tuple->t_self, policy_tuple, updated, NULL);
+	bms_free(updated);
 
 	InvokeObjectPostAlterHook(PolicyRelationId, opoloid, 0);
 
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
index d4c6a16cd01..2486eb7df83 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -46,9 +46,9 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 	Oid			funcargtypes[1];
 	Relation	rel;
 	TupleDesc	tupDesc;
-	Datum		values[Natts_pg_language];
-	bool		nulls[Natts_pg_language];
-	bool		replaces[Natts_pg_language];
+	Datum		values[Natts_pg_language] = {0};
+	bool		nulls[Natts_pg_language] = {false};
+	Bitmapset  *updated = NULL;
 	NameData	langname;
 	HeapTuple	oldtup;
 	HeapTuple	tup;
@@ -104,19 +104,15 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 	tupDesc = RelationGetDescr(rel);
 
 	/* Prepare data to be inserted */
-	memset(values, 0, sizeof(values));
-	memset(nulls, false, sizeof(nulls));
-	memset(replaces, true, sizeof(replaces));
-
 	namestrcpy(&langname, languageName);
-	values[Anum_pg_language_lanname - 1] = NameGetDatum(&langname);
-	values[Anum_pg_language_lanowner - 1] = ObjectIdGetDatum(languageOwner);
-	values[Anum_pg_language_lanispl - 1] = BoolGetDatum(true);
-	values[Anum_pg_language_lanpltrusted - 1] = BoolGetDatum(stmt->pltrusted);
-	values[Anum_pg_language_lanplcallfoid - 1] = ObjectIdGetDatum(handlerOid);
-	values[Anum_pg_language_laninline - 1] = ObjectIdGetDatum(inlineOid);
-	values[Anum_pg_language_lanvalidator - 1] = ObjectIdGetDatum(valOid);
-	nulls[Anum_pg_language_lanacl - 1] = true;
+	HeapTupleUpdateValue(pg_language, lanname, NameGetDatum(&langname), values, nulls, updated);
+	HeapTupleUpdateValue(pg_language, lanowner, ObjectIdGetDatum(languageOwner), values, nulls, updated);
+	HeapTupleUpdateValue(pg_language, lanispl, BoolGetDatum(true), values, nulls, updated);
+	HeapTupleUpdateValue(pg_language, lanpltrusted, BoolGetDatum(stmt->pltrusted), values, nulls, updated);
+	HeapTupleUpdateValue(pg_language, lanplcallfoid, ObjectIdGetDatum(handlerOid), values, nulls, updated);
+	HeapTupleUpdateValue(pg_language, laninline, ObjectIdGetDatum(inlineOid), values, nulls, updated);
+	HeapTupleUpdateValue(pg_language, lanvalidator, ObjectIdGetDatum(valOid), values, nulls, updated);
+	HeapTupleUpdateValueNull(pg_language, lanacl, values, nulls, updated);
 
 	/* Check for pre-existing definition */
 	oldtup = SearchSysCache1(LANGNAME, PointerGetDatum(languageName));
@@ -142,13 +138,13 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 		 * Do not change existing oid, ownership or permissions.  Note
 		 * dependency-update code below has to agree with this decision.
 		 */
-		replaces[Anum_pg_language_oid - 1] = false;
-		replaces[Anum_pg_language_lanowner - 1] = false;
-		replaces[Anum_pg_language_lanacl - 1] = false;
+		HeapTupleSetColumnNotUpdated(pg_language, oid, updated);
+		HeapTupleSetColumnNotUpdated(pg_language, lanowner, updated);
+		HeapTupleSetColumnNotUpdated(pg_language, lanacl, updated);
 
 		/* Okay, do it... */
-		tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces);
-		CatalogTupleUpdate(rel, &tup->t_self, tup);
+		tup = heap_update_tuple(oldtup, tupDesc, values, nulls, updated);
+		CatalogTupleUpdate(rel, &tup->t_self, tup, updated, NULL);
 
 		langoid = oldform->oid;
 		ReleaseSysCache(oldtup);
@@ -157,11 +153,10 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 	else
 	{
 		/* Creating a new language */
-		langoid = GetNewOidWithIndex(rel, LanguageOidIndexId,
-									 Anum_pg_language_oid);
-		values[Anum_pg_language_oid - 1] = ObjectIdGetDatum(langoid);
+		langoid = GetNewOidWithIndex(rel, LanguageOidIndexId, Anum_pg_language_oid);
+		HeapTupleUpdateValue(pg_language, oid, ObjectIdGetDatum(langoid), values, nulls, updated);
 		tup = heap_form_tuple(tupDesc, values, nulls);
-		CatalogTupleInsert(rel, tup);
+		CatalogTupleInsert(rel, tup, NULL);
 		is_update = false;
 	}
 
@@ -213,6 +208,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 	InvokeObjectPostCreateHook(LanguageRelationId, myself.objectId, 0);
 
 	table_close(rel, RowExclusiveLock);
+	bms_free(updated);
 
 	return myself;
 }
diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c
index fc3a4c19e65..59a7e6bed1f 100644
--- a/src/backend/commands/publicationcmds.c
+++ b/src/backend/commands/publicationcmds.c
@@ -833,8 +833,8 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
 	Relation	rel;
 	ObjectAddress myself;
 	Oid			puboid;
-	bool		nulls[Natts_pg_publication];
-	Datum		values[Natts_pg_publication];
+	Datum		values[Natts_pg_publication] = {0};
+	bool		nulls[Natts_pg_publication] = {false};
 	HeapTuple	tup;
 	bool		publish_given;
 	PublicationActions pubactions;
@@ -876,9 +876,8 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
 	memset(values, 0, sizeof(values));
 	memset(nulls, false, sizeof(nulls));
 
-	values[Anum_pg_publication_pubname - 1] =
-		DirectFunctionCall1(namein, CStringGetDatum(stmt->pubname));
-	values[Anum_pg_publication_pubowner - 1] = ObjectIdGetDatum(GetUserId());
+	HeapTupleSetValue(pg_publication, pubname, DirectFunctionCall1(namein, CStringGetDatum(stmt->pubname)), values);
+	HeapTupleSetValue(pg_publication, pubowner, ObjectIdGetDatum(GetUserId()), values);
 
 	parse_publication_options(pstate,
 							  stmt->options,
@@ -897,28 +896,20 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
 
 	puboid = GetNewOidWithIndex(rel, PublicationObjectIndexId,
 								Anum_pg_publication_oid);
-	values[Anum_pg_publication_oid - 1] = ObjectIdGetDatum(puboid);
-	values[Anum_pg_publication_puballtables - 1] =
-		BoolGetDatum(stmt->for_all_tables);
-	values[Anum_pg_publication_puballsequences - 1] =
-		BoolGetDatum(stmt->for_all_sequences);
-	values[Anum_pg_publication_pubinsert - 1] =
-		BoolGetDatum(pubactions.pubinsert);
-	values[Anum_pg_publication_pubupdate - 1] =
-		BoolGetDatum(pubactions.pubupdate);
-	values[Anum_pg_publication_pubdelete - 1] =
-		BoolGetDatum(pubactions.pubdelete);
-	values[Anum_pg_publication_pubtruncate - 1] =
-		BoolGetDatum(pubactions.pubtruncate);
-	values[Anum_pg_publication_pubviaroot - 1] =
-		BoolGetDatum(publish_via_partition_root);
-	values[Anum_pg_publication_pubgencols - 1] =
-		CharGetDatum(publish_generated_columns);
+	HeapTupleSetValue(pg_publication, oid, ObjectIdGetDatum(puboid), values);
+	HeapTupleSetValue(pg_publication, puballtables, BoolGetDatum(stmt->for_all_tables), values);
+	HeapTupleSetValue(pg_publication, puballsequences, BoolGetDatum(stmt->for_all_sequences), values);
+	HeapTupleSetValue(pg_publication, pubinsert, BoolGetDatum(pubactions.pubinsert), values);
+	HeapTupleSetValue(pg_publication, pubupdate, BoolGetDatum(pubactions.pubupdate), values);
+	HeapTupleSetValue(pg_publication, pubdelete, BoolGetDatum(pubactions.pubdelete), values);
+	HeapTupleSetValue(pg_publication, pubtruncate, BoolGetDatum(pubactions.pubtruncate), values);
+	HeapTupleSetValue(pg_publication, pubviaroot, BoolGetDatum(publish_via_partition_root), values);
+	HeapTupleSetValue(pg_publication, pubgencols, CharGetDatum(publish_generated_columns), values);
 
 	tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
 
 	/* Insert tuple into catalog. */
-	CatalogTupleInsert(rel, tup);
+	CatalogTupleInsert(rel, tup, NULL);
 	heap_freetuple(tup);
 
 	recordDependencyOnOwner(PublicationRelationId, puboid, GetUserId());
@@ -1001,9 +992,9 @@ static void
 AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
 						Relation rel, HeapTuple tup)
 {
-	bool		nulls[Natts_pg_publication];
-	bool		replaces[Natts_pg_publication];
-	Datum		values[Natts_pg_publication];
+	Datum		values[Natts_pg_publication] = {0};
+	bool		nulls[Natts_pg_publication] = {false};
+	Bitmapset  *updated = NULL;
 	bool		publish_given;
 	PublicationActions pubactions;
 	bool		publish_via_partition_root_given;
@@ -1113,42 +1104,25 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
 	}
 
 	/* Everything ok, form a new tuple. */
-	memset(values, 0, sizeof(values));
-	memset(nulls, false, sizeof(nulls));
-	memset(replaces, false, sizeof(replaces));
-
 	if (publish_given)
 	{
-		values[Anum_pg_publication_pubinsert - 1] = BoolGetDatum(pubactions.pubinsert);
-		replaces[Anum_pg_publication_pubinsert - 1] = true;
-
-		values[Anum_pg_publication_pubupdate - 1] = BoolGetDatum(pubactions.pubupdate);
-		replaces[Anum_pg_publication_pubupdate - 1] = true;
-
-		values[Anum_pg_publication_pubdelete - 1] = BoolGetDatum(pubactions.pubdelete);
-		replaces[Anum_pg_publication_pubdelete - 1] = true;
-
-		values[Anum_pg_publication_pubtruncate - 1] = BoolGetDatum(pubactions.pubtruncate);
-		replaces[Anum_pg_publication_pubtruncate - 1] = true;
+		HeapTupleUpdateValue(pg_publication, pubinsert, BoolGetDatum(pubactions.pubinsert), values, nulls, updated);
+		HeapTupleUpdateValue(pg_publication, pubupdate, BoolGetDatum(pubactions.pubupdate), values, nulls, updated);
+		HeapTupleUpdateValue(pg_publication, pubdelete, BoolGetDatum(pubactions.pubdelete), values, nulls, updated);
+		HeapTupleUpdateValue(pg_publication, pubtruncate, BoolGetDatum(pubactions.pubtruncate), values, nulls, updated);
 	}
 
 	if (publish_via_partition_root_given)
-	{
-		values[Anum_pg_publication_pubviaroot - 1] = BoolGetDatum(publish_via_partition_root);
-		replaces[Anum_pg_publication_pubviaroot - 1] = true;
-	}
+		HeapTupleUpdateValue(pg_publication, pubviaroot, BoolGetDatum(publish_via_partition_root), values, nulls, updated);
 
 	if (publish_generated_columns_given)
-	{
-		values[Anum_pg_publication_pubgencols - 1] = CharGetDatum(publish_generated_columns);
-		replaces[Anum_pg_publication_pubgencols - 1] = true;
-	}
+		HeapTupleUpdateValue(pg_publication, pubgencols, CharGetDatum(publish_generated_columns), values, nulls, updated);
 
-	tup = heap_modify_tuple(tup, RelationGetDescr(rel), values, nulls,
-							replaces);
+	tup = heap_update_tuple(tup, RelationGetDescr(rel), values, nulls, updated);
 
 	/* Update the catalog. */
-	CatalogTupleUpdate(rel, &tup->t_self, tup);
+	CatalogTupleUpdate(rel, &tup->t_self, tup, updated, NULL);
+	bms_free(updated);
 
 	CommandCounterIncrement();
 
@@ -2053,6 +2027,7 @@ static void
 AlterPublicationOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
 {
 	Form_pg_publication form;
+	Bitmapset  *updated = NULL;
 
 	form = (Form_pg_publication) GETSTRUCT(tup);
 
@@ -2089,8 +2064,10 @@ AlterPublicationOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
 		}
 	}
 
-	form->pubowner = newOwnerId;
-	CatalogTupleUpdate(rel, &tup->t_self, tup);
+	HeapTupleUpdateField(pg_publication, pubowner, newOwnerId, form, updated);
+	CatalogTupleUpdate(rel, &tup->t_self, tup, updated, NULL);
+
+	bms_free(updated);
 
 	/* Update owner dependency reference */
 	changeDependencyOnOwner(PublicationRelationId,
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
index 4c51e920626..d3a6dc4da36 100644
--- a/src/backend/commands/schemacmds.c
+++ b/src/backend/commands/schemacmds.c
@@ -255,6 +255,7 @@ RenameSchema(const char *oldname, const char *newname)
 	AclResult	aclresult;
 	ObjectAddress address;
 	Form_pg_namespace nspform;
+	Bitmapset  *updated = NULL;
 
 	rel = table_open(NamespaceRelationId, RowExclusiveLock);
 
@@ -292,7 +293,8 @@ RenameSchema(const char *oldname, const char *newname)
 
 	/* rename */
 	namestrcpy(&nspform->nspname, newname);
-	CatalogTupleUpdate(rel, &tup->t_self, tup);
+	HeapTupleMarkColumnUpdated(pg_namespace, nspname, updated);
+	CatalogTupleUpdate(rel, &tup->t_self, tup, updated, NULL);
 
 	InvokeObjectPostAlterHook(NamespaceRelationId, nspOid, 0);
 
@@ -300,6 +302,7 @@ RenameSchema(const char *oldname, const char *newname)
 
 	table_close(rel, NoLock);
 	heap_freetuple(tup);
+	bms_free(updated);
 
 	return address;
 }
@@ -374,14 +377,14 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
 	 */
 	if (nspForm->nspowner != newOwnerId)
 	{
-		Datum		repl_val[Natts_pg_namespace];
-		bool		repl_null[Natts_pg_namespace];
-		bool		repl_repl[Natts_pg_namespace];
+		Datum		values[Natts_pg_namespace] = {0};
+		bool		nulls[Natts_pg_namespace] = {false};
 		Acl		   *newAcl;
 		Datum		aclDatum;
 		bool		isNull;
 		HeapTuple	newtuple;
 		AclResult	aclresult;
+		Bitmapset  *updated = NULL;
 
 		/* Otherwise, must be owner of the existing object */
 		if (!object_ownercheck(NamespaceRelationId, nspForm->oid, GetUserId()))
@@ -406,11 +409,7 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
 			aclcheck_error(aclresult, OBJECT_DATABASE,
 						   get_database_name(MyDatabaseId));
 
-		memset(repl_null, false, sizeof(repl_null));
-		memset(repl_repl, false, sizeof(repl_repl));
-
-		repl_repl[Anum_pg_namespace_nspowner - 1] = true;
-		repl_val[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(newOwnerId);
+		HeapTupleUpdateValue(pg_namespace, nspowner, ObjectIdGetDatum(newOwnerId), values, nulls, updated);
 
 		/*
 		 * Determine the modified ACL for the new owner.  This is only
@@ -423,19 +422,19 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
 		{
 			newAcl = aclnewowner(DatumGetAclP(aclDatum),
 								 nspForm->nspowner, newOwnerId);
-			repl_repl[Anum_pg_namespace_nspacl - 1] = true;
-			repl_val[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(newAcl);
+			HeapTupleUpdateValue(pg_namespace, nspacl, PointerGetDatum(newAcl), values, nulls, updated);
 		}
 
-		newtuple = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null, repl_repl);
+		newtuple = heap_update_tuple(tup, RelationGetDescr(rel), values, nulls, updated);
 
-		CatalogTupleUpdate(rel, &newtuple->t_self, newtuple);
+		CatalogTupleUpdate(rel, &newtuple->t_self, newtuple, updated, NULL);
 
 		heap_freetuple(newtuple);
 
 		/* Update owner dependency reference */
 		changeDependencyOnOwner(NamespaceRelationId, nspForm->oid,
 								newOwnerId);
+		bms_free(updated);
 	}
 
 	InvokeObjectPostAlterHook(NamespaceRelationId,
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 4160f5b6855..e4a56f4d569 100644
--- a/src/backend/commands/seclabel.c
+++ b/src/backend/commands/seclabel.c
@@ -334,18 +334,16 @@ SetSharedSecurityLabel(const ObjectAddress *object,
 	SysScanDesc scan;
 	HeapTuple	oldtup;
 	HeapTuple	newtup = NULL;
-	Datum		values[Natts_pg_shseclabel];
-	bool		nulls[Natts_pg_shseclabel];
-	bool		replaces[Natts_pg_shseclabel];
+	Datum		values[Natts_pg_shseclabel] = {0};
+	bool		nulls[Natts_pg_shseclabel] = {false};
+	Bitmapset  *updated = NULL;
 
 	/* Prepare to form or update a tuple, if necessary. */
-	memset(nulls, false, sizeof(nulls));
-	memset(replaces, false, sizeof(replaces));
-	values[Anum_pg_shseclabel_objoid - 1] = ObjectIdGetDatum(object->objectId);
-	values[Anum_pg_shseclabel_classoid - 1] = ObjectIdGetDatum(object->classId);
-	values[Anum_pg_shseclabel_provider - 1] = CStringGetTextDatum(provider);
+	HeapTupleUpdateValue(pg_shseclabel, objoid, ObjectIdGetDatum(object->objectId), values, nulls, updated);
+	HeapTupleUpdateValue(pg_shseclabel, classoid, ObjectIdGetDatum(object->classId), values, nulls, updated);
+	HeapTupleUpdateValue(pg_shseclabel, provider, CStringGetTextDatum(provider), values, nulls, updated);
 	if (label != NULL)
-		values[Anum_pg_shseclabel_label - 1] = CStringGetTextDatum(label);
+		HeapTupleUpdateValue(pg_shseclabel, label, CStringGetTextDatum(label), values, nulls, updated);
 
 	/* Use the index to search for a matching old tuple */
 	ScanKeyInit(&keys[0],
@@ -373,10 +371,9 @@ SetSharedSecurityLabel(const ObjectAddress *object,
 			CatalogTupleDelete(pg_shseclabel, &oldtup->t_self);
 		else
 		{
-			replaces[Anum_pg_shseclabel_label - 1] = true;
-			newtup = heap_modify_tuple(oldtup, RelationGetDescr(pg_shseclabel),
-									   values, nulls, replaces);
-			CatalogTupleUpdate(pg_shseclabel, &oldtup->t_self, newtup);
+			HeapTupleUpdateValue(pg_shseclabel, label, CStringGetTextDatum(label), values, nulls, updated);
+			newtup = heap_update_tuple(oldtup, RelationGetDescr(pg_shseclabel), values, nulls, updated);
+			CatalogTupleUpdate(pg_shseclabel, &oldtup->t_self, newtup, updated, NULL);
 		}
 	}
 	systable_endscan(scan);
@@ -386,13 +383,14 @@ SetSharedSecurityLabel(const ObjectAddress *object,
 	{
 		newtup = heap_form_tuple(RelationGetDescr(pg_shseclabel),
 								 values, nulls);
-		CatalogTupleInsert(pg_shseclabel, newtup);
+		CatalogTupleInsert(pg_shseclabel, newtup, NULL);
 	}
 
 	if (newtup != NULL)
 		heap_freetuple(newtup);
 
 	table_close(pg_shseclabel, RowExclusiveLock);
+	bms_free(updated);
 }
 
 /*
@@ -409,9 +407,9 @@ SetSecurityLabel(const ObjectAddress *object,
 	SysScanDesc scan;
 	HeapTuple	oldtup;
 	HeapTuple	newtup = NULL;
-	Datum		values[Natts_pg_seclabel];
-	bool		nulls[Natts_pg_seclabel];
-	bool		replaces[Natts_pg_seclabel];
+	Datum		values[Natts_pg_seclabel] = {0};
+	bool		nulls[Natts_pg_seclabel] = {false};
+	Bitmapset  *updated = NULL;
 
 	/* Shared objects have their own security label catalog. */
 	if (IsSharedRelation(object->classId))
@@ -421,14 +419,12 @@ SetSecurityLabel(const ObjectAddress *object,
 	}
 
 	/* Prepare to form or update a tuple, if necessary. */
-	memset(nulls, false, sizeof(nulls));
-	memset(replaces, false, sizeof(replaces));
-	values[Anum_pg_seclabel_objoid - 1] = ObjectIdGetDatum(object->objectId);
-	values[Anum_pg_seclabel_classoid - 1] = ObjectIdGetDatum(object->classId);
-	values[Anum_pg_seclabel_objsubid - 1] = Int32GetDatum(object->objectSubId);
-	values[Anum_pg_seclabel_provider - 1] = CStringGetTextDatum(provider);
+	HeapTupleUpdateValue(pg_seclabel, objoid, ObjectIdGetDatum(object->objectId), values, nulls, updated);
+	HeapTupleUpdateValue(pg_seclabel, classoid, ObjectIdGetDatum(object->classId), values, nulls, updated);
+	HeapTupleUpdateValue(pg_seclabel, objsubid, Int32GetDatum(object->objectSubId), values, nulls, updated);
+	HeapTupleUpdateValue(pg_seclabel, provider, CStringGetTextDatum(provider), values, nulls, updated);
 	if (label != NULL)
-		values[Anum_pg_seclabel_label - 1] = CStringGetTextDatum(label);
+		HeapTupleUpdateValue(pg_seclabel, label, CStringGetTextDatum(label), values, nulls, updated);
 
 	/* Use the index to search for a matching old tuple */
 	ScanKeyInit(&keys[0],
@@ -460,10 +456,9 @@ SetSecurityLabel(const ObjectAddress *object,
 			CatalogTupleDelete(pg_seclabel, &oldtup->t_self);
 		else
 		{
-			replaces[Anum_pg_seclabel_label - 1] = true;
-			newtup = heap_modify_tuple(oldtup, RelationGetDescr(pg_seclabel),
-									   values, nulls, replaces);
-			CatalogTupleUpdate(pg_seclabel, &oldtup->t_self, newtup);
+			HeapTupleUpdateValue(pg_seclabel, label, CStringGetTextDatum(label), values, nulls, updated);
+			newtup = heap_update_tuple(oldtup, RelationGetDescr(pg_seclabel), values, nulls, updated);
+			CatalogTupleUpdate(pg_seclabel, &oldtup->t_self, newtup, updated, NULL);
 		}
 	}
 	systable_endscan(scan);
@@ -471,9 +466,8 @@ SetSecurityLabel(const ObjectAddress *object,
 	/* If we didn't find an old tuple, insert a new one */
 	if (newtup == NULL && label != NULL)
 	{
-		newtup = heap_form_tuple(RelationGetDescr(pg_seclabel),
-								 values, nulls);
-		CatalogTupleInsert(pg_seclabel, newtup);
+		newtup = heap_form_tuple(RelationGetDescr(pg_seclabel), values, nulls);
+		CatalogTupleInsert(pg_seclabel, newtup, NULL);
 	}
 
 	/* Update indexes, if necessary */
@@ -481,6 +475,7 @@ SetSecurityLabel(const ObjectAddress *object,
 		heap_freetuple(newtup);
 
 	table_close(pg_seclabel, RowExclusiveLock);
+	bms_free(updated);
 }
 
 /*
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index e1b808bbb60..a060211d6a4 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -125,8 +125,8 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
 	TupleDesc	tupDesc;
 	Datum		value[SEQ_COL_LASTCOL];
 	bool		null[SEQ_COL_LASTCOL];
-	Datum		pgs_values[Natts_pg_sequence];
-	bool		pgs_nulls[Natts_pg_sequence];
+	Datum		pgs_values[Natts_pg_sequence] = {0};
+	bool		pgs_nulls[Natts_pg_sequence] = {false};
 	int			i;
 
 	/*
@@ -221,17 +221,17 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
 
 	memset(pgs_nulls, 0, sizeof(pgs_nulls));
 
-	pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid);
-	pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid);
-	pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart);
-	pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement);
-	pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax);
-	pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin);
-	pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache);
-	pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle);
+	HeapTupleSetValue(pg_sequence, seqrelid, ObjectIdGetDatum(seqoid), pgs_values);
+	HeapTupleSetValue(pg_sequence, seqtypid, ObjectIdGetDatum(seqform.seqtypid), pgs_values);
+	HeapTupleSetValue(pg_sequence, seqstart, Int64GetDatumFast(seqform.seqstart), pgs_values);
+	HeapTupleSetValue(pg_sequence, seqincrement, Int64GetDatumFast(seqform.seqincrement), pgs_values);
+	HeapTupleSetValue(pg_sequence, seqmax, Int64GetDatumFast(seqform.seqmax), pgs_values);
+	HeapTupleSetValue(pg_sequence, seqmin, Int64GetDatumFast(seqform.seqmin), pgs_values);
+	HeapTupleSetValue(pg_sequence, seqcache, Int64GetDatumFast(seqform.seqcache), pgs_values);
+	HeapTupleSetValue(pg_sequence, seqcycle, BoolGetDatum(seqform.seqcycle), pgs_values);
 
 	tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls);
-	CatalogTupleInsert(rel, tuple);
+	CatalogTupleInsert(rel, tuple, NULL);
 
 	heap_freetuple(tuple);
 	table_close(rel, RowExclusiveLock);
@@ -444,6 +444,7 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
 	bool		is_called;
 	int64		last_value;
 	HeapTuple	newdatatuple;
+	Bitmapset  *updated = NULL;
 
 	/* Open and lock sequence, and check for ownership along the way. */
 	relid = RangeVarGetRelidExtended(stmt->sequence,
@@ -525,8 +526,15 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
 	if (owned_by)
 		process_owned_by(seqrel, owned_by, stmt->for_identity);
 
+	/*
+	 * Mark all sequence columns as potentially updated since init_params can
+	 * modify any field
+	 */
+	HeapTupleUpdateSetAllColumnsUpdated(pg_sequence, updated);
+
 	/* update the pg_sequence tuple (we could skip this in some cases...) */
-	CatalogTupleUpdate(rel, &seqtuple->t_self, seqtuple);
+	CatalogTupleUpdate(rel, &seqtuple->t_self, seqtuple, updated, NULL);
+	bms_free(updated);
 
 	InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
 
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index c1da79f36ba..9d9e2261c3f 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -71,8 +71,8 @@ CreateStatistics(CreateStatsStmt *stmt, bool check_rights)
 	Oid			namespaceId;
 	Oid			stxowner = GetUserId();
 	HeapTuple	htup;
-	Datum		values[Natts_pg_statistic_ext];
-	bool		nulls[Natts_pg_statistic_ext];
+	Datum		values[Natts_pg_statistic_ext] = {0};
+	bool		nulls[Natts_pg_statistic_ext] = {false};
 	int2vector *stxkeys;
 	List	   *stxexprs = NIL;
 	Datum		exprsDatum;
@@ -531,22 +531,22 @@ CreateStatistics(CreateStatsStmt *stmt, bool check_rights)
 
 	statoid = GetNewOidWithIndex(statrel, StatisticExtOidIndexId,
 								 Anum_pg_statistic_ext_oid);
-	values[Anum_pg_statistic_ext_oid - 1] = ObjectIdGetDatum(statoid);
-	values[Anum_pg_statistic_ext_stxrelid - 1] = ObjectIdGetDatum(relid);
-	values[Anum_pg_statistic_ext_stxname - 1] = NameGetDatum(&stxname);
-	values[Anum_pg_statistic_ext_stxnamespace - 1] = ObjectIdGetDatum(namespaceId);
-	values[Anum_pg_statistic_ext_stxowner - 1] = ObjectIdGetDatum(stxowner);
-	values[Anum_pg_statistic_ext_stxkeys - 1] = PointerGetDatum(stxkeys);
-	nulls[Anum_pg_statistic_ext_stxstattarget - 1] = true;
-	values[Anum_pg_statistic_ext_stxkind - 1] = PointerGetDatum(stxkind);
-
-	values[Anum_pg_statistic_ext_stxexprs - 1] = exprsDatum;
+	HeapTupleSetValue(pg_statistic_ext, oid, ObjectIdGetDatum(statoid), values);
+	HeapTupleSetValue(pg_statistic_ext, stxrelid, ObjectIdGetDatum(relid), values);
+	HeapTupleSetValue(pg_statistic_ext, stxname, NameGetDatum(&stxname), values);
+	HeapTupleSetValue(pg_statistic_ext, stxnamespace, ObjectIdGetDatum(namespaceId), values);
+	HeapTupleSetValue(pg_statistic_ext, stxowner, ObjectIdGetDatum(stxowner), values);
+	HeapTupleSetValue(pg_statistic_ext, stxkeys, PointerGetDatum(stxkeys), values);
+	HeapTupleSetValueNull(pg_statistic_ext, stxstattarget, values, nulls);
+	HeapTupleSetValue(pg_statistic_ext, stxkind, PointerGetDatum(stxkind), values);
+
+	HeapTupleSetValue(pg_statistic_ext, stxexprs, exprsDatum, values);
 	if (exprsDatum == (Datum) 0)
-		nulls[Anum_pg_statistic_ext_stxexprs - 1] = true;
+		HeapTupleSetValueNull(pg_statistic_ext, stxexprs, values, nulls);
 
 	/* insert it into pg_statistic_ext */
 	htup = heap_form_tuple(statrel->rd_att, values, nulls);
-	CatalogTupleInsert(statrel, htup);
+	CatalogTupleInsert(statrel, htup, NULL);
 	heap_freetuple(htup);
 
 	relation_close(statrel, RowExclusiveLock);
@@ -642,9 +642,9 @@ AlterStatistics(AlterStatsStmt *stmt)
 	Oid			stxoid;
 	HeapTuple	oldtup;
 	HeapTuple	newtup;
-	Datum		repl_val[Natts_pg_statistic_ext];
-	bool		repl_null[Natts_pg_statistic_ext];
-	bool		repl_repl[Natts_pg_statistic_ext];
+	Datum		values[Natts_pg_statistic_ext] = {0};
+	bool		nulls[Natts_pg_statistic_ext] = {false};
+	Bitmapset  *updated = NULL;
 	ObjectAddress address;
 	int			newtarget = 0;
 	bool		newtarget_default;
@@ -719,23 +719,16 @@ AlterStatistics(AlterStatsStmt *stmt)
 		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_STATISTIC_EXT,
 					   NameListToString(stmt->defnames));
 
-	/* Build new tuple. */
-	memset(repl_val, 0, sizeof(repl_val));
-	memset(repl_null, false, sizeof(repl_null));
-	memset(repl_repl, false, sizeof(repl_repl));
-
 	/* replace the stxstattarget column */
-	repl_repl[Anum_pg_statistic_ext_stxstattarget - 1] = true;
 	if (!newtarget_default)
-		repl_val[Anum_pg_statistic_ext_stxstattarget - 1] = Int16GetDatum(newtarget);
+		HeapTupleUpdateValue(pg_statistic_ext, stxstattarget, Int16GetDatum(newtarget), values, nulls, updated);
 	else
-		repl_null[Anum_pg_statistic_ext_stxstattarget - 1] = true;
+		HeapTupleUpdateValueNull(pg_statistic_ext, stxstattarget, values, nulls, updated);
 
-	newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
-							   repl_val, repl_null, repl_repl);
+	newtup = heap_update_tuple(oldtup, RelationGetDescr(rel), values, nulls, updated);
 
 	/* Update system catalog. */
-	CatalogTupleUpdate(rel, &newtup->t_self, newtup);
+	CatalogTupleUpdate(rel, &newtup->t_self, newtup, updated, NULL);
 
 	InvokeObjectPostAlterHook(StatisticExtRelationId, stxoid, 0);
 
@@ -746,6 +739,7 @@ AlterStatistics(AlterStatsStmt *stmt)
 	 * other fields, there is no need to update dependencies.
 	 */
 
+	bms_free(updated);
 	heap_freetuple(newtup);
 	ReleaseSysCache(oldtup);
 
diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c
index d6674f20fc2..d7909e49396 100644
--- a/src/backend/commands/subscriptioncmds.c
+++ b/src/backend/commands/subscriptioncmds.c
@@ -589,8 +589,8 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt,
 	Relation	rel;
 	ObjectAddress myself;
 	Oid			subid;
-	bool		nulls[Natts_pg_subscription];
-	Datum		values[Natts_pg_subscription];
+	Datum		values[Natts_pg_subscription] = {0};
+	bool		nulls[Natts_pg_subscription] = {false};
 	Oid			owner = GetUserId();
 	HeapTuple	tup;
 	char	   *conninfo;
@@ -710,47 +710,35 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt,
 
 	subid = GetNewOidWithIndex(rel, SubscriptionObjectIndexId,
 							   Anum_pg_subscription_oid);
-	values[Anum_pg_subscription_oid - 1] = ObjectIdGetDatum(subid);
-	values[Anum_pg_subscription_subdbid - 1] = ObjectIdGetDatum(MyDatabaseId);
-	values[Anum_pg_subscription_subskiplsn - 1] = LSNGetDatum(InvalidXLogRecPtr);
-	values[Anum_pg_subscription_subname - 1] =
-		DirectFunctionCall1(namein, CStringGetDatum(stmt->subname));
-	values[Anum_pg_subscription_subowner - 1] = ObjectIdGetDatum(owner);
-	values[Anum_pg_subscription_subenabled - 1] = BoolGetDatum(opts.enabled);
-	values[Anum_pg_subscription_subbinary - 1] = BoolGetDatum(opts.binary);
-	values[Anum_pg_subscription_substream - 1] = CharGetDatum(opts.streaming);
-	values[Anum_pg_subscription_subtwophasestate - 1] =
-		CharGetDatum(opts.twophase ?
-					 LOGICALREP_TWOPHASE_STATE_PENDING :
-					 LOGICALREP_TWOPHASE_STATE_DISABLED);
-	values[Anum_pg_subscription_subdisableonerr - 1] = BoolGetDatum(opts.disableonerr);
-	values[Anum_pg_subscription_subpasswordrequired - 1] = BoolGetDatum(opts.passwordrequired);
-	values[Anum_pg_subscription_subrunasowner - 1] = BoolGetDatum(opts.runasowner);
-	values[Anum_pg_subscription_subfailover - 1] = BoolGetDatum(opts.failover);
-	values[Anum_pg_subscription_subretaindeadtuples - 1] =
-		BoolGetDatum(opts.retaindeadtuples);
-	values[Anum_pg_subscription_submaxretention - 1] =
-		Int32GetDatum(opts.maxretention);
-	values[Anum_pg_subscription_subretentionactive - 1] =
-		Int32GetDatum(opts.retaindeadtuples);
-	values[Anum_pg_subscription_subconninfo - 1] =
-		CStringGetTextDatum(conninfo);
+	HeapTupleSetValue(pg_subscription, oid, ObjectIdGetDatum(subid), values);
+	HeapTupleSetValue(pg_subscription, subdbid, ObjectIdGetDatum(MyDatabaseId), values);
+	HeapTupleSetValue(pg_subscription, subskiplsn, LSNGetDatum(InvalidXLogRecPtr), values);
+	HeapTupleSetValue(pg_subscription, subname, DirectFunctionCall1(namein, CStringGetDatum(stmt->subname)), values);
+	HeapTupleSetValue(pg_subscription, subowner, ObjectIdGetDatum(owner), values);
+	HeapTupleSetValue(pg_subscription, subenabled, BoolGetDatum(opts.enabled), values);
+	HeapTupleSetValue(pg_subscription, subbinary, BoolGetDatum(opts.binary), values);
+	HeapTupleSetValue(pg_subscription, substream, CharGetDatum(opts.streaming), values);
+	HeapTupleSetValue(pg_subscription, subtwophasestate, CharGetDatum(opts.twophase ? LOGICALREP_TWOPHASE_STATE_PENDING : LOGICALREP_TWOPHASE_STATE_DISABLED), values);
+	HeapTupleSetValue(pg_subscription, subdisableonerr, BoolGetDatum(opts.disableonerr), values);
+	HeapTupleSetValue(pg_subscription, subpasswordrequired, BoolGetDatum(opts.passwordrequired), values);
+	HeapTupleSetValue(pg_subscription, subrunasowner, BoolGetDatum(opts.runasowner), values);
+	HeapTupleSetValue(pg_subscription, subfailover, BoolGetDatum(opts.failover), values);
+	HeapTupleSetValue(pg_subscription, subretaindeadtuples, BoolGetDatum(opts.retaindeadtuples), values);
+	HeapTupleSetValue(pg_subscription, submaxretention, Int32GetDatum(opts.maxretention), values);
+	HeapTupleSetValue(pg_subscription, subretentionactive, Int32GetDatum(opts.retaindeadtuples), values);
+	HeapTupleSetValue(pg_subscription, subconninfo, CStringGetTextDatum(conninfo), values);
 	if (opts.slot_name)
-		values[Anum_pg_subscription_subslotname - 1] =
-			DirectFunctionCall1(namein, CStringGetDatum(opts.slot_name));
+		HeapTupleSetValue(pg_subscription, subslotname, DirectFunctionCall1(namein, CStringGetDatum(opts.slot_name)), values);
 	else
-		nulls[Anum_pg_subscription_subslotname - 1] = true;
-	values[Anum_pg_subscription_subsynccommit - 1] =
-		CStringGetTextDatum(opts.synchronous_commit);
-	values[Anum_pg_subscription_subpublications - 1] =
-		publicationListToArray(publications);
-	values[Anum_pg_subscription_suborigin - 1] =
-		CStringGetTextDatum(opts.origin);
+		HeapTupleSetValueNull(pg_subscription, subslotname, values, nulls);
+	HeapTupleSetValue(pg_subscription, subsynccommit, CStringGetTextDatum(opts.synchronous_commit), values);
+	HeapTupleSetValue(pg_subscription, subpublications, publicationListToArray(publications), values);
+	HeapTupleSetValue(pg_subscription, suborigin, CStringGetTextDatum(opts.origin), values);
 
 	tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
 
 	/* Insert tuple into catalog. */
-	CatalogTupleInsert(rel, tup);
+	CatalogTupleInsert(rel, tup, NULL);
 	heap_freetuple(tup);
 
 	recordDependencyOnOwner(SubscriptionRelationId, subid, owner);
@@ -1335,9 +1323,8 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt,
 {
 	Relation	rel;
 	ObjectAddress myself;
-	bool		nulls[Natts_pg_subscription];
-	bool		replaces[Natts_pg_subscription];
-	Datum		values[Natts_pg_subscription];
+	Datum		values[Natts_pg_subscription] = {0};
+	bool		nulls[Natts_pg_subscription] = {false};
 	HeapTuple	tup;
 	Oid			subid;
 	bool		update_tuple = false;
@@ -1352,6 +1339,7 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt,
 	Form_pg_subscription form;
 	bits32		supported_opts;
 	SubOpts		opts = {0};
+	Bitmapset  *updated = NULL;
 
 	rel = table_open(SubscriptionRelationId, RowExclusiveLock);
 
@@ -1396,7 +1384,6 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt,
 	/* Form a new tuple. */
 	memset(values, 0, sizeof(values));
 	memset(nulls, false, sizeof(nulls));
-	memset(replaces, false, sizeof(replaces));
 
 	switch (stmt->kind)
 	{
@@ -1431,41 +1418,27 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt,
 										"slot_name = NONE")));
 
 					if (opts.slot_name)
-						values[Anum_pg_subscription_subslotname - 1] =
-							DirectFunctionCall1(namein, CStringGetDatum(opts.slot_name));
+						HeapTupleUpdateValue(pg_subscription, subslotname,
+											 DirectFunctionCall1(namein, CStringGetDatum(opts.slot_name)), values, nulls, updated);
 					else
-						nulls[Anum_pg_subscription_subslotname - 1] = true;
-					replaces[Anum_pg_subscription_subslotname - 1] = true;
+						HeapTupleUpdateValueNull(pg_subscription, subslotname, values, nulls, updated);
 				}
 
 				if (opts.synchronous_commit)
-				{
-					values[Anum_pg_subscription_subsynccommit - 1] =
-						CStringGetTextDatum(opts.synchronous_commit);
-					replaces[Anum_pg_subscription_subsynccommit - 1] = true;
-				}
+					HeapTupleUpdateValue(pg_subscription, subsynccommit,
+										 CStringGetTextDatum(opts.synchronous_commit), values, nulls, updated);
 
 				if (IsSet(opts.specified_opts, SUBOPT_BINARY))
-				{
-					values[Anum_pg_subscription_subbinary - 1] =
-						BoolGetDatum(opts.binary);
-					replaces[Anum_pg_subscription_subbinary - 1] = true;
-				}
+					HeapTupleUpdateValue(pg_subscription, subbinary,
+										 BoolGetDatum(opts.binary), values, nulls, updated);
 
 				if (IsSet(opts.specified_opts, SUBOPT_STREAMING))
-				{
-					values[Anum_pg_subscription_substream - 1] =
-						CharGetDatum(opts.streaming);
-					replaces[Anum_pg_subscription_substream - 1] = true;
-				}
+					HeapTupleUpdateValue(pg_subscription, substream,
+										 CharGetDatum(opts.streaming), values, nulls, updated);
 
 				if (IsSet(opts.specified_opts, SUBOPT_DISABLE_ON_ERR))
-				{
-					values[Anum_pg_subscription_subdisableonerr - 1]
-						= BoolGetDatum(opts.disableonerr);
-					replaces[Anum_pg_subscription_subdisableonerr - 1]
-						= true;
-				}
+					HeapTupleUpdateValue(pg_subscription, subdisableonerr,
+										 BoolGetDatum(opts.disableonerr), values, nulls, updated);
 
 				if (IsSet(opts.specified_opts, SUBOPT_PASSWORD_REQUIRED))
 				{
@@ -1476,18 +1449,13 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt,
 								 errmsg("password_required=false is superuser-only"),
 								 errhint("Subscriptions with the password_required option set to false may only be created or modified by the superuser.")));
 
-					values[Anum_pg_subscription_subpasswordrequired - 1]
-						= BoolGetDatum(opts.passwordrequired);
-					replaces[Anum_pg_subscription_subpasswordrequired - 1]
-						= true;
+					HeapTupleUpdateValue(pg_subscription, subpasswordrequired,
+										 BoolGetDatum(opts.passwordrequired), values, nulls, updated);
 				}
 
 				if (IsSet(opts.specified_opts, SUBOPT_RUN_AS_OWNER))
-				{
-					values[Anum_pg_subscription_subrunasowner - 1] =
-						BoolGetDatum(opts.runasowner);
-					replaces[Anum_pg_subscription_subrunasowner - 1] = true;
-				}
+					HeapTupleUpdateValue(pg_subscription, subrunasowner,
+										 BoolGetDatum(opts.runasowner), values, nulls, updated);
 
 				if (IsSet(opts.specified_opts, SUBOPT_TWOPHASE_COMMIT))
 				{
@@ -1546,11 +1514,10 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt,
 								 errhint("Resolve these transactions and try again.")));
 
 					/* Change system catalog accordingly */
-					values[Anum_pg_subscription_subtwophasestate - 1] =
-						CharGetDatum(opts.twophase ?
-									 LOGICALREP_TWOPHASE_STATE_PENDING :
-									 LOGICALREP_TWOPHASE_STATE_DISABLED);
-					replaces[Anum_pg_subscription_subtwophasestate - 1] = true;
+					HeapTupleUpdateValue(pg_subscription, subtwophasestate,
+										 CharGetDatum(opts.twophase ?
+													  LOGICALREP_TWOPHASE_STATE_PENDING :
+													  LOGICALREP_TWOPHASE_STATE_DISABLED), values, nulls, updated);
 				}
 
 				if (IsSet(opts.specified_opts, SUBOPT_FAILOVER))
@@ -1565,16 +1532,14 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt,
 					CheckAlterSubOption(sub, "failover", update_failover,
 										isTopLevel);
 
-					values[Anum_pg_subscription_subfailover - 1] =
-						BoolGetDatum(opts.failover);
-					replaces[Anum_pg_subscription_subfailover - 1] = true;
+					HeapTupleUpdateValue(pg_subscription, subfailover,
+										 BoolGetDatum(opts.failover), values, nulls, updated);
 				}
 
 				if (IsSet(opts.specified_opts, SUBOPT_RETAIN_DEAD_TUPLES))
 				{
-					values[Anum_pg_subscription_subretaindeadtuples - 1] =
-						BoolGetDatum(opts.retaindeadtuples);
-					replaces[Anum_pg_subscription_subretaindeadtuples - 1] = true;
+					HeapTupleUpdateValue(pg_subscription, subretaindeadtuples,
+										 BoolGetDatum(opts.retaindeadtuples), values, nulls, updated);
 
 					/*
 					 * Update the retention status only if there's a change in
@@ -1592,9 +1557,8 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt,
 					 */
 					if (opts.retaindeadtuples != sub->retaindeadtuples)
 					{
-						values[Anum_pg_subscription_subretentionactive - 1] =
-							BoolGetDatum(opts.retaindeadtuples);
-						replaces[Anum_pg_subscription_subretentionactive - 1] = true;
+						HeapTupleUpdateValue(pg_subscription, subretentionactive,
+											 BoolGetDatum(opts.retaindeadtuples), values, nulls, updated);
 
 						retention_active = opts.retaindeadtuples;
 					}
@@ -1629,9 +1593,8 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt,
 
 				if (IsSet(opts.specified_opts, SUBOPT_MAX_RETENTION_DURATION))
 				{
-					values[Anum_pg_subscription_submaxretention - 1] =
-						Int32GetDatum(opts.maxretention);
-					replaces[Anum_pg_subscription_submaxretention - 1] = true;
+					HeapTupleUpdateValue(pg_subscription, submaxretention,
+										 Int32GetDatum(opts.maxretention), values, nulls, updated);
 
 					max_retention = opts.maxretention;
 				}
@@ -1650,9 +1613,8 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt,
 
 				if (IsSet(opts.specified_opts, SUBOPT_ORIGIN))
 				{
-					values[Anum_pg_subscription_suborigin - 1] =
-						CStringGetTextDatum(opts.origin);
-					replaces[Anum_pg_subscription_suborigin - 1] = true;
+					HeapTupleUpdateValue(pg_subscription, suborigin,
+										 CStringGetTextDatum(opts.origin), values, nulls, updated);
 
 					/*
 					 * Check if changes from different origins may be received
@@ -1689,9 +1651,8 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt,
 										   WARNING, sub->retaindeadtuples,
 										   sub->retentionactive, false);
 
-				values[Anum_pg_subscription_subenabled - 1] =
-					BoolGetDatum(opts.enabled);
-				replaces[Anum_pg_subscription_subenabled - 1] = true;
+				HeapTupleUpdateValue(pg_subscription, subenabled,
+									 BoolGetDatum(opts.enabled), values, nulls, updated);
 
 				if (opts.enabled)
 					ApplyLauncherWakeupAtCommit();
@@ -1715,9 +1676,8 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt,
 			walrcv_check_conninfo(stmt->conninfo,
 								  sub->passwordrequired && !sub->ownersuperuser);
 
-			values[Anum_pg_subscription_subconninfo - 1] =
-				CStringGetTextDatum(stmt->conninfo);
-			replaces[Anum_pg_subscription_subconninfo - 1] = true;
+			HeapTupleUpdateValue(pg_subscription, subconninfo,
+								 CStringGetTextDatum(stmt->conninfo), values, nulls, updated);
 			update_tuple = true;
 
 			/*
@@ -1734,9 +1694,8 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt,
 				parse_subscription_options(pstate, stmt->options,
 										   supported_opts, &opts);
 
-				values[Anum_pg_subscription_subpublications - 1] =
-					publicationListToArray(stmt->publication);
-				replaces[Anum_pg_subscription_subpublications - 1] = true;
+				HeapTupleUpdateValue(pg_subscription, subpublications,
+									 publicationListToArray(stmt->publication), values, nulls, updated);
 
 				update_tuple = true;
 
@@ -1782,9 +1741,8 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt,
 										   supported_opts, &opts);
 
 				publist = merge_publications(sub->publications, stmt->publication, isadd, stmt->subname);
-				values[Anum_pg_subscription_subpublications - 1] =
-					publicationListToArray(publist);
-				replaces[Anum_pg_subscription_subpublications - 1] = true;
+				HeapTupleUpdateValue(pg_subscription, subpublications,
+									 publicationListToArray(publist), values, nulls, updated);
 
 				update_tuple = true;
 
@@ -1915,8 +1873,7 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt,
 										LSN_FORMAT_ARGS(remote_lsn))));
 				}
 
-				values[Anum_pg_subscription_subskiplsn - 1] = LSNGetDatum(opts.lsn);
-				replaces[Anum_pg_subscription_subskiplsn - 1] = true;
+				HeapTupleUpdateValue(pg_subscription, subskiplsn, LSNGetDatum(opts.lsn), values, nulls, updated);
 
 				update_tuple = true;
 				break;
@@ -1930,10 +1887,9 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt,
 	/* Update the catalog if needed. */
 	if (update_tuple)
 	{
-		tup = heap_modify_tuple(tup, RelationGetDescr(rel), values, nulls,
-								replaces);
+		tup = heap_update_tuple(tup, RelationGetDescr(rel), values, nulls, updated);
 
-		CatalogTupleUpdate(rel, &tup->t_self, tup);
+		CatalogTupleUpdate(rel, &tup->t_self, tup, updated, NULL);
 
 		heap_freetuple(tup);
 	}
@@ -2000,6 +1956,7 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt,
 	/* Wake up related replication workers to handle this change quickly. */
 	LogicalRepWorkersWakeupAtCommit(subid);
 
+	bms_free(updated);
 	return myself;
 }
 
@@ -2355,6 +2312,7 @@ AlterSubscriptionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
 {
 	Form_pg_subscription form;
 	AclResult	aclresult;
+	Bitmapset  *updated = NULL;
 
 	form = (Form_pg_subscription) GETSTRUCT(tup);
 
@@ -2391,8 +2349,8 @@ AlterSubscriptionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
 		aclcheck_error(aclresult, OBJECT_DATABASE,
 					   get_database_name(MyDatabaseId));
 
-	form->subowner = newOwnerId;
-	CatalogTupleUpdate(rel, &tup->t_self, tup);
+	HeapTupleUpdateField(pg_subscription, subowner, newOwnerId, form, updated);
+	CatalogTupleUpdate(rel, &tup->t_self, tup, updated, NULL);
 
 	/* Update owner dependency reference */
 	changeDependencyOnOwner(SubscriptionRelationId,
@@ -2405,6 +2363,7 @@ AlterSubscriptionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
 	/* Wake up related background processes to handle this change quickly. */
 	ApplyLauncherWakeupAtCommit();
 	LogicalRepWorkersWakeupAtCommit(form->oid);
+	bms_free(updated);
 }
 
 /*
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index f976c0e5c7e..8cdfea1ccfb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -3647,6 +3647,7 @@ SetRelationHasSubclass(Oid relationId, bool relhassubclass)
 	Relation	relationRelation;
 	HeapTuple	tuple;
 	Form_pg_class classtuple;
+	Bitmapset  *updated = NULL;
 
 	Assert(CheckRelationOidLockedByMe(relationId,
 									  ShareUpdateExclusiveLock, false) ||
@@ -3664,8 +3665,8 @@ SetRelationHasSubclass(Oid relationId, bool relhassubclass)
 
 	if (classtuple->relhassubclass != relhassubclass)
 	{
-		classtuple->relhassubclass = relhassubclass;
-		CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
+		HeapTupleUpdateField(pg_class, relhassubclass, relhassubclass, classtuple, updated);
+		CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple, updated, NULL);
 	}
 	else
 	{
@@ -3674,6 +3675,7 @@ SetRelationHasSubclass(Oid relationId, bool relhassubclass)
 	}
 
 	heap_freetuple(tuple);
+	bms_free(updated);
 	table_close(relationRelation, RowExclusiveLock);
 }
 
@@ -3753,6 +3755,8 @@ SetRelationTableSpace(Relation rel,
 	HeapTuple	tuple;
 	ItemPointerData otid;
 	Form_pg_class rd_rel;
+	Bitmapset  *updated = NULL;
+	Oid			field = InvalidOid;
 	Oid			reloid = RelationGetRelid(rel);
 
 	Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
@@ -3767,11 +3771,16 @@ SetRelationTableSpace(Relation rel,
 	rd_rel = (Form_pg_class) GETSTRUCT(tuple);
 
 	/* Update the pg_class row. */
-	rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
-		InvalidOid : newTableSpaceId;
+	if (newTableSpaceId != MyDatabaseTableSpace)
+		field = newTableSpaceId;
+
+	HeapTupleUpdateField(pg_class, reltablespace, field, rd_rel, updated);
+
 	if (RelFileNumberIsValid(newRelFilenumber))
-		rd_rel->relfilenode = newRelFilenumber;
-	CatalogTupleUpdate(pg_class, &otid, tuple);
+		HeapTupleUpdateField(pg_class, relfilenode, newRelFilenumber, rd_rel, updated);
+
+	CatalogTupleUpdate(pg_class, &otid, tuple, updated, NULL);
+
 	UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
 
 	/*
@@ -3783,6 +3792,7 @@ SetRelationTableSpace(Relation rel,
 									 rd_rel->reltablespace);
 
 	heap_freetuple(tuple);
+	bms_free(updated);
 	table_close(pg_class, RowExclusiveLock);
 }
 
@@ -3852,6 +3862,7 @@ renameatt_internal(Oid myrelid,
 	HeapTuple	atttup;
 	Form_pg_attribute attform;
 	AttrNumber	attnum;
+	Bitmapset  *updated = NULL;
 
 	/*
 	 * Grab an exclusive lock on the target table, which we will NOT release
@@ -3965,9 +3976,10 @@ renameatt_internal(Oid myrelid,
 	(void) check_for_column_name_collision(targetrelation, newattname, false);
 
 	/* apply the update */
-	namestrcpy(&(attform->attname), newattname);
-
-	CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
+	namestrcpy(&attform->attname, newattname);
+	HeapTupleMarkColumnUpdated(pg_attribute, attname, updated);
+	CatalogTupleUpdate(attrelation, &atttup->t_self, atttup, updated, NULL);
+	bms_free(updated);
 
 	InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
 
@@ -4273,6 +4285,7 @@ RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bo
 	HeapTuple	reltup;
 	Form_pg_class relform;
 	Oid			namespaceId;
+	Bitmapset  *updated = NULL;
 
 	/*
 	 * Grab a lock on the target relation, which we will NOT release until end
@@ -4318,9 +4331,12 @@ RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bo
 	 * Update pg_class tuple with new relname.  (Scribbling on reltup is OK
 	 * because it's a copy...)
 	 */
-	namestrcpy(&(relform->relname), newrelname);
 
-	CatalogTupleUpdate(relrelation, &otid, reltup);
+	namestrcpy(&relform->relname, newrelname);
+	HeapTupleMarkColumnUpdated(pg_class, relname, updated);
+	CatalogTupleUpdate(relrelation, &otid, reltup, updated, NULL);
+	bms_free(updated);
+
 	UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
 
 	InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
@@ -4363,6 +4379,7 @@ ResetRelRewrite(Oid myrelid)
 	Relation	relrelation;	/* for RELATION relation */
 	HeapTuple	reltup;
 	Form_pg_class relform;
+	Bitmapset  *updated = NULL;
 
 	/*
 	 * Find relation's pg_class tuple.
@@ -4374,13 +4391,11 @@ ResetRelRewrite(Oid myrelid)
 		elog(ERROR, "cache lookup failed for relation %u", myrelid);
 	relform = (Form_pg_class) GETSTRUCT(reltup);
 
-	/*
-	 * Update pg_class tuple.
-	 */
-	relform->relrewrite = InvalidOid;
-
-	CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
+	/* Update pg_class tuple */
+	HeapTupleUpdateField(pg_class, relrewrite, InvalidOid, relform, updated);
+	CatalogTupleUpdate(relrelation, &reltup->t_self, reltup, updated, NULL);
 
+	bms_free(updated);
 	heap_freetuple(reltup);
 	table_close(relrelation, RowExclusiveLock);
 }
@@ -7264,6 +7279,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	AlterTableCmd *childcmd;
 	ObjectAddress address;
 	TupleDesc	tupdesc;
+	Bitmapset  *updated = NULL;
 
 	/* since this function recurses, it could be driven to stack overflow */
 	check_stack_depth();
@@ -7323,8 +7339,13 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 				ereport(ERROR,
 						errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 						errmsg("too many inheritance parents"));
-			CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
 
+			HeapTupleMarkColumnUpdated(pg_attribute, attinhcount, updated);
+
+			CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple, updated, NULL);
+
+			bms_free(updated);
+			updated = NULL;
 			heap_freetuple(tuple);
 
 			/* Inform the user about the merge */
@@ -7422,9 +7443,10 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	/*
 	 * Update pg_class tuple as appropriate
 	 */
-	relform->relnatts = newattnum;
-
-	CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
+	Assert(bms_is_empty(updated));
+	HeapTupleUpdateField(pg_class, relnatts, newattnum, relform, updated);
+	CatalogTupleUpdate(pgclass, &reltup->t_self, reltup, updated, NULL);
+	bms_free(updated);
 
 	heap_freetuple(reltup);
 
@@ -7888,6 +7910,7 @@ set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
 	{
 		Relation	attr_rel;
 		HeapTuple	tuple;
+		Bitmapset  *updated = NULL;
 
 		attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
 
@@ -7901,8 +7924,9 @@ set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
 
 		attr = (Form_pg_attribute) GETSTRUCT(tuple);
 
-		attr->attnotnull = true;
-		CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
+		HeapTupleUpdateField(pg_attribute, attnotnull, true, attr, updated);
+		CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple, updated, NULL);
+		bms_free(updated);
 
 		/*
 		 * If the nullness isn't already proven by validated constraints, have
@@ -7981,6 +8005,7 @@ ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
 	{
 		Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
 		bool		changed = false;
+		Bitmapset  *updated = NULL;
 
 		/*
 		 * Don't let a NO INHERIT constraint be changed into inherit.
@@ -8004,11 +8029,12 @@ ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
 				ereport(ERROR,
 						errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 						errmsg("too many inheritance parents"));
+			HeapTupleMarkColumnUpdated(pg_constraint, coninhcount, updated);
 			changed = true;
 		}
 		else if (!conForm->conislocal)
 		{
-			conForm->conislocal = true;
+			HeapTupleUpdateField(pg_constraint, conislocal, true, conForm, updated);
 			changed = true;
 		}
 		else if (!conForm->convalidated)
@@ -8027,7 +8053,8 @@ ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
 
 			constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
 
-			CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
+			CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple, updated, NULL);
+			bms_free(updated);
 			ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
 			table_close(constr_rel, RowExclusiveLock);
 		}
@@ -8275,6 +8302,7 @@ ATExecAddIdentity(Relation rel, const char *colName,
 	AttrNumber	attnum;
 	ObjectAddress address;
 	ColumnDef  *cdef = castNode(ColumnDef, def);
+	Bitmapset  *updated = NULL;
 	bool		ispartitioned;
 
 	ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
@@ -8355,8 +8383,9 @@ ATExecAddIdentity(Relation rel, const char *colName,
 				 errmsg("column \"%s\" of relation \"%s\" already has a default value",
 						colName, RelationGetRelationName(rel))));
 
-	attTup->attidentity = cdef->identity;
-	CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
+	HeapTupleUpdateField(pg_attribute, attidentity, cdef->identity, attTup, updated);
+	CatalogTupleUpdate(attrelation, &tuple->t_self, tuple, updated, NULL);
+	bms_free(updated);
 
 	InvokeObjectPostAlterHook(RelationRelationId,
 							  RelationGetRelid(rel),
@@ -8469,8 +8498,11 @@ ATExecSetIdentity(Relation rel, const char *colName, Node *def,
 
 	if (generatedEl)
 	{
-		attTup->attidentity = defGetInt32(generatedEl);
-		CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
+		Bitmapset  *updated = NULL;
+
+		HeapTupleUpdateField(pg_attribute, attidentity, defGetInt32(generatedEl), attTup, updated);
+		CatalogTupleUpdate(attrelation, &tuple->t_self, tuple, updated, NULL);
+		bms_free(updated);
 
 		InvokeObjectPostAlterHook(RelationRelationId,
 								  RelationGetRelid(rel),
@@ -8525,6 +8557,7 @@ ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE
 	Oid			seqid;
 	ObjectAddress seqaddress;
 	bool		ispartitioned;
+	Bitmapset  *updated = NULL;
 
 	ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
 	if (ispartitioned && !recurse)
@@ -8573,8 +8606,9 @@ ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE
 		}
 	}
 
-	attTup->attidentity = '\0';
-	CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
+	HeapTupleUpdateField(pg_attribute, attidentity, '\0', attTup, updated);
+	CatalogTupleUpdate(attrelation, &tuple->t_self, tuple, updated, NULL);
+	bms_free(updated);
 
 	InvokeObjectPostAlterHook(RelationRelationId,
 							  RelationGetRelid(rel),
@@ -8836,6 +8870,7 @@ ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMOD
 	Relation	attrelation;
 	Oid			attrdefoid;
 	ObjectAddress address;
+	Bitmapset  *updated = NULL;
 
 	attrelation = table_open(AttributeRelationId, RowExclusiveLock);
 	tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
@@ -8889,8 +8924,9 @@ ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMOD
 	 * Mark the column as no longer generated.  (The atthasdef flag needs to
 	 * get cleared too, but RemoveAttrDefault will handle that.)
 	 */
-	attTup->attgenerated = '\0';
-	CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
+	HeapTupleUpdateField(pg_attribute, attgenerated, '\0', attTup, updated);
+	CatalogTupleUpdate(attrelation, &tuple->t_self, tuple, updated, NULL);
+	bms_free(updated);
 
 	InvokeObjectPostAlterHook(RelationRelationId,
 							  RelationGetRelid(rel),
@@ -8942,9 +8978,9 @@ ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newVa
 	Form_pg_attribute attrtuple;
 	AttrNumber	attnum;
 	ObjectAddress address;
-	Datum		repl_val[Natts_pg_attribute];
-	bool		repl_null[Natts_pg_attribute];
-	bool		repl_repl[Natts_pg_attribute];
+	Datum		values[Natts_pg_attribute] = {0};
+	bool		nulls[Natts_pg_attribute] = {false};
+	Bitmapset  *updated = NULL;
 
 	/*
 	 * We allow referencing columns by numbers only for indexes, since table
@@ -9047,16 +9083,14 @@ ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newVa
 	}
 
 	/* Build new tuple. */
-	memset(repl_null, false, sizeof(repl_null));
-	memset(repl_repl, false, sizeof(repl_repl));
 	if (!newtarget_default)
-		repl_val[Anum_pg_attribute_attstattarget - 1] = Int16GetDatum(newtarget);
+		HeapTupleUpdateValue(pg_attribute, attstattarget, Int16GetDatum(newtarget), values, nulls, updated);
 	else
-		repl_null[Anum_pg_attribute_attstattarget - 1] = true;
-	repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
-	newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
-								 repl_val, repl_null, repl_repl);
-	CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
+		HeapTupleUpdateValueNull(pg_attribute, attstattarget, values, nulls, updated);
+	newtuple = heap_update_tuple(tuple, RelationGetDescr(attrelation),
+								 values, nulls, updated);
+	CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple, updated, NULL);
+	bms_free(updated);
 
 	InvokeObjectPostAlterHook(RelationRelationId,
 							  RelationGetRelid(rel),
@@ -9089,9 +9123,9 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options,
 				newOptions;
 	bool		isnull;
 	ObjectAddress address;
-	Datum		repl_val[Natts_pg_attribute];
-	bool		repl_null[Natts_pg_attribute];
-	bool		repl_repl[Natts_pg_attribute];
+	Datum		values[Natts_pg_attribute] = {0};
+	bool		nulls[Natts_pg_attribute] = {false};
+	Bitmapset  *updated = NULL;
 
 	attrelation = table_open(AttributeRelationId, RowExclusiveLock);
 
@@ -9121,18 +9155,17 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options,
 	(void) attribute_reloptions(newOptions, true);
 
 	/* Build new tuple. */
-	memset(repl_null, false, sizeof(repl_null));
-	memset(repl_repl, false, sizeof(repl_repl));
 	if (newOptions != (Datum) 0)
-		repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
+		HeapTupleUpdateValue(pg_attribute, attoptions, newOptions, values, nulls, updated);
 	else
-		repl_null[Anum_pg_attribute_attoptions - 1] = true;
-	repl_repl[Anum_pg_attribute_attoptions - 1] = true;
-	newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
-								 repl_val, repl_null, repl_repl);
+		HeapTupleUpdateValueNull(pg_attribute, attoptions, values, nulls, updated);
+
+	newtuple = heap_update_tuple(tuple, RelationGetDescr(attrelation),
+								 values, nulls, updated);
 
 	/* Update system catalog. */
-	CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
+	CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple, updated, NULL);
+	bms_free(updated);
 
 	InvokeObjectPostAlterHook(RelationRelationId,
 							  RelationGetRelid(rel),
@@ -9193,19 +9226,21 @@ SetIndexStorageProperties(Relation rel, Relation attrelation,
 		if (HeapTupleIsValid(tuple))
 		{
 			Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
+			Bitmapset  *updated = NULL;
 
 			if (setstorage)
-				attrtuple->attstorage = newstorage;
+				HeapTupleUpdateField(pg_attribute, attstorage, newstorage, attrtuple, updated);
 
 			if (setcompression)
-				attrtuple->attcompression = newcompression;
+				HeapTupleUpdateField(pg_attribute, attcompression, newcompression, attrtuple, updated);
 
-			CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
+			CatalogTupleUpdate(attrelation, &tuple->t_self, tuple, updated, NULL);
 
 			InvokeObjectPostAlterHook(RelationRelationId,
 									  RelationGetRelid(rel),
 									  attrtuple->attnum);
 
+			bms_free(updated);
 			heap_freetuple(tuple);
 		}
 
@@ -9226,6 +9261,7 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE loc
 	Form_pg_attribute attrtuple;
 	AttrNumber	attnum;
 	ObjectAddress address;
+	Bitmapset  *updated = NULL;
 
 	attrelation = table_open(AttributeRelationId, RowExclusiveLock);
 
@@ -9245,9 +9281,9 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE loc
 				 errmsg("cannot alter system column \"%s\"",
 						colName)));
 
-	attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
+	HeapTupleUpdateField(pg_attribute, attstorage, GetAttributeStorage(attrtuple->atttypid, strVal(newValue)), attrtuple, updated);
 
-	CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
+	CatalogTupleUpdate(attrelation, &tuple->t_self, tuple, updated, NULL);
 
 	InvokeObjectPostAlterHook(RelationRelationId,
 							  RelationGetRelid(rel),
@@ -9262,6 +9298,7 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE loc
 							  false, 0,
 							  lockmode);
 
+	bms_free(updated);
 	heap_freetuple(tuple);
 
 	table_close(attrelation, RowExclusiveLock);
@@ -9455,9 +9492,11 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
 				else
 				{
 					/* Child column must survive my deletion */
-					childatt->attinhcount--;
+					Bitmapset  *updated = NULL;
 
-					CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
+					HeapTupleUpdateField(pg_attribute, attinhcount, childatt->attinhcount - 1, childatt, updated);
+					CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple, updated, NULL);
+					bms_free(updated);
 
 					/* Make update visible */
 					CommandCounterIncrement();
@@ -9470,10 +9509,12 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
 				 * we need to mark the inheritors' attributes as locally
 				 * defined rather than inherited.
 				 */
-				childatt->attinhcount--;
-				childatt->attislocal = true;
+				Bitmapset  *updated = NULL;
 
-				CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
+				HeapTupleUpdateField(pg_attribute, attinhcount, childatt->attinhcount - 1, childatt, updated);
+				HeapTupleUpdateField(pg_attribute, attislocal, true, childatt, updated);
+				CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple, updated, NULL);
+				bms_free(updated);
 
 				/* Make update visible */
 				CommandCounterIncrement();
@@ -12686,16 +12727,22 @@ ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
 		{
 			HeapTuple	childtup;
 			Form_pg_constraint childcon;
+			Bitmapset  *updated = NULL;
 
 			childtup = findNotNullConstraint(childoid, colName);
 			if (!childtup)
 				elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
 					 colName, childoid);
+
 			childcon = (Form_pg_constraint) GETSTRUCT(childtup);
 			Assert(childcon->coninhcount > 0);
-			childcon->coninhcount--;
-			childcon->conislocal = true;
-			CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
+
+			HeapTupleUpdateField(pg_constraint, coninhcount, childcon->coninhcount - 1, childcon, updated);
+			HeapTupleUpdateField(pg_constraint, conislocal, true, childcon, updated);
+
+			CatalogTupleUpdate(conrel, &childtup->t_self, childtup, updated, NULL);
+
+			bms_free(updated);
 			heap_freetuple(childtup);
 		}
 		else
@@ -12740,6 +12787,7 @@ AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
 		Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
 		Form_pg_trigger copy_tg;
 		HeapTuple	tgCopyTuple;
+		Bitmapset  *updated = NULL;
 
 		/*
 		 * Remember OIDs of other relation(s) involved in FK constraint.
@@ -12766,12 +12814,14 @@ AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
 		tgCopyTuple = heap_copytuple(tgtuple);
 		copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
 
-		copy_tg->tgdeferrable = deferrable;
-		copy_tg->tginitdeferred = initdeferred;
-		CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
+		HeapTupleUpdateField(pg_trigger, tgdeferrable, deferrable, copy_tg, updated);
+		HeapTupleUpdateField(pg_trigger, tginitdeferred, initdeferred, copy_tg, updated);
+
+		CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple, updated, NULL);
 
 		InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
 
+		bms_free(updated);
 		heap_freetuple(tgCopyTuple);
 	}
 
@@ -12886,6 +12936,7 @@ AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel,
 {
 	HeapTuple	copyTuple;
 	Form_pg_constraint copy_con;
+	Bitmapset  *updated = NULL;
 
 	Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
 		   cmdcon->alterInheritability);
@@ -12895,7 +12946,7 @@ AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel,
 
 	if (cmdcon->alterEnforceability)
 	{
-		copy_con->conenforced = cmdcon->is_enforced;
+		HeapTupleUpdateField(pg_constraint, conenforced, cmdcon->is_enforced, copy_con, updated);
 
 		/*
 		 * NB: The convalidated status is irrelevant when the constraint is
@@ -12904,22 +12955,26 @@ AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel,
 		 * ENFORCED, validation will be performed during phase 3, so it makes
 		 * sense to mark it as valid in that case.
 		 */
-		copy_con->convalidated = cmdcon->is_enforced;
+		HeapTupleUpdateField(pg_constraint, convalidated, cmdcon->is_enforced, copy_con, updated);
 	}
+
 	if (cmdcon->alterDeferrability)
 	{
-		copy_con->condeferrable = cmdcon->deferrable;
-		copy_con->condeferred = cmdcon->initdeferred;
+		HeapTupleUpdateField(pg_constraint, condeferrable, cmdcon->deferrable, copy_con, updated);
+		HeapTupleUpdateField(pg_constraint, condeferred, cmdcon->initdeferred, copy_con, updated);
 	}
+
 	if (cmdcon->alterInheritability)
-		copy_con->connoinherit = cmdcon->noinherit;
+		HeapTupleUpdateField(pg_constraint, connoinherit, cmdcon->noinherit, copy_con, updated);
+
+	CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple, updated, NULL);
 
-	CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
 	InvokeObjectPostAlterHook(ConstraintRelationId, copy_con->oid, 0);
 
 	/* Make new constraint flags visible to others */
 	CacheInvalidateRelcacheByRelid(copy_con->conrelid);
 
+	bms_free(updated);
 	heap_freetuple(copyTuple);
 }
 
@@ -13032,6 +13087,7 @@ QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel,
 	AlteredTableInfo *tab;
 	HeapTuple	copyTuple;
 	Form_pg_constraint copy_con;
+	Bitmapset  *updated = NULL;
 
 	con = (Form_pg_constraint) GETSTRUCT(contuple);
 	Assert(con->contype == CONSTRAINT_FOREIGN);
@@ -13128,11 +13184,14 @@ QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel,
 	 */
 	copyTuple = heap_copytuple(contuple);
 	copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
-	copy_con->convalidated = true;
-	CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
+
+	HeapTupleUpdateField(pg_constraint, convalidated, true, copy_con, updated);
+
+	CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple, updated, NULL);
 
 	InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
 
+	bms_free(updated);
 	heap_freetuple(copyTuple);
 }
 
@@ -13158,6 +13217,7 @@ QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
 	NewConstraint *newcon;
 	Datum		val;
 	char	   *conbin;
+	Bitmapset  *updated = NULL;
 
 	con = (Form_pg_constraint) GETSTRUCT(contuple);
 	Assert(con->contype == CONSTRAINT_CHECK);
@@ -13231,11 +13291,14 @@ QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
 	 */
 	copyTuple = heap_copytuple(contuple);
 	copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
-	copy_con->convalidated = true;
-	CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
+
+	HeapTupleUpdateField(pg_constraint, convalidated, true, copy_con, updated);
+
+	CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple, updated, NULL);
 
 	InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
 
+	bms_free(updated);
 	heap_freetuple(copyTuple);
 }
 
@@ -13258,6 +13321,7 @@ QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
 	List	   *children = NIL;
 	AttrNumber	attnum;
 	char	   *colname;
+	Bitmapset  *updated = NULL;
 
 	con = (Form_pg_constraint) GETSTRUCT(contuple);
 	Assert(con->contype == CONSTRAINT_NOTNULL);
@@ -13334,11 +13398,14 @@ QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
 	 */
 	copyTuple = heap_copytuple(contuple);
 	copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
-	copy_con->convalidated = true;
-	CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
+
+	HeapTupleUpdateField(pg_constraint, convalidated, true, copy_con, updated);
+
+	CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple, updated, NULL);
 
 	InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
 
+	bms_free(updated);
 	heap_freetuple(copyTuple);
 }
 
@@ -14213,8 +14280,11 @@ dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior beha
 		/* All good -- reset attnotnull if needed */
 		if (attForm->attnotnull)
 		{
-			attForm->attnotnull = false;
-			CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
+			Bitmapset  *updated = NULL;
+
+			HeapTupleUpdateField(pg_attribute, attnotnull, false, attForm, updated);
+			CatalogTupleUpdate(attrel, &atttup->t_self, atttup, updated, NULL);
+			bms_free(updated);
 		}
 
 		table_close(attrel, RowExclusiveLock);
@@ -14347,11 +14417,16 @@ dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior beha
 			else
 			{
 				/* Child constraint must survive my deletion */
-				childcon->coninhcount--;
-				CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
+				Bitmapset  *updated = NULL;
+
+				HeapTupleUpdateField(pg_constraint, coninhcount, childcon->coninhcount - 1, childcon, updated);
+
+				CatalogTupleUpdate(conrel, &tuple->t_self, tuple, updated, NULL);
 
 				/* Make update visible */
 				CommandCounterIncrement();
+
+				bms_free(updated);
 			}
 		}
 		else
@@ -14362,14 +14437,19 @@ dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior beha
 			 * mark the inheritors' constraints as locally defined rather than
 			 * inherited.
 			 */
-			childcon->coninhcount--;
+			Bitmapset  *updated = NULL;
+
+			HeapTupleUpdateField(pg_constraint, coninhcount, childcon->coninhcount - 1, childcon, updated);
+
 			if (childcon->coninhcount == 0)
-				childcon->conislocal = true;
+				HeapTupleUpdateField(pg_constraint, conislocal, true, childcon, updated);
 
-			CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
+			CatalogTupleUpdate(conrel, &tuple->t_self, tuple, updated, NULL);
 
 			/* Make update visible */
 			CommandCounterIncrement();
+
+			bms_free(updated);
 		}
 
 		heap_freetuple(tuple);
@@ -14775,6 +14855,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 	SysScanDesc scan;
 	HeapTuple	depTup;
 	ObjectAddress address;
+	Bitmapset  *updated = NULL;
 
 	/*
 	 * Clear all the missing values if we're rewriting the table, since this
@@ -14951,9 +15032,8 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 
 			int			one = 1;
 			bool		isNull;
-			Datum		valuesAtt[Natts_pg_attribute] = {0};
-			bool		nullsAtt[Natts_pg_attribute] = {0};
-			bool		replacesAtt[Natts_pg_attribute] = {0};
+			Datum		values[Natts_pg_attribute] = {0};
+			bool		nulls[Natts_pg_attribute] = {false};
 			HeapTuple	newTup;
 
 			missingval = array_get_element(missingval,
@@ -14971,35 +15051,36 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 														 tform->typbyval,
 														 tform->typalign));
 
-			valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
-			replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
-			nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
+			HeapTupleUpdateValue(pg_attribute, attmissingval, missingval, values, nulls, updated);
 
-			newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
-									   valuesAtt, nullsAtt, replacesAtt);
+			newTup = heap_update_tuple(heapTup, RelationGetDescr(attrelation),
+									   values, nulls, updated);
 			heap_freetuple(heapTup);
+			bms_free(updated);
+			updated = NULL;
+
 			heapTup = newTup;
 			attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
 		}
 	}
 
-	attTup->atttypid = targettype;
-	attTup->atttypmod = targettypmod;
-	attTup->attcollation = targetcollid;
+	HeapTupleUpdateField(pg_attribute, atttypid, targettype, attTup, updated);
+	HeapTupleUpdateField(pg_attribute, atttypmod, targettypmod, attTup, updated);
+	HeapTupleUpdateField(pg_attribute, attcollation, targetcollid, attTup, updated);
 	if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
 		ereport(ERROR,
 				errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 				errmsg("too many array dimensions"));
-	attTup->attndims = list_length(typeName->arrayBounds);
-	attTup->attlen = tform->typlen;
-	attTup->attbyval = tform->typbyval;
-	attTup->attalign = tform->typalign;
-	attTup->attstorage = tform->typstorage;
-	attTup->attcompression = InvalidCompressionMethod;
+	HeapTupleUpdateField(pg_attribute, attndims, list_length(typeName->arrayBounds), attTup, updated);
+	HeapTupleUpdateField(pg_attribute, attlen, tform->typlen, attTup, updated);
+	HeapTupleUpdateField(pg_attribute, attbyval, tform->typbyval, attTup, updated);
+	HeapTupleUpdateField(pg_attribute, attalign, tform->typalign, attTup, updated);
+	HeapTupleUpdateField(pg_attribute, attstorage, tform->typstorage, attTup, updated);
+	HeapTupleUpdateField(pg_attribute, attcompression, InvalidCompressionMethod, attTup, updated);
 
 	ReleaseSysCache(typeTuple);
 
-	CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
+	CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup, updated, NULL);
 
 	table_close(attrelation, RowExclusiveLock);
 
@@ -15059,6 +15140,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 						RelationGetRelid(rel), attnum);
 
 	/* Cleanup */
+	bms_free(updated);
 	heap_freetuple(heapTup);
 
 	return address;
@@ -15994,9 +16076,9 @@ ATExecAlterColumnGenericOptions(Relation rel,
 	HeapTuple	tuple;
 	HeapTuple	newtuple;
 	bool		isnull;
-	Datum		repl_val[Natts_pg_attribute];
-	bool		repl_null[Natts_pg_attribute];
-	bool		repl_repl[Natts_pg_attribute];
+	Datum		values[Natts_pg_attribute] = {0};
+	bool		nulls[Natts_pg_attribute] = {false};
+	Bitmapset  *updated = NULL;
 	Datum		datum;
 	Form_pg_foreign_table fttableform;
 	Form_pg_attribute atttableform;
@@ -16038,11 +16120,6 @@ ATExecAlterColumnGenericOptions(Relation rel,
 				 errmsg("cannot alter system column \"%s\"", colName)));
 
 
-	/* Initialize buffers for new tuple values */
-	memset(repl_val, 0, sizeof(repl_val));
-	memset(repl_null, false, sizeof(repl_null));
-	memset(repl_repl, false, sizeof(repl_repl));
-
 	/* Extract the current options */
 	datum = SysCacheGetAttr(ATTNAME,
 							tuple,
@@ -16058,18 +16135,15 @@ ATExecAlterColumnGenericOptions(Relation rel,
 									fdw->fdwvalidator);
 
 	if (DatumGetPointer(datum) != NULL)
-		repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
+		HeapTupleUpdateValue(pg_attribute, attfdwoptions, datum, values, nulls, updated);
 	else
-		repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
-
-	repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
+		HeapTupleUpdateValueNull(pg_attribute, attfdwoptions, values, nulls, updated);
 
 	/* Everything looks good - update the tuple */
+	newtuple = heap_update_tuple(tuple, RelationGetDescr(attrel),
+								 values, nulls, updated);
 
-	newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
-								 repl_val, repl_null, repl_repl);
-
-	CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
+	CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple, updated, NULL);
 
 	InvokeObjectPostAlterHook(RelationRelationId,
 							  RelationGetRelid(rel),
@@ -16081,6 +16155,7 @@ ATExecAlterColumnGenericOptions(Relation rel,
 
 	table_close(attrel, RowExclusiveLock);
 
+	bms_free(updated);
 	heap_freetuple(newtuple);
 
 	return address;
@@ -16207,13 +16282,13 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock
 	 */
 	if (tuple_class->relowner != newOwnerId)
 	{
-		Datum		repl_val[Natts_pg_class];
-		bool		repl_null[Natts_pg_class];
-		bool		repl_repl[Natts_pg_class];
+		Datum		values[Natts_pg_class] = {0};
+		bool		nulls[Natts_pg_class] = {false};
 		Acl		   *newAcl;
 		Datum		aclDatum;
 		bool		isNull;
 		HeapTuple	newtuple;
+		Bitmapset  *updated = NULL;
 
 		/* skip permission checks when recursing to index or toast table */
 		if (!recursing)
@@ -16241,11 +16316,7 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock
 			}
 		}
 
-		memset(repl_null, false, sizeof(repl_null));
-		memset(repl_repl, false, sizeof(repl_repl));
-
-		repl_repl[Anum_pg_class_relowner - 1] = true;
-		repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
+		HeapTupleUpdateValue(pg_class, relowner, ObjectIdGetDatum(newOwnerId), values, nulls, updated);
 
 		/*
 		 * Determine the modified ACL for the new owner.  This is only
@@ -16258,14 +16329,14 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock
 		{
 			newAcl = aclnewowner(DatumGetAclP(aclDatum),
 								 tuple_class->relowner, newOwnerId);
-			repl_repl[Anum_pg_class_relacl - 1] = true;
-			repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
+			HeapTupleUpdateValue(pg_class, relacl, PointerGetDatum(newAcl), values, nulls, updated);
 		}
 
-		newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
+		newtuple = heap_update_tuple(tuple, RelationGetDescr(class_rel), values, nulls, updated);
 
-		CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
+		CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple, updated, NULL);
 
+		bms_free(updated);
 		heap_freetuple(newtuple);
 
 		/*
@@ -16357,9 +16428,9 @@ change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
 	while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
 	{
 		Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
-		Datum		repl_val[Natts_pg_attribute];
-		bool		repl_null[Natts_pg_attribute];
-		bool		repl_repl[Natts_pg_attribute];
+		Datum		values[Natts_pg_attribute] = {0};
+		bool		nulls[Natts_pg_attribute] = {false};
+		Bitmapset  *updated = NULL;
 		Acl		   *newAcl;
 		Datum		aclDatum;
 		bool		isNull;
@@ -16377,20 +16448,17 @@ change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
 		if (isNull)
 			continue;
 
-		memset(repl_null, false, sizeof(repl_null));
-		memset(repl_repl, false, sizeof(repl_repl));
-
 		newAcl = aclnewowner(DatumGetAclP(aclDatum),
 							 oldOwnerId, newOwnerId);
-		repl_repl[Anum_pg_attribute_attacl - 1] = true;
-		repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
+		HeapTupleUpdateValue(pg_attribute, attacl, PointerGetDatum(newAcl), values, nulls, updated);
 
-		newtuple = heap_modify_tuple(attributeTuple,
+		newtuple = heap_update_tuple(attributeTuple,
 									 RelationGetDescr(attRelation),
-									 repl_val, repl_null, repl_repl);
+									 values, nulls, updated);
 
-		CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
+		CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple, updated, NULL);
 
+		bms_free(updated);
 		heap_freetuple(newtuple);
 	}
 	systable_endscan(scan);
@@ -16559,6 +16627,7 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
 	HeapTuple	tuple;
 	Form_pg_class rd_rel;
 	Oid			reloid = RelationGetRelid(rel);
+	Bitmapset  *updated = NULL;
 
 	/*
 	 * Shouldn't be called on relations having storage; these are processed in
@@ -16576,17 +16645,18 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
 
 	/* Update the pg_class row. */
 	oldAccessMethodId = rd_rel->relam;
-	rd_rel->relam = newAccessMethodId;
+	HeapTupleUpdateField(pg_class, relam, newAccessMethodId, rd_rel, updated);
 
 	/* Leave if no update required */
 	if (rd_rel->relam == oldAccessMethodId)
 	{
+		bms_free(updated);
 		heap_freetuple(tuple);
 		table_close(pg_class, RowExclusiveLock);
 		return;
 	}
 
-	CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
+	CatalogTupleUpdate(pg_class, &tuple->t_self, tuple, updated, NULL);
 
 	/*
 	 * Update the dependency on the new access method.  No dependency is added
@@ -16634,6 +16704,7 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
 
 	InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
 
+	bms_free(updated);
 	heap_freetuple(tuple);
 	table_close(pg_class, RowExclusiveLock);
 }
@@ -16681,9 +16752,9 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 	HeapTuple	newtuple;
 	Datum		datum;
 	Datum		newOptions;
-	Datum		repl_val[Natts_pg_class];
-	bool		repl_null[Natts_pg_class];
-	bool		repl_repl[Natts_pg_class];
+	Datum		values[Natts_pg_class] = {0};
+	bool		nulls[Natts_pg_class] = {false};
+	Bitmapset  *updated = NULL;
 	const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
 
 	if (defList == NIL && operation != AT_ReplaceRelOptions)
@@ -16785,25 +16856,23 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 	 * All we need do here is update the pg_class row; the new options will be
 	 * propagated into relcaches during post-commit cache inval.
 	 */
-	memset(repl_val, 0, sizeof(repl_val));
-	memset(repl_null, false, sizeof(repl_null));
-	memset(repl_repl, false, sizeof(repl_repl));
 
 	if (newOptions != (Datum) 0)
-		repl_val[Anum_pg_class_reloptions - 1] = newOptions;
+		HeapTupleUpdateValue(pg_class, reloptions, newOptions, values, nulls, updated);
 	else
-		repl_null[Anum_pg_class_reloptions - 1] = true;
+		HeapTupleUpdateValueNull(pg_class, reloptions, values, nulls, updated);
 
-	repl_repl[Anum_pg_class_reloptions - 1] = true;
+	newtuple = heap_update_tuple(tuple, RelationGetDescr(pgclass),
+								 values, nulls, updated);
 
-	newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
-								 repl_val, repl_null, repl_repl);
+	CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple, updated, NULL);
 
-	CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
 	UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
 
 	InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
 
+	bms_free(updated);
+	updated = NULL;
 	heap_freetuple(newtuple);
 
 	ReleaseSysCache(tuple);
@@ -16845,26 +16914,25 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 
 		(void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
 
-		memset(repl_val, 0, sizeof(repl_val));
-		memset(repl_null, false, sizeof(repl_null));
-		memset(repl_repl, false, sizeof(repl_repl));
+		memset(values, 0, sizeof(values));
+		memset(nulls, false, sizeof(nulls));
+		Assert(bms_is_empty(updated));
 
 		if (newOptions != (Datum) 0)
-			repl_val[Anum_pg_class_reloptions - 1] = newOptions;
+			HeapTupleUpdateValue(pg_class, reloptions, newOptions, values, nulls, updated);
 		else
-			repl_null[Anum_pg_class_reloptions - 1] = true;
-
-		repl_repl[Anum_pg_class_reloptions - 1] = true;
+			HeapTupleUpdateValueNull(pg_class, reloptions, values, nulls, updated);
 
-		newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
-									 repl_val, repl_null, repl_repl);
+		newtuple = heap_update_tuple(tuple, RelationGetDescr(pgclass),
+									 values, nulls, updated);
 
-		CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
+		CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple, updated, NULL);
 
 		InvokeObjectPostAlterHookArg(RelationRelationId,
 									 RelationGetRelid(toastrel), 0,
 									 InvalidOid, true);
 
+		bms_free(updated);
 		heap_freetuple(newtuple);
 
 		ReleaseSysCache(tuple);
@@ -17536,6 +17604,7 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispart
 	for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
 	{
 		Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
+		Bitmapset  *updated = NULL;
 		char	   *parent_attname = NameStr(parent_att->attname);
 		HeapTuple	tuple;
 
@@ -17620,6 +17689,8 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispart
 						errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 						errmsg("too many inheritance parents"));
 
+			HeapTupleMarkColumnUpdated(pg_attribute, attinhcount, updated);
+
 			/*
 			 * In case of partitions, we must enforce that value of attislocal
 			 * is same in all partitions. (Note: there are only inherited
@@ -17628,10 +17699,12 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispart
 			if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
 			{
 				Assert(child_att->attinhcount == 1);
-				child_att->attislocal = false;
+				HeapTupleUpdateField(pg_attribute, attislocal, false, child_att, updated);
 			}
 
-			CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
+			CatalogTupleUpdate(attrrel, &tuple->t_self, tuple, updated, NULL);
+
+			bms_free(updated);
 			heap_freetuple(tuple);
 		}
 		else
@@ -17694,6 +17767,7 @@ MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
 		HeapTuple	child_tuple;
 		AttrNumber	parent_attno;
 		bool		found = false;
+		Bitmapset  *updated = NULL;
 
 		if (parent_con->contype != CONSTRAINT_CHECK &&
 			parent_con->contype != CONSTRAINT_NOTNULL)
@@ -17802,6 +17876,8 @@ MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
 						errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 						errmsg("too many inheritance parents"));
 
+			HeapTupleMarkColumnUpdated(pg_constraint, coninhcount, updated);
+
 			/*
 			 * In case of partitions, an inherited constraint must be
 			 * inherited only once since it cannot have multiple parents and
@@ -17810,10 +17886,12 @@ MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
 			if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
 			{
 				Assert(child_con->coninhcount == 1);
-				child_con->conislocal = false;
+				HeapTupleUpdateField(pg_constraint, conislocal, false, child_con, updated);
 			}
 
-			CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
+			CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy, updated, NULL);
+
+			bms_free(updated);
 			heap_freetuple(child_copy);
 
 			found = true;
@@ -17917,6 +17995,7 @@ MarkInheritDetached(Relation child_rel, Relation parent_rel)
 	while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
 	{
 		Form_pg_inherits inhForm;
+		Bitmapset  *updated = NULL;
 
 		inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
 		if (inhForm->inhdetachpending)
@@ -17931,14 +18010,18 @@ MarkInheritDetached(Relation child_rel, Relation parent_rel)
 		if (inhForm->inhrelid == RelationGetRelid(child_rel))
 		{
 			HeapTuple	newtup;
+			Form_pg_inherits classForm;
 
 			newtup = heap_copytuple(inheritsTuple);
-			((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
+			classForm = (Form_pg_inherits) GETSTRUCT(newtup);
+
+			HeapTupleUpdateField(pg_inherits, inhdetachpending, true, classForm, updated);
+
+			CatalogTupleUpdate(catalogRelation, &inheritsTuple->t_self, newtup, updated, NULL);
 
-			CatalogTupleUpdate(catalogRelation,
-							   &inheritsTuple->t_self,
-							   newtup);
 			found = true;
+
+			bms_free(updated);
 			heap_freetuple(newtup);
 			/* keep looking, to ensure we catch others pending detach */
 		}
@@ -18023,6 +18106,7 @@ RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
 	while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
 	{
 		Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
+		Bitmapset  *updated = NULL;
 
 		/* Ignore if dropped or not inherited */
 		if (att->attisdropped)
@@ -18037,11 +18121,14 @@ RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
 			HeapTuple	copyTuple = heap_copytuple(attributeTuple);
 			Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
 
-			copy_att->attinhcount--;
+			HeapTupleUpdateField(pg_attribute, attinhcount, copy_att->attinhcount - 1, copy_att, updated);
+
 			if (copy_att->attinhcount == 0)
-				copy_att->attislocal = true;
+				HeapTupleUpdateField(pg_attribute, attislocal, true, copy_att, updated);
 
-			CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
+			CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple, updated, NULL);
+
+			bms_free(updated);
 			heap_freetuple(copyTuple);
 		}
 	}
@@ -18103,6 +18190,7 @@ RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
 	{
 		Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
 		bool		match = false;
+		Bitmapset  *updated = NULL;
 
 		/*
 		 * Match CHECK constraints by name, not-null constraints by column
@@ -18148,11 +18236,14 @@ RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
 				elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
 					 RelationGetRelid(child_rel), NameStr(copy_con->conname));
 
-			copy_con->coninhcount--;
+			HeapTupleUpdateField(pg_constraint, coninhcount, copy_con->coninhcount - 1, copy_con, updated);
+
 			if (copy_con->coninhcount == 0)
-				copy_con->conislocal = true;
+				HeapTupleUpdateField(pg_constraint, conislocal, true, copy_con, updated);
 
-			CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
+			CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple, updated, NULL);
+
+			bms_free(updated);
 			heap_freetuple(copyTuple);
 		}
 	}
@@ -18258,6 +18349,7 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
 	ObjectAddress tableobj,
 				typeobj;
 	HeapTuple	classtuple;
+	Bitmapset  *updated = NULL;
 
 	/* Validate the type. */
 	typetuple = typenameType(NULL, ofTypename, NULL);
@@ -18363,11 +18455,14 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
 	classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
 	if (!HeapTupleIsValid(classtuple))
 		elog(ERROR, "cache lookup failed for relation %u", relid);
-	((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
-	CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
+
+	HeapTupleUpdateField(pg_class, reloftype, typeid, (Form_pg_class) GETSTRUCT(classtuple), updated);
+
+	CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple, updated, NULL);
 
 	InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
 
+	bms_free(updated);
 	heap_freetuple(classtuple);
 	table_close(relationRelation, RowExclusiveLock);
 
@@ -18388,6 +18483,7 @@ ATExecDropOf(Relation rel, LOCKMODE lockmode)
 	Oid			relid = RelationGetRelid(rel);
 	Relation	relationRelation;
 	HeapTuple	tuple;
+	Bitmapset  *updated = NULL;
 
 	if (!OidIsValid(rel->rd_rel->reloftype))
 		ereport(ERROR,
@@ -18408,11 +18504,14 @@ ATExecDropOf(Relation rel, LOCKMODE lockmode)
 	tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
 	if (!HeapTupleIsValid(tuple))
 		elog(ERROR, "cache lookup failed for relation %u", relid);
-	((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
-	CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
+
+	HeapTupleUpdateField(pg_class, reloftype, InvalidOid, (Form_pg_class) GETSTRUCT(tuple), updated);
+
+	CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple, updated, NULL);
 
 	InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
 
+	bms_free(updated);
 	heap_freetuple(tuple);
 	table_close(relationRelation, RowExclusiveLock);
 }
@@ -18450,8 +18549,11 @@ relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
 	pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
 	if (pg_class_form->relreplident != ri_type)
 	{
-		pg_class_form->relreplident = ri_type;
-		CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
+		Bitmapset  *updated = NULL;
+
+		HeapTupleUpdateField(pg_class, relreplident, ri_type, pg_class_form, updated);
+		CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple, updated, NULL);
+		bms_free(updated);
 	}
 	table_close(pg_class, RowExclusiveLock);
 	heap_freetuple(pg_class_tuple);
@@ -18464,6 +18566,7 @@ relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
 	{
 		Oid			thisIndexOid = lfirst_oid(index);
 		bool		dirty = false;
+		Bitmapset  *updated = NULL;
 
 		pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
 											 ObjectIdGetDatum(thisIndexOid));
@@ -18477,7 +18580,7 @@ relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
 			if (!pg_index_form->indisreplident)
 			{
 				dirty = true;
-				pg_index_form->indisreplident = true;
+				HeapTupleUpdateField(pg_index, indisreplident, true, pg_index_form, updated);
 			}
 		}
 		else
@@ -18486,13 +18589,13 @@ relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
 			if (pg_index_form->indisreplident)
 			{
 				dirty = true;
-				pg_index_form->indisreplident = false;
+				HeapTupleUpdateField(pg_index, indisreplident, false, pg_index_form, updated);
 			}
 		}
 
 		if (dirty)
 		{
-			CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
+			CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple, updated, NULL);
 			InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
 										 InvalidOid, is_internal);
 
@@ -18505,6 +18608,7 @@ relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
 			 */
 			CacheInvalidateRelcache(rel);
 		}
+		bms_free(updated);
 		heap_freetuple(pg_index_tuple);
 	}
 
@@ -18634,6 +18738,7 @@ ATExecSetRowSecurity(Relation rel, bool rls)
 	Relation	pg_class;
 	Oid			relid;
 	HeapTuple	tuple;
+	Bitmapset  *updated = NULL;
 
 	relid = RelationGetRelid(rel);
 
@@ -18645,13 +18750,14 @@ ATExecSetRowSecurity(Relation rel, bool rls)
 	if (!HeapTupleIsValid(tuple))
 		elog(ERROR, "cache lookup failed for relation %u", relid);
 
-	((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
-	CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
+	HeapTupleUpdateField(pg_class, relrowsecurity, rls, (Form_pg_class) GETSTRUCT(tuple), updated);
+	CatalogTupleUpdate(pg_class, &tuple->t_self, tuple, updated, NULL);
 
 	InvokeObjectPostAlterHook(RelationRelationId,
 							  RelationGetRelid(rel), 0);
 
 	table_close(pg_class, RowExclusiveLock);
+	bms_free(updated);
 	heap_freetuple(tuple);
 }
 
@@ -18664,6 +18770,7 @@ ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
 	Relation	pg_class;
 	Oid			relid;
 	HeapTuple	tuple;
+	Bitmapset  *updated = NULL;
 
 	relid = RelationGetRelid(rel);
 
@@ -18674,13 +18781,14 @@ ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
 	if (!HeapTupleIsValid(tuple))
 		elog(ERROR, "cache lookup failed for relation %u", relid);
 
-	((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
-	CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
+	HeapTupleUpdateField(pg_class, relforcerowsecurity, force_rls, (Form_pg_class) GETSTRUCT(tuple), updated);
+	CatalogTupleUpdate(pg_class, &tuple->t_self, tuple, updated, NULL);
 
 	InvokeObjectPostAlterHook(RelationRelationId,
 							  RelationGetRelid(rel), 0);
 
 	table_close(pg_class, RowExclusiveLock);
+	bms_free(updated);
 	heap_freetuple(tuple);
 }
 
@@ -18695,9 +18803,9 @@ ATExecGenericOptions(Relation rel, List *options)
 	ForeignDataWrapper *fdw;
 	HeapTuple	tuple;
 	bool		isnull;
-	Datum		repl_val[Natts_pg_foreign_table];
-	bool		repl_null[Natts_pg_foreign_table];
-	bool		repl_repl[Natts_pg_foreign_table];
+	Datum		values[Natts_pg_foreign_table] = {0};
+	bool		nulls[Natts_pg_foreign_table] = {false};
+	Bitmapset  *updated = NULL;
 	Datum		datum;
 	Form_pg_foreign_table tableform;
 
@@ -18717,10 +18825,6 @@ ATExecGenericOptions(Relation rel, List *options)
 	server = GetForeignServer(tableform->ftserver);
 	fdw = GetForeignDataWrapper(server->fdwid);
 
-	memset(repl_val, 0, sizeof(repl_val));
-	memset(repl_null, false, sizeof(repl_null));
-	memset(repl_repl, false, sizeof(repl_repl));
-
 	/* Extract the current options */
 	datum = SysCacheGetAttr(FOREIGNTABLEREL,
 							tuple,
@@ -18736,18 +18840,16 @@ ATExecGenericOptions(Relation rel, List *options)
 									fdw->fdwvalidator);
 
 	if (DatumGetPointer(datum) != NULL)
-		repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
+		HeapTupleUpdateValue(pg_foreign_table, ftoptions, datum, values, nulls, updated);
 	else
-		repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
-
-	repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
+		HeapTupleUpdateValueNull(pg_foreign_table, ftoptions, values, nulls, updated);
 
 	/* Everything looks good - update the tuple */
 
-	tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
-							  repl_val, repl_null, repl_repl);
+	tuple = heap_update_tuple(tuple, RelationGetDescr(ftrel),
+							  values, nulls, updated);
 
-	CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
+	CatalogTupleUpdate(ftrel, &tuple->t_self, tuple, updated, NULL);
 
 	/*
 	 * Invalidate relcache so that all sessions will refresh any cached plans
@@ -18760,6 +18862,7 @@ ATExecGenericOptions(Relation rel, List *options)
 
 	table_close(ftrel, RowExclusiveLock);
 
+	bms_free(updated);
 	heap_freetuple(tuple);
 }
 
@@ -18781,6 +18884,7 @@ ATExecSetCompression(Relation rel,
 	char	   *compression;
 	char		cmethod;
 	ObjectAddress address;
+	Bitmapset  *updated = NULL;
 
 	compression = strVal(newValue);
 
@@ -18809,8 +18913,8 @@ ATExecSetCompression(Relation rel,
 	cmethod = GetAttributeCompression(atttableform->atttypid, compression);
 
 	/* update pg_attribute entry */
-	atttableform->attcompression = cmethod;
-	CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
+	HeapTupleUpdateField(pg_attribute, attcompression, cmethod, atttableform, updated);
+	CatalogTupleUpdate(attrel, &tuple->t_self, tuple, updated, NULL);
 
 	InvokeObjectPostAlterHook(RelationRelationId,
 							  RelationGetRelid(rel),
@@ -18825,6 +18929,7 @@ ATExecSetCompression(Relation rel,
 							  true, cmethod,
 							  lockmode);
 
+	bms_free(updated);
 	heap_freetuple(tuple);
 
 	table_close(attrel, RowExclusiveLock);
@@ -19088,6 +19193,7 @@ AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
 	Form_pg_class classForm;
 	ObjectAddress thisobj;
 	bool		already_done = false;
+	Bitmapset  *updated = NULL;
 
 	/* no rel lock for relkind=c so use LOCKTAG_TUPLE */
 	classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
@@ -19121,9 +19227,10 @@ AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
 							get_namespace_name(newNspOid))));
 
 		/* classTup is a copy, so OK to scribble on */
-		classForm->relnamespace = newNspOid;
+		HeapTupleUpdateField(pg_class, relnamespace, newNspOid, classForm, updated);
 
-		CatalogTupleUpdate(classRel, &otid, classTup);
+		CatalogTupleUpdate(classRel, &otid, classTup, updated, NULL);
+		bms_free(updated);
 		UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
 
 
@@ -21134,9 +21241,9 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
 	List	   *fks;
 	ListCell   *cell;
 	List	   *indexes;
-	Datum		new_val[Natts_pg_class];
-	bool		new_null[Natts_pg_class],
-				new_repl[Natts_pg_class];
+	Datum		new_val[Natts_pg_class] = {0};
+	bool		new_null[Natts_pg_class] = {false};
+	Bitmapset  *updated = NULL;
 	HeapTuple	tuple,
 				newtuple;
 	Relation	trigrel = NULL;
@@ -21386,17 +21493,13 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
 	Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
 
 	/* Clear relpartbound and reset relispartition */
-	memset(new_val, 0, sizeof(new_val));
-	memset(new_null, false, sizeof(new_null));
-	memset(new_repl, false, sizeof(new_repl));
-	new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
-	new_null[Anum_pg_class_relpartbound - 1] = true;
-	new_repl[Anum_pg_class_relpartbound - 1] = true;
-	newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
-								 new_val, new_null, new_repl);
-
-	((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
-	CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
+	HeapTupleUpdateValueNull(pg_class, relpartbound, new_val, new_null, updated);
+	newtuple = heap_update_tuple(tuple, RelationGetDescr(classRel),
+								 new_val, new_null, updated);
+
+	HeapTupleUpdateField(pg_class, relispartition, false, (Form_pg_class) GETSTRUCT(newtuple), updated);
+	CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple, updated, NULL);
+	bms_free(updated);
 	heap_freetuple(newtuple);
 	table_close(classRel, RowExclusiveLock);
 
@@ -21816,6 +21919,7 @@ validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
 	int			tuples = 0;
 	HeapTuple	inhTup;
 	bool		updated = false;
+	Bitmapset  *updated_cols = NULL;
 
 	Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
 
@@ -21868,10 +21972,11 @@ validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
 				 RelationGetRelid(partedIdx));
 		indexForm = (Form_pg_index) GETSTRUCT(indTup);
 
-		indexForm->indisvalid = true;
+		HeapTupleUpdateField(pg_index, indisvalid, true, indexForm, updated_cols);
 		updated = true;
 
-		CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
+		CatalogTupleUpdate(idxRel, &indTup->t_self, indTup, updated_cols, NULL);
+		bms_free(updated_cols);
 
 		table_close(idxRel, RowExclusiveLock);
 		heap_freetuple(indTup);
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 0b064891932..b7fe7abf86a 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -208,8 +208,8 @@ Oid
 CreateTableSpace(CreateTableSpaceStmt *stmt)
 {
 	Relation	rel;
-	Datum		values[Natts_pg_tablespace];
-	bool		nulls[Natts_pg_tablespace] = {0};
+	Datum		values[Natts_pg_tablespace] = {0};
+	bool		nulls[Natts_pg_tablespace] = {false};
 	HeapTuple	tuple;
 	Oid			tablespaceoid;
 	char	   *location;
@@ -325,12 +325,10 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
 	else
 		tablespaceoid = GetNewOidWithIndex(rel, TablespaceOidIndexId,
 										   Anum_pg_tablespace_oid);
-	values[Anum_pg_tablespace_oid - 1] = ObjectIdGetDatum(tablespaceoid);
-	values[Anum_pg_tablespace_spcname - 1] =
-		DirectFunctionCall1(namein, CStringGetDatum(stmt->tablespacename));
-	values[Anum_pg_tablespace_spcowner - 1] =
-		ObjectIdGetDatum(ownerId);
-	nulls[Anum_pg_tablespace_spcacl - 1] = true;
+	HeapTupleSetValue(pg_tablespace, oid, ObjectIdGetDatum(tablespaceoid), values);
+	HeapTupleSetValue(pg_tablespace, spcname, DirectFunctionCall1(namein, CStringGetDatum(stmt->tablespacename)), values);
+	HeapTupleSetValue(pg_tablespace, spcowner, ObjectIdGetDatum(ownerId), values);
+	HeapTupleSetValueNull(pg_tablespace, spcacl, values, nulls);
 
 	/* Generate new proposed spcoptions (text array) */
 	newOptions = transformRelOptions((Datum) 0,
@@ -338,13 +336,13 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
 									 NULL, NULL, false, false);
 	(void) tablespace_reloptions(newOptions, true);
 	if (newOptions != (Datum) 0)
-		values[Anum_pg_tablespace_spcoptions - 1] = newOptions;
+		HeapTupleSetValue(pg_tablespace, spcoptions, newOptions, values);
 	else
-		nulls[Anum_pg_tablespace_spcoptions - 1] = true;
+		HeapTupleSetValueNull(pg_tablespace, spcoptions, values, nulls);
 
 	tuple = heap_form_tuple(rel->rd_att, values, nulls);
 
-	CatalogTupleInsert(rel, tuple);
+	CatalogTupleInsert(rel, tuple, NULL);
 
 	heap_freetuple(tuple);
 
@@ -937,6 +935,7 @@ RenameTableSpace(const char *oldname, const char *newname)
 	HeapTuple	newtuple;
 	Form_pg_tablespace newform;
 	ObjectAddress address;
+	Bitmapset  *updated = NULL;
 
 	/* Search pg_tablespace */
 	rel = table_open(TableSpaceRelationId, RowExclusiveLock);
@@ -996,14 +995,15 @@ RenameTableSpace(const char *oldname, const char *newname)
 
 	/* OK, update the entry */
 	namestrcpy(&(newform->spcname), newname);
-
-	CatalogTupleUpdate(rel, &newtuple->t_self, newtuple);
+	HeapTupleMarkColumnUpdated(pg_tablespace, spcname, updated);
+	CatalogTupleUpdate(rel, &newtuple->t_self, newtuple, updated, NULL);
 
 	InvokeObjectPostAlterHook(TableSpaceRelationId, tspId, 0);
 
 	ObjectAddressSet(address, TableSpaceRelationId, tspId);
 
 	table_close(rel, NoLock);
+	bms_free(updated);
 
 	return address;
 }
@@ -1021,10 +1021,10 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
 	Oid			tablespaceoid;
 	Datum		datum;
 	Datum		newOptions;
-	Datum		repl_val[Natts_pg_tablespace];
+	Datum		values[Natts_pg_tablespace] = {0};
+	bool		nulls[Natts_pg_tablespace] = {false};
+	Bitmapset  *updated = NULL;
 	bool		isnull;
-	bool		repl_null[Natts_pg_tablespace];
-	bool		repl_repl[Natts_pg_tablespace];
 	HeapTuple	newtuple;
 
 	/* Search pg_tablespace */
@@ -1058,26 +1058,25 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
 	(void) tablespace_reloptions(newOptions, true);
 
 	/* Build new tuple. */
-	memset(repl_null, false, sizeof(repl_null));
-	memset(repl_repl, false, sizeof(repl_repl));
 	if (newOptions != (Datum) 0)
-		repl_val[Anum_pg_tablespace_spcoptions - 1] = newOptions;
+		HeapTupleUpdateValue(pg_tablespace, spcoptions, newOptions, values, nulls, updated);
 	else
-		repl_null[Anum_pg_tablespace_spcoptions - 1] = true;
-	repl_repl[Anum_pg_tablespace_spcoptions - 1] = true;
-	newtuple = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val,
-								 repl_null, repl_repl);
+		HeapTupleUpdateValueNull(pg_tablespace, spcoptions, values, nulls, updated);
+	newtuple = heap_update_tuple(tup, RelationGetDescr(rel), values,
+								 nulls, updated);
 
 	/* Update system catalog. */
-	CatalogTupleUpdate(rel, &newtuple->t_self, newtuple);
+	CatalogTupleUpdate(rel, &newtuple->t_self, newtuple, updated, NULL);
 
 	InvokeObjectPostAlterHook(TableSpaceRelationId, tablespaceoid, 0);
 
 	heap_freetuple(newtuple);
+	bms_free(updated);
 
 	/* Conclude heap scan. */
 	table_endscan(scandesc);
 	table_close(rel, NoLock);
+	bms_free(updated);
 
 	return tablespaceoid;
 }
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index d30fda660eb..a4e60bde795 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -186,8 +186,9 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 	int2vector *tgattr;
 	List	   *whenRtable;
 	char	   *qual;
-	Datum		values[Natts_pg_trigger];
-	bool		nulls[Natts_pg_trigger];
+	Datum		values[Natts_pg_trigger] = {0};
+	bool		nulls[Natts_pg_trigger] = {false};
+	Bitmapset  *updated = NULL;
 	Relation	rel;
 	AclResult	aclresult;
 	Relation	tgrel;
@@ -751,8 +752,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 	if (!trigger_exists)
 	{
 		/* Generate the OID for the new trigger. */
-		trigoid = GetNewOidWithIndex(tgrel, TriggerOidIndexId,
-									 Anum_pg_trigger_oid);
+		trigoid = GetNewOidWithIndex(tgrel, TriggerOidIndexId, Anum_pg_trigger_oid);
 	}
 	else
 	{
@@ -862,22 +862,19 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 	/*
 	 * Build the new pg_trigger tuple.
 	 */
-	memset(nulls, false, sizeof(nulls));
-
-	values[Anum_pg_trigger_oid - 1] = ObjectIdGetDatum(trigoid);
-	values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
-	values[Anum_pg_trigger_tgparentid - 1] = ObjectIdGetDatum(parentTriggerOid);
-	values[Anum_pg_trigger_tgname - 1] = DirectFunctionCall1(namein,
-															 CStringGetDatum(trigname));
-	values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(funcoid);
-	values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype);
-	values[Anum_pg_trigger_tgenabled - 1] = CharGetDatum(trigger_fires_when);
-	values[Anum_pg_trigger_tgisinternal - 1] = BoolGetDatum(isInternal);
-	values[Anum_pg_trigger_tgconstrrelid - 1] = ObjectIdGetDatum(constrrelid);
-	values[Anum_pg_trigger_tgconstrindid - 1] = ObjectIdGetDatum(indexOid);
-	values[Anum_pg_trigger_tgconstraint - 1] = ObjectIdGetDatum(constraintOid);
-	values[Anum_pg_trigger_tgdeferrable - 1] = BoolGetDatum(stmt->deferrable);
-	values[Anum_pg_trigger_tginitdeferred - 1] = BoolGetDatum(stmt->initdeferred);
+	HeapTupleUpdateValue(pg_trigger, oid, ObjectIdGetDatum(trigoid), values, nulls, updated);
+	HeapTupleUpdateValue(pg_trigger, tgrelid, ObjectIdGetDatum(RelationGetRelid(rel)), values, nulls, updated);
+	HeapTupleUpdateValue(pg_trigger, tgparentid, ObjectIdGetDatum(parentTriggerOid), values, nulls, updated);
+	HeapTupleUpdateValue(pg_trigger, tgname, DirectFunctionCall1(namein, CStringGetDatum(trigname)), values, nulls, updated);
+	HeapTupleUpdateValue(pg_trigger, tgfoid, ObjectIdGetDatum(funcoid), values, nulls, updated);
+	HeapTupleUpdateValue(pg_trigger, tgtype, Int16GetDatum(tgtype), values, nulls, updated);
+	HeapTupleUpdateValue(pg_trigger, tgenabled, CharGetDatum(trigger_fires_when), values, nulls, updated);
+	HeapTupleUpdateValue(pg_trigger, tgisinternal, BoolGetDatum(isInternal), values, nulls, updated);
+	HeapTupleUpdateValue(pg_trigger, tgconstrrelid, ObjectIdGetDatum(constrrelid), values, nulls, updated);
+	HeapTupleUpdateValue(pg_trigger, tgconstrindid, ObjectIdGetDatum(indexOid), values, nulls, updated);
+	HeapTupleUpdateValue(pg_trigger, tgconstraint, ObjectIdGetDatum(constraintOid), values, nulls, updated);
+	HeapTupleUpdateValue(pg_trigger, tgdeferrable, BoolGetDatum(stmt->deferrable), values, nulls, updated);
+	HeapTupleUpdateValue(pg_trigger, tginitdeferred, BoolGetDatum(stmt->initdeferred), values, nulls, updated);
 
 	if (stmt->args)
 	{
@@ -912,15 +909,13 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 			}
 			strcpy(d, "\\000");
 		}
-		values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(nargs);
-		values[Anum_pg_trigger_tgargs - 1] = DirectFunctionCall1(byteain,
-																 CStringGetDatum(args));
+		HeapTupleUpdateValue(pg_trigger, tgnargs, Int16GetDatum(nargs), values, nulls, updated);
+		HeapTupleUpdateValue(pg_trigger, tgargs, DirectFunctionCall1(byteain, CStringGetDatum(args)), values, nulls, updated);
 	}
 	else
 	{
-		values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(0);
-		values[Anum_pg_trigger_tgargs - 1] = DirectFunctionCall1(byteain,
-																 CStringGetDatum(""));
+		HeapTupleUpdateValue(pg_trigger, tgnargs, Int16GetDatum(0), values, nulls, updated);
+		HeapTupleUpdateValue(pg_trigger, tgargs, DirectFunctionCall1(byteain, CStringGetDatum("")), values, nulls, updated);
 	}
 
 	/* build column number array if it's a column-specific trigger */
@@ -961,24 +956,23 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 		}
 	}
 	tgattr = buildint2vector(columns, ncolumns);
-	values[Anum_pg_trigger_tgattr - 1] = PointerGetDatum(tgattr);
+	HeapTupleUpdateValue(pg_trigger, tgattr, PointerGetDatum(tgattr), values, nulls, updated);
 
 	/* set tgqual if trigger has WHEN clause */
 	if (qual)
-		values[Anum_pg_trigger_tgqual - 1] = CStringGetTextDatum(qual);
+		HeapTupleUpdateValue(pg_trigger, tgqual, CStringGetTextDatum(qual), values, nulls, updated);
 	else
-		nulls[Anum_pg_trigger_tgqual - 1] = true;
+		HeapTupleUpdateValueNull(pg_trigger, tgqual, values, nulls, updated);
 
 	if (oldtablename)
-		values[Anum_pg_trigger_tgoldtable - 1] = DirectFunctionCall1(namein,
-																	 CStringGetDatum(oldtablename));
+		HeapTupleUpdateValue(pg_trigger, tgoldtable, DirectFunctionCall1(namein, CStringGetDatum(oldtablename)), values, nulls, updated);
 	else
-		nulls[Anum_pg_trigger_tgoldtable - 1] = true;
+		HeapTupleUpdateValueNull(pg_trigger, tgoldtable, values, nulls, updated);
+
 	if (newtablename)
-		values[Anum_pg_trigger_tgnewtable - 1] = DirectFunctionCall1(namein,
-																	 CStringGetDatum(newtablename));
+		HeapTupleUpdateValue(pg_trigger, tgnewtable, DirectFunctionCall1(namein, CStringGetDatum(newtablename)), values, nulls, updated);
 	else
-		nulls[Anum_pg_trigger_tgnewtable - 1] = true;
+		HeapTupleUpdateValueNull(pg_trigger, tgnewtable, values, nulls, updated);
 
 	/*
 	 * Insert or replace tuple in pg_trigger.
@@ -986,27 +980,29 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 	if (!trigger_exists)
 	{
 		tuple = heap_form_tuple(tgrel->rd_att, values, nulls);
-		CatalogTupleInsert(tgrel, tuple);
+		CatalogTupleInsert(tgrel, tuple, NULL);
 	}
 	else
 	{
 		HeapTuple	newtup;
 
 		newtup = heap_form_tuple(tgrel->rd_att, values, nulls);
-		CatalogTupleUpdate(tgrel, &tuple->t_self, newtup);
+		CatalogTupleUpdate(tgrel, &tuple->t_self, newtup, updated, NULL);
 		heap_freetuple(newtup);
 	}
 
 	heap_freetuple(tuple);		/* free either original or new tuple */
 	table_close(tgrel, RowExclusiveLock);
+	bms_free(updated);
+	updated = NULL;
 
-	pfree(DatumGetPointer(values[Anum_pg_trigger_tgname - 1]));
-	pfree(DatumGetPointer(values[Anum_pg_trigger_tgargs - 1]));
-	pfree(DatumGetPointer(values[Anum_pg_trigger_tgattr - 1]));
+	pfree(DatumGetPointer(HeapTupleValue(pg_trigger, tgname, values)));
+	pfree(DatumGetPointer(HeapTupleValue(pg_trigger, tgargs, values)));
+	pfree(DatumGetPointer(HeapTupleValue(pg_trigger, tgattr, values)));
 	if (oldtablename)
-		pfree(DatumGetPointer(values[Anum_pg_trigger_tgoldtable - 1]));
+		pfree(DatumGetPointer(HeapTupleValue(pg_trigger, tgoldtable, values)));
 	if (newtablename)
-		pfree(DatumGetPointer(values[Anum_pg_trigger_tgnewtable - 1]));
+		pfree(DatumGetPointer(HeapTupleValue(pg_trigger, tgnewtable, values)));
 
 	/*
 	 * Update relation's pg_class entry; if necessary; and if not, send an SI
@@ -1020,9 +1016,9 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 			 RelationGetRelid(rel));
 	if (!((Form_pg_class) GETSTRUCT(tuple))->relhastriggers)
 	{
-		((Form_pg_class) GETSTRUCT(tuple))->relhastriggers = true;
-
-		CatalogTupleUpdate(pgrel, &tuple->t_self, tuple);
+		HeapTupleUpdateField(pg_class, relhastriggers, true, (Form_pg_class) GETSTRUCT(tuple), updated);
+		CatalogTupleUpdate(pgrel, &tuple->t_self, tuple, updated, NULL);
+		bms_free(updated);
 
 		CommandCounterIncrement();
 	}
@@ -1229,6 +1225,7 @@ TriggerSetParentTrigger(Relation trigRel,
 				newtup;
 	ObjectAddress depender;
 	ObjectAddress referenced;
+	Bitmapset  *updated = NULL;
 
 	/*
 	 * Find the trigger to delete.
@@ -1253,9 +1250,9 @@ TriggerSetParentTrigger(Relation trigRel,
 			elog(ERROR, "trigger %u already has a parent trigger",
 				 childTrigId);
 
-		trigForm->tgparentid = parentTrigId;
+		HeapTupleUpdateField(pg_trigger, tgparentid, parentTrigId, trigForm, updated);
 
-		CatalogTupleUpdate(trigRel, &tuple->t_self, newtup);
+		CatalogTupleUpdate(trigRel, &tuple->t_self, newtup, updated, NULL);
 
 		ObjectAddressSet(depender, TriggerRelationId, childTrigId);
 
@@ -1267,9 +1264,9 @@ TriggerSetParentTrigger(Relation trigRel,
 	}
 	else
 	{
-		trigForm->tgparentid = InvalidOid;
+		HeapTupleUpdateField(pg_trigger, tgparentid, InvalidOid, trigForm, updated);
 
-		CatalogTupleUpdate(trigRel, &tuple->t_self, newtup);
+		CatalogTupleUpdate(trigRel, &tuple->t_self, newtup, updated, NULL);
 
 		deleteDependencyRecordsForClass(TriggerRelationId, childTrigId,
 										TriggerRelationId,
@@ -1281,6 +1278,7 @@ TriggerSetParentTrigger(Relation trigRel,
 
 	heap_freetuple(newtup);
 	systable_endscan(tgscan);
+	bms_free(updated);
 }
 
 
@@ -1586,6 +1584,7 @@ renametrig_internal(Relation tgrel, Relation targetrel, HeapTuple trigtup,
 	Form_pg_trigger tgform;
 	ScanKeyData key[2];
 	SysScanDesc tgscan;
+	Bitmapset  *updated = NULL;
 
 	/* If the trigger already has the new name, nothing to do. */
 	tgform = (Form_pg_trigger) GETSTRUCT(trigtup);
@@ -1632,8 +1631,8 @@ renametrig_internal(Relation tgrel, Relation targetrel, HeapTuple trigtup,
 					   RelationGetRelationName(targetrel)));
 
 	namestrcpy(&tgform->tgname, newname);
-
-	CatalogTupleUpdate(tgrel, &tuple->t_self, tuple);
+	HeapTupleMarkColumnUpdated(pg_trigger, tgname, updated);
+	CatalogTupleUpdate(tgrel, &tuple->t_self, tuple, updated, NULL);
 
 	InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
 
@@ -1643,6 +1642,7 @@ renametrig_internal(Relation tgrel, Relation targetrel, HeapTuple trigtup,
 	 * (Ideally this should happen automatically...)
 	 */
 	CacheInvalidateRelcache(targetrel);
+	bms_free(updated);
 }
 
 /*
@@ -1784,11 +1784,12 @@ EnableDisableTrigger(Relation rel, const char *tgname, Oid tgparent,
 			/* need to change this one ... make a copy to scribble on */
 			HeapTuple	newtup = heap_copytuple(tuple);
 			Form_pg_trigger newtrig = (Form_pg_trigger) GETSTRUCT(newtup);
+			Bitmapset  *updated = NULL;
 
-			newtrig->tgenabled = fires_when;
-
-			CatalogTupleUpdate(tgrel, &newtup->t_self, newtup);
+			HeapTupleUpdateField(pg_trigger, tgenabled, fires_when, newtrig, updated);
+			CatalogTupleUpdate(tgrel, &newtup->t_self, newtup, updated, NULL);
 
+			bms_free(updated);
 			heap_freetuple(newtup);
 
 			changed = true;
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index a12ce33bae4..3526558b923 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -187,8 +187,8 @@ DefineTSParser(List *names, List *parameters)
 	ListCell   *pl;
 	Relation	prsRel;
 	HeapTuple	tup;
-	Datum		values[Natts_pg_ts_parser];
-	bool		nulls[Natts_pg_ts_parser];
+	Datum		values[Natts_pg_ts_parser] = {0};
+	bool		nulls[Natts_pg_ts_parser] = {false};
 	NameData	pname;
 	Oid			prsOid;
 	Oid			namespaceoid;
@@ -210,10 +210,10 @@ DefineTSParser(List *names, List *parameters)
 
 	prsOid = GetNewOidWithIndex(prsRel, TSParserOidIndexId,
 								Anum_pg_ts_parser_oid);
-	values[Anum_pg_ts_parser_oid - 1] = ObjectIdGetDatum(prsOid);
+	HeapTupleSetValue(pg_ts_parser, oid, ObjectIdGetDatum(prsOid), values);
 	namestrcpy(&pname, prsname);
-	values[Anum_pg_ts_parser_prsname - 1] = NameGetDatum(&pname);
-	values[Anum_pg_ts_parser_prsnamespace - 1] = ObjectIdGetDatum(namespaceoid);
+	HeapTupleSetValue(pg_ts_parser, prsname, NameGetDatum(&pname), values);
+	HeapTupleSetValue(pg_ts_parser, prsnamespace, ObjectIdGetDatum(namespaceoid), values);
 
 	/*
 	 * loop over the definition list and extract the information we need.
@@ -224,28 +224,23 @@ DefineTSParser(List *names, List *parameters)
 
 		if (strcmp(defel->defname, "start") == 0)
 		{
-			values[Anum_pg_ts_parser_prsstart - 1] =
-				get_ts_parser_func(defel, Anum_pg_ts_parser_prsstart);
+			HeapTupleSetValue(pg_ts_parser, prsstart, get_ts_parser_func(defel, Anum_pg_ts_parser_prsstart), values);
 		}
 		else if (strcmp(defel->defname, "gettoken") == 0)
 		{
-			values[Anum_pg_ts_parser_prstoken - 1] =
-				get_ts_parser_func(defel, Anum_pg_ts_parser_prstoken);
+			HeapTupleSetValue(pg_ts_parser, prstoken, get_ts_parser_func(defel, Anum_pg_ts_parser_prstoken), values);
 		}
 		else if (strcmp(defel->defname, "end") == 0)
 		{
-			values[Anum_pg_ts_parser_prsend - 1] =
-				get_ts_parser_func(defel, Anum_pg_ts_parser_prsend);
+			HeapTupleSetValue(pg_ts_parser, prsend, get_ts_parser_func(defel, Anum_pg_ts_parser_prsend), values);
 		}
 		else if (strcmp(defel->defname, "headline") == 0)
 		{
-			values[Anum_pg_ts_parser_prsheadline - 1] =
-				get_ts_parser_func(defel, Anum_pg_ts_parser_prsheadline);
+			HeapTupleSetValue(pg_ts_parser, prsheadline, get_ts_parser_func(defel, Anum_pg_ts_parser_prsheadline), values);
 		}
 		else if (strcmp(defel->defname, "lextypes") == 0)
 		{
-			values[Anum_pg_ts_parser_prslextype - 1] =
-				get_ts_parser_func(defel, Anum_pg_ts_parser_prslextype);
+			HeapTupleSetValue(pg_ts_parser, prslextype, get_ts_parser_func(defel, Anum_pg_ts_parser_prslextype), values);
 		}
 		else
 			ereport(ERROR,
@@ -257,22 +252,22 @@ DefineTSParser(List *names, List *parameters)
 	/*
 	 * Validation
 	 */
-	if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_parser_prsstart - 1])))
+	if (!OidIsValid(DatumGetObjectId(HeapTupleValue(pg_ts_parser, prsstart, values))))
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 				 errmsg("text search parser start method is required")));
 
-	if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_parser_prstoken - 1])))
+	if (!OidIsValid(DatumGetObjectId(HeapTupleValue(pg_ts_parser, prstoken, values))))
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 				 errmsg("text search parser gettoken method is required")));
 
-	if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_parser_prsend - 1])))
+	if (!OidIsValid(DatumGetObjectId(HeapTupleValue(pg_ts_parser, prsend, values))))
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 				 errmsg("text search parser end method is required")));
 
-	if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_parser_prslextype - 1])))
+	if (!OidIsValid(DatumGetObjectId(HeapTupleValue(pg_ts_parser, prslextype, values))))
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 				 errmsg("text search parser lextypes method is required")));
@@ -282,7 +277,7 @@ DefineTSParser(List *names, List *parameters)
 	 */
 	tup = heap_form_tuple(prsRel->rd_att, values, nulls);
 
-	CatalogTupleInsert(prsRel, tup);
+	CatalogTupleInsert(prsRel, tup, NULL);
 
 	address = makeParserDependencies(tup);
 
@@ -399,8 +394,8 @@ DefineTSDictionary(List *names, List *parameters)
 	ListCell   *pl;
 	Relation	dictRel;
 	HeapTuple	tup;
-	Datum		values[Natts_pg_ts_dict];
-	bool		nulls[Natts_pg_ts_dict];
+	Datum		values[Natts_pg_ts_dict] = {0};
+	bool		nulls[Natts_pg_ts_dict] = {false};
 	NameData	dname;
 	Oid			templId = InvalidOid;
 	List	   *dictoptions = NIL;
@@ -458,21 +453,20 @@ DefineTSDictionary(List *names, List *parameters)
 
 	dictOid = GetNewOidWithIndex(dictRel, TSDictionaryOidIndexId,
 								 Anum_pg_ts_dict_oid);
-	values[Anum_pg_ts_dict_oid - 1] = ObjectIdGetDatum(dictOid);
+	HeapTupleSetValue(pg_ts_dict, oid, ObjectIdGetDatum(dictOid), values);
 	namestrcpy(&dname, dictname);
-	values[Anum_pg_ts_dict_dictname - 1] = NameGetDatum(&dname);
-	values[Anum_pg_ts_dict_dictnamespace - 1] = ObjectIdGetDatum(namespaceoid);
-	values[Anum_pg_ts_dict_dictowner - 1] = ObjectIdGetDatum(GetUserId());
-	values[Anum_pg_ts_dict_dicttemplate - 1] = ObjectIdGetDatum(templId);
+	HeapTupleSetValue(pg_ts_dict, dictname, NameGetDatum(&dname), values);
+	HeapTupleSetValue(pg_ts_dict, dictnamespace, ObjectIdGetDatum(namespaceoid), values);
+	HeapTupleSetValue(pg_ts_dict, dictowner, ObjectIdGetDatum(GetUserId()), values);
+	HeapTupleSetValue(pg_ts_dict, dicttemplate, ObjectIdGetDatum(templId), values);
 	if (dictoptions)
-		values[Anum_pg_ts_dict_dictinitoption - 1] =
-			PointerGetDatum(serialize_deflist(dictoptions));
+		HeapTupleSetValue(pg_ts_dict, dictinitoption, PointerGetDatum(serialize_deflist(dictoptions)), values);
 	else
-		nulls[Anum_pg_ts_dict_dictinitoption - 1] = true;
+		HeapTupleSetValueNull(pg_ts_dict, dictinitoption, values, nulls);
 
 	tup = heap_form_tuple(dictRel->rd_att, values, nulls);
 
-	CatalogTupleInsert(dictRel, tup);
+	CatalogTupleInsert(dictRel, tup, NULL);
 
 	address = makeDictionaryDependencies(tup);
 
@@ -500,9 +494,9 @@ AlterTSDictionary(AlterTSDictionaryStmt *stmt)
 	List	   *dictoptions;
 	Datum		opt;
 	bool		isnull;
-	Datum		repl_val[Natts_pg_ts_dict];
-	bool		repl_null[Natts_pg_ts_dict];
-	bool		repl_repl[Natts_pg_ts_dict];
+	Datum		values[Natts_pg_ts_dict] = {0};
+	bool		nulls[Natts_pg_ts_dict] = {false};
+	Bitmapset  *updated = NULL;
 	ObjectAddress address;
 
 	dictId = get_ts_dict_oid(stmt->dictname, false);
@@ -564,21 +558,15 @@ AlterTSDictionary(AlterTSDictionaryStmt *stmt)
 	/*
 	 * Looks good, update
 	 */
-	memset(repl_val, 0, sizeof(repl_val));
-	memset(repl_null, false, sizeof(repl_null));
-	memset(repl_repl, false, sizeof(repl_repl));
-
 	if (dictoptions)
-		repl_val[Anum_pg_ts_dict_dictinitoption - 1] =
-			PointerGetDatum(serialize_deflist(dictoptions));
+		HeapTupleUpdateValue(pg_ts_dict, dictinitoption, PointerGetDatum(serialize_deflist(dictoptions)), values, nulls, updated);
 	else
-		repl_null[Anum_pg_ts_dict_dictinitoption - 1] = true;
-	repl_repl[Anum_pg_ts_dict_dictinitoption - 1] = true;
+		HeapTupleUpdateValueNull(pg_ts_dict, dictinitoption, values, nulls, updated);
 
-	newtup = heap_modify_tuple(tup, RelationGetDescr(rel),
-							   repl_val, repl_null, repl_repl);
+	newtup = heap_update_tuple(tup, RelationGetDescr(rel),
+							   values, nulls, updated);
 
-	CatalogTupleUpdate(rel, &newtup->t_self, newtup);
+	CatalogTupleUpdate(rel, &newtup->t_self, newtup, updated, NULL);
 
 	InvokeObjectPostAlterHook(TSDictionaryRelationId, dictId, 0);
 
@@ -594,6 +582,7 @@ AlterTSDictionary(AlterTSDictionaryStmt *stmt)
 	ReleaseSysCache(tup);
 
 	table_close(rel, RowExclusiveLock);
+	bms_free(updated);
 
 	return address;
 }
@@ -692,8 +681,8 @@ DefineTSTemplate(List *names, List *parameters)
 	ListCell   *pl;
 	Relation	tmplRel;
 	HeapTuple	tup;
-	Datum		values[Natts_pg_ts_template];
-	bool		nulls[Natts_pg_ts_template];
+	Datum		values[Natts_pg_ts_template] = {0};
+	bool		nulls[Natts_pg_ts_template] = {false};
 	NameData	dname;
 	int			i;
 	Oid			tmplOid;
@@ -719,10 +708,10 @@ DefineTSTemplate(List *names, List *parameters)
 
 	tmplOid = GetNewOidWithIndex(tmplRel, TSTemplateOidIndexId,
 								 Anum_pg_ts_dict_oid);
-	values[Anum_pg_ts_template_oid - 1] = ObjectIdGetDatum(tmplOid);
+	HeapTupleSetValue(pg_ts_template, oid, ObjectIdGetDatum(tmplOid), values);
 	namestrcpy(&dname, tmplname);
-	values[Anum_pg_ts_template_tmplname - 1] = NameGetDatum(&dname);
-	values[Anum_pg_ts_template_tmplnamespace - 1] = ObjectIdGetDatum(namespaceoid);
+	HeapTupleSetValue(pg_ts_template, tmplname, NameGetDatum(&dname), values);
+	HeapTupleSetValue(pg_ts_template, tmplnamespace, ObjectIdGetDatum(namespaceoid), values);
 
 	/*
 	 * loop over the definition list and extract the information we need.
@@ -733,15 +722,11 @@ DefineTSTemplate(List *names, List *parameters)
 
 		if (strcmp(defel->defname, "init") == 0)
 		{
-			values[Anum_pg_ts_template_tmplinit - 1] =
-				get_ts_template_func(defel, Anum_pg_ts_template_tmplinit);
-			nulls[Anum_pg_ts_template_tmplinit - 1] = false;
+			HeapTupleSetValue(pg_ts_template, tmplinit, get_ts_template_func(defel, Anum_pg_ts_template_tmplinit), values);
 		}
 		else if (strcmp(defel->defname, "lexize") == 0)
 		{
-			values[Anum_pg_ts_template_tmpllexize - 1] =
-				get_ts_template_func(defel, Anum_pg_ts_template_tmpllexize);
-			nulls[Anum_pg_ts_template_tmpllexize - 1] = false;
+			HeapTupleSetValue(pg_ts_template, tmpllexize, get_ts_template_func(defel, Anum_pg_ts_template_tmpllexize), values);
 		}
 		else
 			ereport(ERROR,
@@ -753,7 +738,7 @@ DefineTSTemplate(List *names, List *parameters)
 	/*
 	 * Validation
 	 */
-	if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_template_tmpllexize - 1])))
+	if (!OidIsValid(DatumGetObjectId(HeapTupleValue(pg_ts_template, tmpllexize, values))))
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 				 errmsg("text search template lexize method is required")));
@@ -763,7 +748,7 @@ DefineTSTemplate(List *names, List *parameters)
 	 */
 	tup = heap_form_tuple(tmplRel->rd_att, values, nulls);
 
-	CatalogTupleInsert(tmplRel, tup);
+	CatalogTupleInsert(tmplRel, tup, NULL);
 
 	address = makeTSTemplateDependencies(tup);
 
@@ -901,8 +886,8 @@ DefineTSConfiguration(List *names, List *parameters, ObjectAddress *copied)
 	Relation	cfgRel;
 	Relation	mapRel = NULL;
 	HeapTuple	tup;
-	Datum		values[Natts_pg_ts_config];
-	bool		nulls[Natts_pg_ts_config];
+	Datum		values[Natts_pg_ts_config] = {0};
+	bool		nulls[Natts_pg_ts_config] = {false};
 	AclResult	aclresult;
 	Oid			namespaceoid;
 	char	   *cfgname;
@@ -991,16 +976,16 @@ DefineTSConfiguration(List *names, List *parameters, ObjectAddress *copied)
 
 	cfgOid = GetNewOidWithIndex(cfgRel, TSConfigOidIndexId,
 								Anum_pg_ts_config_oid);
-	values[Anum_pg_ts_config_oid - 1] = ObjectIdGetDatum(cfgOid);
+	HeapTupleSetValue(pg_ts_config, oid, ObjectIdGetDatum(cfgOid), values);
 	namestrcpy(&cname, cfgname);
-	values[Anum_pg_ts_config_cfgname - 1] = NameGetDatum(&cname);
-	values[Anum_pg_ts_config_cfgnamespace - 1] = ObjectIdGetDatum(namespaceoid);
-	values[Anum_pg_ts_config_cfgowner - 1] = ObjectIdGetDatum(GetUserId());
-	values[Anum_pg_ts_config_cfgparser - 1] = ObjectIdGetDatum(prsOid);
+	HeapTupleSetValue(pg_ts_config, cfgname, NameGetDatum(&cname), values);
+	HeapTupleSetValue(pg_ts_config, cfgnamespace, ObjectIdGetDatum(namespaceoid), values);
+	HeapTupleSetValue(pg_ts_config, cfgowner, ObjectIdGetDatum(GetUserId()), values);
+	HeapTupleSetValue(pg_ts_config, cfgparser, ObjectIdGetDatum(prsOid), values);
 
 	tup = heap_form_tuple(cfgRel->rd_att, values, nulls);
 
-	CatalogTupleInsert(cfgRel, tup);
+	CatalogTupleInsert(cfgRel, tup, NULL);
 
 	if (OidIsValid(sourceOid))
 	{
@@ -1058,10 +1043,10 @@ DefineTSConfiguration(List *names, List *parameters, ObjectAddress *copied)
 			memset(slot[slot_stored_count]->tts_isnull, false,
 				   slot[slot_stored_count]->tts_tupleDescriptor->natts * sizeof(bool));
 
-			slot[slot_stored_count]->tts_values[Anum_pg_ts_config_map_mapcfg - 1] = ObjectIdGetDatum(cfgOid);
-			slot[slot_stored_count]->tts_values[Anum_pg_ts_config_map_maptokentype - 1] = Int32GetDatum(cfgmap->maptokentype);
-			slot[slot_stored_count]->tts_values[Anum_pg_ts_config_map_mapseqno - 1] = Int32GetDatum(cfgmap->mapseqno);
-			slot[slot_stored_count]->tts_values[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(cfgmap->mapdict);
+			HeapTupleSetValue(pg_ts_config_map, mapcfg, ObjectIdGetDatum(cfgOid), slot[slot_stored_count]->tts_values);
+			HeapTupleSetValue(pg_ts_config_map, maptokentype, Int32GetDatum(cfgmap->maptokentype), slot[slot_stored_count]->tts_values);
+			HeapTupleSetValue(pg_ts_config_map, mapseqno, Int32GetDatum(cfgmap->mapseqno), slot[slot_stored_count]->tts_values);
+			HeapTupleSetValue(pg_ts_config_map, mapdict, ObjectIdGetDatum(cfgmap->mapdict), slot[slot_stored_count]->tts_values);
 
 			ExecStoreVirtualTuple(slot[slot_stored_count]);
 			slot_stored_count++;
@@ -1069,16 +1054,16 @@ DefineTSConfiguration(List *names, List *parameters, ObjectAddress *copied)
 			/* If slots are full, insert a batch of tuples */
 			if (slot_stored_count == max_slots)
 			{
-				CatalogTuplesMultiInsertWithInfo(mapRel, slot, slot_stored_count,
-												 indstate);
+				CatalogTuplesMultiInsert(mapRel, slot, slot_stored_count,
+										 indstate);
 				slot_stored_count = 0;
 			}
 		}
 
 		/* Insert any tuples left in the buffer */
 		if (slot_stored_count > 0)
-			CatalogTuplesMultiInsertWithInfo(mapRel, slot, slot_stored_count,
-											 indstate);
+			CatalogTuplesMultiInsert(mapRel, slot, slot_stored_count,
+									 indstate);
 
 		for (int i = 0; i < slot_init_count; i++)
 			ExecDropSingleTupleTableSlot(slot[i]);
@@ -1402,22 +1387,18 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
 			 */
 			if (cfgmap->mapdict == dictOld)
 			{
-				Datum		repl_val[Natts_pg_ts_config_map];
-				bool		repl_null[Natts_pg_ts_config_map];
-				bool		repl_repl[Natts_pg_ts_config_map];
+				Datum		values[Natts_pg_ts_config_map] = {0};
+				bool		nulls[Natts_pg_ts_config_map] = {false};
+				Bitmapset  *updated = NULL;
 				HeapTuple	newtup;
 
-				memset(repl_val, 0, sizeof(repl_val));
-				memset(repl_null, false, sizeof(repl_null));
-				memset(repl_repl, false, sizeof(repl_repl));
-
-				repl_val[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictNew);
-				repl_repl[Anum_pg_ts_config_map_mapdict - 1] = true;
+				HeapTupleUpdateValue(pg_ts_config_map, mapdict, ObjectIdGetDatum(dictNew), values, nulls, updated);
 
-				newtup = heap_modify_tuple(maptup,
+				newtup = heap_update_tuple(maptup,
 										   RelationGetDescr(relMap),
-										   repl_val, repl_null, repl_repl);
-				CatalogTupleUpdateWithInfo(relMap, &newtup->t_self, newtup, indstate);
+										   values, nulls, updated);
+				CatalogTupleUpdate(relMap, &newtup->t_self, newtup, updated, indstate);
+				bms_free(updated);
 			}
 		}
 
@@ -1451,10 +1432,10 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
 				memset(slot[slotCount]->tts_isnull, false,
 					   slot[slotCount]->tts_tupleDescriptor->natts * sizeof(bool));
 
-				slot[slotCount]->tts_values[Anum_pg_ts_config_map_mapcfg - 1] = ObjectIdGetDatum(cfgId);
-				slot[slotCount]->tts_values[Anum_pg_ts_config_map_maptokentype - 1] = Int32GetDatum(ts->num);
-				slot[slotCount]->tts_values[Anum_pg_ts_config_map_mapseqno - 1] = Int32GetDatum(j + 1);
-				slot[slotCount]->tts_values[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictIds[j]);
+				HeapTupleSetValue(pg_ts_config_map, mapcfg, ObjectIdGetDatum(cfgId), slot[slotCount]->tts_values);
+				HeapTupleSetValue(pg_ts_config_map, maptokentype, Int32GetDatum(ts->num), slot[slotCount]->tts_values);
+				HeapTupleSetValue(pg_ts_config_map, mapseqno, Int32GetDatum(j + 1), slot[slotCount]->tts_values);
+				HeapTupleSetValue(pg_ts_config_map, mapdict, ObjectIdGetDatum(dictIds[j]), slot[slotCount]->tts_values);
 
 				ExecStoreVirtualTuple(slot[slotCount]);
 				slotCount++;
@@ -1462,8 +1443,8 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
 				/* If slots are full, insert a batch of tuples */
 				if (slotCount == nslots)
 				{
-					CatalogTuplesMultiInsertWithInfo(relMap, slot, slotCount,
-													 indstate);
+					CatalogTuplesMultiInsert(relMap, slot, slotCount,
+											 indstate);
 					slotCount = 0;
 				}
 			}
@@ -1471,8 +1452,8 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
 
 		/* Insert any tuples left in the buffer */
 		if (slotCount > 0)
-			CatalogTuplesMultiInsertWithInfo(relMap, slot, slotCount,
-											 indstate);
+			CatalogTuplesMultiInsert(relMap, slot, slotCount,
+									 indstate);
 
 		for (i = 0; i < nslots; i++)
 			ExecDropSingleTupleTableSlot(slot[i]);
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index e5fa0578889..ad2acdb575c 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2623,9 +2623,9 @@ AlterDomainDefault(List *names, Node *defaultRaw)
 	Relation	rel;
 	char	   *defaultValue;
 	Node	   *defaultExpr = NULL; /* NULL if no default specified */
-	Datum		new_record[Natts_pg_type] = {0};
-	bool		new_record_nulls[Natts_pg_type] = {0};
-	bool		new_record_repl[Natts_pg_type] = {0};
+	Datum		values[Natts_pg_type] = {0};
+	bool		nulls[Natts_pg_type] = {false};
+	Bitmapset  *updated = NULL;
 	HeapTuple	newtuple;
 	Form_pg_type typTup;
 	ObjectAddress address;
@@ -2673,10 +2673,8 @@ AlterDomainDefault(List *names, Node *defaultRaw)
 		{
 			/* Default is NULL, drop it */
 			defaultExpr = NULL;
-			new_record_nulls[Anum_pg_type_typdefaultbin - 1] = true;
-			new_record_repl[Anum_pg_type_typdefaultbin - 1] = true;
-			new_record_nulls[Anum_pg_type_typdefault - 1] = true;
-			new_record_repl[Anum_pg_type_typdefault - 1] = true;
+			HeapTupleUpdateValueNull(pg_type, typdefaultbin, values, nulls, updated);
+			HeapTupleUpdateValueNull(pg_type, typdefault, values, nulls, updated);
 		}
 		else
 		{
@@ -2691,27 +2689,21 @@ AlterDomainDefault(List *names, Node *defaultRaw)
 			/*
 			 * Form an updated tuple with the new default and write it back.
 			 */
-			new_record[Anum_pg_type_typdefaultbin - 1] = CStringGetTextDatum(nodeToString(defaultExpr));
-
-			new_record_repl[Anum_pg_type_typdefaultbin - 1] = true;
-			new_record[Anum_pg_type_typdefault - 1] = CStringGetTextDatum(defaultValue);
-			new_record_repl[Anum_pg_type_typdefault - 1] = true;
+			HeapTupleUpdateValue(pg_type, typdefaultbin, CStringGetTextDatum(nodeToString(defaultExpr)), values, nulls, updated);
+			HeapTupleUpdateValue(pg_type, typdefault, CStringGetTextDatum(defaultValue), values, nulls, updated);
 		}
 	}
 	else
 	{
 		/* ALTER ... DROP DEFAULT */
-		new_record_nulls[Anum_pg_type_typdefaultbin - 1] = true;
-		new_record_repl[Anum_pg_type_typdefaultbin - 1] = true;
-		new_record_nulls[Anum_pg_type_typdefault - 1] = true;
-		new_record_repl[Anum_pg_type_typdefault - 1] = true;
+		HeapTupleUpdateValueNull(pg_type, typdefaultbin, values, nulls, updated);
+		HeapTupleUpdateValueNull(pg_type, typdefault, values, nulls, updated);
 	}
 
-	newtuple = heap_modify_tuple(tup, RelationGetDescr(rel),
-								 new_record, new_record_nulls,
-								 new_record_repl);
+	newtuple = heap_update_tuple(tup, RelationGetDescr(rel),
+								 values, nulls, updated);
 
-	CatalogTupleUpdate(rel, &tup->t_self, newtuple);
+	CatalogTupleUpdate(rel, &tup->t_self, newtuple, updated, NULL);
 
 	/* Rebuild dependencies */
 	GenerateTypeDependencies(newtuple,
@@ -2731,6 +2723,7 @@ AlterDomainDefault(List *names, Node *defaultRaw)
 	/* Clean up */
 	table_close(rel, RowExclusiveLock);
 	heap_freetuple(newtuple);
+	bms_free(updated);
 
 	return address;
 }
@@ -2751,6 +2744,7 @@ AlterDomainNotNull(List *names, bool notNull)
 	HeapTuple	tup;
 	Form_pg_type typTup;
 	ObjectAddress address = InvalidObjectAddress;
+	Bitmapset  *updated = NULL;
 
 	/* Make a TypeName so we can use standard type lookup machinery */
 	typename = makeTypeNameFromNameList(names);
@@ -2806,9 +2800,9 @@ AlterDomainNotNull(List *names, bool notNull)
 	 * Okay to update pg_type row.  We can scribble on typTup because it's a
 	 * copy.
 	 */
-	typTup->typnotnull = notNull;
+	HeapTupleUpdateField(pg_type, typnotnull, notNull, typTup, updated);
 
-	CatalogTupleUpdate(typrel, &tup->t_self, tup);
+	CatalogTupleUpdate(typrel, &tup->t_self, tup, updated, NULL);
 
 	InvokeObjectPostAlterHook(TypeRelationId, domainoid, 0);
 
@@ -2817,6 +2811,7 @@ AlterDomainNotNull(List *names, bool notNull)
 	/* Clean up */
 	heap_freetuple(tup);
 	table_close(typrel, RowExclusiveLock);
+	bms_free(updated);
 
 	return address;
 }
@@ -2842,6 +2837,7 @@ AlterDomainDropConstraint(List *names, const char *constrName,
 	HeapTuple	contup;
 	bool		found = false;
 	ObjectAddress address;
+	Bitmapset  *updated = NULL;
 
 	/* Make a TypeName so we can use standard type lookup machinery */
 	typename = makeTypeNameFromNameList(names);
@@ -2885,8 +2881,8 @@ AlterDomainDropConstraint(List *names, const char *constrName,
 
 		if (construct->contype == CONSTRAINT_NOTNULL)
 		{
-			((Form_pg_type) GETSTRUCT(tup))->typnotnull = false;
-			CatalogTupleUpdate(rel, &tup->t_self, tup);
+			HeapTupleUpdateField(pg_type, typnotnull, false, (Form_pg_type) GETSTRUCT(tup), updated);
+			CatalogTupleUpdate(rel, &tup->t_self, tup, updated, NULL);
 		}
 
 		conobj.classId = ConstraintRelationId;
@@ -2925,6 +2921,7 @@ AlterDomainDropConstraint(List *names, const char *constrName,
 
 	/* Clean up */
 	table_close(rel, RowExclusiveLock);
+	bms_free(updated);
 
 	return address;
 }
@@ -2946,6 +2943,7 @@ AlterDomainAddConstraint(List *names, Node *newConstraint,
 	Constraint *constr;
 	char	   *ccbin;
 	ObjectAddress address = InvalidObjectAddress;
+	Bitmapset  *updated = NULL;
 
 	/* Make a TypeName so we can use standard type lookup machinery */
 	typename = makeTypeNameFromNameList(names);
@@ -3014,14 +3012,15 @@ AlterDomainAddConstraint(List *names, Node *newConstraint,
 		if (!constr->skip_validation)
 			validateDomainNotNullConstraint(domainoid);
 
-		typTup->typnotnull = true;
-		CatalogTupleUpdate(typrel, &tup->t_self, tup);
+		HeapTupleUpdateField(pg_type, typnotnull, true, typTup, updated);
+		CatalogTupleUpdate(typrel, &tup->t_self, tup, updated, NULL);
 	}
 
 	ObjectAddressSet(address, TypeRelationId, domainoid);
 
 	/* Clean up */
 	table_close(typrel, RowExclusiveLock);
+	bms_free(updated);
 
 	return address;
 }
@@ -3048,6 +3047,7 @@ AlterDomainValidateConstraint(List *names, const char *constrName)
 	HeapTuple	copyTuple;
 	ScanKeyData skey[3];
 	ObjectAddress address;
+	Bitmapset  *updated = NULL;
 
 	/* Make a TypeName so we can use standard type lookup machinery */
 	typename = makeTypeNameFromNameList(names);
@@ -3113,8 +3113,8 @@ AlterDomainValidateConstraint(List *names, const char *constrName)
 	 */
 	copyTuple = heap_copytuple(tuple);
 	copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
-	copy_con->convalidated = true;
-	CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
+	HeapTupleUpdateField(pg_constraint, convalidated, true, copy_con, updated);
+	CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple, updated, NULL);
 
 	InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
 
@@ -3128,6 +3128,7 @@ AlterDomainValidateConstraint(List *names, const char *constrName)
 	table_close(conrel, RowExclusiveLock);
 
 	ReleaseSysCache(tup);
+	bms_free(updated);
 
 	return address;
 }
@@ -4002,9 +4003,9 @@ AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
 	Relation	rel;
 	HeapTuple	tup;
 	Form_pg_type typTup;
-	Datum		repl_val[Natts_pg_type];
-	bool		repl_null[Natts_pg_type];
-	bool		repl_repl[Natts_pg_type];
+	Datum		values[Natts_pg_type] = {0};
+	bool		nulls[Natts_pg_type] = {false};
+	Bitmapset  *updated = NULL;
 	Acl		   *newAcl;
 	Datum		aclDatum;
 	bool		isNull;
@@ -4016,11 +4017,7 @@ AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
 		elog(ERROR, "cache lookup failed for type %u", typeOid);
 	typTup = (Form_pg_type) GETSTRUCT(tup);
 
-	memset(repl_null, false, sizeof(repl_null));
-	memset(repl_repl, false, sizeof(repl_repl));
-
-	repl_repl[Anum_pg_type_typowner - 1] = true;
-	repl_val[Anum_pg_type_typowner - 1] = ObjectIdGetDatum(newOwnerId);
+	HeapTupleUpdateValue(pg_type, typowner, ObjectIdGetDatum(newOwnerId), values, nulls, updated);
 
 	aclDatum = heap_getattr(tup,
 							Anum_pg_type_typacl,
@@ -4031,14 +4028,12 @@ AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
 	{
 		newAcl = aclnewowner(DatumGetAclP(aclDatum),
 							 typTup->typowner, newOwnerId);
-		repl_repl[Anum_pg_type_typacl - 1] = true;
-		repl_val[Anum_pg_type_typacl - 1] = PointerGetDatum(newAcl);
+		HeapTupleUpdateValue(pg_type, typacl, PointerGetDatum(newAcl), values, nulls, updated);
 	}
 
-	tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
-							repl_repl);
+	tup = heap_update_tuple(tup, RelationGetDescr(rel), values, nulls, updated);
 
-	CatalogTupleUpdate(rel, &tup->t_self, tup);
+	CatalogTupleUpdate(rel, &tup->t_self, tup, updated, NULL);
 
 	/* If it has an array type, update that too */
 	if (OidIsValid(typTup->typarray))
@@ -4059,6 +4054,7 @@ AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
 
 	/* Clean up */
 	table_close(rel, RowExclusiveLock);
+	bms_free(updated);
 }
 
 /*
@@ -4179,6 +4175,7 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
 	Oid			arrayOid;
 	bool		isCompositeType;
 	ObjectAddress thisobj;
+	Bitmapset  *updated = NULL;
 
 	/*
 	 * Make sure we haven't moved this object previously.
@@ -4244,9 +4241,9 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
 		/* OK, modify the pg_type row */
 
 		/* tup is a copy, so we can scribble directly on it */
-		typform->typnamespace = nspOid;
+		HeapTupleUpdateField(pg_type, typnamespace, nspOid, typform, updated);
 
-		CatalogTupleUpdate(rel, &tup->t_self, tup);
+		CatalogTupleUpdate(rel, &tup->t_self, tup, updated, NULL);
 	}
 
 	/*
@@ -4310,6 +4307,7 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
 								   true,	/* errorOnTableType */
 								   objsMoved);
 
+	bms_free(updated);
 	return oldNspOid;
 }
 
@@ -4577,9 +4575,9 @@ AlterTypeRecurse(Oid typeOid, bool isImplicitArray,
 				 HeapTuple tup, Relation catalog,
 				 AlterTypeRecurseParams *atparams)
 {
-	Datum		values[Natts_pg_type];
-	bool		nulls[Natts_pg_type];
-	bool		replaces[Natts_pg_type];
+	Datum		values[Natts_pg_type] = {0};
+	bool		nulls[Natts_pg_type] = {false};
+	Bitmapset  *updated = NULL;
 	HeapTuple	newtup;
 	SysScanDesc scan;
 	ScanKeyData key[1];
@@ -4589,50 +4587,25 @@ AlterTypeRecurse(Oid typeOid, bool isImplicitArray,
 	check_stack_depth();
 
 	/* Update the current type's tuple */
-	memset(values, 0, sizeof(values));
-	memset(nulls, 0, sizeof(nulls));
-	memset(replaces, 0, sizeof(replaces));
-
 	if (atparams->updateStorage)
-	{
-		replaces[Anum_pg_type_typstorage - 1] = true;
-		values[Anum_pg_type_typstorage - 1] = CharGetDatum(atparams->storage);
-	}
+		HeapTupleUpdateValue(pg_type, typstorage, CharGetDatum(atparams->storage), values, nulls, updated);
 	if (atparams->updateReceive)
-	{
-		replaces[Anum_pg_type_typreceive - 1] = true;
-		values[Anum_pg_type_typreceive - 1] = ObjectIdGetDatum(atparams->receiveOid);
-	}
+		HeapTupleUpdateValue(pg_type, typreceive, ObjectIdGetDatum(atparams->receiveOid), values, nulls, updated);
 	if (atparams->updateSend)
-	{
-		replaces[Anum_pg_type_typsend - 1] = true;
-		values[Anum_pg_type_typsend - 1] = ObjectIdGetDatum(atparams->sendOid);
-	}
+		HeapTupleUpdateValue(pg_type, typsend, ObjectIdGetDatum(atparams->sendOid), values, nulls, updated);
 	if (atparams->updateTypmodin)
-	{
-		replaces[Anum_pg_type_typmodin - 1] = true;
-		values[Anum_pg_type_typmodin - 1] = ObjectIdGetDatum(atparams->typmodinOid);
-	}
+		HeapTupleUpdateValue(pg_type, typmodin, ObjectIdGetDatum(atparams->typmodinOid), values, nulls, updated);
 	if (atparams->updateTypmodout)
-	{
-		replaces[Anum_pg_type_typmodout - 1] = true;
-		values[Anum_pg_type_typmodout - 1] = ObjectIdGetDatum(atparams->typmodoutOid);
-	}
+		HeapTupleUpdateValue(pg_type, typmodout, ObjectIdGetDatum(atparams->typmodoutOid), values, nulls, updated);
 	if (atparams->updateAnalyze)
-	{
-		replaces[Anum_pg_type_typanalyze - 1] = true;
-		values[Anum_pg_type_typanalyze - 1] = ObjectIdGetDatum(atparams->analyzeOid);
-	}
+		HeapTupleUpdateValue(pg_type, typanalyze, ObjectIdGetDatum(atparams->analyzeOid), values, nulls, updated);
 	if (atparams->updateSubscript)
-	{
-		replaces[Anum_pg_type_typsubscript - 1] = true;
-		values[Anum_pg_type_typsubscript - 1] = ObjectIdGetDatum(atparams->subscriptOid);
-	}
+		HeapTupleUpdateValue(pg_type, typsubscript, ObjectIdGetDatum(atparams->subscriptOid), values, nulls, updated);
 
-	newtup = heap_modify_tuple(tup, RelationGetDescr(catalog),
-							   values, nulls, replaces);
+	newtup = heap_update_tuple(tup, RelationGetDescr(catalog),
+							   values, nulls, updated);
 
-	CatalogTupleUpdate(catalog, &newtup->t_self, newtup);
+	CatalogTupleUpdate(catalog, &newtup->t_self, newtup, updated, NULL);
 
 	/* Rebuild dependencies for this type */
 	GenerateTypeDependencies(newtup,
@@ -4717,4 +4690,5 @@ AlterTypeRecurse(Oid typeOid, bool isImplicitArray,
 	}
 
 	systable_endscan(scan);
+	bms_free(updated);
 }
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 8fb9ea25db0..8609703bc63 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -135,8 +135,8 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
 	Relation	pg_authid_rel;
 	TupleDesc	pg_authid_dsc;
 	HeapTuple	tuple;
-	Datum		new_record[Natts_pg_authid] = {0};
-	bool		new_record_nulls[Natts_pg_authid] = {0};
+	Datum		values[Natts_pg_authid] = {0};
+	bool		nulls[Natts_pg_authid] = {false};
 	Oid			currentUserId = GetUserId();
 	Oid			roleid;
 	ListCell   *item;
@@ -406,15 +406,14 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
 	/*
 	 * Build a tuple to insert
 	 */
-	new_record[Anum_pg_authid_rolname - 1] =
-		DirectFunctionCall1(namein, CStringGetDatum(stmt->role));
-	new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper);
-	new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit);
-	new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole);
-	new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb);
-	new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin);
-	new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication);
-	new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
+	HeapTupleSetValue(pg_authid, rolname, DirectFunctionCall1(namein, CStringGetDatum(stmt->role)), values);
+	HeapTupleSetValue(pg_authid, rolsuper, BoolGetDatum(issuper), values);
+	HeapTupleSetValue(pg_authid, rolinherit, BoolGetDatum(inherit), values);
+	HeapTupleSetValue(pg_authid, rolcreaterole, BoolGetDatum(createrole), values);
+	HeapTupleSetValue(pg_authid, rolcreatedb, BoolGetDatum(createdb), values);
+	HeapTupleSetValue(pg_authid, rolcanlogin, BoolGetDatum(canlogin), values);
+	HeapTupleSetValue(pg_authid, rolreplication, BoolGetDatum(isreplication), values);
+	HeapTupleSetValue(pg_authid, rolconnlimit, Int32GetDatum(connlimit), values);
 
 	if (password)
 	{
@@ -438,24 +437,24 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
 		{
 			ereport(NOTICE,
 					(errmsg("empty string is not a valid password, clearing password")));
-			new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
+			HeapTupleSetValueNull(pg_authid, rolpassword, values, nulls);
 		}
 		else
 		{
 			/* Encrypt the password to the requested format. */
 			shadow_pass = encrypt_password(Password_encryption, stmt->role,
 										   password);
-			new_record[Anum_pg_authid_rolpassword - 1] =
-				CStringGetTextDatum(shadow_pass);
+			HeapTupleSetValue(pg_authid, rolpassword, CStringGetTextDatum(shadow_pass), values);
 		}
 	}
 	else
-		new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
+		HeapTupleSetValueNull(pg_authid, rolpassword, values, nulls);
 
-	new_record[Anum_pg_authid_rolvaliduntil - 1] = validUntil_datum;
-	new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
+	HeapTupleSetValue(pg_authid, rolvaliduntil, validUntil_datum, values);
+	if (validUntil_null)
+		HeapTupleSetValueNull(pg_authid, rolvaliduntil, values, nulls);
 
-	new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls);
+	HeapTupleSetValue(pg_authid, rolbypassrls, BoolGetDatum(bypassrls), values);
 
 	/*
 	 * pg_largeobject_metadata contains pg_authid.oid's, so we use the
@@ -477,14 +476,14 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
 									Anum_pg_authid_oid);
 	}
 
-	new_record[Anum_pg_authid_oid - 1] = ObjectIdGetDatum(roleid);
+	HeapTupleSetValue(pg_authid, oid, ObjectIdGetDatum(roleid), values);
 
-	tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls);
+	tuple = heap_form_tuple(pg_authid_dsc, values, nulls);
 
 	/*
 	 * Insert new record in the pg_authid table
 	 */
-	CatalogTupleInsert(pg_authid_rel, tuple);
+	CatalogTupleInsert(pg_authid_rel, tuple, NULL);
 
 	/*
 	 * Advance command counter so we can see new record; else tests in
@@ -619,9 +618,9 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
 Oid
 AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 {
-	Datum		new_record[Natts_pg_authid] = {0};
-	bool		new_record_nulls[Natts_pg_authid] = {0};
-	bool		new_record_repl[Natts_pg_authid] = {0};
+	Datum		values[Natts_pg_authid] = {0};
+	bool		nulls[Natts_pg_authid] = {false};
+	Bitmapset  *updated = NULL;
 	Relation	pg_authid_rel;
 	TupleDesc	pg_authid_dsc;
 	HeapTuple	tuple,
@@ -871,45 +870,26 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 					 errdetail("The bootstrap superuser must have the %s attribute.",
 							   "SUPERUSER")));
 
-		new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(should_be_super);
-		new_record_repl[Anum_pg_authid_rolsuper - 1] = true;
+		HeapTupleUpdateValue(pg_authid, rolsuper, BoolGetDatum(should_be_super), values, nulls, updated);
 	}
 
 	if (dinherit)
-	{
-		new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(boolVal(dinherit->arg));
-		new_record_repl[Anum_pg_authid_rolinherit - 1] = true;
-	}
+		HeapTupleUpdateValue(pg_authid, rolinherit, BoolGetDatum(boolVal(dinherit->arg)), values, nulls, updated);
 
 	if (dcreaterole)
-	{
-		new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(boolVal(dcreaterole->arg));
-		new_record_repl[Anum_pg_authid_rolcreaterole - 1] = true;
-	}
+		HeapTupleUpdateValue(pg_authid, rolcreaterole, BoolGetDatum(boolVal(dcreaterole->arg)), values, nulls, updated);
 
 	if (dcreatedb)
-	{
-		new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(boolVal(dcreatedb->arg));
-		new_record_repl[Anum_pg_authid_rolcreatedb - 1] = true;
-	}
+		HeapTupleUpdateValue(pg_authid, rolcreatedb, BoolGetDatum(boolVal(dcreatedb->arg)), values, nulls, updated);
 
 	if (dcanlogin)
-	{
-		new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(boolVal(dcanlogin->arg));
-		new_record_repl[Anum_pg_authid_rolcanlogin - 1] = true;
-	}
+		HeapTupleUpdateValue(pg_authid, rolcanlogin, BoolGetDatum(boolVal(dcanlogin->arg)), values, nulls, updated);
 
 	if (disreplication)
-	{
-		new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(boolVal(disreplication->arg));
-		new_record_repl[Anum_pg_authid_rolreplication - 1] = true;
-	}
+		HeapTupleUpdateValue(pg_authid, rolreplication, BoolGetDatum(boolVal(disreplication->arg)), values, nulls, updated);
 
 	if (dconnlimit)
-	{
-		new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
-		new_record_repl[Anum_pg_authid_rolconnlimit - 1] = true;
-	}
+		HeapTupleUpdateValue(pg_authid, rolconnlimit, Int32GetDatum(connlimit), values, nulls, updated);
 
 	/* password */
 	if (password)
@@ -923,40 +903,33 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 		{
 			ereport(NOTICE,
 					(errmsg("empty string is not a valid password, clearing password")));
-			new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
+			HeapTupleSetValueNull(pg_authid, rolpassword, values, nulls);
 		}
 		else
 		{
 			/* Encrypt the password to the requested format. */
 			shadow_pass = encrypt_password(Password_encryption, rolename,
 										   password);
-			new_record[Anum_pg_authid_rolpassword - 1] =
-				CStringGetTextDatum(shadow_pass);
+			HeapTupleUpdateValue(pg_authid, rolpassword, CStringGetTextDatum(shadow_pass), values, nulls, updated);
 		}
-		new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
 	}
 
 	/* unset password */
 	if (dpassword && dpassword->arg == NULL)
-	{
-		new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
-		new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
-	}
+		HeapTupleUpdateValueNull(pg_authid, rolpassword, values, nulls, updated);
 
 	/* valid until */
-	new_record[Anum_pg_authid_rolvaliduntil - 1] = validUntil_datum;
-	new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
-	new_record_repl[Anum_pg_authid_rolvaliduntil - 1] = true;
+	HeapTupleUpdateValue(pg_authid, rolvaliduntil, validUntil_datum, values, nulls, updated);
+	if (validUntil_null)
+		HeapTupleUpdateValueNull(pg_authid, rolvaliduntil, values, nulls, updated);
 
 	if (dbypassRLS)
-	{
-		new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(boolVal(dbypassRLS->arg));
-		new_record_repl[Anum_pg_authid_rolbypassrls - 1] = true;
-	}
+		HeapTupleUpdateValue(pg_authid, rolbypassrls, BoolGetDatum(boolVal(dbypassRLS->arg)), values, nulls, updated);
 
-	new_tuple = heap_modify_tuple(tuple, pg_authid_dsc, new_record,
-								  new_record_nulls, new_record_repl);
-	CatalogTupleUpdate(pg_authid_rel, &tuple->t_self, new_tuple);
+	new_tuple = heap_update_tuple(tuple, pg_authid_dsc, values,
+								  nulls, updated);
+	CatalogTupleUpdate(pg_authid_rel, &tuple->t_self, new_tuple, updated, NULL);
+	bms_free(updated);
 
 	InvokeObjectPostAlterHook(AuthIdRelationId, roleid, 0);
 
@@ -1340,10 +1313,9 @@ RenameRole(const char *oldname, const char *newname)
 	Relation	rel;
 	Datum		datum;
 	bool		isnull;
-	Datum		repl_val[Natts_pg_authid];
-	bool		repl_null[Natts_pg_authid];
-	bool		repl_repl[Natts_pg_authid];
-	int			i;
+	Datum		values[Natts_pg_authid] = {0};
+	bool		nulls[Natts_pg_authid] = {false};
+	Bitmapset  *updated = NULL;
 	Oid			roleid;
 	ObjectAddress address;
 	Form_pg_authid authform;
@@ -1434,29 +1406,22 @@ RenameRole(const char *oldname, const char *newname)
 							   "CREATEROLE", "ADMIN", NameStr(authform->rolname))));
 	}
 
-	/* OK, construct the modified tuple */
-	for (i = 0; i < Natts_pg_authid; i++)
-		repl_repl[i] = false;
-
-	repl_repl[Anum_pg_authid_rolname - 1] = true;
-	repl_val[Anum_pg_authid_rolname - 1] = DirectFunctionCall1(namein,
-															   CStringGetDatum(newname));
-	repl_null[Anum_pg_authid_rolname - 1] = false;
+	HeapTupleUpdateValue(pg_authid, rolname, DirectFunctionCall1(namein, CStringGetDatum(newname)), values, nulls, updated);
 
 	datum = heap_getattr(oldtuple, Anum_pg_authid_rolpassword, dsc, &isnull);
 
 	if (!isnull && get_password_type(TextDatumGetCString(datum)) == PASSWORD_TYPE_MD5)
 	{
 		/* MD5 uses the username as salt, so just clear it on a rename */
-		repl_repl[Anum_pg_authid_rolpassword - 1] = true;
-		repl_null[Anum_pg_authid_rolpassword - 1] = true;
+		HeapTupleUpdateValueNull(pg_authid, rolpassword, values, nulls, updated);
 
 		ereport(NOTICE,
 				(errmsg("MD5 password cleared because of role rename")));
 	}
 
-	newtuple = heap_modify_tuple(oldtuple, dsc, repl_val, repl_null, repl_repl);
-	CatalogTupleUpdate(rel, &oldtuple->t_self, newtuple);
+	newtuple = heap_update_tuple(oldtuple, dsc, values, nulls, updated);
+	CatalogTupleUpdate(rel, &oldtuple->t_self, newtuple, updated, NULL);
+	bms_free(updated);
 
 	InvokeObjectPostAlterHook(AuthIdRelationId, roleid, 0);
 
@@ -1824,17 +1789,14 @@ AddRoleMems(Oid currentUserId, const char *rolename, Oid roleid,
 		Oid			memberid = lfirst_oid(iditem);
 		HeapTuple	authmem_tuple;
 		HeapTuple	tuple;
-		Datum		new_record[Natts_pg_auth_members] = {0};
-		bool		new_record_nulls[Natts_pg_auth_members] = {0};
-		bool		new_record_repl[Natts_pg_auth_members] = {0};
+		Datum		values[Natts_pg_auth_members] = {0};
+		bool		nulls[Natts_pg_auth_members] = {false};
+		Bitmapset  *updated = NULL;
 
 		/* Common initialization for possible insert or update */
-		new_record[Anum_pg_auth_members_roleid - 1] =
-			ObjectIdGetDatum(roleid);
-		new_record[Anum_pg_auth_members_member - 1] =
-			ObjectIdGetDatum(memberid);
-		new_record[Anum_pg_auth_members_grantor - 1] =
-			ObjectIdGetDatum(grantorId);
+		HeapTupleUpdateValue(pg_auth_members, roleid, ObjectIdGetDatum(roleid), values, nulls, updated);
+		HeapTupleUpdateValue(pg_auth_members, member, ObjectIdGetDatum(memberid), values, nulls, updated);
+		HeapTupleUpdateValue(pg_auth_members, grantor, ObjectIdGetDatum(grantorId), values, nulls, updated);
 
 		/* Find any existing tuple */
 		authmem_tuple = SearchSysCache3(AUTHMEMROLEMEM,
@@ -1858,30 +1820,21 @@ AddRoleMems(Oid currentUserId, const char *rolename, Oid roleid,
 			if ((popt->specified & GRANT_ROLE_SPECIFIED_ADMIN) != 0
 				&& authmem_form->admin_option != popt->admin)
 			{
-				new_record[Anum_pg_auth_members_admin_option - 1] =
-					BoolGetDatum(popt->admin);
-				new_record_repl[Anum_pg_auth_members_admin_option - 1] =
-					true;
+				HeapTupleUpdateValue(pg_auth_members, admin_option, BoolGetDatum(popt->admin), values, nulls, updated);
 				at_least_one_change = true;
 			}
 
 			if ((popt->specified & GRANT_ROLE_SPECIFIED_INHERIT) != 0
 				&& authmem_form->inherit_option != popt->inherit)
 			{
-				new_record[Anum_pg_auth_members_inherit_option - 1] =
-					BoolGetDatum(popt->inherit);
-				new_record_repl[Anum_pg_auth_members_inherit_option - 1] =
-					true;
+				HeapTupleUpdateValue(pg_auth_members, inherit_option, BoolGetDatum(popt->inherit), values, nulls, updated);
 				at_least_one_change = true;
 			}
 
 			if ((popt->specified & GRANT_ROLE_SPECIFIED_SET) != 0
 				&& authmem_form->set_option != popt->set)
 			{
-				new_record[Anum_pg_auth_members_set_option - 1] =
-					BoolGetDatum(popt->set);
-				new_record_repl[Anum_pg_auth_members_set_option - 1] =
-					true;
+				HeapTupleUpdateValue(pg_auth_members, set_option, BoolGetDatum(popt->set), values, nulls, updated);
 				at_least_one_change = true;
 			}
 
@@ -1895,10 +1848,8 @@ AddRoleMems(Oid currentUserId, const char *rolename, Oid roleid,
 				continue;
 			}
 
-			tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc,
-									  new_record,
-									  new_record_nulls, new_record_repl);
-			CatalogTupleUpdate(pg_authmem_rel, &tuple->t_self, tuple);
+			tuple = heap_update_tuple(authmem_tuple, pg_authmem_dsc, values, nulls, updated);
+			CatalogTupleUpdate(pg_authmem_rel, &tuple->t_self, tuple, updated, NULL);
 
 			ReleaseSysCache(authmem_tuple);
 		}
@@ -1912,10 +1863,8 @@ AddRoleMems(Oid currentUserId, const char *rolename, Oid roleid,
 			 * Either they were specified, or the defaults as set by
 			 * InitGrantRoleOptions are correct.
 			 */
-			new_record[Anum_pg_auth_members_admin_option - 1] =
-				BoolGetDatum(popt->admin);
-			new_record[Anum_pg_auth_members_set_option - 1] =
-				BoolGetDatum(popt->set);
+			HeapTupleSetValue(pg_auth_members, admin_option, BoolGetDatum(popt->admin), values);
+			HeapTupleSetValue(pg_auth_members, set_option, BoolGetDatum(popt->set), values);
 
 			/*
 			 * If the user specified a value for the inherit option, use
@@ -1923,8 +1872,7 @@ AddRoleMems(Oid currentUserId, const char *rolename, Oid roleid,
 			 * on the role-level property.
 			 */
 			if ((popt->specified & GRANT_ROLE_SPECIFIED_INHERIT) != 0)
-				new_record[Anum_pg_auth_members_inherit_option - 1] =
-					BoolGetDatum(popt->inherit);
+				HeapTupleSetValue(pg_auth_members, inherit_option, BoolGetDatum(popt->inherit), values);
 			else
 			{
 				HeapTuple	mrtup;
@@ -1934,18 +1882,16 @@ AddRoleMems(Oid currentUserId, const char *rolename, Oid roleid,
 				if (!HeapTupleIsValid(mrtup))
 					elog(ERROR, "cache lookup failed for role %u", memberid);
 				mrform = (Form_pg_authid) GETSTRUCT(mrtup);
-				new_record[Anum_pg_auth_members_inherit_option - 1] =
-					BoolGetDatum(mrform->rolinherit);
+				HeapTupleSetValue(pg_auth_members, inherit_option, BoolGetDatum(mrform->rolinherit), values);
 				ReleaseSysCache(mrtup);
 			}
 
 			/* get an OID for the new row and insert it */
 			objectId = GetNewOidWithIndex(pg_authmem_rel, AuthMemOidIndexId,
 										  Anum_pg_auth_members_oid);
-			new_record[Anum_pg_auth_members_oid - 1] = ObjectIdGetDatum(objectId);
-			tuple = heap_form_tuple(pg_authmem_dsc,
-									new_record, new_record_nulls);
-			CatalogTupleInsert(pg_authmem_rel, tuple);
+			HeapTupleSetValue(pg_auth_members, oid, ObjectIdGetDatum(objectId), values);
+			tuple = heap_form_tuple(pg_authmem_dsc, values, nulls);
+			CatalogTupleInsert(pg_authmem_rel, tuple, NULL);
 
 			/* updateAclDependencies wants to pfree array inputs */
 			newmembers[0] = grantorId;
@@ -1955,6 +1901,8 @@ AddRoleMems(Oid currentUserId, const char *rolename, Oid roleid,
 								  1, newmembers);
 		}
 
+		bms_free(updated);
+
 		/* CCI after each change, in case there are duplicates in list */
 		CommandCounterIncrement();
 	}
@@ -1987,7 +1935,6 @@ DelRoleMems(Oid currentUserId, const char *rolename, Oid roleid,
 	ListCell   *iditem;
 	CatCList   *memlist;
 	RevokeRoleGrantAction *actions;
-	int			i;
 
 	Assert(list_length(memberSpecs) == list_length(memberIds));
 
@@ -2035,7 +1982,7 @@ DelRoleMems(Oid currentUserId, const char *rolename, Oid roleid,
 	 * left alone, deleted, or just have the admin_option flag cleared.
 	 * Perform the appropriate action in each case.
 	 */
-	for (i = 0; i < memlist->n_members; ++i)
+	for (int i = 0; i < memlist->n_members; ++i)
 	{
 		HeapTuple	authmem_tuple;
 		Form_pg_auth_members authmem_form;
@@ -2060,39 +2007,24 @@ DelRoleMems(Oid currentUserId, const char *rolename, Oid roleid,
 		{
 			/* Just turn off the specified option */
 			HeapTuple	tuple;
-			Datum		new_record[Natts_pg_auth_members] = {0};
-			bool		new_record_nulls[Natts_pg_auth_members] = {0};
-			bool		new_record_repl[Natts_pg_auth_members] = {0};
+			Datum		values[Natts_pg_auth_members] = {0};
+			bool		nulls[Natts_pg_auth_members] = {false};
+			Bitmapset  *updated = NULL;
 
 			/* Build a tuple to update with */
 			if (actions[i] == RRG_REMOVE_ADMIN_OPTION)
-			{
-				new_record[Anum_pg_auth_members_admin_option - 1] =
-					BoolGetDatum(false);
-				new_record_repl[Anum_pg_auth_members_admin_option - 1] =
-					true;
-			}
+				HeapTupleUpdateValue(pg_auth_members, admin_option, BoolGetDatum(false), values, nulls, updated);
 			else if (actions[i] == RRG_REMOVE_INHERIT_OPTION)
-			{
-				new_record[Anum_pg_auth_members_inherit_option - 1] =
-					BoolGetDatum(false);
-				new_record_repl[Anum_pg_auth_members_inherit_option - 1] =
-					true;
-			}
+				HeapTupleUpdateValue(pg_auth_members, inherit_option, BoolGetDatum(false), values, nulls, updated);
 			else if (actions[i] == RRG_REMOVE_SET_OPTION)
-			{
-				new_record[Anum_pg_auth_members_set_option - 1] =
-					BoolGetDatum(false);
-				new_record_repl[Anum_pg_auth_members_set_option - 1] =
-					true;
-			}
+				HeapTupleUpdateValue(pg_auth_members, set_option, BoolGetDatum(false), values, nulls, updated);
 			else
 				elog(ERROR, "unknown role revoke action");
 
-			tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc,
-									  new_record,
-									  new_record_nulls, new_record_repl);
-			CatalogTupleUpdate(pg_authmem_rel, &tuple->t_self, tuple);
+			tuple = heap_update_tuple(authmem_tuple, pg_authmem_dsc,
+									  values, nulls, updated);
+			CatalogTupleUpdate(pg_authmem_rel, &tuple->t_self, tuple, updated, NULL);
+			bms_free(updated);
 		}
 	}
 
diff --git a/src/backend/replication/logical/origin.c b/src/backend/replication/logical/origin.c
index 13808a4674b..45d3b18d31f 100644
--- a/src/backend/replication/logical/origin.c
+++ b/src/backend/replication/logical/origin.c
@@ -315,8 +315,8 @@ replorigin_create(const char *roname)
 
 	for (roident = InvalidOid + 1; roident < PG_UINT16_MAX; roident++)
 	{
-		bool		nulls[Natts_pg_replication_origin];
-		Datum		values[Natts_pg_replication_origin];
+		Datum		values[Natts_pg_replication_origin] = {0};
+		bool		nulls[Natts_pg_replication_origin] = {false};
 		bool		collides;
 
 		CHECK_FOR_INTERRUPTS();
@@ -343,11 +343,11 @@ replorigin_create(const char *roname)
 			 */
 			memset(&nulls, 0, sizeof(nulls));
 
-			values[Anum_pg_replication_origin_roident - 1] = ObjectIdGetDatum(roident);
-			values[Anum_pg_replication_origin_roname - 1] = roname_d;
+			HeapTupleSetValue(pg_replication_origin, roident, ObjectIdGetDatum(roident), values);
+			HeapTupleSetValue(pg_replication_origin, roname, roname_d, values);
 
 			tuple = heap_form_tuple(RelationGetDescr(rel), values, nulls);
-			CatalogTupleInsert(rel, tuple);
+			CatalogTupleInsert(rel, tuple, NULL);
 			CommandCounterIncrement();
 			break;
 		}
diff --git a/src/backend/replication/logical/tablesync.c b/src/backend/replication/logical/tablesync.c
index 67e57520386..e9cffb6d246 100644
--- a/src/backend/replication/logical/tablesync.c
+++ b/src/backend/replication/logical/tablesync.c
@@ -1650,9 +1650,9 @@ UpdateTwoPhaseState(Oid suboid, char new_state)
 {
 	Relation	rel;
 	HeapTuple	tup;
-	bool		nulls[Natts_pg_subscription];
-	bool		replaces[Natts_pg_subscription];
-	Datum		values[Natts_pg_subscription];
+	Datum		values[Natts_pg_subscription] = {0};
+	bool		nulls[Natts_pg_subscription] = {false};
+	Bitmapset  *updated = NULL;
 
 	Assert(new_state == LOGICALREP_TWOPHASE_STATE_DISABLED ||
 		   new_state == LOGICALREP_TWOPHASE_STATE_PENDING ||
@@ -1665,19 +1665,13 @@ UpdateTwoPhaseState(Oid suboid, char new_state)
 			 "cache lookup failed for subscription oid %u",
 			 suboid);
 
-	/* Form a new tuple. */
-	memset(values, 0, sizeof(values));
-	memset(nulls, false, sizeof(nulls));
-	memset(replaces, false, sizeof(replaces));
-
 	/* And update/set two_phase state */
-	values[Anum_pg_subscription_subtwophasestate - 1] = CharGetDatum(new_state);
-	replaces[Anum_pg_subscription_subtwophasestate - 1] = true;
+	HeapTupleUpdateValue(pg_subscription, subtwophasestate, CharGetDatum(new_state), values, nulls, updated);
 
-	tup = heap_modify_tuple(tup, RelationGetDescr(rel),
-							values, nulls, replaces);
-	CatalogTupleUpdate(rel, &tup->t_self, tup);
+	tup = heap_update_tuple(tup, RelationGetDescr(rel), values, nulls, updated);
+	CatalogTupleUpdate(rel, &tup->t_self, tup, updated, NULL);
 
-	heap_freetuple(tup);
 	table_close(rel, RowExclusiveLock);
+	bms_free(updated);
+	heap_freetuple(tup);
 }
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index ad281e7069b..7efd77fba27 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -6125,21 +6125,12 @@ clear_subscription_skip_lsn(XLogRecPtr finish_lsn)
 	 */
 	if (subform->subskiplsn == myskiplsn)
 	{
-		bool		nulls[Natts_pg_subscription];
-		bool		replaces[Natts_pg_subscription];
-		Datum		values[Natts_pg_subscription];
-
-		memset(values, 0, sizeof(values));
-		memset(nulls, false, sizeof(nulls));
-		memset(replaces, false, sizeof(replaces));
+		Bitmapset  *updated = NULL;
 
 		/* reset subskiplsn */
-		values[Anum_pg_subscription_subskiplsn - 1] = LSNGetDatum(InvalidXLogRecPtr);
-		replaces[Anum_pg_subscription_subskiplsn - 1] = true;
-
-		tup = heap_modify_tuple(tup, RelationGetDescr(rel), values, nulls,
-								replaces);
-		CatalogTupleUpdate(rel, &tup->t_self, tup);
+		HeapTupleUpdateField(pg_subscription, subskiplsn, LSNGetDatum(InvalidXLogRecPtr), subform, updated);
+		CatalogTupleUpdate(rel, &tup->t_self, tup, updated, NULL);
+		bms_free(updated);
 
 		if (myskiplsn != finish_lsn)
 			ereport(WARNING,
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 6a223fbeaa4..121910b005e 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -59,8 +59,8 @@ InsertRule(const char *rulname,
 {
 	char	   *evqual = nodeToString(event_qual);
 	char	   *actiontree = nodeToString((Node *) action);
-	Datum		values[Natts_pg_rewrite];
-	bool		nulls[Natts_pg_rewrite] = {0};
+	Datum		values[Natts_pg_rewrite] = {0};
+	bool		nulls[Natts_pg_rewrite] = {false};
 	NameData	rname;
 	Relation	pg_rewrite_desc;
 	HeapTuple	tup,
@@ -69,18 +69,19 @@ InsertRule(const char *rulname,
 	ObjectAddress myself,
 				referenced;
 	bool		is_update = false;
+	Bitmapset  *updated = NULL;
 
 	/*
 	 * Set up *nulls and *values arrays
 	 */
 	namestrcpy(&rname, rulname);
-	values[Anum_pg_rewrite_rulename - 1] = NameGetDatum(&rname);
-	values[Anum_pg_rewrite_ev_class - 1] = ObjectIdGetDatum(eventrel_oid);
-	values[Anum_pg_rewrite_ev_type - 1] = CharGetDatum(evtype + '0');
-	values[Anum_pg_rewrite_ev_enabled - 1] = CharGetDatum(RULE_FIRES_ON_ORIGIN);
-	values[Anum_pg_rewrite_is_instead - 1] = BoolGetDatum(evinstead);
-	values[Anum_pg_rewrite_ev_qual - 1] = CStringGetTextDatum(evqual);
-	values[Anum_pg_rewrite_ev_action - 1] = CStringGetTextDatum(actiontree);
+	HeapTupleUpdateValue(pg_rewrite, rulename, NameGetDatum(&rname), values, nulls, updated);
+	HeapTupleUpdateValue(pg_rewrite, ev_class, ObjectIdGetDatum(eventrel_oid), values, nulls, updated);
+	HeapTupleUpdateValue(pg_rewrite, ev_type, CharGetDatum(evtype + '0'), values, nulls, updated);
+	HeapTupleUpdateValue(pg_rewrite, ev_enabled, CharGetDatum(RULE_FIRES_ON_ORIGIN), values, nulls, updated);
+	HeapTupleUpdateValue(pg_rewrite, is_instead, BoolGetDatum(evinstead), values, nulls, updated);
+	HeapTupleUpdateValue(pg_rewrite, ev_qual, CStringGetTextDatum(evqual), values, nulls, updated);
+	HeapTupleUpdateValue(pg_rewrite, ev_action, CStringGetTextDatum(actiontree), values, nulls, updated);
 
 	/*
 	 * Ready to store new pg_rewrite tuple
@@ -96,8 +97,6 @@ InsertRule(const char *rulname,
 
 	if (HeapTupleIsValid(oldtup))
 	{
-		bool		replaces[Natts_pg_rewrite] = {0};
-
 		if (!replace)
 			ereport(ERROR,
 					(errcode(ERRCODE_DUPLICATE_OBJECT),
@@ -107,16 +106,14 @@ InsertRule(const char *rulname,
 		/*
 		 * When replacing, we don't need to replace every attribute
 		 */
-		replaces[Anum_pg_rewrite_ev_type - 1] = true;
-		replaces[Anum_pg_rewrite_is_instead - 1] = true;
-		replaces[Anum_pg_rewrite_ev_qual - 1] = true;
-		replaces[Anum_pg_rewrite_ev_action - 1] = true;
-
-		tup = heap_modify_tuple(oldtup, RelationGetDescr(pg_rewrite_desc),
-								values, nulls, replaces);
-
-		CatalogTupleUpdate(pg_rewrite_desc, &tup->t_self, tup);
-
+		HeapTupleUpdateValue(pg_rewrite, ev_type, CharGetDatum(evtype + '0'), values, nulls, updated);
+		HeapTupleUpdateValue(pg_rewrite, is_instead, BoolGetDatum(evinstead), values, nulls, updated);
+		HeapTupleUpdateValue(pg_rewrite, ev_qual, CStringGetTextDatum(evqual), values, nulls, updated);
+		HeapTupleUpdateValue(pg_rewrite, ev_action, CStringGetTextDatum(actiontree), values, nulls, updated);
+
+		tup = heap_update_tuple(oldtup, RelationGetDescr(pg_rewrite_desc),
+								values, nulls, updated);
+		CatalogTupleUpdate(pg_rewrite_desc, &tup->t_self, tup, updated, NULL);
 		ReleaseSysCache(oldtup);
 
 		rewriteObjectId = ((Form_pg_rewrite) GETSTRUCT(tup))->oid;
@@ -127,14 +124,12 @@ InsertRule(const char *rulname,
 		rewriteObjectId = GetNewOidWithIndex(pg_rewrite_desc,
 											 RewriteOidIndexId,
 											 Anum_pg_rewrite_oid);
-		values[Anum_pg_rewrite_oid - 1] = ObjectIdGetDatum(rewriteObjectId);
-
+		HeapTupleUpdateValue(pg_rewrite, oid, ObjectIdGetDatum(rewriteObjectId), values, nulls, updated);
 		tup = heap_form_tuple(pg_rewrite_desc->rd_att, values, nulls);
-
-		CatalogTupleInsert(pg_rewrite_desc, tup);
+		CatalogTupleInsert(pg_rewrite_desc, tup, NULL);
 	}
 
-
+	bms_free(updated);
 	heap_freetuple(tup);
 
 	/* If replacing, get rid of old dependencies and make new ones */
@@ -727,8 +722,11 @@ EnableDisableRule(Relation rel, const char *rulename,
 	 */
 	if (ruleform->ev_enabled != fires_when)
 	{
-		ruleform->ev_enabled = fires_when;
-		CatalogTupleUpdate(pg_rewrite_desc, &ruletup->t_self, ruletup);
+		Bitmapset  *updated = NULL;
+
+		HeapTupleUpdateField(pg_rewrite, ev_enabled, fires_when, ruleform, updated);
+		CatalogTupleUpdate(pg_rewrite_desc, &ruletup->t_self, ruletup, updated, NULL);
+		bms_free(updated);
 
 		changed = true;
 	}
@@ -799,6 +797,7 @@ RenameRewriteRule(RangeVar *relation, const char *oldName,
 	Form_pg_rewrite ruleform;
 	Oid			ruleOid;
 	ObjectAddress address;
+	Bitmapset  *updated = NULL;
 
 	/*
 	 * Look up name, check permissions, and acquire lock (which we will NOT
@@ -845,8 +844,9 @@ RenameRewriteRule(RangeVar *relation, const char *oldName,
 
 	/* OK, do the update */
 	namestrcpy(&(ruleform->rulename), newName);
-
-	CatalogTupleUpdate(pg_rewrite_desc, &ruletup->t_self, ruletup);
+	HeapTupleMarkColumnUpdated(pg_rewrite, rulename, updated);
+	CatalogTupleUpdate(pg_rewrite_desc, &ruletup->t_self, ruletup, updated, NULL);
+	bms_free(updated);
 
 	InvokeObjectPostAlterHook(RewriteRelationId, ruleOid, 0);
 
diff --git a/src/backend/rewrite/rewriteSupport.c b/src/backend/rewrite/rewriteSupport.c
index fffded88a6e..be6fcfd6dcb 100644
--- a/src/backend/rewrite/rewriteSupport.c
+++ b/src/backend/rewrite/rewriteSupport.c
@@ -18,6 +18,7 @@
 #include "access/table.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_class.h"
+#include "nodes/bitmapset.h"
 #include "catalog/pg_rewrite.h"
 #include "rewrite/rewriteSupport.h"
 #include "utils/inval.h"
@@ -68,9 +69,12 @@ SetRelationRuleStatus(Oid relationId, bool relHasRules)
 	if (classForm->relhasrules != relHasRules)
 	{
 		/* Do the update */
+		Bitmapset  *updated = NULL;
+
 		classForm->relhasrules = relHasRules;
+		updated = bms_add_member(updated, Anum_pg_class_relhasrules - FirstLowInvalidHeapAttributeNumber);
 
-		CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
+		CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple, updated, NULL);
 	}
 	else
 	{
diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c
index b9a8f257042..b0ae54add5d 100644
--- a/src/backend/statistics/extended_stats.c
+++ b/src/backend/statistics/extended_stats.c
@@ -762,20 +762,14 @@ statext_store(Oid statOid, bool inh,
 {
 	Relation	pg_stextdata;
 	HeapTuple	stup;
-	Datum		values[Natts_pg_statistic_ext_data];
-	bool		nulls[Natts_pg_statistic_ext_data];
+	Datum		values[Natts_pg_statistic_ext_data] = {0};
+	bool		nulls[Natts_pg_statistic_ext_data] = {false};
 
 	pg_stextdata = table_open(StatisticExtDataRelationId, RowExclusiveLock);
 
-	memset(nulls, true, sizeof(nulls));
-	memset(values, 0, sizeof(values));
-
 	/* basic info */
-	values[Anum_pg_statistic_ext_data_stxoid - 1] = ObjectIdGetDatum(statOid);
-	nulls[Anum_pg_statistic_ext_data_stxoid - 1] = false;
-
-	values[Anum_pg_statistic_ext_data_stxdinherit - 1] = BoolGetDatum(inh);
-	nulls[Anum_pg_statistic_ext_data_stxdinherit - 1] = false;
+	HeapTupleSetValue(pg_statistic_ext_data, stxoid, ObjectIdGetDatum(statOid), values);
+	HeapTupleSetValue(pg_statistic_ext_data, stxdinherit, BoolGetDatum(inh), values);
 
 	/*
 	 * Construct a new pg_statistic_ext_data tuple, replacing the calculated
@@ -785,29 +779,42 @@ statext_store(Oid statOid, bool inh,
 	{
 		bytea	   *data = statext_ndistinct_serialize(ndistinct);
 
-		nulls[Anum_pg_statistic_ext_data_stxdndistinct - 1] = (data == NULL);
-		values[Anum_pg_statistic_ext_data_stxdndistinct - 1] = PointerGetDatum(data);
+		if (data == NULL)
+			HeapTupleSetValueNull(pg_statistic_ext_data, stxdndistinct, values, nulls);
+		else
+			HeapTupleSetValue(pg_statistic_ext_data, stxdndistinct, PointerGetDatum(data), values);
 	}
+	else
+		HeapTupleSetValueNull(pg_statistic_ext_data, stxdndistinct, values, nulls);
 
 	if (dependencies != NULL)
 	{
 		bytea	   *data = statext_dependencies_serialize(dependencies);
 
-		nulls[Anum_pg_statistic_ext_data_stxddependencies - 1] = (data == NULL);
-		values[Anum_pg_statistic_ext_data_stxddependencies - 1] = PointerGetDatum(data);
+		if (data == NULL)
+			HeapTupleSetValueNull(pg_statistic_ext_data, stxddependencies, values, nulls);
+		else
+			HeapTupleSetValue(pg_statistic_ext_data, stxddependencies, PointerGetDatum(data), values);
 	}
+	else
+		HeapTupleSetValueNull(pg_statistic_ext_data, stxddependencies, values, nulls);
+
 	if (mcv != NULL)
 	{
 		bytea	   *data = statext_mcv_serialize(mcv, stats);
 
-		nulls[Anum_pg_statistic_ext_data_stxdmcv - 1] = (data == NULL);
-		values[Anum_pg_statistic_ext_data_stxdmcv - 1] = PointerGetDatum(data);
+		if (data == NULL)
+			HeapTupleSetValueNull(pg_statistic_ext_data, stxdmcv, values, nulls);
+		else
+			HeapTupleSetValue(pg_statistic_ext_data, stxdmcv, PointerGetDatum(data), values);
 	}
+	else
+		HeapTupleSetValueNull(pg_statistic_ext_data, stxdmcv, values, nulls);
+
 	if (exprs != (Datum) 0)
-	{
-		nulls[Anum_pg_statistic_ext_data_stxdexpr - 1] = false;
-		values[Anum_pg_statistic_ext_data_stxdexpr - 1] = exprs;
-	}
+		HeapTupleSetValue(pg_statistic_ext_data, stxdexpr, exprs, values);
+	else
+		HeapTupleSetValueNull(pg_statistic_ext_data, stxdexpr, values, nulls);
 
 	/*
 	 * Delete the old tuple if it exists, and insert a new one. It's easier
@@ -817,7 +824,7 @@ statext_store(Oid statOid, bool inh,
 
 	/* form and insert a new tuple */
 	stup = heap_form_tuple(RelationGetDescr(pg_stextdata), values, nulls);
-	CatalogTupleInsert(pg_stextdata, stup);
+	CatalogTupleInsert(pg_stextdata, stup, NULL);
 
 	heap_freetuple(stup);
 
@@ -2292,8 +2299,8 @@ serialize_expr_stats(AnlExprData *exprdata, int nexprs)
 					k;
 		VacAttrStats *stats = exprdata[exprno].vacattrstat;
 
-		Datum		values[Natts_pg_statistic];
-		bool		nulls[Natts_pg_statistic];
+		Datum		values[Natts_pg_statistic] = {0};
+		bool		nulls[Natts_pg_statistic] = {false};
 		HeapTuple	stup;
 
 		if (!stats->stats_valid)
@@ -2314,12 +2321,12 @@ serialize_expr_stats(AnlExprData *exprdata, int nexprs)
 			nulls[i] = false;
 		}
 
-		values[Anum_pg_statistic_starelid - 1] = ObjectIdGetDatum(InvalidOid);
-		values[Anum_pg_statistic_staattnum - 1] = Int16GetDatum(InvalidAttrNumber);
-		values[Anum_pg_statistic_stainherit - 1] = BoolGetDatum(false);
-		values[Anum_pg_statistic_stanullfrac - 1] = Float4GetDatum(stats->stanullfrac);
-		values[Anum_pg_statistic_stawidth - 1] = Int32GetDatum(stats->stawidth);
-		values[Anum_pg_statistic_stadistinct - 1] = Float4GetDatum(stats->stadistinct);
+		HeapTupleSetValue(pg_statistic, starelid, ObjectIdGetDatum(InvalidOid), values);
+		HeapTupleSetValue(pg_statistic, staattnum, Int16GetDatum(InvalidAttrNumber), values);
+		HeapTupleSetValue(pg_statistic, stainherit, BoolGetDatum(false), values);
+		HeapTupleSetValue(pg_statistic, stanullfrac, Float4GetDatum(stats->stanullfrac), values);
+		HeapTupleSetValue(pg_statistic, stawidth, Int32GetDatum(stats->stawidth), values);
+		HeapTupleSetValue(pg_statistic, stadistinct, Float4GetDatum(stats->stadistinct), values);
 		i = Anum_pg_statistic_stakind1 - 1;
 		for (k = 0; k < STATISTIC_NUM_SLOTS; k++)
 		{
diff --git a/src/backend/statistics/relation_stats.c b/src/backend/statistics/relation_stats.c
index d6631e9a9a4..5a7904ee1a2 100644
--- a/src/backend/statistics/relation_stats.c
+++ b/src/backend/statistics/relation_stats.c
@@ -18,6 +18,7 @@
 #include "postgres.h"
 
 #include "access/heapam.h"
+#include "access/htup.h"
 #include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "nodes/makefuncs.h"
@@ -79,10 +80,9 @@ relation_statistics_update(FunctionCallInfo fcinfo)
 	bool		update_relallfrozen = false;
 	HeapTuple	ctup;
 	Form_pg_class pgcform;
-	int			replaces[4] = {0};
-	Datum		values[4] = {0};
-	bool		nulls[4] = {0};
-	int			nreplaces = 0;
+	Datum		values[Natts_pg_class] = {0};
+	bool		nulls[Natts_pg_class] = {false};
+	Bitmapset  *updated = NULL;
 	Oid			locked_table = InvalidOid;
 
 	stats_check_required_arg(fcinfo, relarginfo, RELSCHEMA_ARG);
@@ -146,42 +146,26 @@ relation_statistics_update(FunctionCallInfo fcinfo)
 	pgcform = (Form_pg_class) GETSTRUCT(ctup);
 
 	if (update_relpages && relpages != pgcform->relpages)
-	{
-		replaces[nreplaces] = Anum_pg_class_relpages;
-		values[nreplaces] = UInt32GetDatum(relpages);
-		nreplaces++;
-	}
+		HeapTupleUpdateValue(pg_class, relpages, UInt32GetDatum(relpages), values, nulls, updated);
 
 	if (update_reltuples && reltuples != pgcform->reltuples)
-	{
-		replaces[nreplaces] = Anum_pg_class_reltuples;
-		values[nreplaces] = Float4GetDatum(reltuples);
-		nreplaces++;
-	}
+		HeapTupleUpdateValue(pg_class, reltuples, Float4GetDatum(reltuples), values, nulls, updated);
 
 	if (update_relallvisible && relallvisible != pgcform->relallvisible)
-	{
-		replaces[nreplaces] = Anum_pg_class_relallvisible;
-		values[nreplaces] = UInt32GetDatum(relallvisible);
-		nreplaces++;
-	}
+		HeapTupleUpdateValue(pg_class, relallvisible, UInt32GetDatum(relallvisible), values, nulls, updated);
 
 	if (update_relallfrozen && relallfrozen != pgcform->relallfrozen)
-	{
-		replaces[nreplaces] = Anum_pg_class_relallfrozen;
-		values[nreplaces] = UInt32GetDatum(relallfrozen);
-		nreplaces++;
-	}
+		HeapTupleUpdateValue(pg_class, relallfrozen, UInt32GetDatum(relallfrozen), values, nulls, updated);
 
-	if (nreplaces > 0)
+	if (!bms_is_empty(updated))
 	{
 		TupleDesc	tupdesc = RelationGetDescr(crel);
 		HeapTuple	newtup;
 
-		newtup = heap_modify_tuple_by_cols(ctup, tupdesc, nreplaces,
-										   replaces, values, nulls);
-		CatalogTupleUpdate(crel, &newtup->t_self, newtup);
+		newtup = heap_update_tuple(ctup, tupdesc, values, nulls, updated);
+		CatalogTupleUpdate(crel, &newtup->t_self, newtup, updated, NULL);
 		heap_freetuple(newtup);
+		bms_free(updated);
 	}
 
 	ReleaseSysCache(ctup);
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c
index 466c1a856cb..e442a1ca549 100644
--- a/src/backend/storage/large_object/inv_api.c
+++ b/src/backend/storage/large_object/inv_api.c
@@ -562,9 +562,8 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
 	}			workbuf = {0};
 	char	   *workb = VARDATA(&workbuf.hdr);
 	HeapTuple	newtup;
-	Datum		values[Natts_pg_largeobject];
-	bool		nulls[Natts_pg_largeobject];
-	bool		replace[Natts_pg_largeobject];
+	Datum		values[Natts_pg_largeobject] = {0};
+	bool		nulls[Natts_pg_largeobject] = {false};
 	CatalogIndexState indstate;
 
 	Assert(obj_desc);
@@ -610,6 +609,8 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
 
 	while (nwritten < nbytes)
 	{
+		Bitmapset  *updated = NULL;
+
 		/*
 		 * If possible, get next pre-existing page of the LO.  We expect the
 		 * indexscan will deliver these in order --- but there may be holes.
@@ -665,16 +666,11 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
 			/*
 			 * Form and insert updated tuple
 			 */
-			memset(values, 0, sizeof(values));
-			memset(nulls, false, sizeof(nulls));
-			memset(replace, false, sizeof(replace));
-			values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf);
-			replace[Anum_pg_largeobject_data - 1] = true;
-			newtup = heap_modify_tuple(oldtuple, RelationGetDescr(lo_heap_r),
-									   values, nulls, replace);
-			CatalogTupleUpdateWithInfo(lo_heap_r, &newtup->t_self, newtup,
-									   indstate);
+			HeapTupleUpdateValue(pg_largeobject, data, PointerGetDatum(&workbuf), values, nulls, updated);
+			newtup = heap_update_tuple(oldtuple, RelationGetDescr(lo_heap_r), values, nulls, updated);
+			CatalogTupleUpdate(lo_heap_r, &newtup->t_self, newtup, updated, indstate);
 			heap_freetuple(newtup);
+			bms_free(updated);
 
 			/*
 			 * We're done with this old page.
@@ -711,11 +707,11 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
 			 */
 			memset(values, 0, sizeof(values));
 			memset(nulls, false, sizeof(nulls));
-			values[Anum_pg_largeobject_loid - 1] = ObjectIdGetDatum(obj_desc->id);
-			values[Anum_pg_largeobject_pageno - 1] = Int32GetDatum(pageno);
-			values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf);
+			HeapTupleSetValue(pg_largeobject, loid, ObjectIdGetDatum(obj_desc->id), values);
+			HeapTupleSetValue(pg_largeobject, pageno, Int32GetDatum(pageno), values);
+			HeapTupleSetValue(pg_largeobject, data, PointerGetDatum(&workbuf), values);
 			newtup = heap_form_tuple(lo_heap_r->rd_att, values, nulls);
-			CatalogTupleInsertWithInfo(lo_heap_r, newtup, indstate);
+			CatalogTupleInsert(lo_heap_r, newtup, indstate);
 			heap_freetuple(newtup);
 		}
 		pageno++;
@@ -751,9 +747,9 @@ inv_truncate(LargeObjectDesc *obj_desc, int64 len)
 	}			workbuf = {0};
 	char	   *workb = VARDATA(&workbuf.hdr);
 	HeapTuple	newtup;
-	Datum		values[Natts_pg_largeobject];
-	bool		nulls[Natts_pg_largeobject];
-	bool		replace[Natts_pg_largeobject];
+	Datum		values[Natts_pg_largeobject] = {0};
+	bool		nulls[Natts_pg_largeobject] = {false};
+	Bitmapset  *updated = NULL;
 	CatalogIndexState indstate;
 
 	Assert(obj_desc);
@@ -840,14 +836,11 @@ inv_truncate(LargeObjectDesc *obj_desc, int64 len)
 		 */
 		memset(values, 0, sizeof(values));
 		memset(nulls, false, sizeof(nulls));
-		memset(replace, false, sizeof(replace));
-		values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf);
-		replace[Anum_pg_largeobject_data - 1] = true;
-		newtup = heap_modify_tuple(oldtuple, RelationGetDescr(lo_heap_r),
-								   values, nulls, replace);
-		CatalogTupleUpdateWithInfo(lo_heap_r, &newtup->t_self, newtup,
-								   indstate);
+		HeapTupleUpdateValue(pg_largeobject, data, PointerGetDatum(&workbuf), values, nulls, updated);
+		newtup = heap_update_tuple(oldtuple, RelationGetDescr(lo_heap_r), values, nulls, updated);
+		CatalogTupleUpdate(lo_heap_r, &newtup->t_self, newtup, updated, indstate);
 		heap_freetuple(newtup);
+		bms_free(updated);
 	}
 	else
 	{
@@ -879,11 +872,11 @@ inv_truncate(LargeObjectDesc *obj_desc, int64 len)
 		 */
 		memset(values, 0, sizeof(values));
 		memset(nulls, false, sizeof(nulls));
-		values[Anum_pg_largeobject_loid - 1] = ObjectIdGetDatum(obj_desc->id);
-		values[Anum_pg_largeobject_pageno - 1] = Int32GetDatum(pageno);
-		values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf);
+		HeapTupleSetValue(pg_largeobject, loid, ObjectIdGetDatum(obj_desc->id), values);
+		HeapTupleSetValue(pg_largeobject, pageno, Int32GetDatum(pageno), values);
+		HeapTupleSetValue(pg_largeobject, data, PointerGetDatum(&workbuf), values);
 		newtup = heap_form_tuple(lo_heap_r->rd_att, values, nulls);
-		CatalogTupleInsertWithInfo(lo_heap_r, newtup, indstate);
+		CatalogTupleInsert(lo_heap_r, newtup, indstate);
 		heap_freetuple(newtup);
 	}
 
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 6b634c9fff1..61d6c8f669c 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -3775,6 +3775,7 @@ RelationSetNewRelfilenumber(Relation relation, char persistence)
 	MultiXactId minmulti = InvalidMultiXactId;
 	TransactionId freezeXid = InvalidTransactionId;
 	RelFileLocator newrlocator;
+	Bitmapset  *updated = NULL;
 
 	if (!IsBinaryUpgrade)
 	{
@@ -3924,21 +3925,21 @@ RelationSetNewRelfilenumber(Relation relation, char persistence)
 	else
 	{
 		/* Normal case, update the pg_class entry */
-		classform->relfilenode = newrelfilenumber;
+		HeapTupleUpdateField(pg_class, relfilenode, newrelfilenumber, classform, updated);
 
 		/* relpages etc. never change for sequences */
 		if (relation->rd_rel->relkind != RELKIND_SEQUENCE)
 		{
-			classform->relpages = 0;	/* it's empty until further notice */
-			classform->reltuples = -1;
-			classform->relallvisible = 0;
-			classform->relallfrozen = 0;
+			HeapTupleUpdateField(pg_class, relpages, 0, classform, updated);
+			HeapTupleUpdateField(pg_class, reltuples, -1, classform, updated);
+			HeapTupleUpdateField(pg_class, relallvisible, 0, classform, updated);
+			HeapTupleUpdateField(pg_class, relallfrozen, 0, classform, updated);
 		}
-		classform->relfrozenxid = freezeXid;
-		classform->relminmxid = minmulti;
-		classform->relpersistence = persistence;
+		HeapTupleUpdateField(pg_class, relfrozenxid, freezeXid, classform, updated);
+		HeapTupleUpdateField(pg_class, relminmxid, minmulti, classform, updated);
+		HeapTupleUpdateField(pg_class, relpersistence, persistence, classform, updated);
 
-		CatalogTupleUpdate(pg_class, &otid, tuple);
+		CatalogTupleUpdate(pg_class, &otid, tuple, updated, NULL);
 	}
 
 	UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
@@ -3953,6 +3954,7 @@ RelationSetNewRelfilenumber(Relation relation, char persistence)
 	CommandCounterIncrement();
 
 	RelationAssumeNewRelfilelocator(relation);
+	bms_free(updated);
 }
 
 /*
-- 
2.51.2

