From 1cbccca63cdb1dd7a9e31be70f570f71940e01e9 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Thu, 14 Aug 2025 12:15:15 +0900
Subject: [PATCH v5 11/15] Enlarge OID generation to 8 bytes

This adds a new routine called GetNewObjectId8() in varsup.c, which is
able to retrieve a 64b OID.  GetNewObjectId() is kept compatible with
its origin, where we still check that the lower 32 bits of the counter
do not wraparound, handling the FirstNormalObjectId case.

pg_resetwal -o/--next-oid is updated to be able to handle 8-byte OIDs.
---
 src/include/access/transam.h              |  3 +-
 src/include/access/xlog.h                 |  2 +-
 src/include/catalog/pg_control.h          |  2 +-
 src/backend/access/rmgrdesc/xlogdesc.c    |  8 +--
 src/backend/access/transam/varsup.c       | 62 ++++++++++++++++-------
 src/backend/access/transam/xlog.c         |  8 +--
 src/backend/access/transam/xlogrecovery.c |  2 +-
 src/bin/pg_controldata/pg_controldata.c   |  2 +-
 src/bin/pg_resetwal/pg_resetwal.c         | 10 ++--
 doc/src/sgml/ref/pg_resetwal.sgml         |  6 +--
 10 files changed, 66 insertions(+), 39 deletions(-)

diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 7d82cd2eb562..2ef3000bdb1f 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -211,7 +211,7 @@ typedef struct TransamVariablesData
 	/*
 	 * These fields are protected by OidGenLock.
 	 */
-	Oid			nextOid;		/* next OID to assign */
+	Oid8		nextOid;		/* next OID (8 bytes) to assign */
 	uint32		oidCount;		/* OIDs available before must do XLOG work */
 
 	/*
@@ -293,6 +293,7 @@ extern void SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
 extern void AdvanceOldestClogXid(TransactionId oldest_datfrozenxid);
 extern bool ForceTransactionIdLimitUpdate(void);
 extern Oid	GetNewObjectId(void);
+extern Oid8 GetNewObjectId8(void);
 extern void StopGeneratingPinnedObjectIds(void);
 
 #ifdef USE_ASSERT_CHECKING
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index d12798be3d80..21d915ae5802 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -243,7 +243,7 @@ extern void ShutdownXLOG(int code, Datum arg);
 extern bool CreateCheckPoint(int flags);
 extern bool CreateRestartPoint(int flags);
 extern WALAvailability GetWALAvailability(XLogRecPtr targetLSN);
-extern void XLogPutNextOid(Oid nextOid);
+extern void XLogPutNextOid(Oid8 nextOid);
 extern XLogRecPtr XLogRestorePoint(const char *rpName);
 extern void UpdateFullPageWrites(void);
 extern void GetFullPageWriteInfo(XLogRecPtr *RedoRecPtr_p, bool *doPageWrites_p);
diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h
index 63e834a6ce47..c85c84bbfdbb 100644
--- a/src/include/catalog/pg_control.h
+++ b/src/include/catalog/pg_control.h
@@ -42,7 +42,7 @@ typedef struct CheckPoint
 	bool		fullPageWrites; /* current full_page_writes */
 	int			wal_level;		/* current wal_level */
 	FullTransactionId nextXid;	/* next free transaction ID */
-	Oid			nextOid;		/* next free OID */
+	Oid8		nextOid;		/* next free OID */
 	MultiXactId nextMulti;		/* next free MultiXactId */
 	MultiXactOffset nextMultiOffset;	/* next free MultiXact offset */
 	TransactionId oldestXid;	/* cluster-wide minimum datfrozenxid */
diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c
index cd6c2a2f650a..23920d32092a 100644
--- a/src/backend/access/rmgrdesc/xlogdesc.c
+++ b/src/backend/access/rmgrdesc/xlogdesc.c
@@ -66,7 +66,7 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 		CheckPoint *checkpoint = (CheckPoint *) rec;
 
 		appendStringInfo(buf, "redo %X/%08X; "
-						 "tli %u; prev tli %u; fpw %s; wal_level %s; xid %u:%u; oid %u; multi %u; offset %u; "
+						 "tli %u; prev tli %u; fpw %s; wal_level %s; xid %u:%u; oid " OID8_FORMAT "; multi %u; offset %u; "
 						 "oldest xid %u in DB %u; oldest multi %u in DB %u; "
 						 "oldest/newest commit timestamp xid: %u/%u; "
 						 "oldest running xid %u; %s",
@@ -91,10 +91,10 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 	}
 	else if (info == XLOG_NEXTOID)
 	{
-		Oid			nextOid;
+		Oid8		nextOid;
 
-		memcpy(&nextOid, rec, sizeof(Oid));
-		appendStringInfo(buf, "%u", nextOid);
+		memcpy(&nextOid, rec, sizeof(Oid8));
+		appendStringInfo(buf, OID8_FORMAT, nextOid);
 	}
 	else if (info == XLOG_RESTORE_POINT)
 	{
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index fe895787cb72..7dcac9e210e4 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -542,31 +542,51 @@ ForceTransactionIdLimitUpdate(void)
 
 
 /*
- * GetNewObjectId -- allocate a new OID
+ * GetNewObjectId -- allocate a new OID (4 bytes)
  *
- * OIDs are generated by a cluster-wide counter.  Since they are only 32 bits
- * wide, counter wraparound will occur eventually, and therefore it is unwise
- * to assume they are unique unless precautions are taken to make them so.
- * Hence, this routine should generally not be used directly.  The only direct
- * callers should be GetNewOidWithIndex() and GetNewRelFileNumber() in
- * catalog/catalog.c.
+ * OIDs are generated by a cluster-wide counter.  The callers of this routine
+ * expect a 32 bit-wide counter, and counter wraparound will occur eventually,
+ * and therefore it is unwise to assume they are unique unless precautions are
+ * taken to make them so.  This routine should generally not be used directly.
+ * The only direct callers should be GetNewOidWithIndex() and
+ * GetNewRelFileNumber() in catalog/catalog.c.
  */
 Oid
 GetNewObjectId(void)
 {
-	Oid			result;
+	return (Oid) GetNewObjectId8();
+}
+
+/*
+ * GetNewObjectId8 -- allocate a new OID (8 bytes)
+ *
+ * This routine can be called directly if the consumer of the OID allocated
+ * stores the counter in an 8-byte space, where wraparound does not matter.
+ * We still need to care about the wraparound case in the low 32 bits of the
+ * space allocated, GetNewObjectId() expecting OIDs to never be allocated
+ * up to FirstNormalObjectId.
+ */
+Oid8
+GetNewObjectId8(void)
+{
+	Oid8		result;
+	Oid			nextoid_lo;
+	uint32		nextoid_hi;
 
 	/* safety check, we should never get this far in a HS standby */
 	if (RecoveryInProgress())
 		elog(ERROR, "cannot assign OIDs during recovery");
 
 	LWLockAcquire(OidGenLock, LW_EXCLUSIVE);
+	nextoid_lo = (Oid) TransamVariables->nextOid;
+	nextoid_hi = (uint32) (TransamVariables->nextOid >> 32);
 
 	/*
-	 * Check for wraparound of the OID counter.  We *must* not return 0
-	 * (InvalidOid), and in normal operation we mustn't return anything below
-	 * FirstNormalObjectId since that range is reserved for initdb (see
-	 * IsCatalogRelationOid()).  Note we are relying on unsigned comparison.
+	 * Check for wraparound of the OID counter in its lower 4 bytes.  We
+	 * *must* not return 0 (InvalidOid), and in normal operation we
+	 * mustn't return anything below FirstNormalObjectId since that range
+	 * is reserved for initdb (see IsCatalogRelationOid()).  Note we are
+	 * relying on unsigned comparison.
 	 *
 	 * During initdb, we start the OID generator at FirstGenbkiObjectId, so we
 	 * only wrap if before that point when in bootstrap or standalone mode.
@@ -576,26 +596,32 @@ GetNewObjectId(void)
 	 * available for automatic assignment during initdb, while ensuring they
 	 * will never conflict with user-assigned OIDs.
 	 */
-	if (TransamVariables->nextOid < ((Oid) FirstNormalObjectId))
+	if (nextoid_lo < ((Oid) FirstNormalObjectId))
 	{
 		if (IsPostmasterEnvironment)
 		{
 			/* wraparound, or first post-initdb assignment, in normal mode */
-			TransamVariables->nextOid = FirstNormalObjectId;
+			nextoid_lo = FirstNormalObjectId;
 			TransamVariables->oidCount = 0;
 		}
 		else
 		{
 			/* we may be bootstrapping, so don't enforce the full range */
-			if (TransamVariables->nextOid < ((Oid) FirstGenbkiObjectId))
+			if (nextoid_lo < ((Oid) FirstGenbkiObjectId))
 			{
 				/* wraparound in standalone mode (unlikely but possible) */
-				TransamVariables->nextOid = FirstNormalObjectId;
+				nextoid_lo = FirstNormalObjectId;
 				TransamVariables->oidCount = 0;
 			}
 		}
 	}
 
+	/*
+	 * Set next OID in its 8-byte space, skipping the first post-init
+	 * assignment.
+	 */
+	TransamVariables->nextOid = ((Oid8) nextoid_hi) << 32 | nextoid_lo;
+
 	/* If we run out of logged for use oids then we must log more */
 	if (TransamVariables->oidCount == 0)
 	{
@@ -620,7 +646,7 @@ GetNewObjectId(void)
  * to the specified value.
  */
 static void
-SetNextObjectId(Oid nextOid)
+SetNextObjectId(Oid8 nextOid)
 {
 	/* Safety check, this is only allowable during initdb */
 	if (IsPostmasterEnvironment)
@@ -630,7 +656,7 @@ SetNextObjectId(Oid nextOid)
 	LWLockAcquire(OidGenLock, LW_EXCLUSIVE);
 
 	if (TransamVariables->nextOid > nextOid)
-		elog(ERROR, "too late to advance OID counter to %u, it is now %u",
+		elog(ERROR, "too late to advance OID counter to " OID8_FORMAT ", it is now " OID8_FORMAT,
 			 nextOid, TransamVariables->nextOid);
 
 	TransamVariables->nextOid = nextOid;
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index e8909406686d..6194bf5ef5aa 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -8192,10 +8192,10 @@ KeepLogSeg(XLogRecPtr recptr, XLogSegNo *logSegNo)
  * Write a NEXTOID log record
  */
 void
-XLogPutNextOid(Oid nextOid)
+XLogPutNextOid(Oid8 nextOid)
 {
 	XLogBeginInsert();
-	XLogRegisterData(&nextOid, sizeof(Oid));
+	XLogRegisterData(&nextOid, sizeof(Oid8));
 	(void) XLogInsert(RM_XLOG_ID, XLOG_NEXTOID);
 
 	/*
@@ -8418,7 +8418,7 @@ xlog_redo(XLogReaderState *record)
 
 	if (info == XLOG_NEXTOID)
 	{
-		Oid			nextOid;
+		Oid8		nextOid;
 
 		/*
 		 * We used to try to take the maximum of TransamVariables->nextOid and
@@ -8427,7 +8427,7 @@ xlog_redo(XLogReaderState *record)
 		 * anyway, better to just believe the record exactly.  We still take
 		 * OidGenLock while setting the variable, just in case.
 		 */
-		memcpy(&nextOid, XLogRecGetData(record), sizeof(Oid));
+		memcpy(&nextOid, XLogRecGetData(record), sizeof(Oid8));
 		LWLockAcquire(OidGenLock, LW_EXCLUSIVE);
 		TransamVariables->nextOid = nextOid;
 		TransamVariables->oidCount = 0;
diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c
index f23ec8969c27..00807eb66874 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -880,7 +880,7 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr,
 							LSN_FORMAT_ARGS(checkPoint.redo),
 							wasShutdown ? "true" : "false"));
 	ereport(DEBUG1,
-			(errmsg_internal("next transaction ID: " UINT64_FORMAT "; next OID: %u",
+			(errmsg_internal("next transaction ID: " UINT64_FORMAT "; next OID: " OID8_FORMAT,
 							 U64FromFullTransactionId(checkPoint.nextXid),
 							 checkPoint.nextOid)));
 	ereport(DEBUG1,
diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c
index 10de058ce91f..992111d3a1d2 100644
--- a/src/bin/pg_controldata/pg_controldata.c
+++ b/src/bin/pg_controldata/pg_controldata.c
@@ -260,7 +260,7 @@ main(int argc, char *argv[])
 	printf(_("Latest checkpoint's NextXID:          %u:%u\n"),
 		   EpochFromFullTransactionId(ControlFile->checkPointCopy.nextXid),
 		   XidFromFullTransactionId(ControlFile->checkPointCopy.nextXid));
-	printf(_("Latest checkpoint's NextOID:          %u\n"),
+	printf(_("Latest checkpoint's NextOID:          " OID8_FORMAT "\n"),
 		   ControlFile->checkPointCopy.nextOid);
 	printf(_("Latest checkpoint's NextMultiXactId:  %u\n"),
 		   ControlFile->checkPointCopy.nextMulti);
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index 7a4e4eb95706..c1039a8a4d16 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -68,7 +68,7 @@ static TransactionId set_oldest_xid = 0;
 static TransactionId set_xid = 0;
 static TransactionId set_oldest_commit_ts_xid = 0;
 static TransactionId set_newest_commit_ts_xid = 0;
-static Oid	set_oid = 0;
+static Oid8 set_oid = 0;
 static MultiXactId set_mxid = 0;
 static MultiXactOffset set_mxoff = (MultiXactOffset) -1;
 static TimeLineID minXlogTli = 0;
@@ -225,7 +225,7 @@ main(int argc, char *argv[])
 
 			case 'o':
 				errno = 0;
-				set_oid = strtoul(optarg, &endptr, 0);
+				set_oid = strtou64(optarg, &endptr, 0);
 				if (endptr == optarg || *endptr != '\0' || errno != 0)
 				{
 					pg_log_error("invalid argument for option %s", "-o");
@@ -755,7 +755,7 @@ PrintControlValues(bool guessed)
 	printf(_("Latest checkpoint's NextXID:          %u:%u\n"),
 		   EpochFromFullTransactionId(ControlFile.checkPointCopy.nextXid),
 		   XidFromFullTransactionId(ControlFile.checkPointCopy.nextXid));
-	printf(_("Latest checkpoint's NextOID:          %u\n"),
+	printf(_("Latest checkpoint's NextOID:          " OID8_FORMAT "\n"),
 		   ControlFile.checkPointCopy.nextOid);
 	printf(_("Latest checkpoint's NextMultiXactId:  %u\n"),
 		   ControlFile.checkPointCopy.nextMulti);
@@ -839,7 +839,7 @@ PrintNewControlValues(void)
 
 	if (set_oid != 0)
 	{
-		printf(_("NextOID:                              %u\n"),
+		printf(_("NextOID:                              " OID8_FORMAT "\n"),
 			   ControlFile.checkPointCopy.nextOid);
 	}
 
@@ -1208,7 +1208,7 @@ usage(void)
 	printf(_("  -e, --epoch=XIDEPOCH             set next transaction ID epoch\n"));
 	printf(_("  -l, --next-wal-file=WALFILE      set minimum starting location for new WAL\n"));
 	printf(_("  -m, --multixact-ids=MXID,MXID    set next and oldest multitransaction ID\n"));
-	printf(_("  -o, --next-oid=OID               set next OID\n"));
+	printf(_("  -o, --next-oid=OID8              set next OID (8 bytes)\n"));
 	printf(_("  -O, --multixact-offset=OFFSET    set next multitransaction offset\n"));
 	printf(_("  -u, --oldest-transaction-id=XID  set oldest transaction ID\n"));
 	printf(_("  -x, --next-transaction-id=XID    set next transaction ID\n"));
diff --git a/doc/src/sgml/ref/pg_resetwal.sgml b/doc/src/sgml/ref/pg_resetwal.sgml
index 2c019c2aac6e..b03251cedbbe 100644
--- a/doc/src/sgml/ref/pg_resetwal.sgml
+++ b/doc/src/sgml/ref/pg_resetwal.sgml
@@ -279,11 +279,11 @@ PostgreSQL documentation
    </varlistentry>
 
    <varlistentry>
-    <term><option>-o <replaceable class="parameter">oid</replaceable></option></term>
-    <term><option>--next-oid=<replaceable class="parameter">oid</replaceable></option></term>
+    <term><option>-o <replaceable class="parameter">oid8</replaceable></option></term>
+    <term><option>--next-oid=<replaceable class="parameter">oid8</replaceable></option></term>
     <listitem>
      <para>
-      Manually set the next OID.
+      Manually set the next OID (8 bytes).
      </para>
 
      <para>
-- 
2.50.0

