Skip to content

Commit aa01051

Browse files
committed
pg_upgrade: Preserve database OIDs.
Commit 9a974cb arranged to preserve relfilenodes and tablespace OIDs. For similar reasons, also arrange to preserve database OIDs. One problem is that, up until now, the OIDs assigned to the template0 and postgres databases have not been fixed. This could be a problem when upgrading, because pg_upgrade might try to migrate a database from the old cluster to the new cluster while keeping the OID and find a different database with that OID, resulting in a failure. If it finds a database with the same name and the same OID that's OK: it will be dropped and recreated. But the same OID and a different name is a problem. To prevent that, fix the OIDs for postgres and template0 to specific values less than 16384. To avoid running afoul of this rule, these values should not be changed in future releases. It's not a problem that these OIDs aren't fixed in existing releases, because the OIDs that we're assigning here weren't used for either of these databases in any previous release. Thus, there's no chance that an upgrade of a cluster from any previous release will collide with the OIDs we're assigning here. And going forward, the OIDs will always be fixed, so the only potential collision is with a system database having the same name and the same OID, which is OK. This patch lets users assign a specific OID to a database as well, provided however that it can't be less than 16384. I (rhaas) thought it might be better not to expose this capability to users, but the consensus was otherwise, so the syntax is documented. Letting users assign OIDs below 16384 would not be OK, though, because a user-created database with a low-numbered OID might collide with a system-created database in a future release. We therefore prohibit that. Shruthi KC, based on an earlier patch from Antonin Houska, reviewed and with some adjustments by me. Discussion: http://postgr.es/m/CA+TgmoYgTwYcUmB=e8+hRHOFA0kkS6Kde85+UNdon6q7bt1niQ@mail.gmail.com Discussion: http://postgr.es/m/CAASxf_Mnwm1Dh2vd5FAhVX6S1nwNSZUB1z12VddYtM++H2+p7w@mail.gmail.com
1 parent 4f0bcc7 commit aa01051

File tree

11 files changed

+120
-22
lines changed

11 files changed

+120
-22
lines changed

doc/src/sgml/ref/create_database.sgml

+17-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ CREATE DATABASE <replaceable class="parameter">name</replaceable>
3131
[ TABLESPACE [=] <replaceable class="parameter">tablespace_name</replaceable> ]
3232
[ ALLOW_CONNECTIONS [=] <replaceable class="parameter">allowconn</replaceable> ]
3333
[ CONNECTION LIMIT [=] <replaceable class="parameter">connlimit</replaceable> ]
34-
[ IS_TEMPLATE [=] <replaceable class="parameter">istemplate</replaceable> ] ]
34+
[ IS_TEMPLATE [=] <replaceable class="parameter">istemplate</replaceable> ]
35+
[ OID [=] <replaceable class="parameter">oid</replaceable> ] ]
3536
</synopsis>
3637
</refsynopsisdiv>
3738

@@ -203,6 +204,21 @@ CREATE DATABASE <replaceable class="parameter">name</replaceable>
203204
</para>
204205
</listitem>
205206
</varlistentry>
207+
208+
<varlistentry>
209+
<term><replaceable class="parameter">oid</replaceable></term>
210+
<listitem>
211+
<para>
212+
The object identifier to be used for the new database. If this
213+
parameter is not specified, the database will choose a suitable
214+
OID automatically. This parameter is primarily intended for internal
215+
use by <application>pg_upgrade</application>, and only
216+
<application>pg_upgrade</application> can specify a value less
217+
than 16384.
218+
</para>
219+
</listitem>
220+
</varlistentry>
221+
206222
</variablelist>
207223

208224
<para>

src/backend/commands/dbcommands.c

