From 0872423fa7e12ff7d52a02281433c85bedb1c90d Mon Sep 17 00:00:00 2001
From: David Geier <geidav.pg@gmail.com>
Date: Thu, 3 Sep 2026 15:25:09 +0200
Subject: [PATCH v1 7/8] Remove rd_index

---
 contrib/amcheck/verify_common.c             |  2 +-
 contrib/amcheck/verify_nbtree.c             |  2 +-
 contrib/pgstattuple/pgstatindex.c           |  6 +--
 contrib/pgstattuple/pgstattuple.c           |  2 +-
 src/backend/access/brin/brin.c              |  4 +-
 src/backend/access/common/toast_internals.c |  6 +--
 src/backend/access/gin/ginfast.c            |  2 +-
 src/backend/access/heap/heapam.c            |  4 +-
 src/backend/access/index/genam.c            |  6 +--
 src/backend/access/nbtree/nbtutils.c        |  2 +-
 src/backend/access/spgist/spgutils.c        | 12 +++---
 src/backend/catalog/catalog.c               |  4 +-
 src/backend/catalog/index.c                 |  8 ++--
 src/backend/catalog/indexing.c              |  4 +-
 src/backend/commands/indexcmds.c            | 14 +++---
 src/backend/commands/matview.c              |  4 +-
 src/backend/commands/propgraphcmds.c        |  2 +-
 src/backend/commands/repack.c               | 20 ++++-----
 src/backend/commands/tablecmds.c            | 44 +++++++++----------
 src/backend/commands/vacuum.c               |  2 +-
 src/backend/executor/execIndexing.c         | 16 +++----
 src/backend/executor/execPartition.c        | 10 ++---
 src/backend/executor/execReplication.c      |  2 +-
 src/backend/executor/nodeIndexonlyscan.c    |  2 +-
 src/backend/optimizer/util/plancat.c        | 12 +++---
 src/backend/parser/parse_utilcmd.c          |  2 +-
 src/backend/replication/logical/conflict.c  |  2 +-
 src/backend/replication/logical/relation.c  |  8 ++--
 src/backend/statistics/stat_utils.c         |  4 +-
 src/backend/storage/lmgr/predicate.c        |  8 ++--
 src/backend/utils/adt/ri_triggers.c         |  2 +-
 src/backend/utils/cache/catcache.c          |  4 +-
 src/backend/utils/cache/relcache.c          | 48 ++++++++++-----------
 src/include/utils/rel.h                     | 20 ++++++++-
 src/include/utils/rel_internal.h            |  1 -
 35 files changed, 152 insertions(+), 139 deletions(-)

diff --git a/contrib/amcheck/verify_common.c b/contrib/amcheck/verify_common.c
index 46e53b3f99a..5e7228287ba 100644
--- a/contrib/amcheck/verify_common.c
+++ b/contrib/amcheck/verify_common.c
@@ -181,7 +181,7 @@ index_checkable(Relation rel, Oid am_id)
 				 errdetail("Index \"%s\" is associated with temporary relation.",
 						   RelationGetRelationName(rel))));
 
-	if (!rel->rd_index->indisvalid)
+	if (!RelationGetIndex(rel)->indisvalid)
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("cannot check index \"%s\"",
diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c
index 3ef2d66f826..092ecb5a1c2 100644
--- a/contrib/amcheck/verify_nbtree.c
+++ b/contrib/amcheck/verify_nbtree.c
@@ -456,7 +456,7 @@ bt_check_every_level(Relation rel, Relation heaprel, bool heapkeyspace,
 		 * horizon.  Throwing an error here should be very rare.  It doesn't
 		 * seem worth using a secondary snapshot to avoid this.
 		 */
-		if (IsolationUsesXactSnapshot() && rel->rd_index->indcheckxmin &&
+		if (IsolationUsesXactSnapshot() && RelationGetIndex(rel)->indcheckxmin &&
 			!TransactionIdPrecedes(HeapTupleHeaderGetXmin(rel->rd_indextuple->t_data),
 								   state->snapshot->xmin))
 			ereport(ERROR,
diff --git a/contrib/pgstattuple/pgstatindex.c b/contrib/pgstattuple/pgstatindex.c
index 8951ad0aac4..ae0fba426f3 100644
--- a/contrib/pgstattuple/pgstatindex.c
+++ b/contrib/pgstattuple/pgstatindex.c
@@ -244,7 +244,7 @@ pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo)
 	 * but the results could be confusing.  For example, the index's size
 	 * could be too low for a valid index of the table.
 	 */
-	if (!rel->rd_index->indisvalid)
+	if (!RelationGetIndex(rel)->indisvalid)
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("index \"%s\" is not valid",
@@ -558,7 +558,7 @@ pgstatginindex_internal(Oid relid, FunctionCallInfo fcinfo)
 				 errmsg("cannot access temporary indexes of other sessions")));
 
 	/* see pgstatindex_impl */
-	if (!rel->rd_index->indisvalid)
+	if (!RelationGetIndex(rel)->indisvalid)
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("index \"%s\" is not valid",
@@ -648,7 +648,7 @@ pgstathashindex(PG_FUNCTION_ARGS)
 				 errmsg("cannot access temporary indexes of other sessions")));
 
 	/* see pgstatindex_impl */
-	if (!rel->rd_index->indisvalid)
+	if (!RelationGetIndex(rel)->indisvalid)
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("index \"%s\" is not valid",
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index 6a7f8cb4a7c..89116b52250 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -262,7 +262,7 @@ pgstat_relation(Relation rel, FunctionCallInfo fcinfo)
 	else if (rel->rd_rel->relkind == RELKIND_INDEX)
 	{
 		/* see pgstatindex_impl */
-		if (!rel->rd_index->indisvalid)
+		if (!RelationGetIndex(rel)->indisvalid)
 			ereport(ERROR,
 					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 					 errmsg("index \"%s\" is not valid",
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index bdb30752e09..e6e4c2db011 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -1469,7 +1469,7 @@ brin_summarize_range(PG_FUNCTION_ARGS)
 						RelationGetRelationName(indexRel))));
 
 	/* see gin_clean_pending_list() */
-	if (indexRel->rd_index->indisvalid)
+	if (RelationGetIndex(indexRel)->indisvalid)
 		brinsummarize(indexRel, heapRel, heapBlk, true, &numSummarized, NULL);
 	else
 		ereport(DEBUG1,
@@ -1558,7 +1558,7 @@ brin_desummarize_range(PG_FUNCTION_ARGS)
 						RelationGetRelationName(indexRel))));
 
 	/* see gin_clean_pending_list() */
