From 02df324bdcbaf4ff475025b11c37afea9c86623b Mon Sep 17 00:00:00 2001
From: Antonin Houska <ah@cybertec.at>
Date: Fri, 5 Jul 2019 16:24:02 +0200
Subject: [PATCH 13/17] Make database OID constant for postgres database.

Later in the patch series we teach pg_upgrade to preserve OIDs of
relfilenodes, tablespaces and databases. To ensure the last, we need to make
the OID of the "postgres" database constant because pg_upgrade can drop it in
the new cluster and create it again.
---
 src/backend/commands/dbcommands.c | 21 +++++++++++++++++----
 src/include/catalog/pg_database.h | 14 ++++++++++++++
 2 files changed, 31 insertions(+), 4 deletions(-)

diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 7ea783d096..4ffb6dccf4 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -513,11 +513,24 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
 	 */
 	pg_database_rel = table_open(DatabaseRelationId, RowExclusiveLock);
 
-	do
+	/* The "postgres" database has fixed OID. */
+	if (strcmp(dbname, "postgres") == 0)
 	{
-		dboid = GetNewOidWithIndex(pg_database_rel, DatabaseOidIndexId,
-								   Anum_pg_database_oid);
-	} while (check_db_file_conflict(dboid));
+		dboid = PostgresDbOid;
+
+		if (check_db_file_conflict(dboid))
+			ereport(ERROR,
+					(errcode(ERRCODE_DUPLICATE_DATABASE),
+					 errmsg("database \"%s\" already exists", dbname)));
+	}
+	else
+	{
+		do
+		{
+			dboid = GetNewOidWithIndex(pg_database_rel, DatabaseOidIndexId,
+									   Anum_pg_database_oid);
+		} while (check_db_file_conflict(dboid));
+	}
 
 	/*
 	 * Insert a new tuple into pg_database.  This establishes our ownership of
diff --git a/src/include/catalog/pg_database.h b/src/include/catalog/pg_database.h
index 06fea45f53..ebb17e3051 100644
--- a/src/include/catalog/pg_database.h
+++ b/src/include/catalog/pg_database.h
@@ -80,4 +80,18 @@ CATALOG(pg_database,1262,DatabaseRelationId) BKI_SHARED_RELATION BKI_ROWTYPE_OID
  */
 typedef FormData_pg_database *Form_pg_database;
 
+/*
+ * The "postgres" database has reserved pg_database(oid) because initially
+ * it's below FirstNormalObjectId, and thus pg_upgrade cannot guarantee that
+ * the same OID will be assigned in the new cluster (unlike TemplateDbOid,
+ * which has always OID=1). Since database OID is used to construct encryption
+ * initialization vector (tweak), different OID would break data decryption in
+ * the new cluster.
+ *
+ * The easiest way to avoid collision with any system OID is to use the
+ * maximum system OID. initdb won't reach this value and GetNewObjectId() will
+ * always skip that.
+ */
+#define PostgresDbOid	(FirstNormalObjectId - 1)
+
 #endif							/* PG_DATABASE_H */
-- 
2.13.7

