diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2006-01-19 04:45:58 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2006-01-19 04:45:58 +0000 |
commit | 9fad6e338be9428a7c6bd14b1f08057f20161253 (patch) | |
tree | 8f6908f2cbe97b4773384ed5154c46cede2ee581 /src/backend/commands/tablespace.c | |
parent | 754da88e19e56a6aaba06a57f45fdf1b5ae792a3 (diff) | |
download | postgresql-9fad6e338be9428a7c6bd14b1f08057f20161253.tar.gz postgresql-9fad6e338be9428a7c6bd14b1f08057f20161253.zip |
It turns out that TablespaceCreateDbspace fails badly if a relcache flush
occurs when it tries to heap_open pg_tablespace. When control returns to
smgrcreate, that routine will be holding a dangling pointer to a closed
SMgrRelation, resulting in mayhem. This is of course a consequence of
the violation of proper module layering inherent in having smgr.c call
a tablespace command routine, but the simplest fix seems to be to change
the locking mechanism. There's no real need for TablespaceCreateDbspace
to touch pg_tablespace at all --- it's only opening it as a way of locking
against a parallel DROP TABLESPACE command. A much better answer is to
create a special-purpose LWLock to interlock these two operations.
This drops TablespaceCreateDbspace quite a few layers down the food chain
and makes it something reasonably safe for smgr to call.
Diffstat (limited to 'src/backend/commands/tablespace.c')
-rw-r--r-- | src/backend/commands/tablespace.c | 55 |
1 files changed, 25 insertions, 30 deletions
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c index a1cb5fd8078..de051399ebc 100644 --- a/src/backend/commands/tablespace.c +++ b/src/backend/commands/tablespace.c @@ -37,7 +37,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.15 2004/12/31 21:59:41 pgsql Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.15.4.1 2006/01/19 04:45:57 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -83,12 +83,9 @@ static void set_short_version(const char *path); * If tablespaces are not supported, this is just a no-op; CREATE DATABASE * is expected to create the default subdirectory for the database. * - * isRedo indicates that we are creating an object during WAL replay; - * we can skip doing locking in that case (and should do so to avoid - * any possible problems with pg_tablespace not being valid). - * - * Also, when isRedo is true, we will cope with the possibility of the - * tablespace not being there either --- this could happen if we are + * isRedo indicates that we are creating an object during WAL replay. + * In this case we will cope with the possibility of the tablespace + * directory not being there either --- this could happen if we are * replaying an operation on a table in a subsequently-dropped tablespace. * We handle this by making a directory in the place where the tablespace * symlink would normally be. This isn't an exact replay of course, but @@ -118,16 +115,10 @@ TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo) if (errno == ENOENT) { /* - * Acquire ExclusiveLock on pg_tablespace to ensure that no - * DROP TABLESPACE or TablespaceCreateDbspace is running - * concurrently. Simple reads from pg_tablespace are OK. + * Acquire TablespaceCreateLock to ensure that no DROP TABLESPACE + * or TablespaceCreateDbspace is running concurrently. */ - Relation rel; - - if (!isRedo) - rel = heap_openr(TableSpaceRelationName, ExclusiveLock); - else - rel = NULL; + LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE); /* * Recheck to see if someone created the directory while we @@ -166,9 +157,7 @@ TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo) } } - /* OK to drop the exclusive lock */ - if (!isRedo) - heap_close(rel, ExclusiveLock); + LWLockRelease(TablespaceCreateLock); } else { @@ -403,15 +392,10 @@ DropTableSpace(DropTableSpaceStmt *stmt) PreventTransactionChain((void *) stmt, "DROP TABLESPACE"); /* - * Acquire ExclusiveLock on pg_tablespace to ensure that no one else - * is trying to do DROP TABLESPACE or TablespaceCreateDbspace - * concurrently. - */ - rel = heap_openr(TableSpaceRelationName, ExclusiveLock); - - /* * Find the target tuple */ + rel = heap_openr(TableSpaceRelationName, RowExclusiveLock); + ScanKeyInit(&entry[0], Anum_pg_tablespace_spcname, BTEqualStrategyNumber, F_NAMEEQ, @@ -448,6 +432,12 @@ DropTableSpace(DropTableSpaceStmt *stmt) heap_endscan(scandesc); /* + * Acquire TablespaceCreateLock to ensure that no TablespaceCreateDbspace + * is running concurrently. + */ + LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE); + + /* * Try to remove the physical infrastructure */ if (!remove_tablespace_directories(tablespaceoid, false)) @@ -470,6 +460,11 @@ DropTableSpace(DropTableSpaceStmt *stmt) (void) XLogInsert(RM_TBLSPC_ID, XLOG_TBLSPC_DROP, rdata); } + /* + * Allow TablespaceCreateDbspace again. + */ + LWLockRelease(TablespaceCreateLock); + /* We keep the lock on pg_tablespace until commit */ heap_close(rel, NoLock); @@ -507,10 +502,10 @@ remove_tablespace_directories(Oid tablespaceoid, bool redo) * next attempt to use the tablespace from that database will simply * recreate the subdirectory via TablespaceCreateDbspace.) * - * Since we hold exclusive lock, no one else should be creating any fresh - * subdirectories in parallel. It is possible that new files are - * being created within subdirectories, though, so the rmdir call - * could fail. Worst consequence is a less friendly error message. + * Since we hold TablespaceCreateLock, no one else should be creating any + * fresh subdirectories in parallel. It is possible that new files are + * being created within subdirectories, though, so the rmdir call could + * fail. Worst consequence is a less friendly error message. */ dirdesc = AllocateDir(location); if (dirdesc == NULL) |