From 03ee1e96425f885348617f244037c1e9e9a4675b Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Thu, 14 Aug 2025 11:03:17 +0900
Subject: [PATCH v5 01/15] Implement oid8 data type

This new identifier type will be used for 8-byte TOAST values, and can
be useful for other purposes, not yet defined as of writing this patch.
The following operators are added for this data type:
- Casts with integer types and OID.
- btree and hash operators
- min/max functions.
- Tests and documentation.

XXX: Requires catversion bump.
---
 src/include/c.h                           |  11 +-
 src/include/catalog/pg_aggregate.dat      |   6 +
 src/include/catalog/pg_amop.dat           |  23 +++
 src/include/catalog/pg_amproc.dat         |  12 ++
 src/include/catalog/pg_cast.dat           |  14 ++
 src/include/catalog/pg_opclass.dat        |   4 +
 src/include/catalog/pg_operator.dat       |  26 +++
 src/include/catalog/pg_opfamily.dat       |   4 +
 src/include/catalog/pg_proc.dat           |  64 +++++++
 src/include/catalog/pg_type.dat           |   5 +
 src/include/fmgr.h                        |   2 +
 src/include/postgres.h                    |  20 +++
 src/include/postgres_ext.h                |   1 -
 src/backend/access/nbtree/nbtcompare.c    |  82 +++++++++
 src/backend/bootstrap/bootstrap.c         |   2 +
 src/backend/utils/adt/Makefile            |   1 +
 src/backend/utils/adt/int8.c              |   8 +
 src/backend/utils/adt/meson.build         |   1 +
 src/backend/utils/adt/oid8.c              | 171 +++++++++++++++++++
 src/fe_utils/print.c                      |   1 +
 src/test/regress/expected/oid8.out        | 196 ++++++++++++++++++++++
 src/test/regress/expected/oid8.sql        |   0
 src/test/regress/expected/opr_sanity.out  |   7 +
 src/test/regress/expected/type_sanity.out |   1 +
 src/test/regress/parallel_schedule        |   2 +-
 src/test/regress/sql/oid8.sql             |  57 +++++++
 src/test/regress/sql/type_sanity.sql      |   1 +
 doc/src/sgml/datatype.sgml                |  11 ++
 doc/src/sgml/func/func-aggregate.sgml     |   8 +-
 29 files changed, 734 insertions(+), 7 deletions(-)
 create mode 100644 src/backend/utils/adt/oid8.c
 create mode 100644 src/test/regress/expected/oid8.out
 create mode 100644 src/test/regress/expected/oid8.sql
 create mode 100644 src/test/regress/sql/oid8.sql

diff --git a/src/include/c.h b/src/include/c.h
index 39022f8a9dd7..ea2d5d4a1640 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -530,6 +530,7 @@ typedef uint32 bits32;			/* >= 32 bits */
 /* snprintf format strings to use for 64-bit integers */
 #define INT64_FORMAT "%" PRId64
 #define UINT64_FORMAT "%" PRIu64
+#define OID8_FORMAT "%" PRIu64
 
 /*
  * 128-bit signed and unsigned integers
@@ -616,7 +617,7 @@ typedef double float8;
 #define FLOAT8PASSBYVAL true
 
 /*
- * Oid, RegProcedure, TransactionId, SubTransactionId, MultiXactId,
+ * Oid, Oid8, RegProcedure, TransactionId, SubTransactionId, MultiXactId,
  * CommandId
  */
 
@@ -648,6 +649,12 @@ typedef uint32 CommandId;
 #define FirstCommandId	((CommandId) 0)
 #define InvalidCommandId	(~(CommandId)0)
 
