mirror of
https://github.com/postgres/postgres.git
synced 2025-10-27 00:12:01 +03:00
Several changes here, not very related but touching some of the same files.
* Buffer refcount cleanup (per my "progress report" to pghackers, 9/22). * Add links to backend PROC structs to sinval's array of per-backend info, and use these links for routines that need to check the state of all backends (rather than the slow, complicated search of the ShmemIndex hashtable that was used before). Add databaseOID to PROC structs. * Use this to implement an interlock that prevents DESTROY DATABASE of a database containing running backends. (It's a little tricky to prevent a concurrently-starting backend from getting in there, since the new backend is not able to lock anything at the time it tries to look up its database in pg_database. My solution is to recheck that the DB is OK at the end of InitPostgres. It may not be a 100% solution, but it's a lot better than no interlock at all...) * In ALTER TABLE RENAME, flush buffers for the relation before doing the rename of the physical files, to ensure we don't get failures later from mdblindwrt(). * Update TRUNCATE patch so that it actually compiles against current sources :-(. You should do "make clean all" after pulling these changes.
This commit is contained in:
@@ -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);
|
||||
|
||||
/*
|
||||
* remove the pg_database tuple FIRST, this may fail due to
|
||||
* permissions problems
|
||||
*/
|
||||
snprintf(buf, 512,
|
||||
"delete from pg_database where pg_database.oid = \'%u\'::oid", db_id);
|
||||
pg_exec_query_dest(buf, dest, false);
|
||||
/* stop the vacuum daemon (dead code...) */
|
||||
stop_vacuum(dbpath, dbname);
|
||||
|
||||
/* drop pages for this database that are in the shared buffer cache */
|
||||
/*
|
||||
* 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.)
|
||||
*/
|
||||
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);
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -7,12 +7,14 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/rename.c,v 1.33 1999/09/18 19:06:40 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/rename.c,v 1.34 1999/09/24 00:24:17 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/catname.h"
|
||||
#include "utils/syscache.h"
|
||||
@@ -21,6 +23,7 @@
|
||||
#include "catalog/catalog.h"
|
||||
#include "commands/rename.h"
|
||||
#include "miscadmin.h"
|
||||
#include "storage/smgr.h"
|
||||
#include "optimizer/prep.h"
|
||||
#include "utils/acl.h"
|
||||
|
||||
@@ -166,19 +169,6 @@ renameatt(char *relname,
|
||||
|
||||
/*
|
||||
* renamerel - change the name of a relation
|
||||
*
|
||||
* Relname attribute is changed in relation catalog.
|
||||
* No record of the previous relname is kept (correct?).
|
||||
*
|
||||
* scan relation catalog
|
||||
* for name conflict
|
||||
* for original relation (if not arg)
|
||||
* modify relname in relation tuple
|
||||
* insert modified relation in relation catalog
|
||||
* delete original relation from relation catalog
|
||||
*
|
||||
* XXX Will currently lose track of a relation if it is unable to
|
||||
* properly replace the new relation tuple.
|
||||
*/
|
||||
void
|
||||
renamerel(char *oldrelname, char *newrelname)
|
||||
@@ -206,8 +196,55 @@ renamerel(char *oldrelname, char *newrelname)
|
||||
* until end of transaction.
|
||||
*/
|
||||
targetrelation = heap_openr(oldrelname, AccessExclusiveLock);
|
||||
heap_close(targetrelation, NoLock); /* close rel but keep lock! */
|
||||
|
||||
/* ----------------
|
||||
* RENAME TABLE within a transaction block is dangerous, because
|
||||
* if the transaction is later rolled back we have no way to
|
||||
* undo the rename of the relation's physical file. For now, allow it
|
||||
* but emit a warning message.
|
||||
* Someday we might want to consider postponing the physical rename
|
||||
* until transaction commit, but that's a lot of work...
|
||||
* The only case that actually works right is for relations created
|
||||
* in the current transaction, since the post-abort state would be that
|
||||
* they don't exist anyway. So, no warning in that case.
|
||||
* ----------------
|
||||
*/
|
||||
if (IsTransactionBlock() && ! targetrelation->rd_myxactonly)
|
||||
elog(NOTICE, "Caution: RENAME TABLE cannot be rolled back, so don't abort now");
|
||||
|
||||
/*
|
||||
* Flush all blocks of the relation out of the buffer pool. We need this
|
||||
* because the blocks are marked with the relation's name as well as OID.
|
||||
* If some backend tries to write a dirty buffer with mdblindwrt after
|
||||
* we've renamed the physical file, we'll be in big trouble.
|
||||
*
|
||||
* Since we hold the exclusive lock on the relation, we don't have to
|
||||
* worry about more blocks being read in while we finish the rename.
|
||||
*/
|
||||
if (FlushRelationBuffers(targetrelation, (BlockNumber) 0, true) < 0)
|
||||
elog(ERROR, "renamerel: unable to flush relation from buffer pool");
|
||||
|
||||
/*
|
||||
* Make sure smgr and lower levels close the relation's files.
|
||||
* (Next access to rel will reopen them.)
|
||||
*
|
||||
* Note: we rely on shared cache invalidation message to make other
|
||||
* backends close and re-open the files.
|
||||
*/
|
||||
smgrclose(DEFAULT_SMGR, targetrelation);
|
||||
|
||||
/*
|
||||
* Close rel, but keep exclusive lock!
|
||||
*
|
||||
* Note: we don't do anything about updating the relcache entry;
|
||||
* we assume it will be flushed by shared cache invalidate.
|
||||
* XXX is this good enough? What if relation is myxactonly?
|
||||
*/
|
||||
heap_close(targetrelation, NoLock);
|
||||
|
||||
/*
|
||||
* Find relation's pg_class tuple, and make sure newrelname isn't in use.
|
||||
*/
|
||||
relrelation = heap_openr(RelationRelationName, RowExclusiveLock);
|
||||
|
||||
oldreltup = SearchSysCacheTupleCopy(RELNAME,
|
||||
@@ -220,14 +257,17 @@ renamerel(char *oldrelname, char *newrelname)
|
||||
elog(ERROR, "renamerel: relation \"%s\" exists", newrelname);
|
||||
|
||||
/*
|
||||
* XXX need to close relation and flush dirty buffers here!
|
||||
* Perform physical rename of files. If this fails, we haven't yet
|
||||
* done anything irreversible.
|
||||
*
|
||||
* XXX smgr.c ought to provide an interface for this; doing it
|
||||
* directly is bletcherous.
|
||||
*/
|
||||
|
||||
/* rename the path first, so if this fails the rename's not done */
|
||||
strcpy(oldpath, relpath(oldrelname));
|
||||
strcpy(newpath, relpath(newrelname));
|
||||
if (rename(oldpath, newpath) < 0)
|
||||
elog(ERROR, "renamerel: unable to rename file: %s", oldpath);
|
||||
elog(ERROR, "renamerel: unable to rename %s to %s: %m",
|
||||
oldpath, newpath);
|
||||
|
||||
/* rename additional segments of relation, too */
|
||||
for (i = 1;; i++)
|
||||
@@ -235,13 +275,22 @@ renamerel(char *oldrelname, char *newrelname)
|
||||
sprintf(toldpath, "%s.%d", oldpath, i);
|
||||
sprintf(tnewpath, "%s.%d", newpath, i);
|
||||
if (rename(toldpath, tnewpath) < 0)
|
||||
break;
|
||||
{
|
||||
/* expected case is that there's not another segment file */
|
||||
if (errno == ENOENT)
|
||||
break;
|
||||
/* otherwise we're up the creek... */
|
||||
elog(ERROR, "renamerel: unable to rename %s to %s: %m",
|
||||
toldpath, tnewpath);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Update pg_class tuple with new relname.
|
||||
*/
|
||||
StrNCpy((((Form_pg_class) GETSTRUCT(oldreltup))->relname.data),
|
||||
newrelname, NAMEDATALEN);
|
||||
|
||||
/* insert fixed rel tuple */
|
||||
heap_replace(relrelation, &oldreltup->t_self, oldreltup, NULL);
|
||||
|
||||
/* keep the system catalog indices current */
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.120 1999/09/18 19:06:41 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.121 1999/09/24 00:24:17 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "commands/vacuum.h"
|
||||
#include "miscadmin.h"
|
||||
#include "parser/parse_oper.h"
|
||||
#include "storage/sinval.h"
|
||||
#include "storage/smgr.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/inval.h"
|
||||
@@ -46,8 +47,6 @@
|
||||
|
||||
/* #include <port-protos.h> *//* Why? */
|
||||
|
||||
extern int BlowawayRelationBuffers(Relation rel, BlockNumber block);
|
||||
|
||||
bool VacuumRunning = false;
|
||||
|
||||
static Portal vc_portal;
|
||||
@@ -1838,9 +1837,9 @@ Elapsed %u/%u sec.",
|
||||
/* truncate relation */
|
||||
if (blkno < nblocks)
|
||||
{
|
||||
i = BlowawayRelationBuffers(onerel, blkno);
|
||||
i = FlushRelationBuffers(onerel, blkno, false);
|
||||
if (i < 0)
|
||||
elog(FATAL, "VACUUM (vc_rpfheap): BlowawayRelationBuffers returned %d", i);
|
||||
elog(FATAL, "VACUUM (vc_rpfheap): FlushRelationBuffers returned %d", i);
|
||||
blkno = smgrtruncate(DEFAULT_SMGR, onerel, blkno);
|
||||
Assert(blkno >= 0);
|
||||
vacrelstats->num_pages = blkno; /* set new number of blocks */
|
||||
@@ -1902,12 +1901,14 @@ vc_vacheap(VRelStats *vacrelstats, Relation onerel, VPageList vacuum_pages)
|
||||
/*
|
||||
* we have to flush "empty" end-pages (if changed, but who knows
|
||||
* it) before truncation
|
||||
*
|
||||
* XXX wouldn't passing 'true' to FlushRelationBuffers do the job?
|
||||
*/
|
||||
FlushBufferPool(!TransactionFlushEnabled());
|
||||
|
||||
i = BlowawayRelationBuffers(onerel, nblocks);
|
||||
i = FlushRelationBuffers(onerel, nblocks, false);
|
||||
if (i < 0)
|
||||
elog(FATAL, "VACUUM (vc_vacheap): BlowawayRelationBuffers returned %d", i);
|
||||
elog(FATAL, "VACUUM (vc_vacheap): FlushRelationBuffers returned %d", i);
|
||||
|
||||
nblocks = smgrtruncate(DEFAULT_SMGR, onerel, nblocks);
|
||||
Assert(nblocks >= 0);
|
||||
|
||||
Reference in New Issue
Block a user