On Sat, Sep 3, 2022 at 1:50 PM Dilip Kumar <dilipbal...@gmail.com> wrote:
>
> On Tue, Aug 30, 2022 at 9:23 PM Robert Haas <robertmh...@gmail.com> wrote:
>
> > Well, that's very awkward. It doesn't seem like it would be very
> > difficult to teach pg_upgrade to call pg_restore without --clean and
> > just do the drop database itself, but that doesn't really help,
> > because pg_restore will in any event be creating the new database.
> > That doesn't seem like something we can practically refactor out,
> > because only pg_dump knows what properties to use when creating the
> > new database. What we could do is have the dump include a command like
> > SELECT pg_binary_upgrade_move_things_out_of_the_way(some_arguments_here),
> > but that doesn't really help very much, because passing the whole list
> > of relfilenode values from the old database seems pretty certain to be
> > a bad idea. The whole idea here was that we'd be able to build a hash
> > table on the new database's system table OIDs, and it seems like
> > that's not going to work.
>
> Right.
>
> > We could try to salvage some portion of the idea by making
> > pg_binary_upgrade_move_things_out_of_the_way() take a more restricted
> > set of arguments, like the smallest and largest relfilenode values
> > from the old database, and then we'd just need to move things that
> > overlap. But that feels pretty hit-or-miss to me as to whether it
> > actually avoids any work, and
> > pg_binary_upgrade_move_things_out_of_the_way() might also be annoying
> > to write. So perhaps we have to go back to the drawing board here.
>
> So as of now, we have two open options 1) the current approach and
> what patch is following to use Oid as relfilenode for the system
> tables when initially created.  2) call
> pg_binary_upgrade_move_things_out_of_the_way() which force rewrite all
> the system tables.
>
> Another idea that I am not very sure how feasible is. Can we change
> the dump such that in binary upgrade mode it will not use template0 as
> a template database (in creating database command) but instead some
> new database as a template e.g. template-XYZ?   And later for conflict
> checking, we will create this template-XYZ database on the new cluster
> and then we will perform all the conflict check (from all the
> databases of the old cluster) and rewrite operations on this database.
> And later all the databases will be created using template-XYZ as the
> template and all the rewriting stuff we have done is still intact.
> The problems I could think of are 1) only for a binary upgrade we will
> have to change the pg_dump.  2) we will have to use another database
> name as the reserved database name but what if that name is already in
> use in the previous cluster?

While we are still thinking on this issue, I have rebased the patch on
the latest head and fixed a couple of minor issues.

-- 
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com
From a645d39c6109269bb74ed8f0627e61ecf9b0535a Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilip.ku...@enterprisedb.com>
Date: Fri, 26 Aug 2022 10:20:18 +0530
Subject: [PATCH v16] Widen relfilenumber from 32 bits to 56 bits
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Currently relfilenumber is 32 bits wide and that has a risk of wraparound so
the relfilenumber can be reused.  And to guard against the relfilenumber reuse
there is some complicated hack which leaves a 0-length tombstone file around
until the next checkpoint.  And when we allocate a new relfilenumber
we also need to loop to check the on disk conflict.

As part of this patch we are making the relfilenumber 56 bits wide and there will be
no provision for wraparound.  So after this change we will be able to get rid of the
0-length tombstone file and the loop for checking the on-disk conflict of the
relfilenumbers.

The reason behind making it 56 bits wide instead of directly making 64 bits wide is
that if we make it 64 bits wide then the size of the BufferTag will be increased which
will increase the memory usage and that may also impact the performance.  So in order
to avoid that, inside the buffer tag, we will use 8 bits for the fork number and 56 bits
for the relfilenumber.
---
 contrib/pg_buffercache/Makefile                    |   4 +-
 .../pg_buffercache/pg_buffercache--1.3--1.4.sql    |  30 ++++
 contrib/pg_buffercache/pg_buffercache.control      |   2 +-
 contrib/pg_buffercache/pg_buffercache_pages.c      |  39 ++++-
 contrib/pg_prewarm/autoprewarm.c                   |   4 +-
 contrib/pg_walinspect/expected/pg_walinspect.out   |   4 +-
 contrib/pg_walinspect/sql/pg_walinspect.sql        |   4 +-
 contrib/test_decoding/expected/rewrite.out         |   2 +-
 contrib/test_decoding/sql/rewrite.sql              |   2 +-
 doc/src/sgml/catalogs.sgml                         |   2 +-
 doc/src/sgml/pgbuffercache.sgml                    |   2 +-
 doc/src/sgml/storage.sgml                          |   5 +-
 src/backend/access/gin/ginxlog.c                   |   2 +-
 src/backend/access/rmgrdesc/gistdesc.c             |   2 +-
 src/backend/access/rmgrdesc/heapdesc.c             |   2 +-
 src/backend/access/rmgrdesc/nbtdesc.c              |   2 +-
 src/backend/access/rmgrdesc/seqdesc.c              |   2 +-
 src/backend/access/rmgrdesc/xlogdesc.c             |  21 ++-
 src/backend/access/transam/README                  |   5 +-
 src/backend/access/transam/varsup.c                | 183 ++++++++++++++++++++-
 src/backend/access/transam/xlog.c                  |  57 +++++++
 src/backend/access/transam/xlogprefetcher.c        |  14 +-
 src/backend/access/transam/xlogrecovery.c          |   6 +-
 src/backend/access/transam/xlogutils.c             |  12 +-
 src/backend/backup/basebackup.c                    |   2 +-
 src/backend/catalog/catalog.c                      |  95 -----------
 src/backend/catalog/heap.c                         |  25 +--
 src/backend/catalog/index.c                        |  11 +-
 src/backend/catalog/storage.c                      |   8 +
 src/backend/commands/tablecmds.c                   |  12 +-
 src/backend/commands/tablespace.c                  |   2 +-
 src/backend/nodes/gen_node_support.pl              |   4 +-
 src/backend/replication/logical/decode.c           |   1 +
 src/backend/replication/logical/reorderbuffer.c    |   2 +-
 src/backend/storage/file/reinit.c                  |  28 ++--
 src/backend/storage/freespace/fsmpage.c            |   2 +-
 src/backend/storage/lmgr/lwlocknames.txt           |   1 +
 src/backend/storage/smgr/md.c                      |   7 +
 src/backend/storage/smgr/smgr.c                    |   2 +-
 src/backend/utils/adt/dbsize.c                     |   7 +-
 src/backend/utils/adt/pg_upgrade_support.c         |  13 +-
 src/backend/utils/cache/relcache.c                 |   2 +-
 src/backend/utils/cache/relfilenumbermap.c         |  65 +++-----
 src/backend/utils/misc/pg_controldata.c            |   9 +-
 src/bin/pg_checksums/pg_checksums.c                |   4 +-
 src/bin/pg_controldata/pg_controldata.c            |   2 +
 src/bin/pg_dump/pg_dump.c                          |  26 +--
 src/bin/pg_rewind/filemap.c                        |   6 +-
 src/bin/pg_upgrade/info.c                          |   3 +-
 src/bin/pg_upgrade/pg_upgrade.c                    |   6 +-
 src/bin/pg_upgrade/relfilenumber.c                 |   4 +-
 src/bin/pg_waldump/pg_waldump.c                    |   2 +-
 src/bin/scripts/t/090_reindexdb.pl                 |   2 +-
 src/common/relpath.c                               |  20 +--
 src/fe_utils/option_utils.c                        |  39 +++++
 src/include/access/transam.h                       |  35 ++++
 src/include/access/xlog.h                          |   2 +
 src/include/catalog/catalog.h                      |   3 -
 src/include/catalog/pg_class.h                     |  16 +-
 src/include/catalog/pg_control.h                   |   2 +
 src/include/catalog/pg_proc.dat                    |  10 +-
 src/include/common/relpath.h                       |   1 +
 src/include/fe_utils/option_utils.h                |   2 +
 src/include/postgres_ext.h                         |  10 +-
 src/include/storage/buf_internals.h                |  62 ++++++-
 src/include/storage/relfilelocator.h               |  12 +-
 src/test/regress/expected/alter_table.out          |  24 ++-
 src/test/regress/expected/fast_default.out         |   4 +-
 src/test/regress/expected/oidjoins.out             |   2 +-
 src/test/regress/sql/alter_table.sql               |   8 +-
 src/test/regress/sql/fast_default.sql              |   4 +-
 71 files changed, 685 insertions(+), 332 deletions(-)
 create mode 100644 contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql

diff --git a/contrib/pg_buffercache/Makefile b/contrib/pg_buffercache/Makefile
index d74b3e8..4d88eba 100644
--- a/contrib/pg_buffercache/Makefile
+++ b/contrib/pg_buffercache/Makefile
@@ -6,8 +6,8 @@ OBJS = \
 	pg_buffercache_pages.o
 
 EXTENSION = pg_buffercache
-DATA = pg_buffercache--1.2.sql pg_buffercache--1.2--1.3.sql \
-	pg_buffercache--1.1--1.2.sql pg_buffercache--1.0--1.1.sql
+DATA = pg_buffercache--1.0--1.1.sql pg_buffercache--1.1--1.2.sql pg_buffercache--1.2.sql \
+	pg_buffercache--1.2--1.3.sql pg_buffercache--1.3--1.4.sql
 PGFILEDESC = "pg_buffercache - monitoring of shared buffer cache in real-time"
 
 REGRESS = pg_buffercache
diff --git a/contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql b/contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql
new file mode 100644
index 0000000..50956b1
--- /dev/null
+++ b/contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql
@@ -0,0 +1,30 @@
+/* contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_buffercache UPDATE TO '1.4'" to load this file. \quit
+
+/* First we have to remove them from the extension */
+ALTER EXTENSION pg_buffercache DROP VIEW pg_buffercache;
+ALTER EXTENSION pg_buffercache DROP FUNCTION pg_buffercache_pages();
+
+/* Then we can drop them */
+DROP VIEW pg_buffercache;
+DROP FUNCTION pg_buffercache_pages();
+
+/* Now redefine */
+CREATE FUNCTION pg_buffercache_pages()
+RETURNS SETOF RECORD
+AS 'MODULE_PATHNAME', 'pg_buffercache_pages_v1_4'
+LANGUAGE C PARALLEL SAFE;
+
+CREATE VIEW pg_buffercache AS
+	SELECT P.* FROM pg_buffercache_pages() AS P
+	(bufferid integer, relfilenode int8, reltablespace oid, reldatabase oid,
+	 relforknumber int2, relblocknumber int8, isdirty bool, usagecount int2,
+	 pinning_backends int4);
+
+-- Don't want these to be available to public.
+REVOKE ALL ON FUNCTION pg_buffercache_pages() FROM PUBLIC;
+REVOKE ALL ON pg_buffercache FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION pg_buffercache_pages() TO pg_monitor;
+GRANT SELECT ON pg_buffercache TO pg_monitor;
diff --git a/contrib/pg_buffercache/pg_buffercache.control b/contrib/pg_buffercache/pg_buffercache.control
index 8c060ae..a82ae5f 100644
--- a/contrib/pg_buffercache/pg_buffercache.control
+++ b/contrib/pg_buffercache/pg_buffercache.control
@@ -1,5 +1,5 @@
 # pg_buffercache extension
 comment = 'examine the shared buffer cache'
-default_version = '1.3'
+default_version = '1.4'
 module_pathname = '$libdir/pg_buffercache'
 relocatable = true
diff --git a/contrib/pg_buffercache/pg_buffercache_pages.c b/contrib/pg_buffercache/pg_buffercache_pages.c
index c5754ea..912cbd8 100644
--- a/contrib/pg_buffercache/pg_buffercache_pages.c
+++ b/contrib/pg_buffercache/pg_buffercache_pages.c
@@ -59,9 +59,10 @@ typedef struct
  * relation node/tablespace/database/blocknum and dirty indicator.
  */
 PG_FUNCTION_INFO_V1(pg_buffercache_pages);
+PG_FUNCTION_INFO_V1(pg_buffercache_pages_v1_4);
 
-Datum
-pg_buffercache_pages(PG_FUNCTION_ARGS)
+static Datum
+pg_buffercache_pages_internal(PG_FUNCTION_ARGS, Oid rfn_typid)
 {
 	FuncCallContext *funcctx;
 	Datum		result;
@@ -103,7 +104,7 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
 		TupleDescInitEntry(tupledesc, (AttrNumber) 1, "bufferid",
 						   INT4OID, -1, 0);
 		TupleDescInitEntry(tupledesc, (AttrNumber) 2, "relfilenode",
-						   OIDOID, -1, 0);
+						   rfn_typid, -1, 0);
 		TupleDescInitEntry(tupledesc, (AttrNumber) 3, "reltablespace",
 						   OIDOID, -1, 0);
 		TupleDescInitEntry(tupledesc, (AttrNumber) 4, "reldatabase",
@@ -209,7 +210,24 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
 		}
 		else
 		{
-			values[1] = ObjectIdGetDatum(fctx->record[i].relfilenumber);
+			if (rfn_typid == INT8OID)
+				values[1] =
+					Int64GetDatum((int64) fctx->record[i].relfilenumber);
+			else
+			{
+				Assert(rfn_typid == OIDOID);
+
+				if (fctx->record[i].relfilenumber > OID_MAX)
+					ereport(ERROR,
+							errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							errmsg("relfilenode %lld is too large to be represented as an OID",
+									(long long) fctx->record[i].relfilenumber),
+							errhint("Upgrade the extension using ALTER EXTENSION pg_buffercache UPDATE"));
+
+				values[1] =
+					ObjectIdGetDatum((Oid) fctx->record[i].relfilenumber);
+			}
+
 			nulls[1] = false;
 			values[2] = ObjectIdGetDatum(fctx->record[i].reltablespace);
 			nulls[2] = false;
@@ -237,3 +255,16 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
 	else
 		SRF_RETURN_DONE(funcctx);
 }