+/* 8-byte Object ID */
+typedef uint64 Oid8;
+
+#define InvalidOid8		((Oid8) 0)
+#define OID8_MAX	UINT64_MAX
+#define atooid8(x) ((Oid8) strtou64((x), NULL, 10))
 
 /* ----------------
  *		Variable-length datatypes all share the 'struct varlena' header.
@@ -754,6 +761,8 @@ typedef NameData *Name;
 
 #define OidIsValid(objectId)  ((bool) ((objectId) != InvalidOid))
 
+#define Oid8IsValid(objectId)  ((bool) ((objectId) != InvalidOid8))
+
 #define RegProcedureIsValid(p)	OidIsValid(p)
 
 
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index d6aa1f6ec478..75acf4ef96cd 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -104,6 +104,9 @@
 { aggfnoid => 'max(oid)', aggtransfn => 'oidlarger',
   aggcombinefn => 'oidlarger', aggsortop => '>(oid,oid)',
   aggtranstype => 'oid' },
+{ aggfnoid => 'max(oid8)', aggtransfn => 'oid8larger',
+  aggcombinefn => 'oid8larger', aggsortop => '>(oid8,oid8)',
+  aggtranstype => 'oid8' },
 { aggfnoid => 'max(float4)', aggtransfn => 'float4larger',
   aggcombinefn => 'float4larger', aggsortop => '>(float4,float4)',
   aggtranstype => 'float4' },
@@ -178,6 +181,9 @@
 { aggfnoid => 'min(oid)', aggtransfn => 'oidsmaller',
   aggcombinefn => 'oidsmaller', aggsortop => '<(oid,oid)',
   aggtranstype => 'oid' },
+{ aggfnoid => 'min(oid8)', aggtransfn => 'oid8smaller',
+  aggcombinefn => 'oid8smaller', aggsortop => '<(oid8,oid8)',
+  aggtranstype => 'oid8' },
 { aggfnoid => 'min(float4)', aggtransfn => 'float4smaller',
   aggcombinefn => 'float4smaller', aggsortop => '<(float4,float4)',
   aggtranstype => 'float4' },
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 2a693cfc31c6..2c3004d53611 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -180,6 +180,24 @@
 { amopfamily => 'btree/oid_ops', amoplefttype => 'oid', amoprighttype => 'oid',
   amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'btree' },
 
+# btree oid8_ops
+
+{ amopfamily => 'btree/oid8_ops', amoplefttype => 'oid8',
+  amoprighttype => 'oid8', amopstrategy => '1', amopopr => '<(oid8,oid8)',
+  amopmethod => 'btree' },
+{ amopfamily => 'btree/oid8_ops', amoplefttype => 'oid8',
+  amoprighttype => 'oid8', amopstrategy => '2', amopopr => '<=(oid8,oid8)',
+  amopmethod => 'btree' },
+{ amopfamily => 'btree/oid8_ops', amoplefttype => 'oid8',
+  amoprighttype => 'oid8', amopstrategy => '3', amopopr => '=(oid8,oid8)',
+  amopmethod => 'btree' },
+{ amopfamily => 'btree/oid8_ops', amoplefttype => 'oid8',
+  amoprighttype => 'oid8', amopstrategy => '4', amopopr => '>=(oid8,oid8)',
+  amopmethod => 'btree' },
+{ amopfamily => 'btree/oid8_ops', amoplefttype => 'oid8',
+  amoprighttype => 'oid8', amopstrategy => '5', amopopr => '>(oid8,oid8)',
+  amopmethod => 'btree' },
+
 # btree xid8_ops
 
 { amopfamily => 'btree/xid8_ops', amoplefttype => 'xid8',
@@ -974,6 +992,11 @@
 { amopfamily => 'hash/oid_ops', amoplefttype => 'oid', amoprighttype => 'oid',
   amopstrategy => '1', amopopr => '=(oid,oid)', amopmethod => 'hash' },
 
+# oid8_ops
+{ amopfamily => 'hash/oid8_ops', amoplefttype => 'oid8',
+  amoprighttype => 'oid8', amopstrategy => '1', amopopr => '=(oid8,oid8)',
+  amopmethod => 'hash' },
+
 # oidvector_ops
 { amopfamily => 'hash/oidvector_ops', amoplefttype => 'oidvector',
   amoprighttype => 'oidvector', amopstrategy => '1',
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index e3477500baa7..d3719b3610c4 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -213,6 +213,14 @@
   amprocrighttype => 'oid', amprocnum => '4', amproc => 'btequalimage' },
 { amprocfamily => 'btree/oid_ops', amproclefttype => 'oid',
   amprocrighttype => 'oid', amprocnum => '6', amproc => 'btoidskipsupport' },
+{ amprocfamily => 'btree/oid8_ops', amproclefttype => 'oid8',
+  amprocrighttype => 'oid8', amprocnum => '1', amproc => 'btoid8cmp' },
+{ amprocfamily => 'btree/oid8_ops', amproclefttype => 'oid8',
+  amprocrighttype => 'oid8', amprocnum => '2', amproc => 'btoid8sortsupport' },
+{ amprocfamily => 'btree/oid8_ops', amproclefttype => 'oid8',
+  amprocrighttype => 'oid8', amprocnum => '4', amproc => 'btequalimage' },
+{ amprocfamily => 'btree/oid8_ops', amproclefttype => 'oid8',
+  amprocrighttype => 'oid8', amprocnum => '6', amproc => 'btoid8skipsupport' },
 { amprocfamily => 'btree/oidvector_ops', amproclefttype => 'oidvector',
   amprocrighttype => 'oidvector', amprocnum => '1',
   amproc => 'btoidvectorcmp' },
@@ -432,6 +440,10 @@
   amprocrighttype => 'xid8', amprocnum => '1', amproc => 'hashxid8' },
 { amprocfamily => 'hash/xid8_ops', amproclefttype => 'xid8',
   amprocrighttype => 'xid8', amprocnum => '2', amproc => 'hashxid8extended' },
+{ amprocfamily => 'hash/oid8_ops', amproclefttype => 'oid8',
+  amprocrighttype => 'oid8', amprocnum => '1', amproc => 'hashoid8' },
+{ amprocfamily => 'hash/oid8_ops', amproclefttype => 'oid8',
+  amprocrighttype => 'oid8', amprocnum => '2', amproc => 'hashoid8extended' },
 { amprocfamily => 'hash/cid_ops', amproclefttype => 'cid',
   amprocrighttype => 'cid', amprocnum => '1', amproc => 'hashcid' },
 { amprocfamily => 'hash/cid_ops', amproclefttype => 'cid',
diff --git a/src/include/catalog/pg_cast.dat b/src/include/catalog/pg_cast.dat
index fbfd669587f0..695f6b2a5e73 100644
--- a/src/include/catalog/pg_cast.dat
+++ b/src/include/catalog/pg_cast.dat
@@ -296,6 +296,20 @@
 { castsource => 'regdatabase', casttarget => 'int4', castfunc => '0',
   castcontext => 'a', castmethod => 'b' },
 
+# OID8 category: allow implicit conversion from any integral type (including
+# int8), as well as assignment coercion to int8.
+{ castsource => 'int8', casttarget => 'oid8', castfunc => '0',
+  castcontext => 'i', castmethod => 'b' },
+{ castsource => 'int2', casttarget => 'oid8', castfunc => 'int8(int2)',
+  castcontext => 'i', castmethod => 'f' },
+{ castsource => 'int4', casttarget => 'oid8', castfunc => 'int8(int4)',
+  castcontext => 'i', castmethod => 'f' },
+{ castsource => 'oid8', casttarget => 'int8', castfunc => '0',
+  castcontext => 'a', castmethod => 'b' },
+# Assignment coercion from oid to oid8.
+{ castsource => 'oid', casttarget => 'oid8', castfunc => 'oid8(oid)',
+  castcontext => 'a', castmethod => 'f' },
+
 # String category
 { castsource => 'text', casttarget => 'bpchar', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index 4a9624802aa5..c0de88fabc49 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -177,6 +177,10 @@
   opcintype => 'xid8' },
 { opcmethod => 'btree', opcname => 'xid8_ops', opcfamily => 'btree/xid8_ops',
   opcintype => 'xid8' },
+{ opcmethod => 'hash', opcname => 'oid8_ops', opcfamily => 'hash/oid8_ops',
+  opcintype => 'oid8' },
+{ opcmethod => 'btree', opcname => 'oid8_ops', opcfamily => 'btree/oid8_ops',
+  opcintype => 'oid8' },
 { opcmethod => 'hash', opcname => 'cid_ops', opcfamily => 'hash/cid_ops',
   opcintype => 'cid' },
 { opcmethod => 'hash', opcname => 'tid_ops', opcfamily => 'hash/tid_ops',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 6d9dc1528d6e..87a7255490a7 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3460,4 +3460,30 @@
   oprcode => 'multirange_after_multirange', oprrest => 'multirangesel',
   oprjoin => 'scalargtjoinsel' },
 
+{ oid => '8262', descr => 'equal',
+  oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'oid8',
+  oprright => 'oid8', oprresult => 'bool', oprcom => '=(oid8,oid8)',
+  oprnegate => '<>(oid8,oid8)', oprcode => 'oid8eq', oprrest => 'eqsel',
+  oprjoin => 'eqjoinsel' },
+{ oid => '8263', descr => 'not equal',
+  oprname => '<>', oprleft => 'oid8', oprright => 'oid8', oprresult => 'bool',
+  oprcom => '<>(oid8,oid8)', oprnegate => '=(oid8,oid8)', oprcode => 'oid8ne',
+  oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
+{ oid => '8264', descr => 'less than',
+  oprname => '<', oprleft => 'oid8', oprright => 'oid8', oprresult => 'bool',
+  oprcom => '>(oid8,oid8)', oprnegate => '>=(oid8,oid8)', oprcode => 'oid8lt',
+  oprrest => 'scalarltsel', oprjoin => 'scalarltjoinsel' },
+{ oid => '8265', descr => 'greater than',
+  oprname => '>', oprleft => 'oid8', oprright => 'oid8', oprresult => 'bool',
+  oprcom => '<(oid8,oid8)', oprnegate => '<=(oid8,oid8)', oprcode => 'oid8gt',
+  oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
+{ oid => '8266', descr => 'less than or equal',
+  oprname => '<=', oprleft => 'oid8', oprright => 'oid8', oprresult => 'bool',
+  oprcom => '>=(oid8,oid8)', oprnegate => '>(oid8,oid8)', oprcode => 'oid8le',
+  oprrest => 'scalarlesel', oprjoin => 'scalarlejoinsel' },
+{ oid => '8267', descr => 'greater than or equal',
+  oprname => '>=', oprleft => 'oid8', oprright => 'oid8', oprresult => 'bool',
+  oprcom => '<=(oid8,oid8)', oprnegate => '<(oid8,oid8)', oprcode => 'oid8ge',
+  oprrest => 'scalargesel', oprjoin => 'scalargejoinsel' },
+
 ]
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index f7dcb96b43ce..54472ce97dcd 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -116,6 +116,10 @@
   opfmethod => 'hash', opfname => 'xid8_ops' },
 { oid => '5067',
   opfmethod => 'btree', opfname => 'xid8_ops' },
+{ oid => '8278',
+  opfmethod => 'hash', opfname => 'oid8_ops' },
+{ oid => '8279',
+  opfmethod => 'btree', opfname => 'oid8_ops' },
 { oid => '2226',
   opfmethod => 'hash', opfname => 'cid_ops' },
 { oid => '2227',
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 118d6da1ace0..b78508e83b9b 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -1046,6 +1046,15 @@
 { oid => '6405', descr => 'skip support',
   proname => 'btoidskipsupport', prorettype => 'void',
   proargtypes => 'internal', prosrc => 'btoidskipsupport' },
+{ oid => '8282', descr => 'less-equal-greater',
+  proname => 'btoid8cmp', proleakproof => 't', prorettype => 'int4',
+  proargtypes => 'oid8 oid8', prosrc => 'btoid8cmp' },
+{ oid => '8283', descr => 'sort support',
+  proname => 'btoid8sortsupport', prorettype => 'void',
+  proargtypes => 'internal', prosrc => 'btoid8sortsupport' },
+{ oid => '8284', descr => 'skip support',
+  proname => 'btoid8skipsupport', prorettype => 'void',
+  proargtypes => 'internal', prosrc => 'btoid8skipsupport' },
 { oid => '404', descr => 'less-equal-greater',
   proname => 'btoidvectorcmp', proleakproof => 't', prorettype => 'int4',
   proargtypes => 'oidvector oidvector', prosrc => 'btoidvectorcmp' },
@@ -12576,4 +12585,59 @@
   proargnames => '{pid,io_id,io_generation,state,operation,off,length,target,handle_data_len,raw_result,result,target_desc,f_sync,f_localmem,f_buffered}',
   prosrc => 'pg_get_aios' },
 
+# oid8 related functions
+{ oid => '8255', descr => 'convert oid to oid8',
+  proname => 'oid8', prorettype => 'oid8', proargtypes => 'oid',
+  prosrc => 'oidtooid8' },
+{ oid => '8257', descr => 'I/O',
+  proname => 'oid8in', prorettype => 'oid8', proargtypes => 'cstring',
+  prosrc => 'oid8in' },
+{ oid => '8258', descr => 'I/O',
+  proname => 'oid8out', prorettype => 'cstring', proargtypes => 'oid8',
+  prosrc => 'oid8out' },
+{ oid => '8259', descr => 'I/O',
+  proname => 'oid8recv', prorettype => 'oid8', proargtypes => 'internal',
+  prosrc => 'oid8recv' },
+{ oid => '8260', descr => 'I/O',
+  proname => 'oid8send', prorettype => 'bytea', proargtypes => 'oid8',
+  prosrc => 'oid8send' },
+# Comparators
+{ oid => '8268',
+  proname => 'oid8eq', proleakproof => 't', prorettype => 'bool',
+  proargtypes => 'oid8 oid8', prosrc => 'oid8eq' },
+{ oid => '8269',
+  proname => 'oid8ne', proleakproof => 't', prorettype => 'bool',
+  proargtypes => 'oid8 oid8', prosrc => 'oid8ne' },
+{ oid => '8270',
+  proname => 'oid8lt', proleakproof => 't', prorettype => 'bool',
+  proargtypes => 'oid8 oid8', prosrc => 'oid8lt' },
+{ oid => '8271',
+  proname => 'oid8le', proleakproof => 't', prorettype => 'bool',
+  proargtypes => 'oid8 oid8', prosrc => 'oid8le' },
+{ oid => '8272',
+  proname => 'oid8gt', proleakproof => 't', prorettype => 'bool',
+  proargtypes => 'oid8 oid8', prosrc => 'oid8gt' },
+{ oid => '8273',
+  proname => 'oid8ge', proleakproof => 't', prorettype => 'bool',
+  proargtypes => 'oid8 oid8', prosrc => 'oid8ge' },
+# Aggregates
+{ oid => '8274', descr => 'larger of two',
+  proname => 'oid8larger', prorettype => 'oid8', proargtypes => 'oid8 oid8',
+  prosrc => 'oid8larger' },
+{ oid => '8275', descr => 'smaller of two',
+  proname => 'oid8smaller', prorettype => 'oid8', proargtypes => 'oid8 oid8',
+  prosrc => 'oid8smaller' },
+{ oid => '8276', descr => 'maximum value of all oid8 input values',
+  proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'oid8',
+  proargtypes => 'oid8', prosrc => 'aggregate_dummy' },
+{ oid => '8277', descr => 'minimum value of all oid8 input values',
+  proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'oid8',
+  proargtypes => 'oid8', prosrc => 'aggregate_dummy' },
+{ oid => '8280', descr => 'hash',
+  proname => 'hashoid8', prorettype => 'int4', proargtypes => 'oid8',
+  prosrc => 'hashoid8' },
+{ oid => '8281', descr => 'hash',
+  proname => 'hashoid8extended', prorettype => 'int8',
+  proargtypes => 'oid8 int8', prosrc => 'hashoid8extended' },
+
 ]
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index cb730aeac864..704f2890cb28 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -700,4 +700,9 @@
   typreceive => 'brin_minmax_multi_summary_recv',
   typsend => 'brin_minmax_multi_summary_send', typalign => 'i',
   typstorage => 'x', typcollation => 'default' },
+{ oid => '8256', array_type_oid => '8261',
+  descr => 'object identifier(oid8), 8 bytes',
+  typname => 'oid8', typlen => '8', typbyval => 't',
+  typcategory => 'N', typinput => 'oid8in', typoutput => 'oid8out',
+  typreceive => 'oid8recv', typsend => 'oid8send', typalign => 'd' },
 ]
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index c7236e429724..111588f75c86 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -273,6 +273,7 @@ extern struct varlena *pg_detoast_datum_packed(struct varlena *datum);
 #define PG_GETARG_CHAR(n)	 DatumGetChar(PG_GETARG_DATUM(n))
 #define PG_GETARG_BOOL(n)	 DatumGetBool(PG_GETARG_DATUM(n))
 #define PG_GETARG_OID(n)	 DatumGetObjectId(PG_GETARG_DATUM(n))
+#define PG_GETARG_OID8(n)	 DatumGetObjectId8(PG_GETARG_DATUM(n))
 #define PG_GETARG_POINTER(n) DatumGetPointer(PG_GETARG_DATUM(n))
 #define PG_GETARG_CSTRING(n) DatumGetCString(PG_GETARG_DATUM(n))
 #define PG_GETARG_NAME(n)	 DatumGetName(PG_GETARG_DATUM(n))
@@ -358,6 +359,7 @@ extern struct varlena *pg_detoast_datum_packed(struct varlena *datum);
 #define PG_RETURN_CHAR(x)	 return CharGetDatum(x)
 #define PG_RETURN_BOOL(x)	 return BoolGetDatum(x)
 #define PG_RETURN_OID(x)	 return ObjectIdGetDatum(x)
+#define PG_RETURN_OID8(x)	 return ObjectId8GetDatum(x)
 #define PG_RETURN_POINTER(x) return PointerGetDatum(x)
 #define PG_RETURN_CSTRING(x) return CStringGetDatum(x)
 #define PG_RETURN_NAME(x)	 return NameGetDatum(x)
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 357cbd6fd961..a5a0e3b7cbfa 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -264,6 +264,26 @@ ObjectIdGetDatum(Oid X)
 	return (Datum) X;
 }
 
+/*
+ * DatumGetObjectId8
+ *		Returns 8-byte object identifier value of a datum.
+ */
+static inline Oid8
+DatumGetObjectId8(Datum X)
+{
+	return (Oid8) X;
+}
+
+/*
+ * ObjectId8GetDatum
+ *		Returns datum representation for an 8-byte object identifier
+ */
+static inline Datum
+ObjectId8GetDatum(Oid8 X)
+{
+	return (Datum) X;
+}
+
 /*
  * DatumGetTransactionId
  *		Returns transaction identifier value of a datum.
diff --git a/src/include/postgres_ext.h b/src/include/postgres_ext.h
index bf45c50dcf31..c80b195bf235 100644
--- a/src/include/postgres_ext.h
+++ b/src/include/postgres_ext.h
@@ -41,7 +41,6 @@ typedef unsigned int Oid;
 #define atooid(x) ((Oid) strtoul((x), NULL, 10))
 /* the above needs <stdlib.h> */
 
