diff options
Diffstat (limited to 'src/backend/commands/dbcommands.c')
-rw-r--r-- | src/backend/commands/dbcommands.c | 95 |
1 files changed, 75 insertions, 20 deletions
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index 24eb5b531d3..b243dd173b1 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.40 1999/09/18 19:06:40 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.41 1999/09/24 00:24:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -22,6 +22,7 @@ #include "catalog/pg_shadow.h" #include "commands/dbcommands.h" #include "miscadmin.h" +#include "storage/sinval.h" #include "tcop/tcopprot.h" #include "utils/syscache.h" @@ -89,7 +90,11 @@ destroydb(char *dbname, CommandDest dest) Oid db_id; char *path, dbpath[MAXPGPATH + 1], - buf[512]; + buf[MAXPGPATH + 50]; + Relation pgdbrel; + HeapScanDesc pgdbscan; + ScanKeyData key; + HeapTuple tup; /* * If this call returns, the database exists and we're allowed to @@ -97,36 +102,79 @@ destroydb(char *dbname, CommandDest dest) */ check_permissions("destroydb", dbpath, dbname, &db_id, &user_id); + /* do as much checking as we can... */ if (!OidIsValid(db_id)) elog(FATAL, "pg_database instance has an invalid OID"); - /* stop the vacuum daemon */ - stop_vacuum(dbpath, dbname); - - /* XXX what about stopping backends connected to the target database? */ - path = ExpandDatabasePath(dbpath); if (path == NULL) elog(ERROR, "Unable to locate path '%s'" "\n\tThis may be due to a missing environment variable" " in the server", dbpath); + /* stop the vacuum daemon (dead code...) */ + stop_vacuum(dbpath, dbname); + /* - * remove the pg_database tuple FIRST, this may fail due to - * permissions problems + * Obtain exclusive lock on pg_database. We need this to ensure + * that no new backend starts up in the target database while we + * are deleting it. (Actually, a new backend might still manage to + * start up, because it will read pg_database without any locking + * to discover the database's OID. But it will detect its error + * in ReverifyMyDatabase and shut down before any serious damage + * is done. See postinit.c.) */ - snprintf(buf, 512, - "delete from pg_database where pg_database.oid = \'%u\'::oid", db_id); - pg_exec_query_dest(buf, dest, false); + pgdbrel = heap_openr(DatabaseRelationName, AccessExclusiveLock); + + /* + * Check for active backends in the target database. + */ + if (DatabaseHasActiveBackends(db_id)) + elog(ERROR, "Database '%s' has running backends, can't destroy it", + dbname); + + /* + * Find the database's tuple by OID (should be unique, we trust). + */ + ScanKeyEntryInitialize(&key, 0, ObjectIdAttributeNumber, + F_OIDEQ, ObjectIdGetDatum(db_id)); + + pgdbscan = heap_beginscan(pgdbrel, 0, SnapshotNow, 1, &key); - /* drop pages for this database that are in the shared buffer cache */ + tup = heap_getnext(pgdbscan, 0); + if (!HeapTupleIsValid(tup)) + { + heap_close(pgdbrel, AccessExclusiveLock); + elog(ERROR, "Database '%s', OID %u, not found in pg_database", + dbname, db_id); + } + + /* + * Houston, we have launch commit... + * + * Remove the database's tuple from pg_database. + */ + heap_delete(pgdbrel, &tup->t_self, NULL); + + heap_endscan(pgdbscan); + + /* + * Close pg_database, but keep exclusive lock till commit to ensure + * that any new backend scanning pg_database will see the tuple dead. + */ + heap_close(pgdbrel, NoLock); + + /* + * Drop pages for this database that are in the shared buffer cache. + * This is important to ensure that no remaining backend tries to + * write out a dirty buffer to the dead database later... + */ DropBuffers(db_id); /* - * remove the data directory. If the DELETE above failed, this will - * not be reached + * Remove the database's subdirectory and everything in it. */ - snprintf(buf, 512, "rm -r %s", path); + snprintf(buf, sizeof(buf), "rm -r '%s'", path); system(buf); } @@ -274,22 +322,28 @@ check_permissions(char *command, } /* check_permissions() */ /* - * stop_vacuum() -- stop the vacuum daemon on the database, if one is running. + * stop_vacuum -- stop the vacuum daemon on the database, if one is running. + * + * This is currently dead code, since we don't *have* vacuum daemons. + * If you want to re-enable it, think about the interlock against deleting + * a database out from under running backends, in destroydb() above. */ static void stop_vacuum(char *dbpath, char *dbname) { - char filename[256]; +#ifdef NOT_USED + char filename[MAXPGPATH + 1]; FILE *fp; int pid; if (strchr(dbpath, SEP_CHAR) != 0) { - snprintf(filename, 256, "%s%cbase%c%s%c%s.vacuum", + snprintf(filename, sizeof(filename), "%s%cbase%c%s%c%s.vacuum", DataDir, SEP_CHAR, SEP_CHAR, dbname, SEP_CHAR, dbname); } else - snprintf(filename, 256, "%s%c%s.vacuum", dbpath, SEP_CHAR, dbname); + snprintf(filename, sizeof(filename), "%s%c%s.vacuum", + dbpath, SEP_CHAR, dbname); #ifndef __CYGWIN32__ if ((fp = AllocateFile(filename, "r")) != NULL) @@ -305,4 +359,5 @@ stop_vacuum(char *dbpath, char *dbname) pid, dbname); } } +#endif } |