+52-5
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
115115
HeapTuple tuple;
116116
Datum new_record[Natts_pg_database];
117117
bool new_record_nulls[Natts_pg_database];
118-
Oid dboid;
118+
Oid dboid = InvalidOid;
119119
Oid datdba;
120120
ListCell *option;
121121
DefElem *dtablespacename = NULL;
@@ -215,6 +215,30 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
215215
errhint("Consider using tablespaces instead."),
216216
parser_errposition(pstate, defel->location)));
217217
}
218+
else if (strcmp(defel->defname, "oid") == 0)
219+
{
220+
dboid = defGetInt32(defel);
221+
222+
/*
223+
* We don't normally permit new databases to be created with
224+
* system-assigned OIDs. pg_upgrade tries to preserve database
225+
* OIDs, so we can't allow any database to be created with an
226+
* OID that might be in use in a freshly-initialized cluster
227+
* created by some future version. We assume all such OIDs will
228+
* be from the system-managed OID range.
229+
*
230+
* As an exception, however, we permit any OID to be assigned when
231+
* allow_system_table_mods=on (so that initdb can assign system
232+
* OIDs to template0 and postgres) or when performing a binary
233+
* upgrade (so that pg_upgrade can preserve whatever OIDs it finds
234+
* in the source cluster).
235+
*/
236+
if (dboid < FirstNormalObjectId &&
237+
!allowSystemTableMods && !IsBinaryUpgrade)
238+
ereport(ERROR,
239+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE)),
240+
errmsg("OIDs less than %u are reserved for system objects", FirstNormalObjectId));
241+
}
218242
else
219243
ereport(ERROR,
220244
(errcode(ERRCODE_SYNTAX_ERROR),
@@ -502,11 +526,34 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
502526
*/
503527
pg_database_rel = table_open(DatabaseRelationId, RowExclusiveLock);
504528

505-
do
529+
/*
530+
* If database OID is configured, check if the OID is already in use or
531+
* data directory already exists.
532+
*/
533+
if (OidIsValid(dboid))
506534
{
507-
dboid = GetNewOidWithIndex(pg_database_rel, DatabaseOidIndexId,
508-
Anum_pg_database_oid);
509-
} while (check_db_file_conflict(dboid));
535+
char *existing_dbname = get_database_name(dboid);
536+
537+
if (existing_dbname != NULL)
538+
ereport(ERROR,
539+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE)),
540+
errmsg("database OID %u is already in use by database \"%s\"",
541+
dboid, existing_dbname));
542+
543+
if (check_db_file_conflict(dboid))
544+
ereport(ERROR,
545+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE)),
546+
errmsg("data directory with the specified OID %u already exists", dboid));
547+
}
548+
else
549+
{
550+
/* Select an OID for the new database if is not explicitly configured. */
551+
do
552+
{
553+
dboid = GetNewOidWithIndex(pg_database_rel, DatabaseOidIndexId,
554+
Anum_pg_database_oid);
555+
} while (check_db_file_conflict(dboid));
556+
}
510557

511558
/*
512559
* Insert a new tuple into pg_database. This establishes our ownership of

src/bin/initdb/initdb.c

+20-2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
#include "sys/mman.h"
6060
#endif
6161

62+
#include "access/transam.h"
6263
#include "access/xlog_internal.h"
6364
#include "catalog/pg_authid_d.h"
6465
#include "catalog/pg_class_d.h" /* pgrminclude ignore */
@@ -1838,8 +1839,23 @@ static void
18381839
make_template0(FILE *cmdfd)
18391840
{
18401841
const char *const *line;
1842+
1843+
/*
1844+
* pg_upgrade tries to preserve database OIDs across upgrades. It's smart
1845+
* enough to drop and recreate a conflicting database with the same name,
1846+
* but if the same OID were used for one system-created database in the
1847+
* old cluster and a different system-created database in the new cluster,
1848+
* it would fail. To avoid that, assign a fixed OID to template0 rather
1849+
* than letting the server choose one.
1850+
*
1851+
* (Note that, while the user could have dropped and recreated these
1852+
* objects in the old cluster, the problem scenario only exists if the OID
1853+
* that is in use in the old cluster is also used in the new cluster - and
1854+
* the new cluster should be the result of a fresh initdb.)
1855+
*/
18411856
static const char *const template0_setup[] = {
1842-
"CREATE DATABASE template0 IS_TEMPLATE = true ALLOW_CONNECTIONS = false;\n\n",
1857+
"CREATE DATABASE template0 IS_TEMPLATE = true ALLOW_CONNECTIONS = false OID = "
1858+
CppAsString2(Template0ObjectId) ";\n\n",
18431859

18441860
/*
18451861
* Explicitly revoke public create-schema and create-temp-table
@@ -1869,8 +1885,10 @@ static void
18691885
make_postgres(FILE *cmdfd)
18701886
{
18711887
const char *const *line;
1888+
1889+
/* Assign a fixed OID to postgres, for the same reasons as template0 */
18721890
static const char *const postgres_setup[] = {
1873-
"CREATE DATABASE postgres;\n\n",
1891+
"CREATE DATABASE postgres OID = " CppAsString2(PostgresObjectId) ";\n\n",
18741892
"COMMENT ON DATABASE postgres IS 'default administrative connection database';\n\n",
18751893
NULL
18761894
};

src/bin/pg_dump/pg_dump.c

+10-2
Original file line numberDiff line numberDiff line change
@@ -2838,8 +2838,16 @@ dumpDatabase(Archive *fout)
28382838
* are left to the DATABASE PROPERTIES entry, so that they can be applied
28392839
* after reconnecting to the target DB.
28402840
*/
2841-
appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
2842-
qdatname);
2841+
if (dopt->binary_upgrade)
2842+
{
2843+
appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0 OID = %u",
2844+
qdatname, dbCatId.oid);
2845+
}
2846+
else
2847+
{
2848+
appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
2849+
qdatname);
2850+
}
28432851
if (strlen(encoding) > 0)
28442852
{
28452853
appendPQExpBufferStr(creaQry, " ENCODING = ");

src/bin/pg_upgrade/IMPLEMENTATION

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ by pg_dumpall --- this script effectively creates the complete
8686
user-defined metadata from the old cluster to the new cluster. It
8787
preserves the relfilenode numbers so TOAST and other references
8888
to relfilenodes in user data is preserved. (See binary-upgrade usage
89-
in pg_dump).
89+
in pg_dump). We choose to preserve tablespace and database OIDs as well.
9090