-	if (indexRel->rd_index->indisvalid)
+	if (RelationGetIndex(indexRel)->indisvalid)
 	{
 		/* the revmap does the hard work */
 		do
diff --git a/src/backend/access/common/toast_internals.c b/src/backend/access/common/toast_internals.c
index 77d42e7ed65..031f943dfdb 100644
--- a/src/backend/access/common/toast_internals.c
+++ b/src/backend/access/common/toast_internals.c
@@ -327,11 +327,11 @@ toast_save_datum(Relation rel, Datum value,
 		for (int i = 0; i < num_indexes; i++)
 		{
 			/* Only index relations marked as ready can be updated */
-			if (toastidxs[i]->rd_index->indisready)
+			if (RelationGetIndex(toastidxs[i])->indisready)
 				index_insert(toastidxs[i], t_values, t_isnull,
 							 &(toasttup->t_self),
 							 toastrel,
-							 toastidxs[i]->rd_index->indisunique ?
+							 RelationGetIndex(toastidxs[i])->indisunique ?
 							 UNIQUE_CHECK_YES : UNIQUE_CHECK_NO,
 							 false, NULL);
 		}
@@ -577,7 +577,7 @@ toast_open_indexes(Relation toastrel,
 	{
 		Relation	toastidx = (*toastidxs)[i];
 
-		if (toastidx->rd_index->indisvalid)
+		if (RelationGetIndex(toastidx)->indisvalid)
 		{
 			res = i;
 			found = true;
diff --git a/src/backend/access/gin/ginfast.c b/src/backend/access/gin/ginfast.c
index 46fc60115a8..df5e3545a19 100644
--- a/src/backend/access/gin/ginfast.c
+++ b/src/backend/access/gin/ginfast.c
@@ -1075,7 +1075,7 @@ gin_clean_pending_list(PG_FUNCTION_ARGS)
 	 * is merely awaiting missed aminsert calls, we're capable of processing
 	 * it.  Decline to do so, out of an abundance of caution.
 	 */
-	if (indexRel->rd_index->indisvalid)
+	if (RelationGetIndex(indexRel)->indisvalid)
 	{
 		GinState	ginstate;
 
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index b507b3e996b..8a0d12465e4 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -4419,7 +4419,7 @@ check_lock_if_inplace_updateable_rel(Relation relation,
 				{
 					Relation	irel = index_open(relid, AccessShareLock);
 
-					SET_LOCKTAG_RELATION(tag, dbid, irel->rd_index->indrelid);
+					SET_LOCKTAG_RELATION(tag, dbid, RelationGetIndex(irel)->indrelid);
 					index_close(irel, AccessShareLock);
 				}
 				else
@@ -4473,7 +4473,7 @@ check_inplace_rel_lock(HeapTuple oldtup)
 	{
 		Relation	irel = index_open(relid, AccessShareLock);
 
-		SET_LOCKTAG_RELATION(tag, dbid, irel->rd_index->indrelid);
+		SET_LOCKTAG_RELATION(tag, dbid, RelationGetIndex(irel)->indrelid);
 		index_close(irel, AccessShareLock);
 	}
 	else
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 1408989c568..02d711c0a74 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -199,7 +199,7 @@ BuildIndexValueDescription(Relation indexRelation,
 	 * Next we need to check table-level SELECT access and then, if there is
 	 * no access there, check column-level permissions.
 	 */
-	idxrec = indexRelation->rd_index;
+	idxrec = RelationGetIndex(indexRelation);
 	indrelid = idxrec->indrelid;
 	Assert(indexrelid == idxrec->indexrelid);
 
@@ -444,7 +444,7 @@ systable_beginscan(Relation heapRelation,
 
 			for (j = 0; j < IndexRelationGetNumberOfAttributes(irel); j++)
 			{
-				if (key[i].sk_attno == irel->rd_index->indkey.values[j])
+				if (key[i].sk_attno == RelationGetIndex(irel)->indkey.values[j])
 				{
 					idxkey[i].sk_attno = j + 1;
 					break;
@@ -698,7 +698,7 @@ systable_beginscan_ordered(Relation heapRelation,
 
 		for (j = 0; j < IndexRelationGetNumberOfAttributes(indexRelation); j++)
 		{
-			if (key[i].sk_attno == indexRelation->rd_index->indkey.values[j])
+			if (key[i].sk_attno == RelationGetIndex(indexRelation)->indkey.values[j])
 			{
 				idxkey[i].sk_attno = j + 1;
 				break;
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index b3032b1f1de..474a6e5bea2 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -141,7 +141,7 @@ _bt_mkscankey(Relation rel, IndexTuple itup)
 	 * In NULLS NOT DISTINCT mode, we pretend that there are no null keys, so
 	 * that full uniqueness check is done.
 	 */
-	if (rel->rd_index->indnullsnotdistinct)
+	if (RelationGetIndex(rel)->indnullsnotdistinct)
 		key->anynullkeys = false;
 
 	return key;
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index f2ee333f60d..90a3380a7a0 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -126,14 +126,14 @@ GetIndexInputType(Relation index, AttrNumber indexcol)
 	List	   *indexprs;
 	ListCell   *indexpr_item;
 
-	Assert(index->rd_index != NULL);
-	Assert(indexcol > 0 && indexcol <= index->rd_index->indnkeyatts);
+	Assert(RelationGetIndex(index) != NULL);
+	Assert(indexcol > 0 && indexcol <= RelationGetIndex(index)->indnkeyatts);
 	opcintype = index->rd_opcintype[indexcol - 1];
 	if (!IsPolymorphicType(opcintype))
 		return opcintype;
-	heapcol = index->rd_index->indkey.values[indexcol - 1];
+	heapcol = RelationGetIndex(index)->indkey.values[indexcol - 1];
 	if (heapcol != 0)			/* Simple index column? */
-		return getBaseType(get_atttype(index->rd_index->indrelid, heapcol));
+		return getBaseType(get_atttype(RelationGetIndex(index)->indrelid, heapcol));
 
 	/*
 	 * If the index expressions are already cached, skip calling
@@ -146,9 +146,9 @@ GetIndexInputType(Relation index, AttrNumber indexcol)
 	else
 		indexprs = RelationGetIndexExpressions(index);
 	indexpr_item = list_head(indexprs);
-	for (int i = 1; i <= index->rd_index->indnkeyatts; i++)
+	for (int i = 1; i <= RelationGetIndex(index)->indnkeyatts; i++)
 	{
-		if (index->rd_index->indkey.values[i - 1] == 0)
+		if (RelationGetIndex(index)->indkey.values[i - 1] == 0)
 		{
 			/* expression column */
 			if (indexpr_item == NULL)
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index cf9b88b3e25..a38c398bd66 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -697,7 +697,7 @@ pg_nextoid(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("pg_nextoid() can only be used on system catalogs")));
 
-	if (idx->rd_index->indrelid != RelationGetRelid(rel))
+	if (RelationGetIndex(idx)->indrelid != RelationGetRelid(rel))
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("index \"%s\" does not belong to table \"%s\"",
@@ -721,7 +721,7 @@ pg_nextoid(PG_FUNCTION_ARGS)
 						NameStr(*attname), "oid")));
 
 	if (IndexRelationGetNumberOfKeyAttributes(idx) != 1 ||
-		idx->rd_index->indkey.values[0] != attno)
+		RelationGetIndex(idx)->indkey.values[0] != attno)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("index \"%s\" is not the index for column \"%s\"",
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index ce84c124cc1..deb785303fc 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1250,7 +1250,7 @@ index_create(Relation heapRelation,
 	else
 		Assert(indexRelation->rd_indexcxt != NULL);
 
-	indexRelation->rd_index->indnkeyatts = indexInfo->ii_NumIndexKeyAttrs;
+	RelationGetIndex(indexRelation)->indnkeyatts = indexInfo->ii_NumIndexKeyAttrs;
 
 	/* Validate opclass-specific options */
 	if (opclassOptions)
@@ -1420,7 +1420,7 @@ index_create_copy(Relation heapRelation, uint16 flags,
 							oldInfo->ii_WithoutOverlaps);
 
 	/* fetch exclusion constraint info if any */
-	if (indexRelation->rd_index->indisexclusion)
+	if (RelationGetIndex(indexRelation)->indisexclusion)
 	{
 		/*
 		 * XXX Beware: we're making newInfo point to oldInfo-owned memory.  It
@@ -2461,7 +2461,7 @@ IndexInfo *
 BuildIndexInfo(Relation index)
 {
 	IndexInfo  *ii;
-	Form_pg_index indexStruct = index->rd_index;
+	Form_pg_index indexStruct = RelationGetIndex(index);
 	int			i;
 	int			numAtts;
 
@@ -2521,7 +2521,7 @@ IndexInfo *
 BuildDummyIndexInfo(Relation index)
 {
 	IndexInfo  *ii;
-	Form_pg_index indexStruct = index->rd_index;
+	Form_pg_index indexStruct = RelationGetIndex(index);
 	int			i;
 	int			numAtts;
 
diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c
index fd7d2ec0e3a..bfaec5bf2d7 100644
--- a/src/backend/catalog/indexing.c
+++ b/src/backend/catalog/indexing.c
@@ -135,7 +135,7 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple,
 		Assert(indexInfo->ii_Expressions == NIL);
 		Assert(indexInfo->ii_Predicate == NIL);
 		Assert(indexInfo->ii_ExclusionOps == NULL);
-		Assert(index->rd_index->indimmediate);
+		Assert(RelationGetIndex(index)->indimmediate);
 		Assert(indexInfo->ii_NumIndexKeyAttrs != 0);
 
 		/* see earlier check above */
@@ -172,7 +172,7 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple,
 					 isnull,	/* is-null flags */
 					 &(heapTuple->t_self),	/* tid of heap tuple */
 					 heapRelation,
-					 index->rd_index->indisunique ?
+					 RelationGetIndex(index)->indisunique ?
 					 UNIQUE_CHECK_YES : UNIQUE_CHECK_NO,
 					 false,
 					 indexInfo);
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index b8a0c908637..763a37db586 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -1496,7 +1496,7 @@ DefineIndex(ParseState *pstate,
 														  createdConstraintId,
 														  childRelid);
 
-						if (!cldidx->rd_index->indisvalid)
+						if (!RelationGetIndex(cldidx)->indisvalid)
 							invalidate_parent = true;
 
 						found = true;
@@ -3858,14 +3858,14 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein
 					Relation	indexRelation = index_open(cellOid,
 														   ShareUpdateExclusiveLock);
 
-					if (!indexRelation->rd_index->indisvalid)
+					if (!RelationGetIndex(indexRelation)->indisvalid)
 						ereport(WARNING,
 								(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 								 errmsg("skipping reindex of invalid index \"%s.%s\"",
 										get_namespace_name(get_rel_namespace(cellOid)),
 										get_rel_name(cellOid)),
 								 errhint("Use DROP INDEX or REINDEX INDEX.")));
-					else if (indexRelation->rd_index->indisexclusion)
+					else if (RelationGetIndex(indexRelation)->indisexclusion)
 						ereport(WARNING,
 								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 								 errmsg("cannot reindex exclusion constraint index \"%s.%s\" concurrently, skipping",
@@ -3911,7 +3911,7 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein
 						Relation	indexRelation = index_open(cellOid,
 															   ShareUpdateExclusiveLock);
 
-						if (!indexRelation->rd_index->indisvalid)
+						if (!RelationGetIndex(indexRelation)->indisvalid)
 							ereport(WARNING,
 									(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 									 errmsg("skipping reindex of invalid index \"%s.%s\"",
@@ -4088,7 +4088,7 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein
 		Oid			tablespaceid;
 
 		indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock);
-		heapRel = table_open(indexRel->rd_index->indrelid,
+		heapRel = table_open(RelationGetIndex(indexRel)->indrelid,
 							 ShareUpdateExclusiveLock);
 
 		/*
@@ -4132,7 +4132,7 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein
 		concurrentName = ChooseRelationName(get_rel_name(idx->indexId),
 											NULL,
 											"ccnew",
-											get_rel_namespace(indexRel->rd_index->indrelid),
+											get_rel_namespace(RelationGetIndex(indexRel)->indrelid),
 											false);
 
 		/* Choose the new tablespace, indexes of toast tables are not moved */
@@ -4736,7 +4736,7 @@ IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
 			ObjectAddressSet(partIdx, RelationRelationId, partRelid);
 			ObjectAddressSet(parentIdx, RelationRelationId, parentOid);
 			ObjectAddressSet(partitionTbl, RelationRelationId,
-							 partitionIdx->rd_index->indrelid);
+							 RelationGetIndex(partitionIdx)->indrelid);
 			recordDependencyOn(&partIdx, &parentIdx,
 							   DEPENDENCY_PARTITION_PRI);
 			recordDependencyOn(&partIdx, &partitionTbl,
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index 40748958eaf..a949482d99c 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -727,7 +727,7 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner,
 		indexRel = index_open(indexoid, RowExclusiveLock);
 		if (is_usable_unique_index(indexRel))
 		{
-			Form_pg_index indexStruct = indexRel->rd_index;
+			Form_pg_index indexStruct = RelationGetIndex(indexRel);
 			int			indnkeyatts = indexStruct->indnkeyatts;
 			oidvector  *indclass;
 			Datum		indclassDatum;
@@ -904,7 +904,7 @@ refresh_by_heap_swap(Oid matviewOid, Oid OIDNewHeap, char relpersistence)
 static bool
 is_usable_unique_index(Relation indexRel)
 {
-	Form_pg_index indexStruct = indexRel->rd_index;
+	Form_pg_index indexStruct = RelationGetIndex(indexRel);
 
 	/*
 	 * Must be unique, valid, immediate, non-partial, and be defined over
diff --git a/src/backend/commands/propgraphcmds.c b/src/backend/commands/propgraphcmds.c
index 2bdce331c49..cabcc90d4f3 100644
--- a/src/backend/commands/propgraphcmds.c
+++ b/src/backend/commands/propgraphcmds.c
@@ -343,7 +343,7 @@ propgraph_element_get_key(ParseState *pstate, const List *key_clause, Relation e
 
 			indexDesc = index_open(pkidx, AccessShareLock);
 			a = array_from_attnums(IndexRelationGetNumberOfKeyAttributes(indexDesc),
-								   indexDesc->rd_index->indkey.values);
+								   RelationGetIndex(indexDesc)->indkey.values);
 			index_close(indexDesc, NoLock);
 		}
 	}
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 477c86b2ba6..13b3e238718 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -597,7 +597,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid,
 	 */
 	if (cmd != REPACK_COMMAND_CLUSTER &&
 		!allowSystemTableMods && OidIsValid(indexOid) &&
-		IsCatalogRelation(OldHeap) && !index->rd_index->indisclustered)
+		IsCatalogRelation(OldHeap) && !RelationGetIndex(index)->indisclustered)
 		ereport(ERROR,
 				errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				errmsg("permission denied: \"%s\" is a system catalog",
@@ -746,8 +746,8 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid, LOCKMODE lockmode)
 	/*
 	 * Check that index is in fact an index on the given relation
 	 */
-	if (OldIndex->rd_index == NULL ||
-		OldIndex->rd_index->indrelid != RelationGetRelid(OldHeap))
+	if (RelationGetIndex(OldIndex) == NULL ||
+		RelationGetIndex(OldIndex)->indrelid != RelationGetRelid(OldHeap))
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 				 errmsg("\"%s\" is not an index for table \"%s\"",
@@ -781,7 +781,7 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid, LOCKMODE lockmode)
 	 * might put recently-dead tuples out-of-order in the new table, and there
 	 * is little harm in that.)
 	 */
-	if (!OldIndex->rd_index->indisvalid)
+	if (!RelationGetIndex(OldIndex)->indisvalid)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot cluster on invalid index \"%s\"",
@@ -2893,7 +2893,7 @@ static bool
 find_target_tuple(Relation rel, ChangeContext *chgcxt, TupleTableSlot *locator,
 				  TupleTableSlot *retrieved)
 {
-	Form_pg_index idx = chgcxt->cc_ident_index->rd_index;
+	Form_pg_index idx = RelationGetIndex(chgcxt->cc_ident_index);
 	IndexScanDesc scan;
 	bool		retval = false;
 
@@ -2952,7 +2952,7 @@ identity_key_equal(ChangeContext *chgcxt, TupleTableSlot *locator,
 	for (int i = 0; i < chgcxt->cc_ident_key_nentries; i++)
 	{
 		ScanKey		entry = &chgcxt->cc_ident_key[i];
-		AttrNumber	attno = chgcxt->cc_ident_index->rd_index->indkey.values[i];
+		AttrNumber	attno = RelationGetIndex(chgcxt->cc_ident_index)->indkey.values[i];
 
 		Assert(attno > 0);
 
@@ -3123,7 +3123,7 @@ initialize_change_context(ChangeContext *chgcxt,
 	{
 		Form_pg_index indexForm;
 
-		indexForm = chgcxt->cc_ident_index->rd_index;
+		indexForm = RelationGetIndex(chgcxt->cc_ident_index);
 		chgcxt->cc_ident_key_nentries = indexForm->indnkeyatts;
 		chgcxt->cc_ident_key = (ScanKey) palloc_array(ScanKeyData, indexForm->indnkeyatts);
 		for (int i = 0; i < indexForm->indnkeyatts; i++)
@@ -3167,7 +3167,7 @@ initialize_change_context(ChangeContext *chgcxt,
 	chgcxt->cc_last_key_attno = InvalidAttrNumber;
 	for (int i = 0; i < chgcxt->cc_ident_key_nentries; i++)
 	{
-		AttrNumber	attno = chgcxt->cc_ident_index->rd_index->indkey.values[i];
+		AttrNumber	attno = RelationGetIndex(chgcxt->cc_ident_index)->indkey.values[i];
 
 		Assert(attno > 0);
 		chgcxt->cc_last_key_attno = Max(chgcxt->cc_last_key_attno, attno);
@@ -3432,7 +3432,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 		newName = ChooseRelationName(get_rel_name(oldindex),
 									 NULL,
 									 "repacknew",
-									 get_rel_namespace(ind->rd_index->indrelid),
+									 get_rel_namespace(RelationGetIndex(ind)->indrelid),
 									 false);
 		newindex = index_create_copy(NewHeap, INDEX_CREATE_SUPPRESS_PROGRESS,
 									 oldindex, ind->rd_rel->reltablespace,
@@ -3478,7 +3478,7 @@ copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
 	ScanKeyInit(&skey,
 				Anum_pg_constraint_conrelid,
 				BTEqualStrategyNumber, F_OIDEQ,
-				ObjectIdGetDatum(old_index->rd_index->indrelid));
+				ObjectIdGetDatum(RelationGetIndex(old_index)->indrelid));
 	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
 							  NULL, 1, &skey);
 	desc = RelationGetDescr(rel);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index fd144d783d9..bca3e51fb1c 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -1312,7 +1312,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 
 			if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
 			{
-				if (idxRel->rd_index->indisunique)
+				if (RelationGetIndex(idxRel)->indisunique)
 					ereport(ERROR,
 							(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 							 errmsg("cannot create foreign partition of partitioned table \"%s\"",
@@ -9085,12 +9085,12 @@ ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newVa
 	if (rel->rd_rel->relkind == RELKIND_INDEX ||
 		rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
 	{
-		if (attnum > rel->rd_index->indnkeyatts)
+		if (attnum > RelationGetIndex(rel)->indnkeyatts)
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
 							NameStr(attrtuple->attname), RelationGetRelationName(rel))));
-		else if (rel->rd_index->indkey.values[attnum - 1] != 0)
+		else if (RelationGetIndex(rel)->indkey.values[attnum - 1] != 0)
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
@@ -9225,9 +9225,9 @@ SetIndexStorageProperties(Relation rel, Relation attrelation,
 
 		indrel = index_open(indexoid, lockmode);
 
-		for (int i = 0; i < indrel->rd_index->indnatts; i++)
+		for (int i = 0; i < RelationGetIndex(indrel)->indnatts; i++)
 		{
-			if (indrel->rd_index->indkey.values[i] == attnum)
+			if (RelationGetIndex(indrel)->indkey.values[i] == attnum)
 			{
 				indattnum = i + 1;
 				break;
@@ -14630,8 +14630,8 @@ dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior beha
 				Relation	pk = relation_open(pkindex, AccessShareLock);
 
 				pkattrs = NULL;
-				for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
-					pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
+				for (int i = 0; i < RelationGetIndex(pk)->indnkeyatts; i++)
+					pkattrs = bms_add_member(pkattrs, RelationGetIndex(pk)->indkey.values[i]);
 
 				relation_close(pk, AccessShareLock);
 			}
@@ -19204,8 +19204,8 @@ ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode
 	indexRel = index_open(indexOid, ShareLock);
 
 	/* Check that the index is on the relation we're altering. */
-	if (indexRel->rd_index == NULL ||
-		indexRel->rd_index->indrelid != RelationGetRelid(rel))
+	if (RelationGetIndex(indexRel) == NULL ||
+		RelationGetIndex(indexRel)->indrelid != RelationGetRelid(rel))
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 				 errmsg("\"%s\" is not an index for table \"%s\"",
@@ -19218,14 +19218,14 @@ ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode
 	 * exclusion), we can use that too.
 	 */
 	if ((!indexRel->rd_indam->amcanunique ||
-		 !indexRel->rd_index->indisunique) &&
-		!(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
+		 !RelationGetIndex(indexRel)->indisunique) &&
+		!(RelationGetIndex(indexRel)->indisunique && RelationGetIndex(indexRel)->indisexclusion))
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 				 errmsg("cannot use non-unique index \"%s\" as replica identity",
 						RelationGetRelationName(indexRel))));
 	/* Deferred indexes are not guaranteed to be always unique. */
-	if (!indexRel->rd_index->indimmediate)
+	if (!RelationGetIndex(indexRel)->indimmediate)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot use non-immediate index \"%s\" as replica identity",
@@ -19246,7 +19246,7 @@ ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode
 	/* Check index for nullable columns. */
 	for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
 	{
-		int16		attno = indexRel->rd_index->indkey.values[key];
+		int16		attno = RelationGetIndex(indexRel)->indkey.values[key];
 		Form_pg_attribute attr;
 		HeapTuple	contup;
 		Form_pg_constraint conForm;
@@ -21377,8 +21377,8 @@ AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
 			Oid			idx = lfirst_oid(cell);
 			Relation	idxRel = index_open(idx, AccessShareLock);
 
-			if (idxRel->rd_index->indisunique ||
-				idxRel->rd_index->indisprimary)
+			if (RelationGetIndex(idxRel)->indisunique ||
+				RelationGetIndex(idxRel)->indisprimary)
 				ereport(ERROR,
 						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 						 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
@@ -21437,7 +21437,7 @@ AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
 				continue;
 
 			/* If this index is invalid, can't use it */
-			if (!attachrelIdxRels[i]->rd_index->indisvalid)
+			if (!RelationGetIndex(attachrelIdxRels[i])->indisvalid)
 				continue;
 
 			if (CompareIndexInfo(attachInfos[i], info,
@@ -22361,7 +22361,7 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
 	 * partition, so lock that one too.
 	 */
 	state.partitionOid = InvalidOid;
-	state.parentTblOid = parentIdx->rd_index->indrelid;
+	state.parentTblOid = RelationGetIndex(parentIdx)->indrelid;
 	state.lockedParentTbl = false;
 	partIdxId =
 		RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
@@ -22377,8 +22377,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
 	partIdx = relation_open(partIdxId, AccessExclusiveLock);
 
 	/* we already hold locks on both tables, so this is safe: */
-	parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
-	partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
+	parentTbl = relation_open(RelationGetIndex(parentIdx)->indrelid, AccessShareLock);
+	partTbl = relation_open(RelationGetIndex(partIdx)->indrelid, NoLock);
 
 	ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
 
@@ -22481,7 +22481,7 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
 		 * If it's a primary key, make sure the columns in the partition are
 		 * NOT NULL.
 		 */
-		if (parentIdx->rd_index->indisprimary)
+		if (RelationGetIndex(parentIdx)->indisprimary)
 			verifyPartitionIndexNotNull(childInfo, partTbl);
 
 		/* All good -- do it */
@@ -22494,7 +22494,7 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
 
 		validatePartitionedIndex(parentIdx, parentTbl);
 	}
-	else if (!parentIdx->rd_index->indisvalid)
+	else if (!RelationGetIndex(parentIdx)->indisvalid)
 	{
 		/*
 		 * The index is attached, but the parent is still invalid; see if it
@@ -22627,7 +22627,7 @@ validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
 		parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
 		parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
 		parentTbl = relation_open(parentTblId, AccessExclusiveLock);
-		Assert(!parentIdx->rd_index->indisvalid);
+		Assert(!RelationGetIndex(parentIdx)->indisvalid);
 
 		validatePartitionedIndex(parentIdx, parentTbl);
 
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 161f50ea560..a14dac060e3 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -2441,7 +2441,7 @@ vac_open_indexes(Relation relation, LOCKMODE lockmode,
 		Relation	indrel;
 
 		indrel = index_open(indexoid, lockmode);
-		if (indrel->rd_index->indisready)
+		if (RelationGetIndex(indrel)->indisready)
 			(*Irel)[i++] = indrel;
 		else
 			index_close(indrel, lockmode);
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index eb383812901..f02abe64872 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -219,7 +219,7 @@ ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative)
 		 * If the indexes are to be used for speculative insertion, add extra
 		 * information required by unique index entries.
 		 */
-		if (speculative && ii->ii_Unique && !indexDesc->rd_index->indisexclusion)
+		if (speculative && ii->ii_Unique && !RelationGetIndex(indexDesc)->indisexclusion)
 			BuildSpeculativeIndexInfo(indexDesc, ii);
 
 		relationDescs[i] = indexDesc;
@@ -411,7 +411,7 @@ ExecInsertIndexTuples(ResultRelInfo *resultRelInfo,
 		applyNoDupErr = (flags & EIIT_NO_DUPE_ERROR) &&
 			(arbiterIndexes == NIL ||
 			 list_member_oid(arbiterIndexes,
-							 indexRelation->rd_index->indexrelid));
+							 RelationGetIndex(indexRelation)->indexrelid));
 
 		/*
 		 * The index AM does the actual insertion, plus uniqueness checking.
@@ -426,11 +426,11 @@ ExecInsertIndexTuples(ResultRelInfo *resultRelInfo,
 		 * For a speculative insertion (used by INSERT ... ON CONFLICT), do
 		 * the same as for a deferrable unique index.
 		 */
-		if (!indexRelation->rd_index->indisunique)
+		if (!RelationGetIndex(indexRelation)->indisunique)
 			checkUnique = UNIQUE_CHECK_NO;
 		else if (applyNoDupErr)
 			checkUnique = UNIQUE_CHECK_PARTIAL;
-		else if (indexRelation->rd_index->indimmediate)
+		else if (RelationGetIndex(indexRelation)->indimmediate)
 			checkUnique = UNIQUE_CHECK_YES;
 		else
 			checkUnique = UNIQUE_CHECK_PARTIAL;
@@ -480,7 +480,7 @@ ExecInsertIndexTuples(ResultRelInfo *resultRelInfo,
 				violationOK = true;
 				waitMode = CEOUC_LIVELOCK_PREVENTING_WAIT;
 			}
-			else if (!indexRelation->rd_index->indimmediate)
+			else if (!RelationGetIndex(indexRelation)->indimmediate)
 			{
 				violationOK = true;
 				waitMode = CEOUC_NOWAIT;
@@ -510,7 +510,7 @@ ExecInsertIndexTuples(ResultRelInfo *resultRelInfo,
 			 * speculative conflict, since that always requires a restart.
 			 */
 			result = lappend_oid(result, RelationGetRelid(indexRelation));
-			if (indexRelation->rd_index->indimmediate && specConflict)
+			if (RelationGetIndex(indexRelation)->indimmediate && specConflict)
 				*specConflict = true;
 		}
 	}
@@ -600,10 +600,10 @@ ExecCheckIndexConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
 		/* When specific arbiter indexes requested, only examine them */
 		if (arbiterIndexes != NIL &&
 			!list_member_oid(arbiterIndexes,
-							 indexRelation->rd_index->indexrelid))
+							 RelationGetIndex(indexRelation)->indexrelid))
 			continue;
 
-		if (!indexRelation->rd_index->indimmediate)
+		if (!RelationGetIndex(indexRelation)->indimmediate)
 			ereport(ERROR,
 					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 					 errmsg("ON CONFLICT does not support deferrable unique constraints/exclusion constraints as arbiters"),
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index 0e938cfea1f..0f5780b460f 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -506,7 +506,7 @@ IsIndexCompatibleAsArbiter(Relation arbiterIndexRelation,
 						   Relation indexRelation,
 						   IndexInfo *indexInfo)
 {
-	Assert(arbiterIndexRelation->rd_index->indrelid == indexRelation->rd_index->indrelid);
+	Assert(RelationGetIndex(arbiterIndexRelation)->indrelid == RelationGetIndex(indexRelation)->indrelid);
 
 	/* must match whether they're unique */
 	if (arbiterIndexInfo->ii_Unique != indexInfo->ii_Unique)
@@ -537,8 +537,8 @@ IsIndexCompatibleAsArbiter(Relation arbiterIndexRelation,
 			indexRelation->rd_opfamily[i])
 			return false;
 
-		if (arbiterIndexRelation->rd_index->indkey.values[i] !=
-			indexRelation->rd_index->indkey.values[i])
+		if (RelationGetIndex(arbiterIndexRelation)->indkey.values[i] !=
+			RelationGetIndex(indexRelation)->indkey.values[i])
 			return false;
 	}
 
@@ -837,7 +837,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
 					unparented_ii = leaf_part_rri->ri_IndexRelationInfo[unparented_i];
 
 					Assert(!list_member_oid(arbiterIndexes,
-											unparented_rel->rd_index->indexrelid));
+											RelationGetIndex(unparented_rel)->indexrelid));
 
 					/* Ignore indexes not ready */
 					if (!unparented_ii->ii_ReadyForInserts)
@@ -861,7 +861,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
 													   unparented_ii))
 						{
 							arbiterIndexes = lappend_oid(arbiterIndexes,
-														 unparented_rel->rd_index->indexrelid);
+														 RelationGetIndex(unparented_rel)->indexrelid);
 							additional_arbiters++;
 							break;
 						}
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index b2ca5cbf117..75d4bb332d9 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -62,7 +62,7 @@ build_replindex_scan_key(ScanKey skey, Relation rel, Relation idxrel,
 	int			skey_attoff = 0;
 	Datum		indclassDatum;
 	oidvector  *opclass;
-	int2vector *indkey = &idxrel->rd_index->indkey;
+	int2vector *indkey = &RelationGetIndex(idxrel)->indkey;
 
 	indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, idxrel->rd_indextuple,
 										   Anum_pg_index_indclass);
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 8afcbde6b97..f32b507af25 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -677,7 +677,7 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	}
 
 	indexstate->ioss_NameCStringAttNums = NULL;
-	indnkeyatts = indexRelation->rd_index->indnkeyatts;
+	indnkeyatts = RelationGetIndex(indexRelation)->indnkeyatts;
 	namecount = 0;
 
 	/*
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 4c9c5e9fc33..2793d2c371b 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -236,7 +236,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 			 * Extract info from the relation descriptor for the index.
 			 */
 			indexRelation = index_open(indexoid, lmode);
-			index = indexRelation->rd_index;
+			index = RelationGetIndex(indexRelation);
 
 			/*
 			 * Ignore invalid indexes, since they can't safely be used for
@@ -916,7 +916,7 @@ infer_arbiter_indexes(PlannerInfo *root)
 		 */
 		foreach_ptr(RelationData, idxRel, indexRelList)
 		{
-			Form_pg_index idxForm = idxRel->rd_index;
+			Form_pg_index idxForm = RelationGetIndex(idxRel);
 
 			if (indexOidFromConstraint == idxForm->indexrelid)
 			{
@@ -932,7 +932,7 @@ infer_arbiter_indexes(PlannerInfo *root)
 				{
 					int			attno;
 
-					attno = idxRel->rd_index->indkey.values[natt];
+					attno = RelationGetIndex(idxRel)->indkey.values[natt];
 					if (attno != InvalidAttrNumber)
 						inferAttrs =
 							bms_add_member(inferAttrs,
@@ -966,7 +966,7 @@ infer_arbiter_indexes(PlannerInfo *root)
 		 * enforcement needs to occur there anyway when an inference clause is
 		 * omitted.
 		 */
-		idxForm = idxRel->rd_index;
+		idxForm = RelationGetIndex(idxRel);
 
 		/*
 		 * Ignore indexes that aren't indisready, because we cannot trust
@@ -1058,7 +1058,7 @@ infer_arbiter_indexes(PlannerInfo *root)
 		indexedAttrs = NULL;
 		for (natt = 0; natt < idxForm->indnkeyatts; natt++)
 		{
-			int			attno = idxRel->rd_index->indkey.values[natt];
+			int			attno = RelationGetIndex(idxRel)->indkey.values[natt];
 
 			if (attno != 0)
 				indexedAttrs = bms_add_member(indexedAttrs,
@@ -1247,7 +1247,7 @@ infer_collation_opclass_match(InferenceElem *elem, Relation idxRel,
 		Oid			opfamily = idxRel->rd_opfamily[natt - 1];
 		Oid			opcinputtype = idxRel->rd_opcintype[natt - 1];
 		Oid			collation = idxRel->rd_indcollation[natt - 1];
-		int			attno = idxRel->rd_index->indkey.values[natt - 1];
+		int			attno = RelationGetIndex(idxRel)->indkey.values[natt - 1];
 
 		if (attno != 0)
 			nplain++;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index d83616a8507..bc40fc29a83 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -2443,7 +2443,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 
 		/* Open the index (this will throw an error if it is not an index) */
 		index_rel = index_open(index_oid, AccessShareLock);
-		index_form = index_rel->rd_index;
+		index_form = RelationGetIndex(index_rel);
 
 		/* Check that it does not have an associated constraint already */
 		if (OidIsValid(get_index_constraint(index_oid)))
diff --git a/src/backend/replication/logical/conflict.c b/src/backend/replication/logical/conflict.c
index f0a6ed20c60..ed2cd75b25e 100644
--- a/src/backend/replication/logical/conflict.c
+++ b/src/backend/replication/logical/conflict.c
@@ -325,7 +325,7 @@ InitConflictIndexes(ResultRelInfo *relInfo)
 			continue;
 
 		/* Don't support conflict detection for deferrable index */
-		if (!indexRelation->rd_index->indimmediate)
+		if (!RelationGetIndex(indexRelation)->indimmediate)
 			continue;
 
 		uniqueIndexes = lappend_oid(uniqueIndexes,
diff --git a/src/backend/replication/logical/relation.c b/src/backend/replication/logical/relation.c
index 8ffd2583afb..b9fd0233d96 100644
--- a/src/backend/replication/logical/relation.c
+++ b/src/backend/replication/logical/relation.c
@@ -800,7 +800,7 @@ FindUsableIndexForReplicaIdentityFull(Relation localrel, AttrMap *attrmap)
 		 * permanently invalid leftover of a failed CREATE INDEX CONCURRENTLY
 		 * must never be chosen here.
 		 */
-		isUsableIdx = idxRel->rd_index->indisvalid &&
+		isUsableIdx = RelationGetIndex(idxRel)->indisvalid &&
 			IsIndexUsableForReplicaIdentityFull(idxRel, attrmap);
 		index_close(idxRel, AccessShareLock);
 
@@ -849,14 +849,14 @@ IsIndexUsableForReplicaIdentityFull(Relation idxrel, AttrMap *attrmap)
 	if (!heap_attisnull(idxrel->rd_indextuple, Anum_pg_index_indpred, NULL))
 		return false;
 
-	Assert(idxrel->rd_index->indnatts >= 1);
+	Assert(RelationGetIndex(idxrel)->indnatts >= 1);
 
 	indclass = (oidvector *) DatumGetPointer(SysCacheGetAttrNotNull(INDEXRELID,
 																	idxrel->rd_indextuple,
 																	Anum_pg_index_indclass));
 
 	/* Ensure that the index has a valid equal strategy for each key column */
-	for (int i = 0; i < idxrel->rd_index->indnkeyatts; i++)
+	for (int i = 0; i < RelationGetIndex(idxrel)->indnkeyatts; i++)
 	{
 		Oid			opfamily;
 
@@ -881,7 +881,7 @@ IsIndexUsableForReplicaIdentityFull(Relation idxrel, AttrMap *attrmap)
 	}
 
 	/* The leftmost index field must not be an expression */
-	keycol = idxrel->rd_index->indkey.values[0];
+	keycol = RelationGetIndex(idxrel)->indkey.values[0];
 	if (!AttributeNumberIsValid(keycol))
 		return false;
 
diff --git a/src/backend/statistics/stat_utils.c b/src/backend/statistics/stat_utils.c
index f4ff9ab9b20..2029676463d 100644
--- a/src/backend/statistics/stat_utils.c
+++ b/src/backend/statistics/stat_utils.c
@@ -323,13 +323,13 @@ statatt_get_index_expr(Relation rel, int attnum)
 	 * The index's attnum points directly to a relation attnum, hence it is
 	 * not an expression attribute.
 	 */
-	if (rel->rd_index->indkey.values[attnum - 1] != 0)
+	if (RelationGetIndex(rel)->indkey.values[attnum - 1] != 0)
 		return NULL;
 
 	indexpr_item = list_head(rel->rd_indexprs);
 
 	for (int i = 0; i < attnum - 1; i++)
-		if (rel->rd_index->indkey.values[i] == 0)
+		if (RelationGetIndex(rel)->indkey.values[i] == 0)
 			indexpr_item = lnext(rel->rd_indexprs, indexpr_item);
 
 	if (indexpr_item == NULL)	/* shouldn't happen */
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 0ae85b7d5b4..cd3f725f221 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -2558,7 +2558,7 @@ PredicateLockTID(Relation relation, const ItemPointerData *tid, Snapshot snapsho
 	/*
 	 * Return if this xact wrote it.
 	 */
-	if (relation->rd_index == NULL)
+	if (RelationGetIndex(relation) == NULL)
 	{
 		/* If we wrote it; we already have a write lock. */
 		if (TransactionIdIsCurrentTransactionId(tuple_xid))
@@ -2890,7 +2890,7 @@ DropAllPredicateLocksFromTable(Relation relation, bool transfer)
 
 	dbId = relation->rd_locator.dbOid;
 	relId = relation->rd_id;
-	if (relation->rd_index == NULL)
+	if (RelationGetIndex(relation) == NULL)
 	{
 		isIndex = false;
 		heapId = relId;
@@ -2898,7 +2898,7 @@ DropAllPredicateLocksFromTable(Relation relation, bool transfer)
 	else
 	{
 		isIndex = true;
-		heapId = relation->rd_index->indrelid;
+		heapId = RelationGetIndex(relation)->indrelid;
 	}
 	Assert(heapId != InvalidOid);
 	Assert(transfer || !isIndex);	/* index OID only makes sense with
@@ -4371,7 +4371,7 @@ CheckTableForSerializableConflictIn(Relation relation)
 	 */
 	MyXactDidWrite = true;
 
-	Assert(relation->rd_index == NULL); /* not an index relation */
+	Assert(RelationGetIndex(relation) == NULL); /* not an index relation */
 
 	dbId = relation->rd_locator.dbOid;
 	heapId = relation->rd_id;
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index d4545618634..85911de07b3 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -3658,7 +3658,7 @@ ri_populate_fastpath_metadata(RI_ConstraintInfo *riinfo,
 		 */
 		for (idx_col = 0; idx_col < riinfo->nkeys; idx_col++)
 		{
-			if (idx_rel->rd_index->indkey.values[idx_col] == riinfo->pk_attnums[i])
+			if (RelationGetIndex(idx_rel)->indkey.values[idx_col] == riinfo->pk_attnums[i])
 				break;
 		}
 		Assert(idx_col < riinfo->nkeys);
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 511270edfda..482c1bcdd89 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -1278,8 +1278,8 @@ InitCatCachePhase2(CatCache *cache, bool touch_index)
 		 * catch thinkos in definitions of new catcaches, so we don't worry
 		 * about the pg_am indexes not getting tested.
 		 */
-		Assert(idesc->rd_index->indisunique &&
-			   idesc->rd_index->indimmediate);
+		Assert(RelationGetIndex(idesc)->indisunique &&
+			   RelationGetIndex(idesc)->indimmediate);
 
 		index_close(idesc, AccessShareLock);
 		UnlockRelationOid(cache->cc_reloid, AccessShareLock);
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 0501755dbce..73e67892c6a 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -1463,7 +1463,6 @@ RelationInitIndexAccessInfo(Relation relation)
 			 RelationGetRelid(relation));
 	oldcontext = MemoryContextSwitchTo(CacheMemoryContext);
 	relation->rd_indextuple = heap_copytuple(tuple);
-	relation->rd_index = (Form_pg_index) GETSTRUCT(relation->rd_indextuple);
 	MemoryContextSwitchTo(oldcontext);
 	ReleaseSysCache(tuple);
 
@@ -2338,7 +2337,7 @@ RelationReloadIndexInfo(Relation relation)
 	if (!IsSystemRelation(relation))
 	{
 		HeapTuple	tuple;
-		Form_pg_index index;
+		Form_pg_index index, rd_index;
 
 		tuple = SearchSysCache1(INDEXRELID,
 								ObjectIdGetDatum(RelationGetRelid(relation)));
@@ -2353,17 +2352,18 @@ RelationReloadIndexInfo(Relation relation)
 		 * it's not worth it to track exactly which ones they are.  None of
 		 * the array fields are allowed to change, though.
 		 */
-		relation->rd_index->indisunique = index->indisunique;
-		relation->rd_index->indnullsnotdistinct = index->indnullsnotdistinct;
-		relation->rd_index->indisprimary = index->indisprimary;
-		relation->rd_index->indisexclusion = index->indisexclusion;
-		relation->rd_index->indimmediate = index->indimmediate;
-		relation->rd_index->indisclustered = index->indisclustered;
-		relation->rd_index->indisvalid = index->indisvalid;
-		relation->rd_index->indcheckxmin = index->indcheckxmin;
-		relation->rd_index->indisready = index->indisready;
-		relation->rd_index->indislive = index->indislive;
-		relation->rd_index->indisreplident = index->indisreplident;
+		rd_index = RelationGetIndex(relation);
+		rd_index->indisunique = index->indisunique;
+		rd_index->indnullsnotdistinct = index->indnullsnotdistinct;
+		rd_index->indisprimary = index->indisprimary;
+		rd_index->indisexclusion = index->indisexclusion;
+		rd_index->indimmediate = index->indimmediate;
+		rd_index->indisclustered = index->indisclustered;
+		rd_index->indisvalid = index->indisvalid;
+		rd_index->indcheckxmin = index->indcheckxmin;
+		rd_index->indisready = index->indisready;
+		rd_index->indislive = index->indislive;
+		rd_index->indisreplident = index->indisreplident;
 
 		/* Copy xmin too, as that is needed to make sense of indcheckxmin */
 		HeapTupleHeaderSetXmin(relation->rd_indextuple->t_data,
@@ -5483,7 +5483,7 @@ restart:
 			indexPredicate = NULL;
 
 		/* Can this index be referenced by a foreign key? */
-		isKey = indexDesc->rd_index->indisunique &&
+		isKey = RelationGetIndex(indexDesc)->indisunique &&
 			indexExpressions == NULL &&
 			indexPredicate == NULL;
 
@@ -5504,9 +5504,9 @@ restart:
 			attrs = &hotblockingattrs;
 
 		/* Collect simple attribute references */
-		for (i = 0; i < indexDesc->rd_index->indnatts; i++)
+		for (i = 0; i < RelationGetIndex(indexDesc)->indnatts; i++)
 		{
-			int			attrnum = indexDesc->rd_index->indkey.values[i];
+			int			attrnum = RelationGetIndex(indexDesc)->indkey.values[i];
 
 			/*
 			 * Since we have covering indexes with non-key columns, we must
@@ -5527,15 +5527,15 @@ restart:
 				*attrs = bms_add_member(*attrs,
 										attrnum - FirstLowInvalidHeapAttributeNumber);
 
-				if (isKey && i < indexDesc->rd_index->indnkeyatts)
+				if (isKey && i < RelationGetIndex(indexDesc)->indnkeyatts)
 					uindexattrs = bms_add_member(uindexattrs,
 												 attrnum - FirstLowInvalidHeapAttributeNumber);
 
-				if (isPK && i < indexDesc->rd_index->indnkeyatts)
+				if (isPK && i < RelationGetIndex(indexDesc)->indnkeyatts)
 					pkindexattrs = bms_add_member(pkindexattrs,
 												  attrnum - FirstLowInvalidHeapAttributeNumber);
 
-				if (isIDKey && i < indexDesc->rd_index->indnkeyatts)
+				if (isIDKey && i < RelationGetIndex(indexDesc)->indnkeyatts)
 					idindexattrs = bms_add_member(idindexattrs,
 												  attrnum - FirstLowInvalidHeapAttributeNumber);
 			}
@@ -5676,9 +5676,9 @@ RelationGetIdentityKeyBitmap(Relation relation)
 			 relation->rd_replidindex);
 
 	/* Add referenced attributes to idindexattrs */
-	for (i = 0; i < indexDesc->rd_index->indnatts; i++)
+	for (i = 0; i < RelationGetIndex(indexDesc)->indnatts; i++)
 	{
-		int			attrnum = indexDesc->rd_index->indkey.values[i];
+		int			attrnum = RelationGetIndex(indexDesc)->indkey.values[i];
 
 		/*
 		 * We don't include non-key columns into idindexattrs bitmaps. See
@@ -5686,7 +5686,7 @@ RelationGetIdentityKeyBitmap(Relation relation)
 		 */
 		if (attrnum != 0)
 		{
-			if (i < indexDesc->rd_index->indnkeyatts)
+			if (i < RelationGetIndex(indexDesc)->indnkeyatts)
 				idindexattrs = bms_add_member(idindexattrs,
 											  attrnum - FirstLowInvalidHeapAttributeNumber);
 		}
@@ -5765,7 +5765,7 @@ RelationGetExclusionInfo(Relation indexRelation,
 	ScanKeyInit(&skey[0],
 				Anum_pg_constraint_conrelid,
 				BTEqualStrategyNumber, F_OIDEQ,
-				ObjectIdGetDatum(indexRelation->rd_index->indrelid));
+				ObjectIdGetDatum(RelationGetIndex(indexRelation)->indrelid));
 
 	conrel = table_open(ConstraintRelationId, AccessShareLock);
 	conscan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, true,
@@ -6421,7 +6421,6 @@ load_relcache_init_file(bool shared)
 
 			/* Fix up internal pointers in the tuple -- see heap_copytuple */
 			rel->rd_indextuple->t_data = (HeapTupleHeader) ((char *) rel->rd_indextuple + HEAPTUPLESIZE);
-			rel->rd_index = (Form_pg_index) GETSTRUCT(rel->rd_indextuple);
 
 			/*
 			 * prepare index info context --- parameters should match
@@ -6523,7 +6522,6 @@ load_relcache_init_file(bool shared)
 			if (RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind) || rel->rd_rel->relkind == RELKIND_SEQUENCE)
 				RelationInitTableAccessMethod(rel);
 
-			Assert(rel->rd_index == NULL);
 			Assert(rel->rd_indextuple == NULL);
 			Assert(rel->rd_indexcxt == NULL);
 			Assert(rel->rd_indam == NULL);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 5c39086ca0c..c00657a7969 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -14,6 +14,7 @@
 #ifndef REL_H
 #define REL_H
 
+#include "access/htup_details.h"
 #include "access/tupdesc.h"
 #include "access/xlog.h"
 #include "catalog/catalog.h"
@@ -319,19 +320,34 @@ RelationGetLockRelId(Relation relation)
  */
 #define RelationGetNumberOfAttributes(relation) ((relation)->rd_rel->relnatts)
 
+/*
+ * RelationGetIndex
+ *		Returns the pg_index tuple describing an index relation.
+ *
+ * rd_index is not stored directly in RelationData; it is always equal to
+ * GETSTRUCT(relation->rd_indextuple), so we derive it on the fly instead of
+ * spending a pointer-sized field on it.
+ */
+static inline Form_pg_index
+RelationGetIndex(Relation relation)
+{
+	Assert(relation->rd_indextuple != NULL);
+	return (Form_pg_index) GETSTRUCT(relation->rd_indextuple);
+}
+
 /*
  * IndexRelationGetNumberOfAttributes
  *		Returns the number of attributes in an index.
  */
 #define IndexRelationGetNumberOfAttributes(relation) \
-		((relation)->rd_index->indnatts)
+		(RelationGetIndex(relation)->indnatts)
 
 /*
  * IndexRelationGetNumberOfKeyAttributes
  *		Returns the number of key attributes in an index.
  */
 #define IndexRelationGetNumberOfKeyAttributes(relation) \
-		((relation)->rd_index->indnkeyatts)
+		(RelationGetIndex(relation)->indnkeyatts)
 
 /*
  * RelationGetDescr
diff --git a/src/include/utils/rel_internal.h b/src/include/utils/rel_internal.h
index e059b8bf18b..a9079e72be9 100644
--- a/src/include/utils/rel_internal.h
+++ b/src/include/utils/rel_internal.h
@@ -220,7 +220,6 @@ typedef struct RelationData
 		/* fields used only for an index relation */
 		struct
 		{
-			Form_pg_index rd_index;		/* pg_index tuple describing this index */
 			/* use "struct" here to avoid needing to include htup.h: */
 			struct HeapTupleData *rd_indextuple;	/* all of pg_index tuple */
 
-- 
2.51.0

