Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 1e758d5

Browse files
committed
Add some code to CREATE DATABASE to check for pre-existing subdirectories
that conflict with the OID that we want to use for the new database. This avoids the risk of trying to remove files that maybe we shouldn't remove. Per gripe from Jon Lapham and subsequent discussion of 27-Sep.
1 parent 877f08d commit 1e758d5

File tree

2 files changed

+79
-7
lines changed

2 files changed

+79
-7
lines changed

src/backend/access/transam/xlog.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.251 2006/10/06 17:13:58 petere Exp $
10+
* $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.252 2006/10/18 22:44:11 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -5807,6 +5807,16 @@ XLogPutNextOid(Oid nextOid)
58075807
* record. Therefore, the standard buffer LSN interlock applied to those
58085808
* records will ensure no such OID reaches disk before the NEXTOID record
58095809
* does.
5810+
*
5811+
* Note, however, that the above statement only covers state "within" the
5812+
* database. When we use a generated OID as a file or directory name,
5813+
* we are in a sense violating the basic WAL rule, because that filesystem
5814+
* change may reach disk before the NEXTOID WAL record does. The impact
5815+
* of this is that if a database crash occurs immediately afterward,
5816+
* we might after restart re-generate the same OID and find that it
5817+
* conflicts with the leftover file or directory. But since for safety's
5818+
* sake we always loop until finding a nonconflicting filename, this poses
5819+
* no real problem in practice. See pgsql-hackers discussion 27-Sep-2006.
58105820
*/
58115821
}
58125822

src/backend/commands/dbcommands.c

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
*
1414
*
1515
* IDENTIFICATION
16-
* $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.185 2006/10/04 00:29:51 momjian Exp $
16+
* $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.186 2006/10/18 22:44:12 tgl Exp $
1717
*
1818
*-------------------------------------------------------------------------
1919
*/
@@ -58,6 +58,7 @@ static bool get_db_info(const char *name, LOCKMODE lockmode,
5858
Oid *dbTablespace);
5959
static bool have_createdb_privilege(void);
6060
static void remove_dbtablespaces(Oid db_id);
61+
static bool check_db_file_conflict(Oid db_id);
6162

6263

6364
/*
@@ -335,13 +336,23 @@ createdb(const CreatedbStmt *stmt)
335336
(errcode(ERRCODE_DUPLICATE_DATABASE),
336337
errmsg("database \"%s\" already exists", dbname)));
337338

339+
/*
340+
* Select an OID for the new database, checking that it doesn't have
341+
* a filename conflict with anything already existing in the tablespace
342+
* directories.
343+
*/
344+
pg_database_rel = heap_open(DatabaseRelationId, RowExclusiveLock);
345+
346+
do
347+
{
348+
dboid = GetNewOid(pg_database_rel);
349+
} while (check_db_file_conflict(dboid));
350+
338351
/*
339352
* Insert a new tuple into pg_database. This establishes our ownership of
340353
* the new database name (anyone else trying to insert the same name will
341-
* block on the unique index, and fail after we commit). It also assigns
342-
* the OID that the new database will have.
354+
* block on the unique index, and fail after we commit).
343355
*/
344-
pg_database_rel = heap_open(DatabaseRelationId, RowExclusiveLock);
345356

346357
/* Form tuple */
347358
MemSet(new_record, 0, sizeof(new_record));
@@ -371,7 +382,9 @@ createdb(const CreatedbStmt *stmt)
371382
tuple = heap_formtuple(RelationGetDescr(pg_database_rel),
372383
new_record, new_record_nulls);
373384

374-
dboid = simple_heap_insert(pg_database_rel, tuple);
385+
HeapTupleSetOid(tuple, dboid);
386+
387+
simple_heap_insert(pg_database_rel, tuple);
375388

376389
/* Update indexes */
377390
CatalogUpdateIndexes(pg_database_rel, tuple);
@@ -1216,7 +1229,7 @@ remove_dbtablespaces(Oid db_id)
12161229

12171230
dstpath = GetDatabasePath(db_id, dsttablespace);
12181231

1219-
if (stat(dstpath, &st) < 0 || !S_ISDIR(st.st_mode))
1232+
if (lstat(dstpath, &st) < 0 || !S_ISDIR(st.st_mode))
12201233
{
12211234
/* Assume we can ignore it */
12221235
pfree(dstpath);
@@ -1251,6 +1264,55 @@ remove_dbtablespaces(Oid db_id)
12511264
heap_close(rel, AccessShareLock);
12521265
}
12531266

1267+
/*
1268+
* Check for existing files that conflict with a proposed new DB OID;
1269+
* return TRUE if there are any
1270+
*
1271+
* If there were a subdirectory in any tablespace matching the proposed new
1272+
* OID, we'd get a create failure due to the duplicate name ... and then we'd
1273+
* try to remove that already-existing subdirectory during the cleanup in
1274+
* remove_dbtablespaces. Nuking existing files seems like a bad idea, so
1275+
* instead we make this extra check before settling on the OID of the new
1276+
* database. This exactly parallels what GetNewRelFileNode() does for table
1277+
* relfilenode values.
1278+
*/
1279+
static bool
1280+
check_db_file_conflict(Oid db_id)
1281+
{
1282+
bool result = false;
1283+
Relation rel;
1284+
HeapScanDesc scan;
1285+
HeapTuple tuple;
1286+
1287+
rel = heap_open(TableSpaceRelationId, AccessShareLock);
1288+
scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
1289+
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1290+
{
1291+
Oid dsttablespace = HeapTupleGetOid(tuple);
1292+
char *dstpath;
1293+
struct stat st;
1294+
1295+
/* Don't mess with the global tablespace */
1296+
if (dsttablespace == GLOBALTABLESPACE_OID)
1297+
continue;
1298+
1299+
dstpath = GetDatabasePath(db_id, dsttablespace);
1300+
1301+
if (lstat(dstpath, &st) == 0)
1302+
{
1303+
/* Found a conflicting file (or directory, whatever) */
1304+
pfree(dstpath);
1305+
result = true;
1306+
break;
1307+
}
1308+
1309+
pfree(dstpath);
1310+
}
1311+
1312+
heap_endscan(scan);
1313+
heap_close(rel, AccessShareLock);
1314+
return result;
1315+
}
12541316

12551317
/*
12561318
* get_database_oid - given a database name, look up the OID

0 commit comments

Comments
 (0)