On Tue, Sep 08, 2026 at 04:55:16PM -0700, Bharath Rupireddy wrote: > I don't think pg_control_checkpoint() and pg_controldata emitting an > OID extracted from the OID8 value is the right idea. I understand that > tools and external modules do need to adapt to the wider value, but > that is fine for a new major version, and the control file version > bump gives them the signal to do so. Also, the extra 4 bytes from OID8 > are absorbed by existing padding, so the control file stays well under > the safe size limit of 512 bytes anyway.
I don't mean to extend the on-disk file with an extra 4-bytes for the Oid. I mean to *show* an extra field based on a casted value. These are two different things. > v15-0001-Enlarge-OID-generation-to-8-bytes.patch: this looks good to > me, with one comment. Although the wraparound with an 8-byte OID is > hundreds of years away, and 0001 assumes it never happens, do we need > to handle the case where someone sets the OID counter closer to the > 8-byte OID limit using pg_resetwal and still hits the wraparound? We > can either reject this in pg_resetwal, handle the wraparound case in > the caller, document it, or mention it in the commit message or > somewhere near pg_resetwal. This is not a blocker for getting 0001 in, > just to be clear. I think this needs to be fixed as part of > v15-0006-Add-support-for-oid8-TOAST-values.patch, which does not check > for an existing TOAST OID. A few things worth noting: we have in xlog_redo() and varsup.h a couple of comments that mention wraparound without mentioning the oid8 bits that can bypass it. These need to be slightly adjusted.. I can fancy you with one more case, because there is no entry recheck in the TOAST insert path when using an 8-byte OID: - Insert some TOAST entries with toast_value_type=oid8. - Reset to a past value with pg_resetwal. - Reinsert, failing hard on INSERT due to a duplicated key. My best answer is don't set the counter to the past. Somebody using pg_resetwal is looking for trouble already. It's true that not being able to handle the OID8 value conflicts like the normal TOAST path could be seen as a defect of the OID8 path, but adding an index check (while doable of course), is just wasting resources with 8-bytes, because in practice that's never hit. So "don't set it to the past" seems like an answer good enough? That sounds to me like a documentation change. > v15-0003-Switch-pg_column_toast_chunk_id-return-value-fro.patch: this > looks good to me, with one comment. The chunk_id cast in > cluster-toast-value-reuse.spec seems redundant, because that column > comes from the output of pg_column_toast_chunk_id(). In > misc_functions.sql it is needed. This makes me think about whether we > need to support OID to OID8 comparison in more cases: existing queries > and monitoring tools that do pg_column_toast_chunk_id(x) = some_oid, > or that store the result in an OID column, will now get a > type-mismatch error and have to add an explicit cast. Could we ease > that by making the OID to OID8 cast implicit, so that an OID compared > against an OID8 is promoted automatically? This is not a blocker for > getting 0003 in, just to be clear. My reply here would be to cast the some_oid to oid8, or invent a secondary oid8 specific function, leaving pg_column_toast_chunk_id() alone and failing if trying to use it with an oid8 TOAST table? I suspect that there are not that many users of pg_column_toast_chunk_id() anyway. May be wrong of course, but a cast to oid8 is fine by me. I'm just not sure that it is worth the churn of having a second function with a different return type. > v15-0004-Add-support-for-TOAST-chunk_id-type-in-binary-up.patch: this > looks good to me, with a nit. Use > format_type_be(binary_upgrade_next_toast_chunk_id_typoid) instead of > %u for better readability in the "cannot support toast chunk_id type" > error message. Not sure it matter. That should not be reachable in practice. > v15-0005-Add-relation-option-toast_value_type.patch: some comments. > 1/ Parenthesize defaulttarg. > 2/ Nit: double space after "creating". Noted these down for later. > 3/ I understand that v15-0008 adds more tests, but can we have simple > tests in this patch as well, like creating a table with > toast_value_type='oid' and with an unknown type to verify the error? > This would make this patch independent. Yeah, I guess that you're right to complain here. Most of the reloption tests could be moved to the patch that introduces the reloption, for clarity. > 5/ Following on from (4). Say I create a table with oid, insert data, > change the reloption to oid8, and then run a rewrite (VACUUM FULL, > CLUSTER or REPACK). The final table still uses oid, because the > rewrite preserves the existing TOAST table by swapping contents rather > than recreating it. But the transient table that make_new_heap() > builds does get created with the new type, because it goes through > create_toast_table(), which reads the reloption without checking > whether this is a rewrite. With oid this makes no difference, but once > oid8 is added the transient TOAST table would be oid8 while the > existing TOAST table still says oid. Is that expected? I am not completely sure to follow here? Wouldn't that be the case of create_toast_table() defining a OIDOldToast. In this case, we inherit the type ID from the old table when creating what would be the transient table. See the comment named as "break the world" in create_toast_table() added in the patch. Or perhaps I'm missing your point entirely. > 1/ Measured the additional storage space and impact on query > performance for the same TOAST limits under 4 billion (OID limit). > > At 4 GB TOAST (500k rows × 8 KB), oid8 costs +40% in TOAST index size > and +15% in heap size, with no measurable change to the TOAST table > size or full-detoast scan time. Queries [1]. The heap increase comes > from the on-disk TOAST pointer growing 4 bytes and the tuple > alignment. The TOAST index grows for the same reason on its chunk_id > key. The TOAST table size stays about the same because the extra > chunk_id bytes are small next to the roughly 2 KB of chunk_data and do > not change how many chunks fit per page. Yeah, the cost is part of the game here.. > 2/ Used oid8 for all tables by default and ran regression tests to see > if there are any issues. Did this one as well at some point. I'm pretty sure I've caught some of the rewrite bugs with that, but I don't recall entirely.. > [3] > doesn't match '(?^:(?^ms:heap table "postgres\.public\.test", block 0, > offset 5:\s+)data begins at offset 152 beyond the tuple length 58)' > # Looks like you failed 1 test of 32. > t/004_verify_heapam.pl ... Dubious, test returned 1 (wstat 256, 0x100) > Failed 1/32 subtests This needs manipulation of on-disk data. I have done that at some point but I did not see that as worth the extra cost. Having tests for the valid cases sounded more than enough, because we care about making sure that the vartags are passed around right. Whether they actual report a corruption is less interesting. Before reworking on a new rebase of the whole patch set, attached is an adjusted 0001 for the 8-byte counter, with adjustments for the docs of pg_resetwal and fixes for a few comments that worry about OID wraparound, that I have noticed after more review. How does this part look? -- Michael
From f05d9278a7101075f21f7fc8f52f74f5d22d60f3 Mon Sep 17 00:00:00 2001 From: Michael Paquier <[email protected]> Date: Wed, 9 Sep 2026 09:56:25 +0900 Subject: [PATCH v16] Enlarge OID generation to 8 bytes This change is combined with an update of the control file, with CheckPoint.nextOid enlarged from Oid to Oid8. pg_control_checkpoint(), pg_resetwal and pg_upgrade are updated to accomodate to this change, to make sure that the value is not truncated due to 32-bit limitations when used. This commit adds a new routine called GetNewObjectId8() in varsup.c, which is able to retrieve a 8-byte 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. A couple of comments in the xlog redo logic referred to wraparound issues, which is not the case anymore with this change in place, so refresh these. Tests are added to cover that, across all the tools covered by this change. Bump catalog version. Bump XLOG_PAGE_MAGIC, due to XLOG_NEXTOID. --- src/include/access/transam.h | 8 +-- src/include/access/xlog.h | 2 +- src/include/access/xlog_internal.h | 2 +- src/include/catalog/pg_control.h | 4 +- src/include/catalog/pg_proc.dat | 2 +- src/backend/access/rmgrdesc/xlogdesc.c | 8 +-- src/backend/access/transam/varsup.c | 59 ++++++++++++++++------- src/backend/access/transam/xlog.c | 26 +++++----- src/backend/access/transam/xlogrecovery.c | 2 +- src/backend/utils/misc/pg_controldata.c | 2 +- src/bin/pg_controldata/pg_controldata.c | 2 +- src/bin/pg_resetwal/pg_resetwal.c | 10 ++-- src/bin/pg_resetwal/t/001_basic.pl | 17 +++++++ src/bin/pg_upgrade/controldata.c | 2 +- src/bin/pg_upgrade/pg_upgrade.c | 2 +- src/bin/pg_upgrade/pg_upgrade.h | 2 +- src/bin/pg_upgrade/t/002_pg_upgrade.pl | 25 ++++++++++ doc/src/sgml/func/func-info.sgml | 2 +- doc/src/sgml/ref/pg_resetwal.sgml | 10 ++-- 19 files changed, 128 insertions(+), 59 deletions(-) diff --git a/src/include/access/transam.h b/src/include/access/transam.h index 55a4ab26b348..2d85e4c83c2e 100644 --- a/src/include/access/transam.h +++ b/src/include/access/transam.h @@ -186,8 +186,9 @@ FullTransactionIdAdvance(FullTransactionId *dest) * more painful, however, since some user-defined OIDs will appear in * on-disk data; such a change would probably break pg_upgrade. * - * NOTE: if the OID generator wraps around, we skip over OIDs 0-16383 - * and resume with 16384. This minimizes the odds of OID conflict, by not + * NOTE: the OID generator is 8 bytes wide (see GetNewObjectId8())). For + * its lower 4 bytes, we skip over OIDs 0-16383 and resume with 16384. + * This minimizes the odds of OID conflict when using GetNewObjectId(), by not * reassigning OIDs that might have been assigned during initdb. Critically, * it also ensures that no user-created object will be considered pinned. * ---------- @@ -211,7 +212,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 */ /* @@ -353,6 +354,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 130ba9291096..7a590b7e1eaa 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -284,7 +284,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 XLogRecPtr XLogAssignLSN(void); extern void UpdateFullPageWrites(void); diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index be718993401b..bf609c3c703d 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -32,7 +32,7 @@ /* * Each page of XLOG file has a header like this: */ -#define XLOG_PAGE_MAGIC 0xD121 /* can be used as WAL version indicator */ +#define XLOG_PAGE_MAGIC 0xD122 /* can be used as WAL version indicator */ typedef struct XLogPageHeaderData { diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h index 7b5404460ec2..89ab43dd4fc7 100644 --- a/src/include/catalog/pg_control.h +++ b/src/include/catalog/pg_control.h @@ -22,7 +22,7 @@ /* Version identifier for this pg_control format */ -#define PG_CONTROL_VERSION 1903 +#define PG_CONTROL_VERSION 2000 /* Nonce key length, see below */ #define MOCK_AUTH_NONCE_LEN 32 @@ -43,7 +43,7 @@ typedef struct CheckPoint int wal_level; /* current wal_level */ bool logicalDecodingEnabled; /* current logical decoding status */ 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/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 960763ee50ba..c53ce68c7173 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -12429,7 +12429,7 @@ descr => 'pg_controldata checkpoint state information as a function', proname => 'pg_control_checkpoint', provolatile => 'v', prorettype => 'record', proargtypes => '', - proallargtypes => '{pg_lsn,pg_lsn,text,int4,int4,bool,bool,text,oid,xid,int8,xid,oid,xid,xid,oid,xid,xid,int4,timestamptz}', + proallargtypes => '{pg_lsn,pg_lsn,text,int4,int4,bool,bool,text,oid8,xid,int8,xid,oid,xid,xid,oid,xid,xid,int4,timestamptz}', proargmodes => '{o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}', proargnames => '{checkpoint_lsn,redo_lsn,redo_wal_file,timeline_id,prev_timeline_id,full_page_writes,logical_decoding,next_xid,next_oid,next_multixact_id,next_multi_offset,oldest_xid,oldest_xid_dbid,oldest_active_xid,oldest_multi_xid,oldest_multi_dbid,oldest_commit_ts_xid,newest_commit_ts_xid,data_page_checksum_version,checkpoint_time}', prosrc => 'pg_control_checkpoint' }, diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c index 2468a7d25785..64e749c1e6b7 100644 --- a/src/backend/access/rmgrdesc/xlogdesc.c +++ b/src/backend/access/rmgrdesc/xlogdesc.c @@ -101,7 +101,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; logical decoding %s; xid %u:%u; oid %u; multi %u; offset %" PRIu64 "; " + "tli %u; prev tli %u; fpw %s; wal_level %s; logical decoding %s; xid %u:%u; oid " OID8_FORMAT "; multi %u; offset %" PRIu64 "; " "oldest xid %u in DB %u; oldest multi %u in DB %u; " "oldest/newest commit timestamp xid: %u/%u; " "oldest running xid %u; " @@ -129,10 +129,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 cb68d8fa974a..889bdebe1880 100644 --- a/src/backend/access/transam/varsup.c +++ b/src/backend/access/transam/varsup.c @@ -541,31 +541,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. 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. @@ -575,26 +595,29 @@ 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 */ + 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) { @@ -619,7 +642,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) @@ -629,7 +652,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 3203f2fd4ee2..b75cc23ff36b 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -8604,10 +8604,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); /* @@ -8873,16 +8873,17 @@ xlog_redo(XLogReaderState *record) if (info == XLOG_NEXTOID) { - Oid nextOid; + Oid8 nextOid; /* * We used to try to take the maximum of TransamVariables->nextOid and - * the recorded nextOid, but that fails if the OID counter wraps - * around. Since no OID allocation should be happening during replay - * anyway, better to just believe the record exactly. We still take - * OidGenLock while setting the variable, just in case. + * the recorded nextOid, but that failed back when the counter was 4 + * bytes wide and could wrap around. Since no OID allocation should + * be happening during replay 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; @@ -9010,11 +9011,10 @@ xlog_redo(XLogReaderState *record) * to track OID assignment through XLOG_NEXTOID records. The nextOid * counter is from the start of the checkpoint and might well be stale * compared to later XLOG_NEXTOID records. We could try to take the - * maximum of the nextOid counter and our latest value, but since - * there's no particular guarantee about the speed with which the OID - * counter wraps around, that's a risky thing to do. In any case, - * users of the nextOid counter are required to avoid assignment of - * duplicates, so that a somewhat out-of-date value should be safe. + * maximum of the nextOid counter and our latest value, but there is + * no point in doing so: an online checkpoint records nextOid plus + * oidCountdue, which is never ahead of the last XLOG_NEXTOID record + * that replay has applied. */ /* Handle multixact */ diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c index acac97e89d38..fff8d57ac61d 100644 --- a/src/backend/access/transam/xlogrecovery.c +++ b/src/backend/access/transam/xlogrecovery.c @@ -840,7 +840,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/backend/utils/misc/pg_controldata.c b/src/backend/utils/misc/pg_controldata.c index 9014f0953e99..1f47302fe2a7 100644 --- a/src/backend/utils/misc/pg_controldata.c +++ b/src/backend/utils/misc/pg_controldata.c @@ -124,7 +124,7 @@ pg_control_checkpoint(PG_FUNCTION_ARGS) XidFromFullTransactionId(ControlFile->checkPointCopy.nextXid))); nulls[7] = false; - values[8] = ObjectIdGetDatum(ControlFile->checkPointCopy.nextOid); + values[8] = ObjectId8GetDatum(ControlFile->checkPointCopy.nextOid); nulls[8] = false; values[9] = TransactionIdGetDatum(ControlFile->checkPointCopy.nextMulti); diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c index 6fc87ed114d8..b785f7f40701 100644 --- a/src/bin/pg_controldata/pg_controldata.c +++ b/src/bin/pg_controldata/pg_controldata.c @@ -269,7 +269,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 79f3085d7696..63e4381e03f0 100644 --- a/src/bin/pg_resetwal/pg_resetwal.c +++ b/src/bin/pg_resetwal/pg_resetwal.c @@ -82,7 +82,7 @@ static TransactionId oldest_commit_ts_xid_val; static TransactionId newest_commit_ts_xid_val; static bool next_oid_given = false; -static Oid next_oid_val; +static Oid8 next_oid_val; static bool mxids_given = false; static MultiXactId next_mxid_val; @@ -253,7 +253,7 @@ main(int argc, char *argv[]) case 'o': errno = 0; - next_oid_val = strtouint32_strict(optarg, &endptr, 0); + next_oid_val = strtouint64_strict(optarg, &endptr, 0); if (endptr == optarg || *endptr != '\0' || errno != 0) { pg_log_error("invalid argument for option %s", "-o"); @@ -775,7 +775,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); @@ -861,7 +861,7 @@ PrintNewControlValues(void) if (next_oid_given) { - printf(_("NextOID: %u\n"), + printf(_("NextOID: " OID8_FORMAT "\n"), ControlFile.checkPointCopy.nextOid); } @@ -1236,7 +1236,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/src/bin/pg_resetwal/t/001_basic.pl b/src/bin/pg_resetwal/t/001_basic.pl index 6af97c0a7d99..f54e9bb42231 100644 --- a/src/bin/pg_resetwal/t/001_basic.pl +++ b/src/bin/pg_resetwal/t/001_basic.pl @@ -265,4 +265,21 @@ command_like( $node->start; ok(1, 'server started after reset'); +# Checks for 8 bytes OIDs. +$node->stop; +my $new_nextoid = "4295067296"; # 2^32 + 100,000 +command_ok([ 'pg_resetwal', '--next-oid' => $new_nextoid, $node->data_dir ], + 'runs with --next-oid value above 2^32'); +command_like( + [ 'pg_resetwal', '--dry-run', $node->data_dir ], + qr/^Latest checkpoint's NextOID: *$new_nextoid$/m, + '8-byte --next-oid value preserved in pg_control'); +$node->start; +is( $node->safe_psql( + 'postgres', + "SELECT next_oid >= '$new_nextoid'::oid8 FROM pg_control_checkpoint()" + ), + 't', + 'new 8-byte OID reported'); + done_testing(); diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c index 7543a9880451..f0fa2b1689fa 100644 --- a/src/bin/pg_upgrade/controldata.c +++ b/src/bin/pg_upgrade/controldata.c @@ -245,7 +245,7 @@ get_control_data(ClusterInfo *cluster) pg_fatal("%d: controldata retrieval problem", __LINE__); p++; /* remove ':' char */ - cluster->controldata.chkpnt_nxtoid = str2uint(p); + cluster->controldata.chkpnt_nxtoid = str2uint64(p); got_oid = true; } else if ((p = strstr(bufin, "Latest checkpoint's NextMultiXactId:")) != NULL) diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c index 7366fd4627cc..3704c620603c 100644 --- a/src/bin/pg_upgrade/pg_upgrade.c +++ b/src/bin/pg_upgrade/pg_upgrade.c @@ -196,7 +196,7 @@ main(int argc, char **argv) */ prep_status("Setting next OID for new cluster"); exec_prog(UTILITY_LOG_FILE, NULL, true, true, - "\"%s/pg_resetwal\" -o %u \"%s\"", + "\"%s/pg_resetwal\" -o " OID8_FORMAT " \"%s\"", new_cluster.bindir, old_cluster.controldata.chkpnt_nxtoid, new_cluster.pgdata); check_ok(); diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h index c80e8fb4031c..4cb79bb780e6 100644 --- a/src/bin/pg_upgrade/pg_upgrade.h +++ b/src/bin/pg_upgrade/pg_upgrade.h @@ -216,7 +216,7 @@ typedef struct char nextxlogfile[25]; uint32 chkpnt_nxtxid; uint32 chkpnt_nxtepoch; - uint32 chkpnt_nxtoid; + Oid8 chkpnt_nxtoid; uint32 chkpnt_nxtmulti; uint64 chkpnt_nxtmxoff; uint32 chkpnt_oldstMulti; diff --git a/src/bin/pg_upgrade/t/002_pg_upgrade.pl b/src/bin/pg_upgrade/t/002_pg_upgrade.pl index a5cfd53265d4..728fd6b0b120 100644 --- a/src/bin/pg_upgrade/t/002_pg_upgrade.pl +++ b/src/bin/pg_upgrade/t/002_pg_upgrade.pl @@ -588,6 +588,20 @@ $oldnode->start; $oldnode->safe_psql('postgres', 'DROP DATABASE regression_invalid'); $oldnode->stop; +# The OID counter is 8 bytes wide, check that it is carried. Older versions +# may not support 8-byte OIDs, so skip in this case. +my $big_next_oid = '4295067296'; # 2^32 + 100000 +if (!defined($ENV{oldinstall})) +{ + command_ok( + [ + 'pg_resetwal', + '--next-oid' => $big_next_oid, + $oldnode->data_dir + ], + 'set an 8-byte OID counter in the old instance'); +} + # --check command works here, cleans up pg_upgrade_output.d. command_ok( [ @@ -624,6 +638,17 @@ ok( !-d $newnode->data_dir . "/pg_upgrade_output.d", $newnode->start; +# The 8-byte OID has been carried. +if (!defined($ENV{oldinstall})) +{ + is( $newnode->safe_psql( + 'postgres', + "SELECT next_oid >= '$big_next_oid'::oid8 FROM pg_control_checkpoint()" + ), + 't', + '8-byte OID counter is carried over by pg_upgrade'); +} + # Check if there are any logs coming from pg_upgrade, that would only be # retained on failure. my $log_path = $newnode->data_dir . "/pg_upgrade_output.d"; diff --git a/doc/src/sgml/func/func-info.sgml b/doc/src/sgml/func/func-info.sgml index 1ff023b741c8..e56c9a22c42a 100644 --- a/doc/src/sgml/func/func-info.sgml +++ b/doc/src/sgml/func/func-info.sgml @@ -3433,7 +3433,7 @@ acl | {postgres=arwdDxtm/postgres,foo=r/postgres} <row> <entry><structfield>next_oid</structfield></entry> - <entry><type>oid</type></entry> + <entry><type>oid8</type></entry> </row> <row> diff --git a/doc/src/sgml/ref/pg_resetwal.sgml b/doc/src/sgml/ref/pg_resetwal.sgml index 664c525f481c..d34aa26413c0 100644 --- a/doc/src/sgml/ref/pg_resetwal.sgml +++ b/doc/src/sgml/ref/pg_resetwal.sgml @@ -286,17 +286,19 @@ 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> There is no comparably easy way to determine a next OID that's beyond the largest one in the database, but fortunately it is not critical to - get the next-OID setting right. + get the next-OID setting right, as long as the counter is not moved + backwards. The counter is 8 bytes wide and never wraps around during + the life of a cluster. </para> </listitem> </varlistentry> -- 2.55.0
signature.asc
Description: PGP signature