9191
Finally, pg_upgrade links or copies each user-defined table and its
9292
supporting indexes and toast tables from the old cluster to the new

src/bin/pg_upgrade/info.c

+3-6
Original file line numberDiff line numberDiff line change
@@ -190,10 +190,8 @@ create_rel_filename_map(const char *old_data, const char *new_data,
190190
map->new_tablespace_suffix = new_cluster.tablespace_suffix;
191191
}
192192

193-
map->old_db_oid = old_db->db_oid;
194-
map->new_db_oid = new_db->db_oid;
195-
196-
/* relfilenode is preserved across old and new cluster */
193+
/* DB oid and relfilenodes are preserved between old and new cluster */
194+
map->db_oid = old_db->db_oid;
197195
map->relfilenode = old_rel->relfilenode;
198196

199197
/* used only for logging and error reporting, old/new are identical */
@@ -324,8 +322,7 @@ get_db_infos(ClusterInfo *cluster)
324322
" LEFT OUTER JOIN pg_catalog.pg_tablespace t "
325323
" ON d.dattablespace = t.oid "
326324
"WHERE d.datallowconn = true "
327-
/* we don't preserve pg_database.oid so we sort by name */
328-
"ORDER BY 2");
325+
"ORDER BY 1");
329326

330327
res = executeQueryOrDie(conn, "%s", query);
331328

src/bin/pg_upgrade/pg_upgrade.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,7 @@ typedef struct
145145
const char *new_tablespace;
146146
const char *old_tablespace_suffix;
147147
const char *new_tablespace_suffix;
148-
Oid old_db_oid;
149-
Oid new_db_oid;
148+
Oid db_oid;
150149
Oid relfilenode;
151150
/* the rest are used only for logging and error reporting */
152151
char *nspname; /* namespaces */

src/bin/pg_upgrade/relfilenode.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -193,14 +193,14 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
193193
snprintf(old_file, sizeof(old_file), "%s%s/%u/%u%s%s",
194194
map->old_tablespace,
195195
map->old_tablespace_suffix,
196-
map->old_db_oid,
196+
map->db_oid,
197197
map->relfilenode,
198198
type_suffix,
199199
extent_suffix);
200200
snprintf(new_file, sizeof(new_file), "%s%s/%u/%u%s%s",
201201
map->new_tablespace,
202202
map->new_tablespace_suffix,
203-
map->new_db_oid,
203+
map->db_oid,
204204
map->relfilenode,
205205
type_suffix,
206206
extent_suffix);

src/bin/psql/tab-complete.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -2633,7 +2633,7 @@ psql_completion(const char *text, int start, int end)
26332633
COMPLETE_WITH("OWNER", "TEMPLATE", "ENCODING", "TABLESPACE",
26342634
"IS_TEMPLATE",
26352635
"ALLOW_CONNECTIONS", "CONNECTION LIMIT",
2636-
"LC_COLLATE", "LC_CTYPE", "LOCALE");
2636+
"LC_COLLATE", "LC_CTYPE", "LOCALE", "OID");
26372637

26382638
else if (Matches("CREATE", "DATABASE", MatchAny, "TEMPLATE"))
26392639
COMPLETE_WITH_QUERY(Query_for_list_of_template_databases);

src/include/access/transam.h

+4
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,10 @@ FullTransactionIdAdvance(FullTransactionId *dest)
196196
#define FirstUnpinnedObjectId 12000
197197
#define FirstNormalObjectId 16384
198198

199+
/* OIDs of Template0 and Postgres database are fixed */
200+
#define Template0ObjectId 4
201+
#define PostgresObjectId 5
202+
199203
/*
200204
* VariableCache is a data structure in shared memory that is used to track
201205
* OID and XID assignment state. For largely historical reasons, there is

src/include/catalog/unused_oids

+9
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ my @input_files = glob("pg_*.h");
3232

3333
my $oids = Catalog::FindAllOidsFromHeaders(@input_files);
3434

35+
# Push the template0 and postgres database OIDs.
36+
my $Template0ObjectId =
37+
Catalog::FindDefinedSymbol('access/transam.h', '..', 'Template0ObjectId');
38+
push @{$oids}, $Template0ObjectId;
39+
40+
my $PostgresObjectId =
41+
Catalog::FindDefinedSymbol('access/transam.h', '..', 'PostgresObjectId');
42+
push @{$oids}, $PostgresObjectId;
43+
3544
# Also push FirstGenbkiObjectId to serve as a terminator for the last gap.
3645
my $FirstGenbkiObjectId =
3746
Catalog::FindDefinedSymbol('access/transam.h', '..', 'FirstGenbkiObjectId');

0 commit comments

Comments
 (0)