aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/tablespace.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2006-01-19 04:45:58 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2006-01-19 04:45:58 +0000
commit9fad6e338be9428a7c6bd14b1f08057f20161253 (patch)
tree8f6908f2cbe97b4773384ed5154c46cede2ee581 /src/backend/commands/tablespace.c
parent754da88e19e56a6aaba06a57f45fdf1b5ae792a3 (diff)
downloadpostgresql-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.c55
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)