-
 /*
  * Identifiers of error message fields.  Kept here to keep common
  * between frontend and backend, and also to export them to libpq
diff --git a/src/backend/access/nbtree/nbtcompare.c b/src/backend/access/nbtree/nbtcompare.c
index 188c27b4925f..3f59ba3f1ad0 100644
--- a/src/backend/access/nbtree/nbtcompare.c
+++ b/src/backend/access/nbtree/nbtcompare.c
@@ -498,6 +498,88 @@ btoidskipsupport(PG_FUNCTION_ARGS)
 	PG_RETURN_VOID();
 }
 
+Datum
+btoid8cmp(PG_FUNCTION_ARGS)
+{
+	Oid8		a = PG_GETARG_OID8(0);
+	Oid8		b = PG_GETARG_OID8(1);
+
+	if (a > b)
+		PG_RETURN_INT32(A_GREATER_THAN_B);
+	else if (a == b)
+		PG_RETURN_INT32(0);
+	else
+		PG_RETURN_INT32(A_LESS_THAN_B);
+}
+
+static int
+btoid8fastcmp(Datum x, Datum y, SortSupport ssup)
+{
+	Oid8		a = DatumGetObjectId8(x);
+	Oid8		b = DatumGetObjectId8(y);
+
+	if (a > b)
+		return A_GREATER_THAN_B;
+	else if (a == b)
+		return 0;
+	else
+		return A_LESS_THAN_B;
+}
+
+Datum
+btoid8sortsupport(PG_FUNCTION_ARGS)
+{
+	SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
+
+	ssup->comparator = btoid8fastcmp;
+	PG_RETURN_VOID();
+}
+
+static Datum
+oid8_decrement(Relation rel, Datum existing, bool *underflow)
+{
+	Oid8		oexisting = DatumGetObjectId8(existing);
+
+	if (oexisting == InvalidOid8)
+	{
+		/* return value is undefined */
+		*underflow = true;
+		return (Datum) 0;
+	}
+
+	*underflow = false;
+	return ObjectId8GetDatum(oexisting - 1);
+}
+
+static Datum
+oid8_increment(Relation rel, Datum existing, bool *overflow)
+{
+	Oid8		oexisting = DatumGetObjectId8(existing);
+
+	if (oexisting == OID8_MAX)
+	{
+		/* return value is undefined */
+		*overflow = true;
+		return (Datum) 0;
+	}
+
+	*overflow = false;
+	return ObjectId8GetDatum(oexisting + 1);
+}
+
+Datum
+btoid8skipsupport(PG_FUNCTION_ARGS)
+{
+	SkipSupport sksup = (SkipSupport) PG_GETARG_POINTER(0);
+
+	sksup->decrement = oid8_decrement;
+	sksup->increment = oid8_increment;
+	sksup->low_elem = ObjectId8GetDatum(InvalidOid8);
+	sksup->high_elem = ObjectId8GetDatum(OID8_MAX);
+
+	PG_RETURN_VOID();
+}
+
 Datum
 btoidvectorcmp(PG_FUNCTION_ARGS)
 {
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index fc8638c1b61b..48e6966e6b48 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -115,6 +115,8 @@ static const struct typinfo TypInfo[] = {
 	F_TEXTIN, F_TEXTOUT},
 	{"oid", OIDOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
 	F_OIDIN, F_OIDOUT},
+	{"oid8", OID8OID, 0, 8, true, TYPALIGN_DOUBLE, TYPSTORAGE_PLAIN, InvalidOid,
+	F_OID8IN, F_OID8OUT},
 	{"tid", TIDOID, 0, 6, false, TYPALIGN_SHORT, TYPSTORAGE_PLAIN, InvalidOid,
 	F_TIDIN, F_TIDOUT},
 	{"xid", XIDOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index ffeacf2b819f..42d7e1db2433 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -76,6 +76,7 @@ OBJS = \
 	numeric.o \
 	numutils.o \
 	oid.o \
+	oid8.o \
 	oracle_compat.o \
 	orderedsetaggs.o \
 	partitionfuncs.o \
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index bdea490202a6..9f7466e47b79 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -1323,6 +1323,14 @@ oidtoi8(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64((int64) arg);
 }
 
+Datum
+oidtooid8(PG_FUNCTION_ARGS)
+{
+	Oid			arg = PG_GETARG_OID(0);
+
+	PG_RETURN_OID8((Oid8) arg);
+}
+
 /*
  * non-persistent numeric series generator
  */
diff --git a/src/backend/utils/adt/meson.build b/src/backend/utils/adt/meson.build
index ed9bbd7b9266..74926c32321b 100644
--- a/src/backend/utils/adt/meson.build
+++ b/src/backend/utils/adt/meson.build
@@ -63,6 +63,7 @@ backend_sources += files(
   'numeric.c',
   'numutils.c',
   'oid.c',
+  'oid8.c',
   'oracle_compat.c',
   'orderedsetaggs.c',
   'partitionfuncs.c',
diff --git a/src/backend/utils/adt/oid8.c b/src/backend/utils/adt/oid8.c
new file mode 100644
index 000000000000..6e9ffd96303f
--- /dev/null
+++ b/src/backend/utils/adt/oid8.c
@@ -0,0 +1,171 @@
+/*-------------------------------------------------------------------------
+ *
+ * oid8.c
+ *	  Functions for the built-in type Oid8
+ *
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/adt/oid8.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <ctype.h>
+#include <limits.h>
+
+#include "catalog/pg_type.h"
+#include "libpq/pqformat.h"
+#include "utils/builtins.h"
+
+#define MAXOID8LEN 20
+
+/*****************************************************************************
+ *	 USER I/O ROUTINES														 *
+ *****************************************************************************/
+
+Datum
+oid8in(PG_FUNCTION_ARGS)
+{
+	char	   *s = PG_GETARG_CSTRING(0);
+	Oid8		result;
+
+	result = uint64in_subr(s, NULL, "oid8", fcinfo->context);
+	PG_RETURN_OID8(result);
+}
+
+Datum
+oid8out(PG_FUNCTION_ARGS)
+{
+	Oid8		val = PG_GETARG_OID8(0);
+	char		buf[MAXOID8LEN + 1];
+	char	   *result;
+	int			len;
+
+	len = pg_ulltoa_n(val, buf) + 1;
+	buf[len - 1] = '\0';
+
+	/*
+	 * Since the length is already known, we do a manual palloc() and memcpy()
+	 * to avoid the strlen() call that would otherwise be done in pstrdup().
+	 */
+	result = palloc(len);
+	memcpy(result, buf, len);
+	PG_RETURN_CSTRING(result);
+}
+
+/*
+ *		oid8recv			- converts external binary format to oid8
+ */
+Datum
+oid8recv(PG_FUNCTION_ARGS)
+{
+	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+
+	PG_RETURN_OID8(pq_getmsgint64(buf));
+}
+
+/*
+ *		oid8send			- converts oid8 to binary format
+ */
+Datum
+oid8send(PG_FUNCTION_ARGS)
+{
+	Oid8		arg1 = PG_GETARG_OID8(0);
+	StringInfoData buf;
+
+	pq_begintypsend(&buf);
+	pq_sendint64(&buf, arg1);
+	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}
+
+/*****************************************************************************
+ *	 PUBLIC ROUTINES														 *
+ *****************************************************************************/
+
+Datum
+oid8eq(PG_FUNCTION_ARGS)
+{
+	Oid8		arg1 = PG_GETARG_OID8(0);
+	Oid8		arg2 = PG_GETARG_OID8(1);
+
+	PG_RETURN_BOOL(arg1 == arg2);
+}
+
+Datum
+oid8ne(PG_FUNCTION_ARGS)
+{
+	Oid8		arg1 = PG_GETARG_OID8(0);
+	Oid8		arg2 = PG_GETARG_OID8(1);
+
+	PG_RETURN_BOOL(arg1 != arg2);
+}
+
+Datum
+oid8lt(PG_FUNCTION_ARGS)
+{
+	Oid8		arg1 = PG_GETARG_OID8(0);
+	Oid8		arg2 = PG_GETARG_OID8(1);
+
+	PG_RETURN_BOOL(arg1 < arg2);
+}
+
+Datum
+oid8le(PG_FUNCTION_ARGS)
+{
+	Oid8		arg1 = PG_GETARG_OID8(0);
+	Oid8		arg2 = PG_GETARG_OID8(1);
+
+	PG_RETURN_BOOL(arg1 <= arg2);
+}
+
+Datum
+oid8ge(PG_FUNCTION_ARGS)
+{
+	Oid8		arg1 = PG_GETARG_OID8(0);
+	Oid8		arg2 = PG_GETARG_OID8(1);
+
+	PG_RETURN_BOOL(arg1 >= arg2);
+}
+
+Datum
+oid8gt(PG_FUNCTION_ARGS)
+{
+	Oid8		arg1 = PG_GETARG_OID8(0);
+	Oid8		arg2 = PG_GETARG_OID8(1);
+
+	PG_RETURN_BOOL(arg1 > arg2);
+}
+
+Datum
+hashoid8(PG_FUNCTION_ARGS)
+{
+	return hashint8(fcinfo);
+}
+
+Datum
+hashoid8extended(PG_FUNCTION_ARGS)
+{
+	return hashint8extended(fcinfo);
+}
+
+Datum
+oid8larger(PG_FUNCTION_ARGS)
+{
+	Oid8		arg1 = PG_GETARG_OID8(0);
+	Oid8		arg2 = PG_GETARG_OID8(1);
+
+	PG_RETURN_OID8((arg1 > arg2) ? arg1 : arg2);
+}
+
+Datum
+oid8smaller(PG_FUNCTION_ARGS)
+{
+	Oid8		arg1 = PG_GETARG_OID8(0);
+	Oid8		arg2 = PG_GETARG_OID8(1);
+
+	PG_RETURN_OID8((arg1 < arg2) ? arg1 : arg2);
+}
diff --git a/src/fe_utils/print.c b/src/fe_utils/print.c
index 4af0f32f2fc0..221624707892 100644
--- a/src/fe_utils/print.c
+++ b/src/fe_utils/print.c
@@ -3624,6 +3624,7 @@ column_type_alignment(Oid ftype)
 		case FLOAT8OID:
 		case NUMERICOID:
 		case OIDOID:
+		case OID8OID:
 		case XIDOID:
 		case XID8OID:
 		case CIDOID:
diff --git a/src/test/regress/expected/oid8.out b/src/test/regress/expected/oid8.out
new file mode 100644
index 000000000000..80529214ca53
--- /dev/null
+++ b/src/test/regress/expected/oid8.out
@@ -0,0 +1,196 @@
+--
+-- OID8
+--
+CREATE TABLE OID8_TBL(f1 oid8);
+INSERT INTO OID8_TBL(f1) VALUES ('1234');
+INSERT INTO OID8_TBL(f1) VALUES ('1235');
+INSERT INTO OID8_TBL(f1) VALUES ('987');
+INSERT INTO OID8_TBL(f1) VALUES ('-1040');
+INSERT INTO OID8_TBL(f1) VALUES ('99999999');
+INSERT INTO OID8_TBL(f1) VALUES ('5     ');
+INSERT INTO OID8_TBL(f1) VALUES ('   10  ');
+-- leading/trailing hard tab is also allowed
+INSERT INTO OID8_TBL(f1) VALUES ('	  15 	  ');
+-- bad inputs
+INSERT INTO OID8_TBL(f1) VALUES ('');
+ERROR:  invalid input syntax for type oid8: ""
+LINE 1: INSERT INTO OID8_TBL(f1) VALUES ('');
+                                         ^
+INSERT INTO OID8_TBL(f1) VALUES ('    ');
+ERROR:  invalid input syntax for type oid8: "    "
+LINE 1: INSERT INTO OID8_TBL(f1) VALUES ('    ');
+                                         ^
+INSERT INTO OID8_TBL(f1) VALUES ('asdfasd');
+ERROR:  invalid input syntax for type oid8: "asdfasd"
+LINE 1: INSERT INTO OID8_TBL(f1) VALUES ('asdfasd');
+                                         ^
+INSERT INTO OID8_TBL(f1) VALUES ('99asdfasd');
+ERROR:  invalid input syntax for type oid8: "99asdfasd"
+LINE 1: INSERT INTO OID8_TBL(f1) VALUES ('99asdfasd');
+                                         ^
+INSERT INTO OID8_TBL(f1) VALUES ('5    d');
+ERROR:  invalid input syntax for type oid8: "5    d"
+LINE 1: INSERT INTO OID8_TBL(f1) VALUES ('5    d');
+                                         ^
+INSERT INTO OID8_TBL(f1) VALUES ('    5d');
+ERROR:  invalid input syntax for type oid8: "    5d"
+LINE 1: INSERT INTO OID8_TBL(f1) VALUES ('    5d');
+                                         ^
+INSERT INTO OID8_TBL(f1) VALUES ('5    5');
+ERROR:  invalid input syntax for type oid8: "5    5"
+LINE 1: INSERT INTO OID8_TBL(f1) VALUES ('5    5');
+                                         ^
+INSERT INTO OID8_TBL(f1) VALUES (' - 500');
+ERROR:  invalid input syntax for type oid8: " - 500"
+LINE 1: INSERT INTO OID8_TBL(f1) VALUES (' - 500');
+                                         ^
+INSERT INTO OID8_TBL(f1) VALUES ('3908203590239580293850293850329485');
+ERROR:  value "3908203590239580293850293850329485" is out of range for type oid8
+LINE 1: INSERT INTO OID8_TBL(f1) VALUES ('39082035902395802938502938...
+                                         ^
+INSERT INTO OID8_TBL(f1) VALUES ('-1204982019841029840928340329840934');
+ERROR:  value "-1204982019841029840928340329840934" is out of range for type oid8
+LINE 1: INSERT INTO OID8_TBL(f1) VALUES ('-1204982019841029840928340...
+                                         ^
+SELECT * FROM OID8_TBL;
+          f1          
+----------------------
+                 1234
+                 1235
+                  987
+ 18446744073709550576
+             99999999
+                    5
+                   10
+                   15
+(8 rows)
+
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('1234', 'oid8');
+ pg_input_is_valid 
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('01XYZ', 'oid8');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT * FROM pg_input_error_info('01XYZ', 'oid8');
+                   message                   | detail | hint | sql_error_code 
+---------------------------------------------+--------+------+----------------
+ invalid input syntax for type oid8: "01XYZ" |        |      | 22P02
+(1 row)
+
+SELECT pg_input_is_valid('3908203590239580293850293850329485', 'oid8');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT * FROM pg_input_error_info('-1204982019841029840928340329840934', 'oid8');
+                                  message                                  | detail | hint | sql_error_code 
+---------------------------------------------------------------------------+--------+------+----------------
+ value "-1204982019841029840928340329840934" is out of range for type oid8 |        |      | 22003
+(1 row)
+
+-- Operators
+SELECT o.* FROM OID8_TBL o WHERE o.f1 = 1234;
+  f1  
+------
+ 1234
+(1 row)
+
+SELECT o.* FROM OID8_TBL o WHERE o.f1 <> '1234';
+          f1          
+----------------------
+                 1235
+                  987
+ 18446744073709550576
+             99999999
+                    5
+                   10
+                   15
+(7 rows)
+
+SELECT o.* FROM OID8_TBL o WHERE o.f1 <= '1234';
+  f1  
+------
+ 1234
+  987
+    5
+   10
+   15
+(5 rows)
+
+SELECT o.* FROM OID8_TBL o WHERE o.f1 < '1234';
+ f1  
+-----
+ 987
+   5
+  10
+  15
+(4 rows)
+
+SELECT o.* FROM OID8_TBL o WHERE o.f1 >= '1234';
+          f1          
+----------------------
+                 1234
+                 1235
+ 18446744073709550576
+             99999999
+(4 rows)
+
+SELECT o.* FROM OID8_TBL o WHERE o.f1 > '1234';
+          f1          
+----------------------
+                 1235
+ 18446744073709550576
+             99999999
+(3 rows)
+
+-- Casts
+SELECT 1::int2::oid8;
+ oid8 
+------
+    1
+(1 row)
+
+SELECT 1::int4::oid8;
+ oid8 
+------
+    1
+(1 row)
+
+SELECT 1::int8::oid8;
+ oid8 
+------
+    1
+(1 row)
+
+SELECT 1::oid8::int8;
+ int8 
+------
+    1
+(1 row)
+
+SELECT 1::oid::oid8; -- ok
+ oid8 
+------
+    1
+(1 row)
+
+SELECT 1::oid8::oid; -- not ok
+ERROR:  cannot cast type oid8 to oid
+LINE 1: SELECT 1::oid8::oid;
+                      ^
+-- Aggregates
+SELECT min(f1), max(f1) FROM OID8_TBL;
+ min |         max          
+-----+----------------------
+   5 | 18446744073709550576
+(1 row)
+
+DROP TABLE OID8_TBL;
diff --git a/src/test/regress/expected/oid8.sql b/src/test/regress/expected/oid8.sql
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 20bf9ea9cdf7..1b2a1641029d 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -880,6 +880,13 @@ bytea(integer)
 bytea(bigint)
 bytea_larger(bytea,bytea)
 bytea_smaller(bytea,bytea)
+oid8eq(oid8,oid8)
+oid8ne(oid8,oid8)
+oid8lt(oid8,oid8)
+oid8le(oid8,oid8)
+oid8gt(oid8,oid8)
+oid8ge(oid8,oid8)
+btoid8cmp(oid8,oid8)
 -- Check that functions without argument are not marked as leakproof.
 SELECT p1.oid::regprocedure
 FROM pg_proc p1 JOIN pg_namespace pn
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index 943e56506bf1..9ddcacec6bf4 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -702,6 +702,7 @@ CREATE TABLE tab_core_types AS SELECT
   'abc'::refcursor,
   '1 2'::int2vector,
   '1 2'::oidvector,
+  '1234'::oid8,
   format('%I=UC/%I', USER, USER)::aclitem AS aclitem,
   'a fat cat sat on a mat and ate a fat rat'::tsvector,
   'fat & rat'::tsquery,
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index fbffc67ae601..56e129ce4aa0 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -28,7 +28,7 @@ test: strings md5 numerology point lseg line box path polygon circle date time t
 # geometry depends on point, lseg, line, box, path, polygon, circle
 # horology depends on date, time, timetz, timestamp, timestamptz, interval
 # ----------
-test: geometry horology tstypes regex type_sanity opr_sanity misc_sanity comments expressions unicode xid mvcc database stats_import
+test: geometry horology tstypes regex type_sanity opr_sanity misc_sanity comments expressions unicode xid mvcc database stats_import oid8
 
 # ----------
 # Load huge amounts of data
diff --git a/src/test/regress/sql/oid8.sql b/src/test/regress/sql/oid8.sql
new file mode 100644
index 000000000000..c4f2ae6a2e57
--- /dev/null
+++ b/src/test/regress/sql/oid8.sql
@@ -0,0 +1,57 @@
+--
+-- OID8
+--
+
+CREATE TABLE OID8_TBL(f1 oid8);
+
+INSERT INTO OID8_TBL(f1) VALUES ('1234');
+INSERT INTO OID8_TBL(f1) VALUES ('1235');
+INSERT INTO OID8_TBL(f1) VALUES ('987');
+INSERT INTO OID8_TBL(f1) VALUES ('-1040');
+INSERT INTO OID8_TBL(f1) VALUES ('99999999');
+INSERT INTO OID8_TBL(f1) VALUES ('5     ');
+INSERT INTO OID8_TBL(f1) VALUES ('   10  ');
+-- leading/trailing hard tab is also allowed
+INSERT INTO OID8_TBL(f1) VALUES ('	  15 	  ');
+
+-- bad inputs
+INSERT INTO OID8_TBL(f1) VALUES ('');
+INSERT INTO OID8_TBL(f1) VALUES ('    ');
+INSERT INTO OID8_TBL(f1) VALUES ('asdfasd');
+INSERT INTO OID8_TBL(f1) VALUES ('99asdfasd');
+INSERT INTO OID8_TBL(f1) VALUES ('5    d');
+INSERT INTO OID8_TBL(f1) VALUES ('    5d');
+INSERT INTO OID8_TBL(f1) VALUES ('5    5');
+INSERT INTO OID8_TBL(f1) VALUES (' - 500');
+INSERT INTO OID8_TBL(f1) VALUES ('3908203590239580293850293850329485');
+INSERT INTO OID8_TBL(f1) VALUES ('-1204982019841029840928340329840934');
+
+SELECT * FROM OID8_TBL;
+
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('1234', 'oid8');
+SELECT pg_input_is_valid('01XYZ', 'oid8');
+SELECT * FROM pg_input_error_info('01XYZ', 'oid8');
+SELECT pg_input_is_valid('3908203590239580293850293850329485', 'oid8');
+SELECT * FROM pg_input_error_info('-1204982019841029840928340329840934', 'oid8');
+
+-- Operators
+SELECT o.* FROM OID8_TBL o WHERE o.f1 = 1234;
+SELECT o.* FROM OID8_TBL o WHERE o.f1 <> '1234';
+SELECT o.* FROM OID8_TBL o WHERE o.f1 <= '1234';
+SELECT o.* FROM OID8_TBL o WHERE o.f1 < '1234';
+SELECT o.* FROM OID8_TBL o WHERE o.f1 >= '1234';
+SELECT o.* FROM OID8_TBL o WHERE o.f1 > '1234';
+
+-- Casts
+SELECT 1::int2::oid8;
+SELECT 1::int4::oid8;
+SELECT 1::int8::oid8;
+SELECT 1::oid8::int8;
+SELECT 1::oid::oid8; -- ok
+SELECT 1::oid8::oid; -- not ok
+
+-- Aggregates
+SELECT min(f1), max(f1) FROM OID8_TBL;
+
+DROP TABLE OID8_TBL;
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index df795759bb4c..c2496823d90e 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -530,6 +530,7 @@ CREATE TABLE tab_core_types AS SELECT
   'abc'::refcursor,
   '1 2'::int2vector,
   '1 2'::oidvector,
+  '1234'::oid8,
   format('%I=UC/%I', USER, USER)::aclitem AS aclitem,
   'a fat cat sat on a mat and ate a fat rat'::tsvector,
   'fat & rat'::tsquery,
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index b81d89e26080..66c6aa7f349a 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -4723,6 +4723,10 @@ INSERT INTO mytable VALUES(-1);  -- fails
     <primary>oid</primary>
    </indexterm>
 
+   <indexterm zone="datatype-oid">
+    <primary>oid8</primary>
+   </indexterm>
+
    <indexterm zone="datatype-oid">
     <primary>regclass</primary>
    </indexterm>
@@ -4805,6 +4809,13 @@ INSERT INTO mytable VALUES(-1);  -- fails
     individual tables.
    </para>
 
+   <para>
+    In some contexts, a 64-bit variant <type>oid8</type> is used.
+    It is implemented as an unsigned eight-byte integer. Unlike its
+    <type>oid</type> counterpart, it can ensure uniqueness in large
+    individual tables.
+   </para>
+
    <para>
     The <type>oid</type> type itself has few operations beyond comparison.
     It can be cast to integer, however, and then manipulated using the
diff --git a/doc/src/sgml/func/func-aggregate.sgml b/doc/src/sgml/func/func-aggregate.sgml
index f50b692516b6..a5396048adf3 100644
--- a/doc/src/sgml/func/func-aggregate.sgml
+++ b/doc/src/sgml/func/func-aggregate.sgml
@@ -508,8 +508,8 @@
         Computes the maximum of the non-null input
         values.  Available for any numeric, string, date/time, or enum type,
         as well as <type>bytea</type>, <type>inet</type>, <type>interval</type>,
-        <type>money</type>, <type>oid</type>, <type>pg_lsn</type>,
-        <type>tid</type>, <type>xid8</type>,
+        <type>money</type>, <type>oid</type>, <type>oid8</type>,
+        <type>pg_lsn</type>, <type>tid</type>, <type>xid8</type>,
         and also arrays and composite types containing sortable data types.
        </para></entry>
        <entry>Yes</entry>
@@ -527,8 +527,8 @@
         Computes the minimum of the non-null input
         values.  Available for any numeric, string, date/time, or enum type,
         as well as <type>bytea</type>, <type>inet</type>, <type>interval</type>,
-        <type>money</type>, <type>oid</type>, <type>pg_lsn</type>,
-        <type>tid</type>, <type>xid8</type>,
+        <type>money</type>, <type>oid</type>, <type>oid8</type>,
+        <type>pg_lsn</type>, <type>tid</type>, <type>xid8</type>,
         and also arrays and composite types containing sortable data types.
        </para></entry>
        <entry>Yes</entry>
-- 
2.50.0

