From 5685810dad4322e941103ad73c3ba58c7f09682d Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
Date: Thu, 5 Feb 2026 21:00:12 +0530
Subject: [PATCH v20260206 4/4] Introduce RelkindIsValid() and remove default
 relkind case

... for demonstration purposes.

Author: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
---
 contrib/pg_overexplain/pg_overexplain.c     |  8 +++---
 src/backend/access/common/reloptions.c      | 28 +++++++++------------
 src/backend/catalog/objectaddress.c         |  6 +++--
 src/backend/catalog/pg_class.c              |  6 ++---
 src/backend/parser/parse_relation.c         |  2 ++
 src/backend/replication/logical/worker.c    |  1 +
 src/backend/replication/pgoutput/pgoutput.c |  1 +
 src/backend/utils/adt/ri_triggers.c         |  2 ++
 src/backend/utils/cache/lsyscache.c         |  9 +++++--
 src/backend/utils/cache/relcache.c          |  5 ++++
 src/include/catalog/pg_class.h              | 21 ++++++++++++++++
 11 files changed, 61 insertions(+), 28 deletions(-)

diff --git a/contrib/pg_overexplain/pg_overexplain.c b/contrib/pg_overexplain/pg_overexplain.c
index d575d462d97..520aaf5cc72 100644
--- a/contrib/pg_overexplain/pg_overexplain.c
+++ b/contrib/pg_overexplain/pg_overexplain.c
@@ -404,7 +404,7 @@ overexplain_range_table(PlannedStmt *plannedstmt, ExplainState *es)
 	{
 		RangeTblEntry *rte = rt_fetch(rti, plannedstmt->rtable);
 		char	   *kind = NULL;
-		char	   *relkind;
+		char	   *relkind = NULL;
 
 		/* NULL entries are possible; skip them */
 		if (rte == NULL)
@@ -534,15 +534,13 @@ overexplain_range_table(PlannedStmt *plannedstmt, ExplainState *es)
 			case RELKIND_PARTITIONED_INDEX:
 				relkind = "partitioned_index";
 				break;
-			default:
-				pg_unreachable();
-				relkind = psprintf("%c", rte->relkind);
-				break;
 		}
 
 		/* If there is a relkind, show it */
 		if (relkind != NULL)
 			ExplainPropertyText("Relation Kind", relkind, es);
+		else
+			Assert(!OidIsValid(rte->relid));
 
 		/* If there is a lock mode, show it */
 		if (rte->rellockmode != 0)
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 00b3588cded..faef1bae9a1 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1488,7 +1488,6 @@ bytea *
 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 				  amoptions_function amoptions)
 {
-	bytea	   *options;
 	bool		isnull;
 	Datum		datum;
 	Form_pg_class classForm;
@@ -1502,34 +1501,31 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 
 	classForm = (Form_pg_class) GETSTRUCT(tuple);
 
+	Assert(RelkindIsValid(classForm->relkind));
+
 	/* Parse into appropriate format; don't error out here */
 	switch (classForm->relkind)
 	{
 		case RELKIND_RELATION:
 		case RELKIND_TOASTVALUE:
 		case RELKIND_MATVIEW:
-			options = heap_reloptions(classForm->relkind, datum, false);
-			break;
+			return heap_reloptions(classForm->relkind, datum, false);
+
 		case RELKIND_PARTITIONED_TABLE:
-			options = partitioned_table_reloptions(datum, false);
-			break;
+			return partitioned_table_reloptions(datum, false);
+
 		case RELKIND_VIEW:
-			options = view_reloptions(datum, false);
-			break;
+			return view_reloptions(datum, false);
+
 		case RELKIND_INDEX:
 		case RELKIND_PARTITIONED_INDEX:
-			options = index_reloptions(amoptions, datum, false);
-			break;
+			return index_reloptions(amoptions, datum, false);
+
 		case RELKIND_FOREIGN_TABLE:
-			options = NULL;
-			break;
-		default:
-			Assert(false);		/* can't get here */
-			options = NULL;		/* keep compiler quiet */
-			break;
+			return NULL;
 	}
 
-	return options;
+	pg_unreachable();
 }
 
 static void
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 93b6e26d067..b0a83ea1276 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -6202,8 +6202,10 @@ get_relkind_objtype(Relkind relkind)
 			return OBJECT_FOREIGN_TABLE;
 		case RELKIND_TOASTVALUE:
 			return OBJECT_TABLE;
-		default:
-			/* Per above, don't raise an error */
+		case RELKIND_COMPOSITE_TYPE:
 			return OBJECT_TABLE;
 	}
+
+	/* Unknown relkind, return default object type as per prologue. */
+	return OBJECT_TABLE;
 }
diff --git a/src/backend/catalog/pg_class.c b/src/backend/catalog/pg_class.c
index a841ed20868..59d99fd12c0 100644
--- a/src/backend/catalog/pg_class.c
+++ b/src/backend/catalog/pg_class.c
@@ -45,8 +45,8 @@ errdetail_relkind_not_supported(Relkind relkind)
 			return errdetail("This operation is not supported for partitioned tables.");
 		case RELKIND_PARTITIONED_INDEX:
 			return errdetail("This operation is not supported for partitioned indexes.");
