From 0337ef18dd11a598b0194ba946e1b2dcdd903a18 Mon Sep 17 00:00:00 2001 From: shruthikc-gowda Date: Fri, 21 Jan 2022 18:24:57 +0530 Subject: [PATCH v12] pg_upgrade perserve database OID patch --- doc/src/sgml/ref/create_database.sgml | 18 ++++++++++- src/backend/commands/dbcommands.c | 57 ++++++++++++++++++++++++++++++++--- src/bin/initdb/initdb.c | 22 ++++++++++++-- src/bin/pg_dump/pg_dump.c | 12 ++++++-- src/bin/pg_upgrade/IMPLEMENTATION | 2 +- src/bin/pg_upgrade/info.c | 9 ++---- src/bin/pg_upgrade/pg_upgrade.h | 3 +- src/bin/pg_upgrade/relfilenode.c | 4 +-- src/bin/psql/tab-complete.c | 2 +- src/include/access/transam.h | 4 +++ 10 files changed, 111 insertions(+), 22 deletions(-) diff --git a/doc/src/sgml/ref/create_database.sgml b/doc/src/sgml/ref/create_database.sgml index 41cb406..f22e28d 100644 --- a/doc/src/sgml/ref/create_database.sgml +++ b/doc/src/sgml/ref/create_database.sgml @@ -31,7 +31,8 @@ CREATE DATABASE name [ TABLESPACE [=] tablespace_name ] [ ALLOW_CONNECTIONS [=] allowconn ] [ CONNECTION LIMIT [=] connlimit ] - [ IS_TEMPLATE [=] istemplate ] ] + [ IS_TEMPLATE [=] istemplate ] + [ OID [=] oid ] ] @@ -203,6 +204,21 @@ CREATE DATABASE name + + + oid + + + The object identifier to be used for the new database. If this + parameter is not specified, the database will choose a suitable + OID automatically. This parameter is primarily intended for internal + use by pg_upgrade, and only + pg_upgrade can specify a value less + than 16384. + + + + diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index da83455..02529e5 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -115,7 +115,7 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt) HeapTuple tuple; Datum new_record[Natts_pg_database]; bool new_record_nulls[Natts_pg_database]; - Oid dboid; + Oid dboid = InvalidOid; Oid datdba; ListCell *option; DefElem *dtablespacename = NULL; @@ -215,6 +215,30 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt) errhint("Consider using tablespaces instead."), parser_errposition(pstate, defel->location))); } + else if (strcmp(defel->defname, "oid") == 0) + { + dboid = defGetInt32(defel); + + /* + * We don't normally permit new databases to be created with + * system-assigned OIDs. pg_upgrade tries to preserve database + * OIDs, and we don't want any OIDs that are used in this database + * cluster to be OIDs that might be used for some other cluster in + * a future version of PostgreSQL. We assume all such OIDs will be + * from the system-managed OID range. + * + * As an exception, however, we permit any OID to be assigned when + * allow_system_table_mods=on (so that initdb can assign system + * OIDs to template0 and postgres) or when performing a binary + * upgrade (so that pg_upgrade can preserve whatever OIDs it finds + * in the source cluster). + */ + if (dboid < FirstNormalObjectId && + !allowSystemTableMods && !IsBinaryUpgrade) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE)), + errmsg("OIDs less than %u are reserved for system objects", FirstNormalObjectId)); + } else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), @@ -502,11 +526,34 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt) */ pg_database_rel = table_open(DatabaseRelationId, RowExclusiveLock); - do + /* + * If database OID is configured, check if the OID is already in use or + * data directory already exists. + */ + if (OidIsValid(dboid)) { - dboid = GetNewOidWithIndex(pg_database_rel, DatabaseOidIndexId, - Anum_pg_database_oid); - } while (check_db_file_conflict(dboid)); + char *existing_dbname = get_database_name(dboid); + + if (existing_dbname != NULL) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE)), + errmsg("database OID %u is already in use by database \"%s\"", + dboid, existing_dbname)); + + if (check_db_file_conflict(dboid)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE)), + errmsg("data directory with the specified OID %u already exists", dboid)); + } + else + { + /* Select an OID for the new database if is not explicitly configured. */ + 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/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index 0a2dba7..d78e8e6 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -59,6 +59,7 @@ #include "sys/mman.h" #endif +#include "access/transam.h" #include "access/xlog_internal.h" #include "catalog/pg_authid_d.h" #include "catalog/pg_class_d.h" /* pgrminclude ignore */ @@ -1838,8 +1839,23 @@ static void make_template0(FILE *cmdfd) { const char *const *line; + + /* + * pg_upgrade tries to preserve database OIDs across upgrades. It's smart + * enough to drop and recreate a conflicting database with the same name, + * but if the same OID were used for one system-created database in the + * old cluster and a different system-created database in the new cluster, + * it would fail. To avoid that, assign a fixed OID to template0 rather + * than letting the server choose one. + * + * (Note that, while the user could have dropped and recreated these + * objects in the old cluster, the problem scenario only exists if the OID + * that is in use in the old cluster is also used in the new cluster - and + * the new cluster should be the result of a fresh initdb.) + */ static const char *const template0_setup[] = { - "CREATE DATABASE template0 IS_TEMPLATE = true ALLOW_CONNECTIONS = false;\n\n", + "CREATE DATABASE template0 IS_TEMPLATE = true ALLOW_CONNECTIONS = false OID = " + CppAsString2(Template0ObjectId) ";\n\n", /* * Explicitly revoke public create-schema and create-temp-table @@ -1869,8 +1885,10 @@ static void make_postgres(FILE *cmdfd) { const char *const *line; + + /* Assign a fixed OID to postgres, for the same reasons as template0 */ static const char *const postgres_setup[] = { - "CREATE DATABASE postgres;\n\n", + "CREATE DATABASE postgres OID = " CppAsString2(PostgresObjectId) ";\n\n", "COMMENT ON DATABASE postgres IS 'default administrative connection database';\n\n", NULL }; diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 7c2f1d3..d2847c7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -2838,8 +2838,16 @@ dumpDatabase(Archive *fout) * are left to the DATABASE PROPERTIES entry, so that they can be applied * after reconnecting to the target DB. */ - appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0", - qdatname); + if (dopt->binary_upgrade) + { + appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0 OID = %u", + qdatname, dbCatId.oid); + } + else + { + appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0", + qdatname); + } if (strlen(encoding) > 0) { appendPQExpBufferStr(creaQry, " ENCODING = "); diff --git a/src/bin/pg_upgrade/IMPLEMENTATION b/src/bin/pg_upgrade/IMPLEMENTATION index 69fcd70..358e933 100644 --- a/src/bin/pg_upgrade/IMPLEMENTATION +++ b/src/bin/pg_upgrade/IMPLEMENTATION @@ -84,7 +84,7 @@ cluster using the first part of the pg_dumpall output. Next, pg_upgrade executes the remainder of the script produced earlier by pg_dumpall --- this script effectively creates the complete user-defined metadata from the old cluster to the new cluster. It -preserves the relfilenode numbers so TOAST and other references +preserves the DB, tablespace, and relfilenode OIDs so TOAST and other references to relfilenodes in user data is preserved. (See binary-upgrade usage in pg_dump). diff --git a/src/bin/pg_upgrade/info.c b/src/bin/pg_upgrade/info.c index f7fa082..69ef231 100644 --- a/src/bin/pg_upgrade/info.c +++ b/src/bin/pg_upgrade/info.c @@ -190,10 +190,8 @@ create_rel_filename_map(const char *old_data, const char *new_data, map->new_tablespace_suffix = new_cluster.tablespace_suffix; } - map->old_db_oid = old_db->db_oid; - map->new_db_oid = new_db->db_oid; - - /* relfilenode is preserved across old and new cluster */ + /* DB oid and relfilenodes are preserved between old and new cluster */ + map->db_oid = old_db->db_oid; map->relfilenode = old_rel->relfilenode; /* used only for logging and error reporting, old/new are identical */ @@ -324,8 +322,7 @@ get_db_infos(ClusterInfo *cluster) " LEFT OUTER JOIN pg_catalog.pg_tablespace t " " ON d.dattablespace = t.oid " "WHERE d.datallowconn = true " - /* we don't preserve pg_database.oid so we sort by name */ - "ORDER BY 2"); + "ORDER BY 1"); res = executeQueryOrDie(conn, "%s", query); diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h index da6770d..1db8e3f 100644 --- a/src/bin/pg_upgrade/pg_upgrade.h +++ b/src/bin/pg_upgrade/pg_upgrade.h @@ -145,8 +145,7 @@ typedef struct const char *new_tablespace; const char *old_tablespace_suffix; const char *new_tablespace_suffix; - Oid old_db_oid; - Oid new_db_oid; + Oid db_oid; Oid relfilenode; /* the rest are used only for logging and error reporting */ char *nspname; /* namespaces */ diff --git a/src/bin/pg_upgrade/relfilenode.c b/src/bin/pg_upgrade/relfilenode.c index 6e0253c..2f4deb3 100644 --- a/src/bin/pg_upgrade/relfilenode.c +++ b/src/bin/pg_upgrade/relfilenode.c @@ -193,14 +193,14 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro snprintf(old_file, sizeof(old_file), "%s%s/%u/%u%s%s", map->old_tablespace, map->old_tablespace_suffix, - map->old_db_oid, + map->db_oid, map->relfilenode, type_suffix, extent_suffix); snprintf(new_file, sizeof(new_file), "%s%s/%u/%u%s%s", map->new_tablespace, map->new_tablespace_suffix, - map->new_db_oid, + map->db_oid, map->relfilenode, type_suffix, extent_suffix); diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 6bd33a0..502b5c5 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -2633,7 +2633,7 @@ psql_completion(const char *text, int start, int end) COMPLETE_WITH("OWNER", "TEMPLATE", "ENCODING", "TABLESPACE", "IS_TEMPLATE", "ALLOW_CONNECTIONS", "CONNECTION LIMIT", - "LC_COLLATE", "LC_CTYPE", "LOCALE"); + "LC_COLLATE", "LC_CTYPE", "LOCALE", "OID"); else if (Matches("CREATE", "DATABASE", MatchAny, "TEMPLATE")) COMPLETE_WITH_QUERY(Query_for_list_of_template_databases); diff --git a/src/include/access/transam.h b/src/include/access/transam.h index 338dfca..545a84d 100644 --- a/src/include/access/transam.h +++ b/src/include/access/transam.h @@ -196,6 +196,10 @@ FullTransactionIdAdvance(FullTransactionId *dest) #define FirstUnpinnedObjectId 12000 #define FirstNormalObjectId 16384 +/* OIDs of Template0 and Postgres database are fixed */ +#define Template0ObjectId 16000 +#define PostgresObjectId 16001 + /* * VariableCache is a data structure in shared memory that is used to track * OID and XID assignment state. For largely historical reasons, there is -- 1.8.3.1