+
+/* entry point for old extension version */
+Datum
+pg_buffercache_pages(PG_FUNCTION_ARGS)
+{
+	return pg_buffercache_pages_internal(fcinfo, OIDOID);
+}
+
+Datum
+pg_buffercache_pages_v1_4(PG_FUNCTION_ARGS)
+{
+	return pg_buffercache_pages_internal(fcinfo, INT8OID);
+}
diff --git a/contrib/pg_prewarm/autoprewarm.c b/contrib/pg_prewarm/autoprewarm.c
index c8d673a..6bb6da6 100644
--- a/contrib/pg_prewarm/autoprewarm.c
+++ b/contrib/pg_prewarm/autoprewarm.c
@@ -345,7 +345,7 @@ apw_load_buffers(void)
 	{
 		unsigned	forknum;
 
-		if (fscanf(file, "%u,%u,%u,%u,%u\n", &blkinfo[i].database,
+		if (fscanf(file, "%u,%u," INT64_FORMAT ",%u,%u\n", &blkinfo[i].database,
 				   &blkinfo[i].tablespace, &blkinfo[i].filenumber,
 				   &forknum, &blkinfo[i].blocknum) != 5)
 			ereport(ERROR,
@@ -669,7 +669,7 @@ apw_dump_now(bool is_bgworker, bool dump_unlogged)
 	{
 		CHECK_FOR_INTERRUPTS();
 
-		ret = fprintf(file, "%u,%u,%u,%u,%u\n",
+		ret = fprintf(file, "%u,%u," INT64_FORMAT ",%u,%u\n",
 					  block_info_array[i].database,
 					  block_info_array[i].tablespace,
 					  block_info_array[i].filenumber,
diff --git a/contrib/pg_walinspect/expected/pg_walinspect.out b/contrib/pg_walinspect/expected/pg_walinspect.out
index a1ee743..e9b06ed 100644
--- a/contrib/pg_walinspect/expected/pg_walinspect.out
+++ b/contrib/pg_walinspect/expected/pg_walinspect.out
@@ -54,9 +54,9 @@ SELECT COUNT(*) >= 0 AS ok FROM pg_get_wal_stats_till_end_of_wal(:'wal_lsn1');
 -- ===================================================================
 -- Test for filtering out WAL records of a particular table
 -- ===================================================================
-SELECT oid AS sample_tbl_oid FROM pg_class WHERE relname = 'sample_tbl' \gset
+SELECT relfilenode AS sample_tbl_relfilenode FROM pg_class WHERE relname = 'sample_tbl' \gset
 SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_records_info(:'wal_lsn1', :'wal_lsn2')
-			WHERE block_ref LIKE concat('%', :'sample_tbl_oid', '%') AND resource_manager = 'Heap';
+			WHERE block_ref LIKE concat('%', :'sample_tbl_relfilenode', '%') AND resource_manager = 'Heap';
  ok 
 ----
  t
diff --git a/contrib/pg_walinspect/sql/pg_walinspect.sql b/contrib/pg_walinspect/sql/pg_walinspect.sql
index 1b265ea..5393834 100644
--- a/contrib/pg_walinspect/sql/pg_walinspect.sql
+++ b/contrib/pg_walinspect/sql/pg_walinspect.sql
@@ -39,10 +39,10 @@ SELECT COUNT(*) >= 0 AS ok FROM pg_get_wal_stats_till_end_of_wal(:'wal_lsn1');
 -- Test for filtering out WAL records of a particular table
 -- ===================================================================
 
-SELECT oid AS sample_tbl_oid FROM pg_class WHERE relname = 'sample_tbl' \gset
+SELECT relfilenode AS sample_tbl_relfilenode FROM pg_class WHERE relname = 'sample_tbl' \gset
 
 SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_records_info(:'wal_lsn1', :'wal_lsn2')
-			WHERE block_ref LIKE concat('%', :'sample_tbl_oid', '%') AND resource_manager = 'Heap';
+			WHERE block_ref LIKE concat('%', :'sample_tbl_relfilenode', '%') AND resource_manager = 'Heap';
 
 -- ===================================================================
 -- Test for filtering out WAL records based on resource_manager and
diff --git a/contrib/test_decoding/expected/rewrite.out b/contrib/test_decoding/expected/rewrite.out
index b30999c..8f1aa48 100644
--- a/contrib/test_decoding/expected/rewrite.out
+++ b/contrib/test_decoding/expected/rewrite.out
@@ -106,7 +106,7 @@ VACUUM FULL pg_class;
 -- reindexing of important relations / indexes
 REINDEX TABLE pg_class;
 REINDEX INDEX pg_class_oid_index;
-REINDEX INDEX pg_class_tblspc_relfilenode_index;
+REINDEX INDEX pg_class_relfilenode_index;
 INSERT INTO replication_example(somedata, testcolumn1) VALUES (5, 3);
 BEGIN;
 INSERT INTO replication_example(somedata, testcolumn1) VALUES (6, 4);
diff --git a/contrib/test_decoding/sql/rewrite.sql b/contrib/test_decoding/sql/rewrite.sql
index 62dead3..8983704 100644
--- a/contrib/test_decoding/sql/rewrite.sql
+++ b/contrib/test_decoding/sql/rewrite.sql
@@ -77,7 +77,7 @@ VACUUM FULL pg_class;
 -- reindexing of important relations / indexes
 REINDEX TABLE pg_class;
 REINDEX INDEX pg_class_oid_index;
-REINDEX INDEX pg_class_tblspc_relfilenode_index;
+REINDEX INDEX pg_class_relfilenode_index;
 
 INSERT INTO replication_example(somedata, testcolumn1) VALUES (5, 3);
 
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 00f833d..40d4e9c 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1984,7 +1984,7 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
 
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
-       <structfield>relfilenode</structfield> <type>oid</type>
+       <structfield>relfilenode</structfield> <type>int8</type>
       </para>
       <para>
        Name of the on-disk file of this relation; zero means this
diff --git a/doc/src/sgml/pgbuffercache.sgml b/doc/src/sgml/pgbuffercache.sgml
index a06fd3e..e222265 100644
--- a/doc/src/sgml/pgbuffercache.sgml
+++ b/doc/src/sgml/pgbuffercache.sgml
@@ -62,7 +62,7 @@
 
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
-       <structfield>relfilenode</structfield> <type>oid</type>
+       <structfield>relfilenode</structfield> <type>int8</type>
        (references <link linkend="catalog-pg-class"><structname>pg_class</structname></link>.<structfield>relfilenode</structfield>)
       </para>
       <para>
diff --git a/doc/src/sgml/storage.sgml b/doc/src/sgml/storage.sgml
index e5b9f3f..eefba2f 100644
--- a/doc/src/sgml/storage.sgml
+++ b/doc/src/sgml/storage.sgml
@@ -217,11 +217,10 @@ with the suffix <literal>_init</literal> (see <xref linkend="storage-init"/>).
 
 <caution>
 <para>
-Note that while a table's filenode often matches its OID, this is
-<emphasis>not</emphasis> necessarily the case; some operations, like
+Note that table's filenode are completely different than its OID. Although for
+system catalogs initial filenode matches with its OID, but some operations, like
 <command>TRUNCATE</command>, <command>REINDEX</command>, <command>CLUSTER</command> and some forms
 of <command>ALTER TABLE</command>, can change the filenode while preserving the OID.
-Avoid assuming that filenode and table OID are the same.
 Also, for certain system catalogs including <structname>pg_class</structname> itself,
 <structname>pg_class</structname>.<structfield>relfilenode</structfield> contains zero.  The
 actual filenode number of these catalogs is stored in a lower-level data
diff --git a/src/backend/access/gin/ginxlog.c b/src/backend/access/gin/ginxlog.c
index 41b9211..b75ad79 100644
--- a/src/backend/access/gin/ginxlog.c
+++ b/src/backend/access/gin/ginxlog.c
@@ -100,7 +100,7 @@ ginRedoInsertEntry(Buffer buffer, bool isLeaf, BlockNumber rightblkno, void *rda
 		BlockNumber blknum;
 
 		BufferGetTag(buffer, &locator, &forknum, &blknum);
-		elog(ERROR, "failed to add item to index page in %u/%u/%u",
+		elog(ERROR, "failed to add item to index page in %u/%u/" INT64_FORMAT,
 			 locator.spcOid, locator.dbOid, locator.relNumber);
 	}
 }
diff --git a/src/backend/access/rmgrdesc/gistdesc.c b/src/backend/access/rmgrdesc/gistdesc.c
index 7dd3c1d..c699937 100644
--- a/src/backend/access/rmgrdesc/gistdesc.c
+++ b/src/backend/access/rmgrdesc/gistdesc.c
@@ -26,7 +26,7 @@ out_gistxlogPageUpdate(StringInfo buf, gistxlogPageUpdate *xlrec)
 static void
 out_gistxlogPageReuse(StringInfo buf, gistxlogPageReuse *xlrec)
 {
-	appendStringInfo(buf, "rel %u/%u/%u; blk %u; latestRemovedXid %u:%u",
+	appendStringInfo(buf, "rel %u/%u/" INT64_FORMAT "; blk %u; latestRemovedXid %u:%u",
 					 xlrec->locator.spcOid, xlrec->locator.dbOid,
 					 xlrec->locator.relNumber, xlrec->block,
 					 EpochFromFullTransactionId(xlrec->latestRemovedFullXid),
diff --git a/src/backend/access/rmgrdesc/heapdesc.c b/src/backend/access/rmgrdesc/heapdesc.c
index 923d3bc..f6d278b 100644
--- a/src/backend/access/rmgrdesc/heapdesc.c
+++ b/src/backend/access/rmgrdesc/heapdesc.c
@@ -169,7 +169,7 @@ heap2_desc(StringInfo buf, XLogReaderState *record)
 	{
 		xl_heap_new_cid *xlrec = (xl_heap_new_cid *) rec;
 
-		appendStringInfo(buf, "rel %u/%u/%u; tid %u/%u",
+		appendStringInfo(buf, "rel %u/%u/" INT64_FORMAT "; tid %u/%u",
 						 xlrec->target_locator.spcOid,
 						 xlrec->target_locator.dbOid,
 						 xlrec->target_locator.relNumber,
diff --git a/src/backend/access/rmgrdesc/nbtdesc.c b/src/backend/access/rmgrdesc/nbtdesc.c
index 4843cd5..70feb2d 100644
--- a/src/backend/access/rmgrdesc/nbtdesc.c
+++ b/src/backend/access/rmgrdesc/nbtdesc.c
@@ -100,7 +100,7 @@ btree_desc(StringInfo buf, XLogReaderState *record)
 			{
 				xl_btree_reuse_page *xlrec = (xl_btree_reuse_page *) rec;
 
-				appendStringInfo(buf, "rel %u/%u/%u; latestRemovedXid %u:%u",
+				appendStringInfo(buf, "rel %u/%u/" INT64_FORMAT "; latestRemovedXid %u:%u",
 								 xlrec->locator.spcOid, xlrec->locator.dbOid,
 								 xlrec->locator.relNumber,
 								 EpochFromFullTransactionId(xlrec->latestRemovedFullXid),
diff --git a/src/backend/access/rmgrdesc/seqdesc.c b/src/backend/access/rmgrdesc/seqdesc.c
index b3845f9..45c6ee7 100644
--- a/src/backend/access/rmgrdesc/seqdesc.c
+++ b/src/backend/access/rmgrdesc/seqdesc.c
@@ -25,7 +25,7 @@ seq_desc(StringInfo buf, XLogReaderState *record)
 	xl_seq_rec *xlrec = (xl_seq_rec *) rec;
 
 	if (info == XLOG_SEQ_LOG)
-		appendStringInfo(buf, "rel %u/%u/%u",
+		appendStringInfo(buf, "rel %u/%u/" INT64_FORMAT,
 						 xlrec->locator.spcOid, xlrec->locator.dbOid,
 						 xlrec->locator.relNumber);
 }
diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c
index 6fec485..a723790 100644
--- a/src/backend/access/rmgrdesc/xlogdesc.c
+++ b/src/backend/access/rmgrdesc/xlogdesc.c
@@ -45,8 +45,8 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 		CheckPoint *checkpoint = (CheckPoint *) rec;
 
 		appendStringInfo(buf, "redo %X/%X; "
-						 "tli %u; prev tli %u; fpw %s; xid %u:%u; oid %u; multi %u; offset %u; "
-						 "oldest xid %u in DB %u; oldest multi %u in DB %u; "
+						 "tli %u; prev tli %u; fpw %s; xid %u:%u; relfilenumber " INT64_FORMAT ";oid %u; "
+						 "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",
 						 LSN_FORMAT_ARGS(checkpoint->redo),
@@ -55,6 +55,7 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 						 checkpoint->fullPageWrites ? "true" : "false",
 						 EpochFromFullTransactionId(checkpoint->nextXid),
 						 XidFromFullTransactionId(checkpoint->nextXid),
+						 checkpoint->nextRelFileNumber,
 						 checkpoint->nextOid,
 						 checkpoint->nextMulti,
 						 checkpoint->nextMultiOffset,
@@ -74,6 +75,13 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 		memcpy(&nextOid, rec, sizeof(Oid));
 		appendStringInfo(buf, "%u", nextOid);
 	}
+	else if (info == XLOG_NEXT_RELFILENUMBER)
+	{
+		RelFileNumber nextRelFileNumber;
+
+		memcpy(&nextRelFileNumber, rec, sizeof(RelFileNumber));
+		appendStringInfo(buf, INT64_FORMAT, nextRelFileNumber);
+	}
 	else if (info == XLOG_RESTORE_POINT)
 	{
 		xl_restore_point *xlrec = (xl_restore_point *) rec;
@@ -169,6 +177,9 @@ xlog_identify(uint8 info)
 		case XLOG_NEXTOID:
 			id = "NEXTOID";
 			break;
+		case XLOG_NEXT_RELFILENUMBER:
+			id = "NEXT_RELFILENUMBER";
+			break;
 		case XLOG_SWITCH:
 			id = "SWITCH";
 			break;
@@ -237,7 +248,7 @@ XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty,
 				appendStringInfoChar(buf, ' ');
 
 			appendStringInfo(buf,
-							 "blkref #%d: rel %u/%u/%u fork %s blk %u",
+							 "blkref #%d: rel %u/%u/" INT64_FORMAT " fork %s blk %u",
 							 block_id,
 							 rlocator.spcOid, rlocator.dbOid, rlocator.relNumber,
 							 forkNames[forknum],
@@ -297,7 +308,7 @@ XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty,
 			if (forknum != MAIN_FORKNUM)
 			{
 				appendStringInfo(buf,
-								 ", blkref #%d: rel %u/%u/%u fork %s blk %u",
+								 ", blkref #%d: rel %u/%u/" INT64_FORMAT " fork %s blk %u",
 								 block_id,
 								 rlocator.spcOid, rlocator.dbOid, rlocator.relNumber,
 								 forkNames[forknum],
@@ -306,7 +317,7 @@ XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty,
 			else
 			{
 				appendStringInfo(buf,
-								 ", blkref #%d: rel %u/%u/%u blk %u",
+								 ", blkref #%d: rel %u/%u/" INT64_FORMAT " blk %u",
 								 block_id,
 								 rlocator.spcOid, rlocator.dbOid, rlocator.relNumber,
 								 blk);
diff --git a/src/backend/access/transam/README b/src/backend/access/transam/README
index 734c39a..8911671 100644
--- a/src/backend/access/transam/README
+++ b/src/backend/access/transam/README
@@ -692,8 +692,9 @@ by having database restart search for files that don't have any committed
 entry in pg_class, but that currently isn't done because of the possibility
 of deleting data that is useful for forensic analysis of the crash.
 Orphan files are harmless --- at worst they waste a bit of disk space ---
-because we check for on-disk collisions when allocating new relfilenumber
-OIDs.  So cleaning up isn't really necessary.
+because the relfilenumber counter is monotonically increasing.  The maximum
+value is 2^56-1, and there is no provision for wraparound.  Thus, on-disk
+collisions aren't possible.
 
 3. Deleting a table, which requires an unlink() that could fail.
 
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index 849a7ce..a2f0d35 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -13,12 +13,16 @@
 
 #include "postgres.h"
 
+#include <unistd.h>
+
 #include "access/clog.h"
 #include "access/commit_ts.h"
 #include "access/subtrans.h"
 #include "access/transam.h"
 #include "access/xact.h"
 #include "access/xlogutils.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_tablespace.h"
 #include "commands/dbcommands.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -30,6 +34,15 @@
 /* Number of OIDs to prefetch (preallocate) per XLOG write */
 #define VAR_OID_PREFETCH		8192
 
+/* Number of RelFileNumbers to be logged per XLOG write */
+#define VAR_RELNUMBER_PER_XLOG				512
+
+/*
+ * Need to log more if remaining logged RelFileNumbers are less than the
+ * threshold.  Valid range could be between 0 to VAR_RELNUMBER_PER_XLOG - 1.
+ */
+#define VAR_RELNUMBER_NEW_XLOG_THRESHOLD	256
+
 /* pointer to "variable cache" in shared memory (set up by shmem.c) */
 VariableCache ShmemVariableCache = NULL;
 
@@ -521,8 +534,7 @@ ForceTransactionIdLimitUpdate(void)
  * 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.
+ * caller should be GetNewOidWithIndex() in catalog/catalog.c.
  */
 Oid
 GetNewObjectId(void)
@@ -613,6 +625,173 @@ SetNextObjectId(Oid nextOid)
 }
 
 /*
+ * GetNewRelFileNumber
+ *
+ * Similar to GetNewObjectId but instead of new Oid it generates new
+ * relfilenumber.
+ */
+RelFileNumber
+GetNewRelFileNumber(Oid reltablespace, char relpersistence)
+{
+	RelFileNumber result;
+
+	/* safety check, we should never get this far in a HS standby */
+	if (RecoveryInProgress())
+		elog(ERROR, "cannot assign RelFileNumber during recovery");
+
+	if (IsBinaryUpgrade)
+		elog(ERROR, "cannot assign RelFileNumber during binary upgrade");
+
+	LWLockAcquire(RelFileNumberGenLock, LW_EXCLUSIVE);
+
+	/* check for the wraparound for the relfilenumber counter */
+	if (unlikely(ShmemVariableCache->nextRelFileNumber > MAX_RELFILENUMBER))
+		elog(ERROR, "relfilenumber is out of bound");
+
+	/*
+	 * If the remaining logged values are less than the threshold value then
+	 * log more.  Ideally, we can wait until all relfilenumbers have been
+	 * consumed before logging more.  Nevertheless, if we do that, we must
+	 * immediately flush the logged wal record because we want to ensure that
+	 * the nextRelFileNumber is always larger than any relfilenumber already
+	 * in use on disk.  And, to maintain that invariant, we must make sure
+	 * that the record we log reaches the disk before any new files are
+	 * created with the newly logged range.  So in order to avoid flushing the
+	 * wal immediately, we always log before consuming all the relfilenumber,
+	 * and now we only have to flush the newly logged relfilenumber wal before
+	 * consuming the relfilenumber from this new range.  By the time we need
+	 * to flush this wal, hopefully those have already been flushed with some
+	 * other XLogFlush operation.  Although VAR_RELNUMBER_PER_XLOG is large
+	 * enough that it might not slow things down but it is always better if we
+	 * can avoid some extra XLogFlush.
+	 */
+	if (ShmemVariableCache->loggedRelFileNumber -
+		ShmemVariableCache->nextRelFileNumber <=
+		VAR_RELNUMBER_NEW_XLOG_THRESHOLD)
+	{
+		RelFileNumber newlogrelnum;
+
+		/*
+		 * First time, this will immediately flush the newly logged wal record
+		 * as we don't have anything logged in advance.  From next time
+		 * onwards we will remember the previously logged record pointer and
+		 * we will flush upto that point.
+		 *
+		 * XXX second time, it might try to flush the same thing what is
+		 * already done in the first time but it will logically do nothing so
+		 * it is not worth to add any extra complexity to avoid that.
+		 */
+		newlogrelnum = ShmemVariableCache->nextRelFileNumber +
+			VAR_RELNUMBER_PER_XLOG;
+		LogNextRelFileNumber(newlogrelnum,
+							 &ShmemVariableCache->loggedRelFileNumberRecPtr);
+
+		ShmemVariableCache->loggedRelFileNumber = newlogrelnum;
+	}
+
+	result = ShmemVariableCache->nextRelFileNumber;
+	(ShmemVariableCache->nextRelFileNumber)++;
+
+	LWLockRelease(RelFileNumberGenLock);
+
+#ifdef USE_ASSERT_CHECKING
+
+	{
+		RelFileLocatorBackend rlocator;
+		char	   *rpath;
+		BackendId	backend;
+
+		switch (relpersistence)
+		{
+			case RELPERSISTENCE_TEMP:
+				backend = BackendIdForTempRelations();
+				break;
+			case RELPERSISTENCE_UNLOGGED:
+			case RELPERSISTENCE_PERMANENT:
+				backend = InvalidBackendId;
+				break;
+			default:
+				elog(ERROR, "invalid relpersistence: %c", relpersistence);
+				return InvalidRelFileNumber;	/* placate compiler */
+		}
+
+		/* this logic should match RelationInitPhysicalAddr */
+		rlocator.locator.spcOid =
+			reltablespace ? reltablespace : MyDatabaseTableSpace;
+		rlocator.locator.dbOid = (reltablespace == GLOBALTABLESPACE_OID) ?
+			InvalidOid : MyDatabaseId;
+		rlocator.locator.relNumber = result;
+
+		/*
+		 * The relpath will vary based on the backend ID, so we must
+		 * initialize that properly here to make sure that any collisions
+		 * based on filename are properly detected.
+		 */
+		rlocator.backend = backend;
+
+		/* check for existing file of same name. */
+		rpath = relpath(rlocator, MAIN_FORKNUM);
+		Assert(access(rpath, F_OK) != 0);
+	}
+#endif
+
+	return result;
+}
+
+/*
+ * SetNextRelFileNumber
+ *
+ * This may only be called during pg_upgrade; it advances the RelFileNumber
+ * counter to the specified value if the current value is smaller than the
+ * input value.
+ */
+void
+SetNextRelFileNumber(RelFileNumber relnumber)
+{
+	/* safety check, we should never get this far in a HS standby */
+	if (RecoveryInProgress())
+		elog(ERROR, "cannot forward RelFileNumber during recovery");
+
+	if (!IsBinaryUpgrade)
+		elog(ERROR, "RelFileNumber can be set only during binary upgrade");
+
+	LWLockAcquire(RelFileNumberGenLock, LW_EXCLUSIVE);
+
+	/*
+	 * If previous assigned value of the nextRelFileNumber is already higher
+	 * than the current value then nothing to be done.  This is possible
+	 * because during upgrade the objects are not created in the relfilenumber
+	 * order.
+	 */
+	if (relnumber <= ShmemVariableCache->nextRelFileNumber)
+	{
+		LWLockRelease(RelFileNumberGenLock);
+		return;
+	}
+
+	/*
+	 * If the new relfilenumber to be set is greater than or equal to already
+	 * logged relfilenumber then log again.
+	 *
+	 * XXX The new 'relnumber' to be set can be from any range so we can not
+	 * plan to piggyback the XlogFlush by logging in advance.  And it should
+	 * not really matter as this is only called during binary upgrade.
+	 */
+	if (relnumber >= ShmemVariableCache->loggedRelFileNumber)
+	{
+		RelFileNumber newlogrelnum;
+
+		newlogrelnum = relnumber + VAR_RELNUMBER_PER_XLOG;
+		LogNextRelFileNumber(newlogrelnum, NULL);
+		ShmemVariableCache->loggedRelFileNumber = newlogrelnum;
+	}
+
+	ShmemVariableCache->nextRelFileNumber = relnumber;
+
+	LWLockRelease(RelFileNumberGenLock);
+}
+
+/*
  * StopGeneratingPinnedObjectIds
  *
  * This is called once during initdb to force the OID counter up to
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 7a710e6..48c05d2 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -4538,6 +4538,7 @@ BootStrapXLOG(void)
 	checkPoint.nextXid =
 		FullTransactionIdFromEpochAndXid(0, FirstNormalTransactionId);
 	checkPoint.nextOid = FirstGenbkiObjectId;
+	checkPoint.nextRelFileNumber = FirstNormalRelFileNumber;
 	checkPoint.nextMulti = FirstMultiXactId;
 	checkPoint.nextMultiOffset = 0;
 	checkPoint.oldestXid = FirstNormalTransactionId;
@@ -4551,7 +4552,10 @@ BootStrapXLOG(void)
 
 	ShmemVariableCache->nextXid = checkPoint.nextXid;
 	ShmemVariableCache->nextOid = checkPoint.nextOid;
+	ShmemVariableCache->nextRelFileNumber = checkPoint.nextRelFileNumber;
 	ShmemVariableCache->oidCount = 0;
+	ShmemVariableCache->loggedRelFileNumber = checkPoint.nextRelFileNumber;
+	ShmemVariableCache->loggedRelFileNumberRecPtr = InvalidXLogRecPtr;
 	MultiXactSetNextMXact(checkPoint.nextMulti, checkPoint.nextMultiOffset);
 	AdvanceOldestClogXid(checkPoint.oldestXid);
 	SetTransactionIdLimit(checkPoint.oldestXid, checkPoint.oldestXidDB);
@@ -5017,7 +5021,9 @@ StartupXLOG(void)
 	/* initialize shared memory variables from the checkpoint record */
 	ShmemVariableCache->nextXid = checkPoint.nextXid;
 	ShmemVariableCache->nextOid = checkPoint.nextOid;
+	ShmemVariableCache->nextRelFileNumber = checkPoint.nextRelFileNumber;
 	ShmemVariableCache->oidCount = 0;
+	ShmemVariableCache->loggedRelFileNumber = checkPoint.nextRelFileNumber;
 	MultiXactSetNextMXact(checkPoint.nextMulti, checkPoint.nextMultiOffset);
 	AdvanceOldestClogXid(checkPoint.oldestXid);
 	SetTransactionIdLimit(checkPoint.oldestXid, checkPoint.oldestXidDB);
@@ -6483,6 +6489,14 @@ CreateCheckPoint(int flags)
 		checkPoint.nextOid += ShmemVariableCache->oidCount;
 	LWLockRelease(OidGenLock);
 
+	LWLockAcquire(RelFileNumberGenLock, LW_SHARED);
+	if (shutdown)
+		checkPoint.nextRelFileNumber = ShmemVariableCache->nextRelFileNumber;
+	else
+		checkPoint.nextRelFileNumber = ShmemVariableCache->loggedRelFileNumber;
+
+	LWLockRelease(RelFileNumberGenLock);
+
 	MultiXactGetCheckptMulti(shutdown,
 							 &checkPoint.nextMulti,
 							 &checkPoint.nextMultiOffset,
@@ -7361,6 +7375,35 @@ XLogPutNextOid(Oid nextOid)
 }
 
 /*
+ * Similar to the XLogPutNextOid but instead of writing NEXTOID log record it
+ * writes a NEXT_RELFILENUMBER log record.  If '*prevrecptr' is a valid
+ * XLogRecPtrthen flush the wal upto this record pointer otherwise flush upto
+ * currently logged record.  Also store the currently logged record pointer in
+ * the '*prevrecptr' if prevrecptr is not NULL.
+ */
+void
+LogNextRelFileNumber(RelFileNumber nextrelnumber, XLogRecPtr *prevrecptr)
+{
+	XLogRecPtr	recptr;
+
+	XLogBeginInsert();
+	XLogRegisterData((char *) (&nextrelnumber), sizeof(RelFileNumber));
+	recptr = XLogInsert(RM_XLOG_ID, XLOG_NEXT_RELFILENUMBER);
+
+	/*
+	 * If a valid prevrecptr is passed then flush that xlog record to disk
+	 * otherwise flush the newly logged record.
+	 */
+	if ((prevrecptr != NULL) && !XLogRecPtrIsInvalid(*prevrecptr))
+		XLogFlush(*prevrecptr);
+	else
+		XLogFlush(recptr);
+
+	if (prevrecptr != NULL)
+		*prevrecptr = recptr;
+}
+
+/*
  * Write an XLOG SWITCH record.
  *
  * Here we just blindly issue an XLogInsert request for the record.
@@ -7575,6 +7618,16 @@ xlog_redo(XLogReaderState *record)
 		ShmemVariableCache->oidCount = 0;
 		LWLockRelease(OidGenLock);
 	}
+	if (info == XLOG_NEXT_RELFILENUMBER)
+	{
+		RelFileNumber nextRelFileNumber;
+
+		memcpy(&nextRelFileNumber, XLogRecGetData(record), sizeof(RelFileNumber));
+		LWLockAcquire(RelFileNumberGenLock, LW_EXCLUSIVE);
+		ShmemVariableCache->nextRelFileNumber = nextRelFileNumber;
+		ShmemVariableCache->loggedRelFileNumber = nextRelFileNumber;
+		LWLockRelease(RelFileNumberGenLock);
+	}
 	else if (info == XLOG_CHECKPOINT_SHUTDOWN)
 	{
 		CheckPoint	checkPoint;
@@ -7589,6 +7642,10 @@ xlog_redo(XLogReaderState *record)
 		ShmemVariableCache->nextOid = checkPoint.nextOid;
 		ShmemVariableCache->oidCount = 0;
 		LWLockRelease(OidGenLock);
+		LWLockAcquire(RelFileNumberGenLock, LW_EXCLUSIVE);
+		ShmemVariableCache->nextRelFileNumber = checkPoint.nextRelFileNumber;
+		ShmemVariableCache->loggedRelFileNumber = checkPoint.nextRelFileNumber;
+		LWLockRelease(RelFileNumberGenLock);
 		MultiXactSetNextMXact(checkPoint.nextMulti,
 							  checkPoint.nextMultiOffset);
 
diff --git a/src/backend/access/transam/xlogprefetcher.c b/src/backend/access/transam/xlogprefetcher.c
index 9aa5641..a0c5aca 100644
--- a/src/backend/access/transam/xlogprefetcher.c
+++ b/src/backend/access/transam/xlogprefetcher.c
@@ -611,7 +611,7 @@ XLogPrefetcherNextBlock(uintptr_t pgsr_private, XLogRecPtr *lsn)
 
 #ifdef XLOGPREFETCHER_DEBUG_LEVEL
 						elog(XLOGPREFETCHER_DEBUG_LEVEL,
-							 "suppressing prefetch in relation %u/%u/%u until %X/%X is replayed, which creates the relation",
+							 "suppressing prefetch in relation %u/%u/" INT64_FORMAT " until %X/%X is replayed, which creates the relation",
 							 xlrec->rlocator.spcOid,
 							 xlrec->rlocator.dbOid,
 							 xlrec->rlocator.relNumber,
@@ -634,7 +634,7 @@ XLogPrefetcherNextBlock(uintptr_t pgsr_private, XLogRecPtr *lsn)
 
 #ifdef XLOGPREFETCHER_DEBUG_LEVEL
 					elog(XLOGPREFETCHER_DEBUG_LEVEL,
-						 "suppressing prefetch in relation %u/%u/%u from block %u until %X/%X is replayed, which truncates the relation",
+						 "suppressing prefetch in relation %u/%u/" INT64_FORMAT " from block %u until %X/%X is replayed, which truncates the relation",
 						 xlrec->rlocator.spcOid,
 						 xlrec->rlocator.dbOid,
 						 xlrec->rlocator.relNumber,
@@ -733,7 +733,7 @@ XLogPrefetcherNextBlock(uintptr_t pgsr_private, XLogRecPtr *lsn)
 			{
 #ifdef XLOGPREFETCHER_DEBUG_LEVEL
 				elog(XLOGPREFETCHER_DEBUG_LEVEL,
-					 "suppressing all prefetch in relation %u/%u/%u until %X/%X is replayed, because the relation does not exist on disk",
+					 "suppressing all prefetch in relation %u/%u/" INT64_FORMAT " until %X/%X is replayed, because the relation does not exist on disk",
 					 reln->smgr_rlocator.locator.spcOid,
 					 reln->smgr_rlocator.locator.dbOid,
 					 reln->smgr_rlocator.locator.relNumber,
@@ -754,7 +754,7 @@ XLogPrefetcherNextBlock(uintptr_t pgsr_private, XLogRecPtr *lsn)
 			{
 #ifdef XLOGPREFETCHER_DEBUG_LEVEL
 				elog(XLOGPREFETCHER_DEBUG_LEVEL,
-					 "suppressing prefetch in relation %u/%u/%u from block %u until %X/%X is replayed, because the relation is too small",
+					 "suppressing prefetch in relation %u/%u/" INT64_FORMAT " from block %u until %X/%X is replayed, because the relation is too small",
 					 reln->smgr_rlocator.locator.spcOid,
 					 reln->smgr_rlocator.locator.dbOid,
 					 reln->smgr_rlocator.locator.relNumber,
@@ -793,7 +793,7 @@ XLogPrefetcherNextBlock(uintptr_t pgsr_private, XLogRecPtr *lsn)
 				 * truncated beneath our feet?
 				 */
 				elog(ERROR,
-					 "could not prefetch relation %u/%u/%u block %u",
+					 "could not prefetch relation %u/%u/" INT64_FORMAT " block %u",
 					 reln->smgr_rlocator.locator.spcOid,
 					 reln->smgr_rlocator.locator.dbOid,
 					 reln->smgr_rlocator.locator.relNumber,
@@ -932,7 +932,7 @@ XLogPrefetcherIsFiltered(XLogPrefetcher *prefetcher, RelFileLocator rlocator,
 		{
 #ifdef XLOGPREFETCHER_DEBUG_LEVEL
 			elog(XLOGPREFETCHER_DEBUG_LEVEL,
-				 "prefetch of %u/%u/%u block %u suppressed; filtering until LSN %X/%X is replayed (blocks >= %u filtered)",
+				 "prefetch of %u/%u/" INT64_FORMAT " block %u suppressed; filtering until LSN %X/%X is replayed (blocks >= %u filtered)",
 				 rlocator.spcOid, rlocator.dbOid, rlocator.relNumber, blockno,
 				 LSN_FORMAT_ARGS(filter->filter_until_replayed),
 				 filter->filter_from_block);
@@ -948,7 +948,7 @@ XLogPrefetcherIsFiltered(XLogPrefetcher *prefetcher, RelFileLocator rlocator,
 		{
 #ifdef XLOGPREFETCHER_DEBUG_LEVEL
 			elog(XLOGPREFETCHER_DEBUG_LEVEL,
-				 "prefetch of %u/%u/%u block %u suppressed; filtering until LSN %X/%X is replayed (whole database)",
+				 "prefetch of %u/%u/" INT64_FORMAT " block %u suppressed; filtering until LSN %X/%X is replayed (whole database)",
 				 rlocator.spcOid, rlocator.dbOid, rlocator.relNumber, blockno,
 				 LSN_FORMAT_ARGS(filter->filter_until_replayed));
 #endif
diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c
index ae2af5a..3b4da8e 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -2225,14 +2225,14 @@ xlog_block_info(StringInfo buf, XLogReaderState *record)
 			continue;
 
 		if (forknum != MAIN_FORKNUM)
-			appendStringInfo(buf, "; blkref #%d: rel %u/%u/%u, fork %u, blk %u",
+			appendStringInfo(buf, "; blkref #%d: rel %u/%u/" INT64_FORMAT ", fork %u, blk %u",
 							 block_id,
 							 rlocator.spcOid, rlocator.dbOid,
 							 rlocator.relNumber,
 							 forknum,
 							 blk);
 		else
-			appendStringInfo(buf, "; blkref #%d: rel %u/%u/%u, blk %u",
+			appendStringInfo(buf, "; blkref #%d: rel %u/%u/" INT64_FORMAT ", blk %u",
 							 block_id,
 							 rlocator.spcOid, rlocator.dbOid,
 							 rlocator.relNumber,
@@ -2428,7 +2428,7 @@ verifyBackupPageConsistency(XLogReaderState *record)
 		if (memcmp(replay_image_masked, primary_image_masked, BLCKSZ) != 0)
 		{
 			elog(FATAL,
-				 "inconsistent page found, rel %u/%u/%u, forknum %u, blkno %u",
+				 "inconsistent page found, rel %u/%u/" INT64_FORMAT ", forknum %u, blkno %u",
 				 rlocator.spcOid, rlocator.dbOid, rlocator.relNumber,
 				 forknum, blkno);
 		}
diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c
index 0cda225..b4398e1 100644
--- a/src/backend/access/transam/xlogutils.c
+++ b/src/backend/access/transam/xlogutils.c
@@ -617,17 +617,17 @@ CreateFakeRelcacheEntry(RelFileLocator rlocator)
 	rel->rd_rel->relpersistence = RELPERSISTENCE_PERMANENT;
 
 	/* We don't know the name of the relation; use relfilenumber instead */
-	sprintf(RelationGetRelationName(rel), "%u", rlocator.relNumber);
+	sprintf(RelationGetRelationName(rel), INT64_FORMAT, rlocator.relNumber);
 
 	/*
 	 * We set up the lockRelId in case anything tries to lock the dummy
-	 * relation.  Note that this is fairly bogus since relNumber may be
-	 * different from the relation's OID.  It shouldn't really matter though.
-	 * In recovery, we are running by ourselves and can't have any lock
-	 * conflicts.  While syncing, we already hold AccessExclusiveLock.
+	 * relation.  Note we are setting relId to just FirstNormalObjectId which
+	 * is completely bogus.  It shouldn't really matter though. In recovery,
+	 * we are running by ourselves and can't have any lock conflicts.  While
+	 * syncing, we already hold AccessExclusiveLock.
 	 */
 	rel->rd_lockInfo.lockRelId.dbId = rlocator.dbOid;
-	rel->rd_lockInfo.lockRelId.relId = rlocator.relNumber;
+	rel->rd_lockInfo.lockRelId.relId = FirstNormalObjectId;
 
 	rel->rd_smgr = NULL;
 
diff --git a/src/backend/backup/basebackup.c b/src/backend/backup/basebackup.c
index 3bf3aa6..07cf34a 100644
--- a/src/backend/backup/basebackup.c
+++ b/src/backend/backup/basebackup.c
@@ -1230,7 +1230,7 @@ sendDir(bbsink *sink, const char *path, int basepathlen, bool sizeonly,
 			if (relForkNum != INIT_FORKNUM)
 			{
 				char		initForkFile[MAXPGPATH];
-				char		relNumber[OIDCHARS + 1];
+				char		relNumber[RELNUMBERCHARS + 1];
 
 				/*
 				 * If any other type of fork, check if there is an init fork
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index 2abd6b0..a9bd8ae 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -483,101 +483,6 @@ GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
 }
 
 /*
- * GetNewRelFileNumber
- *		Generate a new relfilenumber that is unique within the
- *		database of the given tablespace.
- *
- * If the relfilenumber will also be used as the relation's OID, pass the
- * opened pg_class catalog, and this routine will guarantee that the result
- * is also an unused OID within pg_class.  If the result is to be used only
- * as a relfilenumber for an existing relation, pass NULL for pg_class.
- *
- * As with GetNewOidWithIndex(), there is some theoretical risk of a race
- * condition, but it doesn't seem worth worrying about.
- *
- * Note: we don't support using this in bootstrap mode.  All relations
- * created by bootstrap have preassigned OIDs, so there's no need.
- */
-RelFileNumber
-GetNewRelFileNumber(Oid reltablespace, Relation pg_class, char relpersistence)
-{
-	RelFileLocatorBackend rlocator;
-	char	   *rpath;
-	bool		collides;
-	BackendId	backend;
-
-	/*
-	 * If we ever get here during pg_upgrade, there's something wrong; all
-	 * relfilenumber assignments during a binary-upgrade run should be
-	 * determined by commands in the dump script.
-	 */
-	Assert(!IsBinaryUpgrade);
-
-	switch (relpersistence)
-	{
-		case RELPERSISTENCE_TEMP:
-			backend = BackendIdForTempRelations();
-			break;
-		case RELPERSISTENCE_UNLOGGED:
-		case RELPERSISTENCE_PERMANENT:
-			backend = InvalidBackendId;
-			break;
-		default:
-			elog(ERROR, "invalid relpersistence: %c", relpersistence);
-			return InvalidRelFileNumber;	/* placate compiler */
-	}
-
-	/* This logic should match RelationInitPhysicalAddr */
-	rlocator.locator.spcOid = reltablespace ? reltablespace : MyDatabaseTableSpace;
-	rlocator.locator.dbOid =
-		(rlocator.locator.spcOid == GLOBALTABLESPACE_OID) ?
-		InvalidOid : MyDatabaseId;
-
-	/*
-	 * The relpath will vary based on the backend ID, so we must initialize
-	 * that properly here to make sure that any collisions based on filename
-	 * are properly detected.
-	 */
-	rlocator.backend = backend;
-
-	do
-	{
-		CHECK_FOR_INTERRUPTS();
-
-		/* Generate the OID */
-		if (pg_class)
-			rlocator.locator.relNumber = GetNewOidWithIndex(pg_class, ClassOidIndexId,
-															Anum_pg_class_oid);
-		else
-			rlocator.locator.relNumber = GetNewObjectId();
-
-		/* Check for existing file of same name */
-		rpath = relpath(rlocator, MAIN_FORKNUM);
-
-		if (access(rpath, F_OK) == 0)
-		{
-			/* definite collision */
-			collides = true;
-		}
-		else
-		{
-			/*
-			 * Here we have a little bit of a dilemma: if errno is something
-			 * other than ENOENT, should we declare a collision and loop? In
-			 * practice it seems best to go ahead regardless of the errno.  If
-			 * there is a colliding file we will get an smgr failure when we
-			 * attempt to create the new relation file.
-			 */
-			collides = false;
-		}
-
-		pfree(rpath);
-	} while (collides);
-
-	return rlocator.locator.relNumber;
-}
-
-/*
  * SQL callable interface for GetNewOidWithIndex().  Outside of initdb's
  * direct insertions into catalog tables, and recovering from corruption, this
  * should rarely be needed.
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 9b03579..c17f60f 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -342,10 +342,18 @@ heap_create(const char *relname,
 	{
 		/*
 		 * If relfilenumber is unspecified by the caller then create storage
-		 * with oid same as relid.
+		 * with relfilenumber same as relid if it is a system table otherwise
+		 * allocate a new relfilenumber.  For more details read comments atop
+		 * FirstNormalRelFileNumber declaration.
 		 */
 		if (!RelFileNumberIsValid(relfilenumber))
-			relfilenumber = relid;
+		{
+			if (relid < FirstNormalObjectId)
+				relfilenumber = relid;
+			else
+				relfilenumber = GetNewRelFileNumber(reltablespace,
+													relpersistence);
+		}
 	}
 
 	/*
@@ -898,7 +906,7 @@ InsertPgClassTuple(Relation pg_class_desc,
 	values[Anum_pg_class_reloftype - 1] = ObjectIdGetDatum(rd_rel->reloftype);
 	values[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(rd_rel->relowner);
 	values[Anum_pg_class_relam - 1] = ObjectIdGetDatum(rd_rel->relam);
-	values[Anum_pg_class_relfilenode - 1] = ObjectIdGetDatum(rd_rel->relfilenode);
+	values[Anum_pg_class_relfilenode - 1] = Int64GetDatum(rd_rel->relfilenode);
 	values[Anum_pg_class_reltablespace - 1] = ObjectIdGetDatum(rd_rel->reltablespace);
 	values[Anum_pg_class_relpages - 1] = Int32GetDatum(rd_rel->relpages);
 	values[Anum_pg_class_reltuples - 1] = Float4GetDatum(rd_rel->reltuples);
@@ -1170,12 +1178,7 @@ heap_create_with_catalog(const char *relname,
 	if (shared_relation && reltablespace != GLOBALTABLESPACE_OID)
 		elog(ERROR, "shared relations must be placed in pg_global tablespace");
 
-	/*
-	 * Allocate an OID for the relation, unless we were told what to use.
-	 *
-	 * The OID will be the relfilenumber as well, so make sure it doesn't
-	 * collide with either pg_class OIDs or existing physical files.
-	 */
+	/* Allocate an OID for the relation, unless we were told what to use. */
 	if (!OidIsValid(relid))
 	{
 		/* Use binary-upgrade override for pg_class.oid and relfilenumber */
@@ -1229,8 +1232,8 @@ heap_create_with_catalog(const char *relname,
 		}
 
 		if (!OidIsValid(relid))
-			relid = GetNewRelFileNumber(reltablespace, pg_class_desc,
-										relpersistence);
+			relid = GetNewOidWithIndex(pg_class_desc, ClassOidIndexId,
+									   Anum_pg_class_oid);
 	}
 
 	/*
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index d7192f3..a509c3e 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -898,12 +898,7 @@ index_create(Relation heapRelation,
 											collationObjectId,
 											classObjectId);
 
-	/*
-	 * Allocate an OID for the index, unless we were told what to use.
-	 *
-	 * The OID will be the relfilenumber as well, so make sure it doesn't
-	 * collide with either pg_class OIDs or existing physical files.
-	 */
+	/* Allocate an OID for the index, unless we were told what to use. */
 	if (!OidIsValid(indexRelationId))
 	{
 		/* Use binary-upgrade override for pg_class.oid and relfilenumber */
@@ -935,8 +930,8 @@ index_create(Relation heapRelation,
 		}
 		else
 		{
-			indexRelationId =
-				GetNewRelFileNumber(tableSpaceId, pg_class, relpersistence);
+			indexRelationId = GetNewOidWithIndex(pg_class, ClassOidIndexId,
+												 Anum_pg_class_oid);
 		}
 	}
 
diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c
index d708af1..080622b 100644
--- a/src/backend/catalog/storage.c
+++ b/src/backend/catalog/storage.c
@@ -968,6 +968,10 @@ smgr_redo(XLogReaderState *record)
 		xl_smgr_create *xlrec = (xl_smgr_create *) XLogRecGetData(record);
 		SMgrRelation reln;
 
+		if (xlrec->rlocator.relNumber > ShmemVariableCache->nextRelFileNumber)
+			elog(ERROR, "unexpected relnumber " INT64_FORMAT " that is bigger than nextRelFileNumber " INT64_FORMAT,
+				 xlrec->rlocator.relNumber, ShmemVariableCache->nextRelFileNumber);
+
 		reln = smgropen(xlrec->rlocator, InvalidBackendId);
 		smgrcreate(reln, xlrec->forkNum, true);
 	}
@@ -981,6 +985,10 @@ smgr_redo(XLogReaderState *record)
 		int			nforks = 0;
 		bool		need_fsm_vacuum = false;
 
+		if (xlrec->rlocator.relNumber > ShmemVariableCache->nextRelFileNumber)
+			elog(ERROR, "unexpected relnumber " INT64_FORMAT "that is bigger than nextRelFileNumber " INT64_FORMAT,
+				 xlrec->rlocator.relNumber, ShmemVariableCache->nextRelFileNumber);
+
 		reln = smgropen(xlrec->rlocator, InvalidBackendId);
 
 		/*
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index dacc989..660b5e4 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -14363,10 +14363,14 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
 	}
 
 	/*
-	 * Relfilenumbers are not unique in databases across tablespaces, so we
-	 * need to allocate a new one in the new tablespace.
-	 */
-	newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
+	 * Generate a new relfilenumber.  We cannot reuse the old relfilenumber
+	 * because of the possibility that that relation will be moved back to the
+	 * original tablespace before the next checkpoint. At that point, the
+	 * first segment of the main fork won't have been unlinked yet, and an
+	 * attempt to create new relation storage with that same relfilenumber
+	 * will fail.
+	 */
+	newrelfilenumber = GetNewRelFileNumber(newTableSpace,
 										   rel->rd_rel->relpersistence);
 
 	/* Open old and new relation */
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index f260b48..728653c 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -267,7 +267,7 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
 	 * parts.
 	 */
 	if (strlen(location) + 1 + strlen(TABLESPACE_VERSION_DIRECTORY) + 1 +
-		OIDCHARS + 1 + OIDCHARS + 1 + FORKNAMECHARS + 1 + OIDCHARS > MAXPGPATH)
+		OIDCHARS + 1 + RELNUMBERCHARS + 1 + FORKNAMECHARS + 1 + OIDCHARS > MAXPGPATH)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 				 errmsg("tablespace location \"%s\" is too long",
diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index b707a09..f809a02 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -961,12 +961,12 @@ _read${n}(void)
 			print $off "\tWRITE_UINT_FIELD($f);\n";
 			print $rff "\tREAD_UINT_FIELD($f);\n" unless $no_read;
 		}
-		elsif ($t eq 'uint64')
+		elsif ($t eq 'uint64' || $t eq 'RelFileNumber')
 		{
 			print $off "\tWRITE_UINT64_FIELD($f);\n";
 			print $rff "\tREAD_UINT64_FIELD($f);\n" unless $no_read;
 		}
-		elsif ($t eq 'Oid' || $t eq 'RelFileNumber')
+		elsif ($t eq 'Oid')
 		{
 			print $off "\tWRITE_OID_FIELD($f);\n";
 			print $rff "\tREAD_OID_FIELD($f);\n" unless $no_read;
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index 1667d72..971e243 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -154,6 +154,7 @@ xlog_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 			break;
 		case XLOG_NOOP:
 		case XLOG_NEXTOID:
+		case XLOG_NEXT_RELFILENUMBER:
 		case XLOG_SWITCH:
 		case XLOG_BACKUP_END:
 		case XLOG_PARAMETER_CHANGE:
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index 89cf9f9..859bc29 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -4932,7 +4932,7 @@ DisplayMapping(HTAB *tuplecid_data)
 	hash_seq_init(&hstat, tuplecid_data);
 	while ((ent = (ReorderBufferTupleCidEnt *) hash_seq_search(&hstat)) != NULL)
 	{
-		elog(DEBUG3, "mapping: node: %u/%u/%u tid: %u/%u cmin: %u, cmax: %u",
+		elog(DEBUG3, "mapping: node: %u/%u/" INT64_FORMAT " tid: %u/%u cmin: %u, cmax: %u",
 			 ent->key.rlocator.dbOid,
 			 ent->key.rlocator.spcOid,
 			 ent->key.rlocator.relNumber,
diff --git a/src/backend/storage/file/reinit.c b/src/backend/storage/file/reinit.c
index 647c458..c3faa68 100644
--- a/src/backend/storage/file/reinit.c
+++ b/src/backend/storage/file/reinit.c
@@ -31,7 +31,7 @@ static void ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname,
 
 typedef struct
 {
-	Oid			reloid;			/* hash key */
+	RelFileNumber		relnumber;			/* hash key */
 } unlogged_relation_entry;
 
 /*
@@ -184,10 +184,10 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
 		 * need to be reset.  Otherwise, this cleanup operation would be
 		 * O(n^2).
 		 */
-		ctl.keysize = sizeof(Oid);
+		ctl.keysize = sizeof(RelFileNumber);
 		ctl.entrysize = sizeof(unlogged_relation_entry);
 		ctl.hcxt = CurrentMemoryContext;
-		hash = hash_create("unlogged relation OIDs", 32, &ctl,
+		hash = hash_create("unlogged relation RelFileNumbers", 32, &ctl,
 						   HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
 		/* Scan the directory. */
@@ -208,10 +208,10 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
 				continue;
 
 			/*
-			 * Put the OID portion of the name into the hash table, if it
-			 * isn't already.
+			 * Put the RELFILENUMBER portion of the name into the hash table,
+			 * if it isn't already.
 			 */
-			ent.reloid = atooid(de->d_name);
+			ent.relnumber = atorelnumber(de->d_name);
 			(void) hash_search(hash, &ent, HASH_ENTER, NULL);
 		}
 
@@ -248,10 +248,10 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
 				continue;
 
 			/*
-			 * See whether the OID portion of the name shows up in the hash
-			 * table.  If so, nuke it!
+			 * See whether the RELFILENUMBER portion of the name shows up in
+			 * the hash table.  If so, nuke it!
 			 */
-			ent.reloid = atooid(de->d_name);
+			ent.relnumber = atorelnumber(de->d_name);
 			if (hash_search(hash, &ent, HASH_FIND, NULL))
 			{
 				snprintf(rm_path, sizeof(rm_path), "%s/%s",
@@ -286,7 +286,7 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
 		{
 			ForkNumber	forkNum;
 			int			relnumchars;
-			char		relnumbuf[OIDCHARS + 1];
+			char		relnumbuf[RELNUMBERCHARS + 1];
 			char		srcpath[MAXPGPATH * 2];
 			char		dstpath[MAXPGPATH];
 
@@ -329,7 +329,7 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
 		{
 			ForkNumber	forkNum;
 			int			relnumchars;
-			char		relnumbuf[OIDCHARS + 1];
+			char		relnumbuf[RELNUMBERCHARS + 1];
 			char		mainpath[MAXPGPATH];
 
 			/* Skip anything that doesn't look like a relation data file. */
@@ -372,8 +372,8 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
  * for a non-temporary relation and false otherwise.
  *
  * NB: If this function returns true, the caller is entitled to assume that
- * *relnumchars has been set to a value no more than OIDCHARS, and thus
- * that a buffer of OIDCHARS+1 characters is sufficient to hold the
+ * *relnumchars has been set to a value no more than RELNUMBERCHARS, and thus
+ * that a buffer of RELNUMBERCHARS+1 characters is sufficient to hold the
  * RelFileNumber portion of the filename.  This is critical to protect against
  * a possible buffer overrun.
  */
@@ -386,7 +386,7 @@ parse_filename_for_nontemp_relation(const char *name, int *relnumchars,
 	/* Look for a non-empty string of digits (that isn't too long). */
 	for (pos = 0; isdigit((unsigned char) name[pos]); ++pos)
 		;
-	if (pos == 0 || pos > OIDCHARS)
+	if (pos == 0 || pos > RELNUMBERCHARS)
 		return false;
 	*relnumchars = pos;
 
diff --git a/src/backend/storage/freespace/fsmpage.c b/src/backend/storage/freespace/fsmpage.c
index af4dab7..172225b 100644
--- a/src/backend/storage/freespace/fsmpage.c
+++ b/src/backend/storage/freespace/fsmpage.c
@@ -273,7 +273,7 @@ restart:
 			BlockNumber blknum;
 
 			BufferGetTag(buf, &rlocator, &forknum, &blknum);
-			elog(DEBUG1, "fixing corrupt FSM block %u, relation %u/%u/%u",
+			elog(DEBUG1, "fixing corrupt FSM block %u, relation %u/%u/" INT64_FORMAT,
 				 blknum, rlocator.spcOid, rlocator.dbOid, rlocator.relNumber);
 
 			/* make sure we hold an exclusive lock */
diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txt
index 6c7cf6c..3c5d041 100644
--- a/src/backend/storage/lmgr/lwlocknames.txt
+++ b/src/backend/storage/lmgr/lwlocknames.txt
@@ -53,3 +53,4 @@ XactTruncationLock					44
 # 45 was XactTruncationLock until removal of BackendRandomLock
 WrapLimitsVacuumLock				46
 NotifyQueueTailLock					47
+RelFileNumberGenLock                48
\ No newline at end of file
diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c
index 3deac49..532bd7f 100644
--- a/src/backend/storage/smgr/md.c
+++ b/src/backend/storage/smgr/md.c
@@ -257,6 +257,13 @@ mdcreate(SMgrRelation reln, ForkNumber forkNum, bool isRedo)
  * next checkpoint, we prevent reassignment of the relfilenumber until it's
  * safe, because relfilenumber assignment skips over any existing file.
  *
+ * XXX. Although all of this was true when relfilenumbers were 32 bits wide,
+ * they are now 56 bits wide and do not wrap around, so in the future we can
+ * change the code to immediately unlink the first segment of the relation
+ * along with all the others. We still do reuse relfilenumbers when createdb()
+ * is performed using the file-copy method or during movedb(), but the scenario
+ * described above can only happen when creating a new relation.
+ *
  * We do not need to go through this dance for temp relations, though, because
  * we never make WAL entries for temp rels, and so a temp rel poses no threat
  * to the health of a regular rel that has taken over its relfilenumber.
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index c1a5feb..ed46ac3 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -154,7 +154,7 @@ smgropen(RelFileLocator rlocator, BackendId backend)
 		/* First time through: initialize the hash table */
 		HASHCTL		ctl;
 
-		ctl.keysize = sizeof(RelFileLocatorBackend);
+		ctl.keysize = SizeOfRelFileLocatorBackend;
 		ctl.entrysize = sizeof(SMgrRelationData);
 		SMgrRelationHash = hash_create("smgr relation table", 400,
 									   &ctl, HASH_ELEM | HASH_BLOBS);
diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c
index 34efa12..9f70f35 100644
--- a/src/backend/utils/adt/dbsize.c
+++ b/src/backend/utils/adt/dbsize.c
@@ -878,7 +878,7 @@ pg_relation_filenode(PG_FUNCTION_ARGS)
 	if (!RelFileNumberIsValid(result))
 		PG_RETURN_NULL();
 
-	PG_RETURN_OID(result);
+	PG_RETURN_INT64(result);
 }
 
 /*
@@ -898,9 +898,12 @@ Datum
 pg_filenode_relation(PG_FUNCTION_ARGS)
 {
 	Oid			reltablespace = PG_GETARG_OID(0);
-	RelFileNumber relfilenumber = PG_GETARG_OID(1);
+	RelFileNumber relfilenumber = PG_GETARG_INT64(1);
 	Oid			heaprel;
 
+	/* check whether the relfilenumber is within a valid range */
+	CHECK_RELFILENUMBER_RANGE(relfilenumber);
+
 	/* test needed so RelidByRelfilenumber doesn't misbehave */
 	if (!RelFileNumberIsValid(relfilenumber))
 		PG_RETURN_NULL();
diff --git a/src/backend/utils/adt/pg_upgrade_support.c b/src/backend/utils/adt/pg_upgrade_support.c
index 797f5f5..fc2faed 100644
--- a/src/backend/utils/adt/pg_upgrade_support.c
+++ b/src/backend/utils/adt/pg_upgrade_support.c
@@ -17,6 +17,7 @@
 #include "catalog/pg_type.h"
 #include "commands/extension.h"
 #include "miscadmin.h"
+#include "storage/relfilelocator.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 
@@ -98,10 +99,12 @@ binary_upgrade_set_next_heap_pg_class_oid(PG_FUNCTION_ARGS)
 Datum
 binary_upgrade_set_next_heap_relfilenode(PG_FUNCTION_ARGS)
 {
-	RelFileNumber relfilenumber = PG_GETARG_OID(0);
+	RelFileNumber relfilenumber = PG_GETARG_INT64(0);
 
 	CHECK_IS_BINARY_UPGRADE;
+	CHECK_RELFILENUMBER_RANGE(relfilenumber);
 	binary_upgrade_next_heap_pg_class_relfilenumber = relfilenumber;
+	SetNextRelFileNumber(relfilenumber + 1);
 
 	PG_RETURN_VOID();
 }
@@ -120,10 +123,12 @@ binary_upgrade_set_next_index_pg_class_oid(PG_FUNCTION_ARGS)
 Datum
 binary_upgrade_set_next_index_relfilenode(PG_FUNCTION_ARGS)
 {
-	RelFileNumber relfilenumber = PG_GETARG_OID(0);
+	RelFileNumber relfilenumber = PG_GETARG_INT64(0);
 
 	CHECK_IS_BINARY_UPGRADE;
+	CHECK_RELFILENUMBER_RANGE(relfilenumber);
 	binary_upgrade_next_index_pg_class_relfilenumber = relfilenumber;
+	SetNextRelFileNumber(relfilenumber + 1);
 
 	PG_RETURN_VOID();
 }
@@ -142,10 +147,12 @@ binary_upgrade_set_next_toast_pg_class_oid(PG_FUNCTION_ARGS)
 Datum
 binary_upgrade_set_next_toast_relfilenode(PG_FUNCTION_ARGS)
 {
-	RelFileNumber relfilenumber = PG_GETARG_OID(0);
+	RelFileNumber relfilenumber = PG_GETARG_INT64(0);
 
 	CHECK_IS_BINARY_UPGRADE;
+	CHECK_RELFILENUMBER_RANGE(relfilenumber);
 	binary_upgrade_next_toast_pg_class_relfilenumber = relfilenumber;
+	SetNextRelFileNumber(relfilenumber + 1);
 
 	PG_RETURN_VOID();
 }
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 00dc0f2..6f4e96d 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -3712,7 +3712,7 @@ RelationSetNewRelfilenumber(Relation relation, char persistence)
 	{
 		/* Allocate a new relfilenumber */
 		newrelfilenumber = GetNewRelFileNumber(relation->rd_rel->reltablespace,
-											   NULL, persistence);
+											   persistence);
 	}
 	else if (relation->rd_rel->relkind == RELKIND_INDEX)
 	{
diff --git a/src/backend/utils/cache/relfilenumbermap.c b/src/backend/utils/cache/relfilenumbermap.c
index c4245d5..cbb18f0 100644
--- a/src/backend/utils/cache/relfilenumbermap.c
+++ b/src/backend/utils/cache/relfilenumbermap.c
@@ -32,17 +32,11 @@
 static HTAB *RelfilenumberMapHash = NULL;
 
 /* built first time through in InitializeRelfilenumberMap */
-static ScanKeyData relfilenumber_skey[2];
+static ScanKeyData relfilenumber_skey[1];
 
 typedef struct
 {
-	Oid			reltablespace;
-	RelFileNumber relfilenumber;
-} RelfilenumberMapKey;
-
-typedef struct
-{
-	RelfilenumberMapKey key;	/* lookup key - must be first */
+	RelFileNumber relfilenumber;	/* lookup key - must be first */
 	Oid			relid;			/* pg_class.oid */
 } RelfilenumberMapEntry;
 
@@ -72,7 +66,7 @@ RelfilenumberMapInvalidateCallback(Datum arg, Oid relid)
 			entry->relid == relid)	/* individual flushed relation */
 		{
 			if (hash_search(RelfilenumberMapHash,
-							(void *) &entry->key,
+							(void *) &entry->relfilenumber,
 							HASH_REMOVE,
 							NULL) == NULL)
 				elog(ERROR, "hash table corrupted");
@@ -88,7 +82,6 @@ static void
 InitializeRelfilenumberMap(void)
 {
 	HASHCTL		ctl;
-	int			i;
 
 	/* Make sure we've initialized CacheMemoryContext. */
 	if (CacheMemoryContext == NULL)
@@ -97,25 +90,20 @@ InitializeRelfilenumberMap(void)
 	/* build skey */
 	MemSet(&relfilenumber_skey, 0, sizeof(relfilenumber_skey));
 
-	for (i = 0; i < 2; i++)
-	{
-		fmgr_info_cxt(F_OIDEQ,
-					  &relfilenumber_skey[i].sk_func,
-					  CacheMemoryContext);
-		relfilenumber_skey[i].sk_strategy = BTEqualStrategyNumber;
-		relfilenumber_skey[i].sk_subtype = InvalidOid;
-		relfilenumber_skey[i].sk_collation = InvalidOid;
-	}
-
-	relfilenumber_skey[0].sk_attno = Anum_pg_class_reltablespace;
-	relfilenumber_skey[1].sk_attno = Anum_pg_class_relfilenode;
+	fmgr_info_cxt(F_INT8EQ,
+				  &relfilenumber_skey[0].sk_func,
+				  CacheMemoryContext);
+	relfilenumber_skey[0].sk_strategy = BTEqualStrategyNumber;
+	relfilenumber_skey[0].sk_subtype = InvalidOid;
+	relfilenumber_skey[0].sk_collation = InvalidOid;
+	relfilenumber_skey[0].sk_attno = Anum_pg_class_relfilenode;
 
 	/*
 	 * Only create the RelfilenumberMapHash now, so we don't end up partially
 	 * initialized when fmgr_info_cxt() above ERRORs out with an out of memory
 	 * error.
 	 */
-	ctl.keysize = sizeof(RelfilenumberMapKey);
+	ctl.keysize = sizeof(RelFileNumber);
 	ctl.entrysize = sizeof(RelfilenumberMapEntry);
 	ctl.hcxt = CacheMemoryContext;
 
@@ -129,7 +117,7 @@ InitializeRelfilenumberMap(void)
 }
 
 /*
- * Map a relation's (tablespace, relfilenumber) to a relation's oid and cache
+ * Map a relation's relfilenumber to a relation's oid and cache
  * the result.
  *
  * Returns InvalidOid if no relation matching the criteria could be found.
@@ -137,26 +125,17 @@ InitializeRelfilenumberMap(void)
 Oid
 RelidByRelfilenumber(Oid reltablespace, RelFileNumber relfilenumber)
 {
-	RelfilenumberMapKey key;
 	RelfilenumberMapEntry *entry;
 	bool		found;
 	SysScanDesc scandesc;
 	Relation	relation;
 	HeapTuple	ntp;
-	ScanKeyData skey[2];
+	ScanKeyData skey[1];
 	Oid			relid;
 
 	if (RelfilenumberMapHash == NULL)
 		InitializeRelfilenumberMap();
 
-	/* pg_class will show 0 when the value is actually MyDatabaseTableSpace */
-	if (reltablespace == MyDatabaseTableSpace)
-		reltablespace = 0;
-
-	MemSet(&key, 0, sizeof(key));
-	key.reltablespace = reltablespace;
-	key.relfilenumber = relfilenumber;
-
 	/*
 	 * Check cache and return entry if one is found. Even if no target
 	 * relation can be found later on we store the negative match and return a
@@ -164,7 +143,8 @@ RelidByRelfilenumber(Oid reltablespace, RelFileNumber relfilenumber)
 	 * since querying invalid values isn't supposed to be a frequent thing,
 	 * but it's basically free.
 	 */
-	entry = hash_search(RelfilenumberMapHash, (void *) &key, HASH_FIND, &found);
+	entry = hash_search(RelfilenumberMapHash, (void *) &relfilenumber,
+						HASH_FIND, &found);
 
 	if (found)
 		return entry->relid;
@@ -195,14 +175,13 @@ RelidByRelfilenumber(Oid reltablespace, RelFileNumber relfilenumber)
 		memcpy(skey, relfilenumber_skey, sizeof(skey));
 
 		/* set scan arguments */
-		skey[0].sk_argument = ObjectIdGetDatum(reltablespace);
-		skey[1].sk_argument = ObjectIdGetDatum(relfilenumber);
+		skey[0].sk_argument = Int64GetDatum(relfilenumber);
 
 		scandesc = systable_beginscan(relation,
-									  ClassTblspcRelfilenodeIndexId,
+									  ClassRelfilenodeIndexId,
 									  true,
 									  NULL,
-									  2,
+									  1,
 									  skey);
 
 		found = false;
@@ -213,11 +192,10 @@ RelidByRelfilenumber(Oid reltablespace, RelFileNumber relfilenumber)
 
 			if (found)
 				elog(ERROR,
-					 "unexpected duplicate for tablespace %u, relfilenumber %u",
-					 reltablespace, relfilenumber);
+					 "unexpected duplicate for relfilenumber " INT64_FORMAT,
+					 relfilenumber);
 			found = true;
 
-			Assert(classform->reltablespace == reltablespace);
 			Assert(classform->relfilenode == relfilenumber);
 			relid = classform->oid;
 		}
@@ -235,7 +213,8 @@ RelidByRelfilenumber(Oid reltablespace, RelFileNumber relfilenumber)
 	 * caused cache invalidations to be executed which would have deleted a
 	 * new entry if we had entered it above.
 	 */
-	entry = hash_search(RelfilenumberMapHash, (void *) &key, HASH_ENTER, &found);
+	entry = hash_search(RelfilenumberMapHash, (void *) &relfilenumber,
+						HASH_ENTER, &found);
 	if (found)
 		elog(ERROR, "corrupted hashtable");
 	entry->relid = relid;
diff --git a/src/backend/utils/misc/pg_controldata.c b/src/backend/utils/misc/pg_controldata.c
index 781f8b8..3c1fef4 100644
--- a/src/backend/utils/misc/pg_controldata.c
+++ b/src/backend/utils/misc/pg_controldata.c
@@ -79,8 +79,8 @@ pg_control_system(PG_FUNCTION_ARGS)
 Datum
 pg_control_checkpoint(PG_FUNCTION_ARGS)
 {
-	Datum		values[18];
-	bool		nulls[18];
+	Datum		values[19];
+	bool		nulls[19];
 	TupleDesc	tupdesc;
 	HeapTuple	htup;
 	ControlFileData *ControlFile;
@@ -129,6 +129,8 @@ pg_control_checkpoint(PG_FUNCTION_ARGS)
 					   XIDOID, -1, 0);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 18, "checkpoint_time",
 					   TIMESTAMPTZOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 19, "next_relfilenumber",
+					   INT8OID, -1, 0);
 	tupdesc = BlessTupleDesc(tupdesc);
 
 	/* Read the control file. */
@@ -202,6 +204,9 @@ pg_control_checkpoint(PG_FUNCTION_ARGS)
 	values[17] = TimestampTzGetDatum(time_t_to_timestamptz(ControlFile->checkPointCopy.time));
 	nulls[17] = false;
 
+	values[18] = Int64GetDatum(ControlFile->checkPointCopy.nextRelFileNumber);
+	nulls[18] = false;
+
 	htup = heap_form_tuple(tupdesc, values, nulls);
 
 	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c
index 324ccf7..ddb5ec1 100644
--- a/src/bin/pg_checksums/pg_checksums.c
+++ b/src/bin/pg_checksums/pg_checksums.c
@@ -485,9 +485,7 @@ main(int argc, char *argv[])
 				mode = PG_MODE_ENABLE;
 				break;
 			case 'f':
-				if (!option_parse_int(optarg, "-f/--filenode", 0,
-									  INT_MAX,
-									  NULL))
+				if (!option_parse_relfilenumber(optarg, "-f/--filenode"))
 					exit(1);
 				only_filenode = pstrdup(optarg);
 				break;
diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c
index c390ec5..4b6ff4d 100644
--- a/src/bin/pg_controldata/pg_controldata.c
+++ b/src/bin/pg_controldata/pg_controldata.c
@@ -250,6 +250,8 @@ 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 NextRelFileNumber:%lld\n"),
+		   (long long) ControlFile->checkPointCopy.nextRelFileNumber);
 	printf(_("Latest checkpoint's NextOID:          %u\n"),
 		   ControlFile->checkPointCopy.nextOid);
 	printf(_("Latest checkpoint's NextMultiXactId:  %u\n"),
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index d25709a..632b7fb 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -3183,15 +3183,15 @@ dumpDatabase(Archive *fout)
 							  atooid(PQgetvalue(lo_res, i, ii_oid)));
 
 			oid = atooid(PQgetvalue(lo_res, i, ii_oid));
-			relfilenumber = atooid(PQgetvalue(lo_res, i, ii_relfilenode));
+			relfilenumber = atorelnumber(PQgetvalue(lo_res, i, ii_relfilenode));
 
 			if (oid == LargeObjectRelationId)
 				appendPQExpBuffer(loOutQry,
-								  "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
+								  "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('" INT64_FORMAT "'::pg_catalog.int8);\n",
 								  relfilenumber);
 			else if (oid == LargeObjectLOidPNIndexId)
 				appendPQExpBuffer(loOutQry,
-								  "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
+								  "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('" INT64_FORMAT "'::pg_catalog.int8);\n",
 								  relfilenumber);
 		}
 
@@ -4876,16 +4876,16 @@ binary_upgrade_set_pg_class_oids(Archive *fout,
 
 	relkind = *PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "relkind"));
 
-	relfilenumber = atooid(PQgetvalue(upgrade_res, 0,
-									  PQfnumber(upgrade_res, "relfilenode")));
+	relfilenumber = atorelnumber(PQgetvalue(upgrade_res, 0,
+											PQfnumber(upgrade_res, "relfilenode")));
 	toast_oid = atooid(PQgetvalue(upgrade_res, 0,
 								  PQfnumber(upgrade_res, "reltoastrelid")));
-	toast_relfilenumber = atooid(PQgetvalue(upgrade_res, 0,
-											PQfnumber(upgrade_res, "toast_relfilenode")));
+	toast_relfilenumber = atorelnumber(PQgetvalue(upgrade_res, 0,
+												  PQfnumber(upgrade_res, "toast_relfilenode")));
 	toast_index_oid = atooid(PQgetvalue(upgrade_res, 0,
 										PQfnumber(upgrade_res, "indexrelid")));
-	toast_index_relfilenumber = atooid(PQgetvalue(upgrade_res, 0,
-												  PQfnumber(upgrade_res, "toast_index_relfilenode")));
+	toast_index_relfilenumber = atorelnumber(PQgetvalue(upgrade_res, 0,
+														PQfnumber(upgrade_res, "toast_index_relfilenode")));
 
 	appendPQExpBufferStr(upgrade_buffer,
 						 "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
@@ -4903,7 +4903,7 @@ binary_upgrade_set_pg_class_oids(Archive *fout,
 		 */
 		if (RelFileNumberIsValid(relfilenumber) && relkind != RELKIND_PARTITIONED_TABLE)
 			appendPQExpBuffer(upgrade_buffer,
-							  "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
+							  "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('" INT64_FORMAT "'::pg_catalog.int8);\n",
 							  relfilenumber);
 
 		/*
@@ -4917,7 +4917,7 @@ binary_upgrade_set_pg_class_oids(Archive *fout,
 							  "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
 							  toast_oid);
 			appendPQExpBuffer(upgrade_buffer,
-							  "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
+							  "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('" INT64_FORMAT "'::pg_catalog.int8);\n",
 							  toast_relfilenumber);
 
 			/* every toast table has an index */
@@ -4925,7 +4925,7 @@ binary_upgrade_set_pg_class_oids(Archive *fout,
 							  "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
 							  toast_index_oid);
 			appendPQExpBuffer(upgrade_buffer,
-							  "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
+							  "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('" INT64_FORMAT "'::pg_catalog.int8);\n",
 							  toast_index_relfilenumber);
 		}
 
@@ -4938,7 +4938,7 @@ binary_upgrade_set_pg_class_oids(Archive *fout,
 						  "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
 						  pg_class_oid);
 		appendPQExpBuffer(upgrade_buffer,
-						  "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
+						  "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('" INT64_FORMAT "'::pg_catalog.int8);\n",
 						  relfilenumber);
 	}
 
diff --git a/src/bin/pg_rewind/filemap.c b/src/bin/pg_rewind/filemap.c
index 269ed64..8be5e66 100644
--- a/src/bin/pg_rewind/filemap.c
+++ b/src/bin/pg_rewind/filemap.c
@@ -538,7 +538,7 @@ isRelDataFile(const char *path)
 	segNo = 0;
 	matched = false;
 
-	nmatch = sscanf(path, "global/%u.%u", &rlocator.relNumber, &segNo);
+	nmatch = sscanf(path, "global/" INT64_FORMAT ".%u", &rlocator.relNumber, &segNo);
 	if (nmatch == 1 || nmatch == 2)
 	{
 		rlocator.spcOid = GLOBALTABLESPACE_OID;
@@ -547,7 +547,7 @@ isRelDataFile(const char *path)
 	}
 	else
 	{
-		nmatch = sscanf(path, "base/%u/%u.%u",
+		nmatch = sscanf(path, "base/%u/" INT64_FORMAT ".%u",
 						&rlocator.dbOid, &rlocator.relNumber, &segNo);
 		if (nmatch == 2 || nmatch == 3)
 		{
@@ -556,7 +556,7 @@ isRelDataFile(const char *path)
 		}
 		else
 		{
-			nmatch = sscanf(path, "pg_tblspc/%u/" TABLESPACE_VERSION_DIRECTORY "/%u/%u.%u",
+			nmatch = sscanf(path, "pg_tblspc/%u/" TABLESPACE_VERSION_DIRECTORY "/%u/" INT64_FORMAT ".%u",
 							&rlocator.spcOid, &rlocator.dbOid, &rlocator.relNumber,
 							&segNo);
 			if (nmatch == 3 || nmatch == 4)
diff --git a/src/bin/pg_upgrade/info.c b/src/bin/pg_upgrade/info.c
index 53ea348..1d3982f 100644
--- a/src/bin/pg_upgrade/info.c
+++ b/src/bin/pg_upgrade/info.c
@@ -527,7 +527,8 @@ get_rel_infos(ClusterInfo *cluster, DbInfo *dbinfo)
 		relname = PQgetvalue(res, relnum, i_relname);
 		curr->relname = pg_strdup(relname);
 
-		curr->relfilenumber = atooid(PQgetvalue(res, relnum, i_relfilenumber));
+		curr->relfilenumber =
+			atorelnumber(PQgetvalue(res, relnum, i_relfilenumber));
 		curr->tblsp_alloc = false;
 
 		/* Is the tablespace oid non-default? */
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 115faa2..7ab1bcc 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -15,10 +15,8 @@
  *	oids are the same between old and new clusters.  This is important
  *	because toast oids are stored as toast pointers in user tables.
  *
- *	While pg_class.oid and pg_class.relfilenode are initially the same in a
- *	cluster, they can diverge due to CLUSTER, REINDEX, or VACUUM FULL. We
- *	control assignments of pg_class.relfilenode because we want the filenames
- *	to match between the old and new cluster.
+ *	We control assignments of pg_class.relfilenode because we want the
+ *	filenames to match between the old and new cluster.
  *
  *	We control assignment of pg_tablespace.oid because we want the oid to match
  *	between the old and new cluster.
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index c3c7402..c8b10e4 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -190,14 +190,14 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		else
 			snprintf(extent_suffix, sizeof(extent_suffix), ".%d", segno);
 
-		snprintf(old_file, sizeof(old_file), "%s%s/%u/%u%s%s",
+		snprintf(old_file, sizeof(old_file), "%s%s/%u/" INT64_FORMAT "%s%s",
 				 map->old_tablespace,
 				 map->old_tablespace_suffix,
 				 map->db_oid,
 				 map->relfilenumber,
 				 type_suffix,
 				 extent_suffix);
-		snprintf(new_file, sizeof(new_file), "%s%s/%u/%u%s%s",
+		snprintf(new_file, sizeof(new_file), "%s%s/%u/" INT64_FORMAT "%s%s",
 				 map->new_tablespace,
 				 map->new_tablespace_suffix,
 				 map->db_oid,
diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c
index 6528113..9aca583 100644
--- a/src/bin/pg_waldump/pg_waldump.c
+++ b/src/bin/pg_waldump/pg_waldump.c
@@ -884,7 +884,7 @@ main(int argc, char **argv)
 				}
 				break;
 			case 'R':
-				if (sscanf(optarg, "%u/%u/%u",
+				if (sscanf(optarg, "%u/%u/" INT64_FORMAT,
 						   &config.filter_by_relation.spcOid,
 						   &config.filter_by_relation.dbOid,
 						   &config.filter_by_relation.relNumber) != 3 ||
diff --git a/src/bin/scripts/t/090_reindexdb.pl b/src/bin/scripts/t/090_reindexdb.pl
index e706d68..de5cee6 100644
--- a/src/bin/scripts/t/090_reindexdb.pl
+++ b/src/bin/scripts/t/090_reindexdb.pl
@@ -40,7 +40,7 @@ my $toast_index = $node->safe_psql('postgres',
 # REINDEX operations.  A set of relfilenodes is saved from the catalogs
 # and then compared with pg_class.
 $node->safe_psql('postgres',
-	'CREATE TABLE index_relfilenodes (parent regclass, indname text, indoid oid, relfilenode oid);'
+	'CREATE TABLE index_relfilenodes (parent regclass, indname text, indoid oid, relfilenode int8);'
 );
 # Save the relfilenode of a set of toast indexes, one from the catalog
 # pg_constraint and one from the test table.
diff --git a/src/common/relpath.c b/src/common/relpath.c
index 1b6b620..0774e3f 100644
--- a/src/common/relpath.c
+++ b/src/common/relpath.c
@@ -149,10 +149,10 @@ GetRelationPath(Oid dbOid, Oid spcOid, RelFileNumber relNumber,
 		Assert(dbOid == 0);
 		Assert(backendId == InvalidBackendId);
 		if (forkNumber != MAIN_FORKNUM)
-			path = psprintf("global/%u_%s",
+			path = psprintf("global/" INT64_FORMAT "_%s",
 							relNumber, forkNames[forkNumber]);
 		else
-			path = psprintf("global/%u", relNumber);
+			path = psprintf("global/" INT64_FORMAT, relNumber);
 	}
 	else if (spcOid == DEFAULTTABLESPACE_OID)
 	{
@@ -160,21 +160,21 @@ GetRelationPath(Oid dbOid, Oid spcOid, RelFileNumber relNumber,
 		if (backendId == InvalidBackendId)
 		{
 			if (forkNumber != MAIN_FORKNUM)
-				path = psprintf("base/%u/%u_%s",
+				path = psprintf("base/%u/" INT64_FORMAT "_%s",
 								dbOid, relNumber,
 								forkNames[forkNumber]);
 			else
-				path = psprintf("base/%u/%u",
+				path = psprintf("base/%u/" INT64_FORMAT,
 								dbOid, relNumber);
 		}
 		else
 		{
 			if (forkNumber != MAIN_FORKNUM)
-				path = psprintf("base/%u/t%d_%u_%s",
+				path = psprintf("base/%u/t%d_" INT64_FORMAT "_%s",
 								dbOid, backendId, relNumber,
 								forkNames[forkNumber]);
 			else
-				path = psprintf("base/%u/t%d_%u",
+				path = psprintf("base/%u/t%d_" INT64_FORMAT,
 								dbOid, backendId, relNumber);
 		}
 	}
@@ -184,24 +184,24 @@ GetRelationPath(Oid dbOid, Oid spcOid, RelFileNumber relNumber,
 		if (backendId == InvalidBackendId)
 		{
 			if (forkNumber != MAIN_FORKNUM)
-				path = psprintf("pg_tblspc/%u/%s/%u/%u_%s",
+				path = psprintf("pg_tblspc/%u/%s/%u/" INT64_FORMAT "_%s",
 								spcOid, TABLESPACE_VERSION_DIRECTORY,
 								dbOid, relNumber,
 								forkNames[forkNumber]);
 			else
-				path = psprintf("pg_tblspc/%u/%s/%u/%u",
+				path = psprintf("pg_tblspc/%u/%s/%u/" INT64_FORMAT,
 								spcOid, TABLESPACE_VERSION_DIRECTORY,
 								dbOid, relNumber);
 		}
 		else
 		{
 			if (forkNumber != MAIN_FORKNUM)
-				path = psprintf("pg_tblspc/%u/%s/%u/t%d_%u_%s",
+				path = psprintf("pg_tblspc/%u/%s/%u/t%d_" INT64_FORMAT "_%s",
 								spcOid, TABLESPACE_VERSION_DIRECTORY,
 								dbOid, backendId, relNumber,
 								forkNames[forkNumber]);
 			else
-				path = psprintf("pg_tblspc/%u/%s/%u/t%d_%u",
+				path = psprintf("pg_tblspc/%u/%s/%u/t%d_" INT64_FORMAT,
 								spcOid, TABLESPACE_VERSION_DIRECTORY,
 								dbOid, backendId, relNumber);
 		}
diff --git a/src/fe_utils/option_utils.c b/src/fe_utils/option_utils.c
index abea881..7d49359 100644
--- a/src/fe_utils/option_utils.c
+++ b/src/fe_utils/option_utils.c
@@ -82,3 +82,42 @@ option_parse_int(const char *optarg, const char *optname,
 		*result = val;
 	return true;
 }
+
+/*
+ * option_parse_relfilenumber
+ *
+ * Parse relfilenumber value for an option.  If the parsing is successful,
+ * returns; if parsing fails, returns false.
+ */
+bool
+option_parse_relfilenumber(const char *optarg, const char *optname)
+{
+	char	   *endptr;
+	int64		val;
+
+	errno = 0;
+	val = strtoi64(optarg, &endptr, 10);
+
+	/*
+	 * Skip any trailing whitespace; if anything but whitespace remains before
+	 * the terminating character, fail.
+	 */
+	while (*endptr != '\0' && isspace((unsigned char) *endptr))
+		endptr++;
+
+	if (*endptr != '\0')
+	{
+		pg_log_error("invalid value \"%s\" for option %s",
+					 optarg, optname);
+		return false;
+	}
+
+	if (val < 0 || val > MAX_RELFILENUMBER)
+	{
+		pg_log_error("%s must be in range " INT64_FORMAT ".." INT64_FORMAT,
+					 optname, (RelFileNumber) 0, MAX_RELFILENUMBER);
+		return false;
+	}
+
+	return true;
+}
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 775471d..36acd8b 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -196,6 +196,30 @@ FullTransactionIdAdvance(FullTransactionId *dest)
 #define FirstUnpinnedObjectId	12000
 #define FirstNormalObjectId		16384
 
+/* ----------
+ * RelFileNumber zero is InvalidRelFileNumber.
+ *
+ * For the system tables (OID < FirstNormalObjectId) the initial storage
+ * will be created with the relfilenumber same as their oid.  And, later for
+ * any storage the relfilenumber allocated by GetNewRelFileNumber() will start
+ * at 100000.  Thus, when upgrading from an older cluster, the relation storage
+ * path for the user table from the old cluster will not conflict with the
+ * relation storage path for the system table from the new cluster.  Anyway,
+ * the new cluster must not have any user tables while upgrading, so we needn't
+ * worry about them.
+ * ----------
+ */
+#define FirstNormalRelFileNumber	((RelFileNumber) 100000)
+
+#define CHECK_RELFILENUMBER_RANGE(relfilenumber)				\
+do {																\
+	if ((relfilenumber) < 0 || (relfilenumber) > MAX_RELFILENUMBER)	\
+		ereport(ERROR,												\
+				errcode(ERRCODE_INVALID_PARAMETER_VALUE),			\
+				errmsg("relfilenumber %lld is out of range",	\
+						(long long) (relfilenumber))); \
+} while (0)
+
 /*
  * VariableCache is a data structure in shared memory that is used to track
  * OID and XID assignment state.  For largely historical reasons, there is
@@ -215,6 +239,14 @@ typedef struct VariableCacheData
 	uint32		oidCount;		/* OIDs available before must do XLOG work */
 
 	/*
+	 * These fields are protected by RelFileNumberGenLock.
+	 */
+	RelFileNumber nextRelFileNumber;	/* next relfilenumber to assign */
+	RelFileNumber loggedRelFileNumber;	/* last logged relfilenumber */
+	XLogRecPtr	loggedRelFileNumberRecPtr;	/* xlog record pointer w.r.t.
+											 * loggedRelFileNumber */
+
+	/*
 	 * These fields are protected by XidGenLock.
 	 */
 	FullTransactionId nextXid;	/* next XID to assign */
@@ -293,6 +325,9 @@ extern void SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
 extern void AdvanceOldestClogXid(TransactionId oldest_datfrozenxid);
 extern bool ForceTransactionIdLimitUpdate(void);
 extern Oid	GetNewObjectId(void);
+extern RelFileNumber GetNewRelFileNumber(Oid reltablespace,
+										 char relpersistence);
+extern void SetNextRelFileNumber(RelFileNumber relnumber);
 extern void StopGeneratingPinnedObjectIds(void);
 
 #ifdef USE_ASSERT_CHECKING
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index cd674c3..78660f1 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -234,6 +234,8 @@ extern void CreateCheckPoint(int flags);
 extern bool CreateRestartPoint(int flags);
 extern WALAvailability GetWALAvailability(XLogRecPtr targetLSN);
 extern void XLogPutNextOid(Oid nextOid);
+extern void LogNextRelFileNumber(RelFileNumber nextrelnumber,
+								 XLogRecPtr *prevrecptr);
 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/catalog.h b/src/include/catalog/catalog.h
index e1c85f9..b452530 100644
--- a/src/include/catalog/catalog.h
+++ b/src/include/catalog/catalog.h
@@ -38,8 +38,5 @@ extern bool IsPinnedObject(Oid classId, Oid objectId);
 
 extern Oid	GetNewOidWithIndex(Relation relation, Oid indexId,
 							   AttrNumber oidcolumn);
-extern RelFileNumber GetNewRelFileNumber(Oid reltablespace,
-										 Relation pg_class,
-										 char relpersistence);
 
 #endif							/* CATALOG_H */
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index e1f4eef..cacfc11 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -34,6 +34,13 @@ CATALOG(pg_class,1259,RelationRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83,Relat
 	/* oid */
 	Oid			oid;
 
+	/* access method; 0 if not a table / index */
+	Oid			relam BKI_DEFAULT(heap) BKI_LOOKUP_OPT(pg_am);
+
+	/* identifier of physical storage file */
+	/* relfilenode == 0 means it is a "mapped" relation, see relmapper.c */
+	int64		relfilenode BKI_DEFAULT(0);
+
 	/* class name */
 	NameData	relname;
 
@@ -49,13 +56,6 @@ CATALOG(pg_class,1259,RelationRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83,Relat
 	/* class owner */
 	Oid			relowner BKI_DEFAULT(POSTGRES) BKI_LOOKUP(pg_authid);
 
-	/* access method; 0 if not a table / index */
-	Oid			relam BKI_DEFAULT(heap) BKI_LOOKUP_OPT(pg_am);
-
-	/* identifier of physical storage file */
-	/* relfilenode == 0 means it is a "mapped" relation, see relmapper.c */
-	Oid			relfilenode BKI_DEFAULT(0);
-
 	/* identifier of table space for relation (0 means default for database) */
 	Oid			reltablespace BKI_DEFAULT(0) BKI_LOOKUP_OPT(pg_tablespace);
 
@@ -154,7 +154,7 @@ typedef FormData_pg_class *Form_pg_class;
 
 DECLARE_UNIQUE_INDEX_PKEY(pg_class_oid_index, 2662, ClassOidIndexId, on pg_class using btree(oid oid_ops));
 DECLARE_UNIQUE_INDEX(pg_class_relname_nsp_index, 2663, ClassNameNspIndexId, on pg_class using btree(relname name_ops, relnamespace oid_ops));
-DECLARE_INDEX(pg_class_tblspc_relfilenode_index, 3455, ClassTblspcRelfilenodeIndexId, on pg_class using btree(reltablespace oid_ops, relfilenode oid_ops));
+DECLARE_INDEX(pg_class_relfilenode_index, 3455, ClassRelfilenodeIndexId, on pg_class using btree(relfilenode int8_ops));
 
 #ifdef EXPOSE_TO_CLIENT_CODE
 
diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h
index 06368e2..096222f 100644
--- a/src/include/catalog/pg_control.h
+++ b/src/include/catalog/pg_control.h
@@ -41,6 +41,7 @@ typedef struct CheckPoint
 								 * timeline (equals ThisTimeLineID otherwise) */
 	bool		fullPageWrites; /* current full_page_writes */
 	FullTransactionId nextXid;	/* next free transaction ID */
+	RelFileNumber nextRelFileNumber;	/* next relfilenumber */
 	Oid			nextOid;		/* next free OID */
 	MultiXactId nextMulti;		/* next free MultiXactId */
 	MultiXactOffset nextMultiOffset;	/* next free MultiXact offset */
@@ -78,6 +79,7 @@ typedef struct CheckPoint
 #define XLOG_FPI						0xB0
 /* 0xC0 is used in Postgres 9.5-11 */
 #define XLOG_OVERWRITE_CONTRECORD		0xD0
+#define XLOG_NEXT_RELFILENUMBER			0xE0
 
 
 /*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index a07e737..8b72f8a 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -7329,11 +7329,11 @@
   proname => 'pg_indexes_size', provolatile => 'v', prorettype => 'int8',
   proargtypes => 'regclass', prosrc => 'pg_indexes_size' },
 { oid => '2999', descr => 'filenode identifier of relation',
-  proname => 'pg_relation_filenode', provolatile => 's', prorettype => 'oid',
+  proname => 'pg_relation_filenode', provolatile => 's', prorettype => 'int8',
   proargtypes => 'regclass', prosrc => 'pg_relation_filenode' },
 { oid => '3454', descr => 'relation OID for filenode and tablespace',
   proname => 'pg_filenode_relation', provolatile => 's',
-  prorettype => 'regclass', proargtypes => 'oid oid',
+  prorettype => 'regclass', proargtypes => 'oid int8',
   prosrc => 'pg_filenode_relation' },
 { oid => '3034', descr => 'file path of relation',
   proname => 'pg_relation_filepath', provolatile => 's', prorettype => 'text',
@@ -11125,15 +11125,15 @@
   prosrc => 'binary_upgrade_set_missing_value' },
 { oid => '4545', descr => 'for use by pg_upgrade',
   proname => 'binary_upgrade_set_next_heap_relfilenode', provolatile => 'v',
-  proparallel => 'u', prorettype => 'void', proargtypes => 'oid',
+  proparallel => 'u', prorettype => 'void', proargtypes => 'int8',
   prosrc => 'binary_upgrade_set_next_heap_relfilenode' },
 { oid => '4546', descr => 'for use by pg_upgrade',
   proname => 'binary_upgrade_set_next_index_relfilenode', provolatile => 'v',
-  proparallel => 'u', prorettype => 'void', proargtypes => 'oid',
+  proparallel => 'u', prorettype => 'void', proargtypes => 'int8',
   prosrc => 'binary_upgrade_set_next_index_relfilenode' },
 { oid => '4547', descr => 'for use by pg_upgrade',
   proname => 'binary_upgrade_set_next_toast_relfilenode', provolatile => 'v',
-  proparallel => 'u', prorettype => 'void', proargtypes => 'oid',
+  proparallel => 'u', prorettype => 'void', proargtypes => 'int8',
   prosrc => 'binary_upgrade_set_next_toast_relfilenode' },
 { oid => '4548', descr => 'for use by pg_upgrade',
   proname => 'binary_upgrade_set_next_pg_tablespace_oid', provolatile => 'v',
diff --git a/src/include/common/relpath.h b/src/include/common/relpath.h
index 3ab7132..f84e22c 100644
--- a/src/include/common/relpath.h
+++ b/src/include/common/relpath.h
@@ -28,6 +28,7 @@
 
 /* Characters to allow for an OID in a relation path */
 #define OIDCHARS		10		/* max chars printed by %u */
+#define RELNUMBERCHARS	20		/* max chars printed by %llu */
 
 /*
  * Stuff for fork names.
diff --git a/src/include/fe_utils/option_utils.h b/src/include/fe_utils/option_utils.h
index 03c09fd..2508a61 100644
--- a/src/include/fe_utils/option_utils.h
+++ b/src/include/fe_utils/option_utils.h
@@ -22,5 +22,7 @@ extern void handle_help_version_opts(int argc, char *argv[],
 extern bool option_parse_int(const char *optarg, const char *optname,
 							 int min_range, int max_range,
 							 int *result);
+extern bool option_parse_relfilenumber(const char *optarg,
+									   const char *optname);
 
 #endif							/* OPTION_UTILS_H */
diff --git a/src/include/postgres_ext.h b/src/include/postgres_ext.h
index c9774fa..12b6b80 100644
--- a/src/include/postgres_ext.h
+++ b/src/include/postgres_ext.h
@@ -48,11 +48,17 @@ typedef PG_INT64_TYPE pg_int64;
 
 /*
  * RelFileNumber data type identifies the specific relation file name.
+ * RelFileNumber is unique within a cluster.
  */
-typedef Oid RelFileNumber;
-#define InvalidRelFileNumber		((RelFileNumber) InvalidOid)
+typedef pg_int64 RelFileNumber;
+
+#define InvalidRelFileNumber		((RelFileNumber) 0)
 #define RelFileNumberIsValid(relnumber) \
 				((bool) ((relnumber) != InvalidRelFileNumber))
+#define atorelnumber(x) ((RelFileNumber) strtoul((x), NULL, 10))
+
+/* Max value of the relfilnumber, relfilnumber is 56 bits wide. */
+#define MAX_RELFILENUMBER		INT64CONST(0x00FFFFFFFFFFFFFF)
 
 /*
  * Identifiers of error message fields.  Kept here to keep common
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 406db6b..1301301 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -92,29 +92,73 @@ typedef struct buftag
 {
 	Oid			spcOid;			/* tablespace oid */
 	Oid			dbOid;			/* database oid */
-	RelFileNumber relNumber;	/* relation file number */
-	ForkNumber	forkNum;		/* fork number */
+
+	/*
+	 * Represents relfilenumber and the fork number.  The 8 high bits of the
+	 * first 32 bit integer represents the fork number and remaining 24 bits
+	 * of the first integer and the 32 bits of the second integer represents
+	 * relfilenumber that makes relfilenumber 56 bits wide.  The reason behind
+	 * making it 56 bits wide instead of directly making 64 bits wide is that
+	 * if we make it 64 bits wide then the size of the BufferTag will be
+	 * increased.  And also instead of using single 64 bits integer we are
+	 * using 2 32 bits integer in order to avoid the 8 byte alignment padding
+	 * for BufferTag structure.
+	 */
+	uint32		relForkDetails[2];
 	BlockNumber blockNum;		/* blknum relative to begin of reln */
 } BufferTag;
 
+/* High relNumber bits in relForkDetails[0] */
+#define	BUFTAG_RELNUM_HIGH_BITS	24
+
+/* Low relNumber bits in relForkDetails[1] */
+#define	BUFTAG_RELNUM_LOW_BITS	32
+
+/* Mask to fetch high bits of relNumber from relForkDetails[0] */
+#define	BUFTAG_RELNUM_HIGH_MASK	((1U << BUFTAG_RELNUM_HIGH_BITS) - 1)
+
+/* Mask to fetch low bits of relNumber from relForkDetails[1] */
+#define	BUFTAG_RELNUM_LOW_MASK	0XFFFFFFFF
+
 static inline RelFileNumber
 BufTagGetRelNumber(const BufferTag *tag)
 {
-	return tag->relNumber;
+	uint64		relnum;
+
+	relnum = ((uint64) tag->relForkDetails[0]) & BUFTAG_RELNUM_HIGH_MASK;
+	relnum = (relnum << BUFTAG_RELNUM_LOW_BITS) | tag->relForkDetails[1];
+
+	Assert(relnum <= MAX_RELFILENUMBER);
+	return (RelFileNumber) relnum;
 }
 
 static inline ForkNumber
 BufTagGetForkNum(const BufferTag *tag)
 {
-	return tag->forkNum;
+	int8		ret;
+
+	StaticAssertStmt(MAX_FORKNUM <= INT8_MAX,
+					 "MAX_FORKNUM can't be greater than INT8_MAX");
+
+	ret = (int8) (tag->relForkDetails[0] >> BUFTAG_RELNUM_HIGH_BITS);
+	return (ForkNumber) ret;
 }
 
 static inline void
 BufTagSetRelForkDetails(BufferTag *tag, RelFileNumber relnumber,
 						ForkNumber forknum)
 {
-	tag->relNumber = relnumber;
-	tag->forkNum = forknum;
+	uint64		relnum;
+
+	Assert(relnumber <= MAX_RELFILENUMBER);
+	Assert(forknum <= MAX_FORKNUM);
+
+	relnum = relnumber;
+
+	tag->relForkDetails[0] = (relnum >> BUFTAG_RELNUM_LOW_BITS) &
+		BUFTAG_RELNUM_HIGH_MASK;
+	tag->relForkDetails[0] |= (forknum << BUFTAG_RELNUM_HIGH_BITS);
+	tag->relForkDetails[1] = relnum & BUFTAG_RELNUM_LOW_MASK;
 }
 
 static inline RelFileLocator
@@ -153,9 +197,9 @@ BufferTagsEqual(const BufferTag *tag1, const BufferTag *tag2)
 {
 	return (tag1->spcOid == tag2->spcOid) &&
 		(tag1->dbOid == tag2->dbOid) &&
-		(tag1->relNumber == tag2->relNumber) &&
-		(tag1->blockNum == tag2->blockNum) &&
-		(tag1->forkNum == tag2->forkNum);
+		(tag1->relForkDetails[0] == tag2->relForkDetails[0]) &&
+		(tag1->relForkDetails[1] == tag2->relForkDetails[1]) &&
+		(tag1->blockNum == tag2->blockNum);
 }
 
 static inline bool
diff --git a/src/include/storage/relfilelocator.h b/src/include/storage/relfilelocator.h
index 10f41f3..ef90464 100644
--- a/src/include/storage/relfilelocator.h
+++ b/src/include/storage/relfilelocator.h
@@ -32,10 +32,11 @@
  * Nonzero dbOid values correspond to pg_database.oid.
  *
  * relNumber identifies the specific relation.  relNumber corresponds to
- * pg_class.relfilenode (NOT pg_class.oid, because we need to be able
- * to assign new physical files to relations in some situations).
- * Notice that relNumber is only unique within a database in a particular
- * tablespace.
+ * pg_class.relfilenode.  Notice that relNumber values are assigned by
+ * GetNewRelFileNumber(), which will only ever assign the same value once
+ * during the lifetime of a cluster.  However, since CREATE DATABASE duplicates
+ * the relfilenumbers of the template database, the values are in practice only
+ * unique within a database, not globally.
  *
  * Note: spcOid must be GLOBALTABLESPACE_OID if and only if dbOid is
  * zero.  We support shared relations only in the "global" tablespace.
@@ -75,6 +76,9 @@ typedef struct RelFileLocatorBackend
 	BackendId	backend;
 } RelFileLocatorBackend;
 
+#define SizeOfRelFileLocatorBackend \
+	(offsetof(RelFileLocatorBackend, backend) + sizeof(BackendId))
+
 #define RelFileLocatorBackendIsTemp(rlocator) \
 	((rlocator).backend != InvalidBackendId)
 
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index d63f4f1..a489ccc 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2164,9 +2164,8 @@ select relname,
   c.oid = oldoid as orig_oid,
   case relfilenode
     when 0 then 'none'
-    when c.oid then 'own'
     when oldfilenode then 'orig'
-    else 'OTHER'
+    else 'new'
     end as storage,
   obj_description(c.oid, 'pg_class') as desc
   from pg_class c left join old_oids using (relname)
@@ -2175,10 +2174,10 @@ select relname,
            relname            | orig_oid | storage |     desc      
 ------------------------------+----------+---------+---------------
  at_partitioned               | t        | none    | 
- at_partitioned_0             | t        | own     | 
- at_partitioned_0_id_name_key | t        | own     | child 0 index
- at_partitioned_1             | t        | own     | 
- at_partitioned_1_id_name_key | t        | own     | child 1 index
+ at_partitioned_0             | t        | orig    | 
+ at_partitioned_0_id_name_key | t        | orig    | child 0 index
+ at_partitioned_1             | t        | orig    | 
+ at_partitioned_1_id_name_key | t        | orig    | child 1 index
  at_partitioned_id_name_key   | t        | none    | parent index
 (6 rows)
 
@@ -2198,9 +2197,8 @@ select relname,
   c.oid = oldoid as orig_oid,
   case relfilenode
     when 0 then 'none'
-    when c.oid then 'own'
     when oldfilenode then 'orig'
-    else 'OTHER'
+    else 'new'
     end as storage,
   obj_description(c.oid, 'pg_class') as desc
   from pg_class c left join old_oids using (relname)
@@ -2209,10 +2207,10 @@ select relname,
            relname            | orig_oid | storage |     desc     
 ------------------------------+----------+---------+--------------
  at_partitioned               | t        | none    | 
- at_partitioned_0             | t        | own     | 
- at_partitioned_0_id_name_key | f        | own     | parent index
- at_partitioned_1             | t        | own     | 
- at_partitioned_1_id_name_key | f        | own     | parent index
+ at_partitioned_0             | t        | orig    | 
+ at_partitioned_0_id_name_key | f        | new     | parent index
+ at_partitioned_1             | t        | orig    | 
+ at_partitioned_1_id_name_key | f        | new     | parent index
  at_partitioned_id_name_key   | f        | none    | parent index
 (6 rows)
 
@@ -2560,7 +2558,7 @@ CREATE FUNCTION check_ddl_rewrite(p_tablename regclass, p_ddl text)
 RETURNS boolean
 LANGUAGE plpgsql AS $$
 DECLARE
-    v_relfilenode oid;
+    v_relfilenode int8;
 BEGIN
     v_relfilenode := relfilenode FROM pg_class WHERE oid = p_tablename;
 
diff --git a/src/test/regress/expected/fast_default.out b/src/test/regress/expected/fast_default.out
index 91f2571..0a35f33 100644
--- a/src/test/regress/expected/fast_default.out
+++ b/src/test/regress/expected/fast_default.out
@@ -3,8 +3,8 @@
 --
 SET search_path = fast_default;
 CREATE SCHEMA fast_default;
-CREATE TABLE m(id OID);
-INSERT INTO m VALUES (NULL::OID);
+CREATE TABLE m(id BIGINT);
+INSERT INTO m VALUES (NULL::BIGINT);
 CREATE FUNCTION set(tabname name) RETURNS VOID
 AS $$
 BEGIN
diff --git a/src/test/regress/expected/oidjoins.out b/src/test/regress/expected/oidjoins.out
index 215eb89..af57470 100644
--- a/src/test/regress/expected/oidjoins.out
+++ b/src/test/regress/expected/oidjoins.out
@@ -74,11 +74,11 @@ NOTICE:  checking pg_type {typcollation} => pg_collation {oid}
 NOTICE:  checking pg_attribute {attrelid} => pg_class {oid}
 NOTICE:  checking pg_attribute {atttypid} => pg_type {oid}
 NOTICE:  checking pg_attribute {attcollation} => pg_collation {oid}
+NOTICE:  checking pg_class {relam} => pg_am {oid}
 NOTICE:  checking pg_class {relnamespace} => pg_namespace {oid}
 NOTICE:  checking pg_class {reltype} => pg_type {oid}
 NOTICE:  checking pg_class {reloftype} => pg_type {oid}
 NOTICE:  checking pg_class {relowner} => pg_authid {oid}
-NOTICE:  checking pg_class {relam} => pg_am {oid}
 NOTICE:  checking pg_class {reltablespace} => pg_tablespace {oid}
 NOTICE:  checking pg_class {reltoastrelid} => pg_class {oid}
 NOTICE:  checking pg_class {relrewrite} => pg_class {oid}
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index e7013f5..c9622f6 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -1478,9 +1478,8 @@ select relname,
   c.oid = oldoid as orig_oid,
   case relfilenode
     when 0 then 'none'
-    when c.oid then 'own'
     when oldfilenode then 'orig'
-    else 'OTHER'
+    else 'new'
     end as storage,
   obj_description(c.oid, 'pg_class') as desc
   from pg_class c left join old_oids using (relname)
@@ -1499,9 +1498,8 @@ select relname,
   c.oid = oldoid as orig_oid,
   case relfilenode
     when 0 then 'none'
-    when c.oid then 'own'
     when oldfilenode then 'orig'
-    else 'OTHER'
+    else 'new'
     end as storage,
   obj_description(c.oid, 'pg_class') as desc
   from pg_class c left join old_oids using (relname)
@@ -1641,7 +1639,7 @@ CREATE FUNCTION check_ddl_rewrite(p_tablename regclass, p_ddl text)
 RETURNS boolean
 LANGUAGE plpgsql AS $$
 DECLARE
-    v_relfilenode oid;
+    v_relfilenode int8;
 BEGIN
     v_relfilenode := relfilenode FROM pg_class WHERE oid = p_tablename;
 
diff --git a/src/test/regress/sql/fast_default.sql b/src/test/regress/sql/fast_default.sql
index 16a3b7c..819ec40 100644
--- a/src/test/regress/sql/fast_default.sql
+++ b/src/test/regress/sql/fast_default.sql
@@ -4,8 +4,8 @@
 
 SET search_path = fast_default;
 CREATE SCHEMA fast_default;
-CREATE TABLE m(id OID);
-INSERT INTO m VALUES (NULL::OID);
+CREATE TABLE m(id BIGINT);
+INSERT INTO m VALUES (NULL::BIGINT);
 
 CREATE FUNCTION set(tabname name) RETURNS VOID
 AS $$
-- 
1.8.3.1

Reply via email to