-		default:
-			elog(ERROR, "unrecognized relkind: '%c'", relkind);
-			return 0;
 	}
+
+	elog(ERROR, "unrecognized relkind: '%c'", relkind);
+	pg_unreachable();
 }
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 3ec8d8de011..851e40a5792 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -1515,6 +1515,7 @@ addRangeTableEntry(ParseState *pstate,
 	rte->relid = RelationGetRelid(rel);
 	rte->inh = inh;
 	rte->relkind = rel->rd_rel->relkind;
+	Assert(RelkindIsValid(rte->relkind));
 	rte->rellockmode = lockmode;
 
 	/*
@@ -1600,6 +1601,7 @@ addRangeTableEntryForRelation(ParseState *pstate,
 	rte->relid = RelationGetRelid(rel);
 	rte->inh = inh;
 	rte->relkind = rel->rd_rel->relkind;
+	Assert(RelkindIsValid(rte->relkind));
 	rte->rellockmode = lockmode;
 
 	/*
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 32725c48623..0d19248f27f 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -884,6 +884,7 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel)
 	rte->rtekind = RTE_RELATION;
 	rte->relid = RelationGetRelid(rel->localrel);
 	rte->relkind = rel->localrel->rd_rel->relkind;
+	Assert(RelkindIsValid(rte->relkind));
 	rte->rellockmode = AccessShareLock;
 
 	addRTEPermissionInfo(&perminfos, rte);
diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c
index 4f1152f7f48..70a8fc7090a 100644
--- a/src/backend/replication/pgoutput/pgoutput.c
+++ b/src/backend/replication/pgoutput/pgoutput.c
@@ -851,6 +851,7 @@ create_estate_for_relation(Relation rel)
 	rte->rtekind = RTE_RELATION;
 	rte->relid = RelationGetRelid(rel);
 	rte->relkind = rel->rd_rel->relkind;
+	Assert(RelkindIsValid(rte->relkind));
 	rte->rellockmode = AccessShareLock;
 
 	addRTEPermissionInfo(&perminfos, rte);
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index bbadecef5f9..802ff4fd0c0 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -1554,6 +1554,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 	rte->rtekind = RTE_RELATION;
 	rte->relid = RelationGetRelid(pk_rel);
 	rte->relkind = pk_rel->rd_rel->relkind;
+	Assert(RelkindIsValid(rte->relkind));
 	rte->rellockmode = AccessShareLock;
 	rte->perminfoindex = list_length(perminfos);
 	rtes = lappend(rtes, rte);
@@ -1566,6 +1567,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 	rte->rtekind = RTE_RELATION;
 	rte->relid = RelationGetRelid(fk_rel);
 	rte->relkind = fk_rel->rd_rel->relkind;
+	Assert(RelkindIsValid(rte->relkind));
 	rte->rellockmode = AccessShareLock;
 	rte->perminfoindex = list_length(perminfos);
 	rtes = lappend(rtes, rte);
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 09fe81880e7..9515563643e 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2162,10 +2162,15 @@ get_rel_relkind(Oid relid)
 
 		result = (Relkind) reltup->relkind;
 		ReleaseSysCache(tp);
+
+		if (!RelkindIsValid(result))
+			elog(ERROR, "relation %u has invalid relkind '%c'",
+				 relid, result);
+
 		return result;
 	}
-	else
-		return '\0';
+
+	elog(ERROR, "cache lookup failed for relation %u", relid);
 }
 
 /*
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index e794f4b942f..01d89d21af3 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -453,6 +453,10 @@ AllocateRelationDesc(Form_pg_class relp)
 
 	MemoryContextSwitchTo(oldcxt);
 
+	if (!RelkindIsValid(relation->rd_rel->relkind))
+		elog(ERROR, "invalid relkind \"%c\" for relation \"%s\"",
+			 relation->rd_rel->relkind, RelationGetRelationName(relation));
+
 	return relation;
 }
 
@@ -3527,6 +3531,7 @@ RelationBuildLocalRelation(const char *relname,
 	bool		nailit;
 
 	Assert(natts >= 0);
+	Assert(RelkindIsValid(relkind));
 
 	/*
 	 * check for creation of a rel that must be nailed in cache.
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index cd0d034f3d6..baa02eff739 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -251,4 +251,25 @@ typedef enum
 
 extern int	errdetail_relkind_not_supported(Relkind relkind);
 
+static inline bool
+RelkindIsValid(char relkind)
+{
+	switch (relkind)
+	{
+		case RELKIND_RELATION:
+		case RELKIND_INDEX:
+		case RELKIND_SEQUENCE:
+		case RELKIND_TOASTVALUE:
+		case RELKIND_VIEW:
+		case RELKIND_MATVIEW:
+		case RELKIND_COMPOSITE_TYPE:
+		case RELKIND_FOREIGN_TABLE:
+		case RELKIND_PARTITIONED_TABLE:
+		case RELKIND_PARTITIONED_INDEX:
+			return true;
+	}
+
+	return false;
+}
+
 #endif							/* PG_CLASS_H */
-- 
2.34.1

