* a way that this is OK.
*
*
- * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.15 2005/10/15 02:49:33 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.21 2006/07/14 14:52:25 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include <unistd.h>
#include "access/heapam.h"
+#include "access/transam.h"
#include "access/twophase_rmgr.h"
+#include "access/xact.h"
#include "catalog/pg_auth_members.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_database.h"
#include "miscadmin.h"
#include "storage/fd.h"
#include "storage/pmsignal.h"
-#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/flatfiles.h"
#include "utils/resowner.h"
-#include "utils/syscache.h"
/* Actual names of the flat files (within $PGDATA) */
/*
* write_database_file: update the flat database file
*
- * A side effect is to determine the oldest database's datfrozenxid
+ * A side effect is to determine the oldest database's datminxid
* so we can set or update the XID wrap limit.
*/
static void
HeapScanDesc scan;
HeapTuple tuple;
NameData oldest_datname;
- TransactionId oldest_datfrozenxid = InvalidTransactionId;
+ TransactionId oldest_datminxid = InvalidTransactionId;
/*
* Create a temporary filename to be renamed later. This prevents the
char *datname;
Oid datoid;
Oid dattablespace;
- TransactionId datfrozenxid,
+ TransactionId datminxid,
datvacuumxid;
datname = NameStr(dbform->datname);
datoid = HeapTupleGetOid(tuple);
dattablespace = dbform->dattablespace;
- datfrozenxid = dbform->datfrozenxid;
+ datminxid = dbform->datminxid;
datvacuumxid = dbform->datvacuumxid;
/*
- * Identify the oldest datfrozenxid, ignoring databases that are not
+ * Identify the oldest datminxid, ignoring databases that are not
* connectable (we assume they are safely frozen). This must match
* the logic in vac_truncate_clog() in vacuum.c.
*/
if (dbform->datallowconn &&
- TransactionIdIsNormal(datfrozenxid))
+ TransactionIdIsNormal(datminxid))
{
- if (oldest_datfrozenxid == InvalidTransactionId ||
- TransactionIdPrecedes(datfrozenxid, oldest_datfrozenxid))
+ if (oldest_datminxid == InvalidTransactionId ||
+ TransactionIdPrecedes(datminxid, oldest_datminxid))
{
- oldest_datfrozenxid = datfrozenxid;
+ oldest_datminxid = datminxid;
namestrcpy(&oldest_datname, datname);
}
}
}
/*
- * The file format is: "dbname" oid tablespace frozenxid vacuumxid
+ * The file format is: "dbname" oid tablespace minxid vacuumxid
*
* The xids are not needed for backend startup, but are of use to
* autovacuum, and might also be helpful for forensic purposes.
*/
fputs_quote(datname, fp);
fprintf(fp, " %u %u %u %u\n",
- datoid, dattablespace, datfrozenxid, datvacuumxid);
+ datoid, dattablespace, datminxid, datvacuumxid);
}
heap_endscan(scan);
tempname, filename)));
/*
- * Set the transaction ID wrap limit using the oldest datfrozenxid
+ * Set the transaction ID wrap limit using the oldest datminxid
*/
- if (oldest_datfrozenxid != InvalidTransactionId)
- SetTransactionIdLimit(oldest_datfrozenxid, &oldest_datname);
+ if (oldest_datminxid != InvalidTransactionId)
+ SetTransactionIdLimit(oldest_datminxid, &oldest_datname);
}
* Convert list of role Oids to list of role names. We must do
* this before re-sorting auth_info.
*
- * We skip the first list element (curr_role itself) since there is
- * no point in writing that a role is a member of itself.
+ * We skip the first list element (curr_role itself) since there
+ * is no point in writing that a role is a member of itself.
*/
for_each_cell(mem, lnext(list_head(roles_list)))
{
CommandCounterIncrement();
/*
- * We use ExclusiveLock to ensure that only one backend writes the flat
- * file(s) at a time. That's sufficient because it's okay to allow plain
- * reads of the tables in parallel. There is some chance of a deadlock
- * here (if we were triggered by a user update of one of the tables, which
- * likely won't have gotten a strong enough lock), so get the locks we
- * need before writing anything.
+ * Open and lock the needed catalog(s).
*
- * For writing the auth file, it's sufficient to ExclusiveLock pg_authid; we
- * take just regular AccessShareLock on pg_auth_members.
+ * Even though we only need AccessShareLock, this could theoretically fail
+ * due to deadlock. In practice, however, our transaction already holds
+ * RowExclusiveLock or better (it couldn't have updated the catalog
+ * without such a lock). This implies that dbcommands.c and other places
+ * that force flat-file updates must not follow the common practice of
+ * dropping catalog locks before commit.
*/
if (database_file_update_subid != InvalidSubTransactionId)
- drel = heap_open(DatabaseRelationId, ExclusiveLock);
+ drel = heap_open(DatabaseRelationId, AccessShareLock);
if (auth_file_update_subid != InvalidSubTransactionId)
{
- arel = heap_open(AuthIdRelationId, ExclusiveLock);
+ arel = heap_open(AuthIdRelationId, AccessShareLock);
mrel = heap_open(AuthMemRelationId, AccessShareLock);
}
+ /*
+ * Obtain special locks to ensure that two transactions don't try to write
+ * the same flat file concurrently. Quite aside from any direct risks of
+ * corrupted output, the winning writer probably wouldn't have seen the
+ * other writer's updates. By taking a lock and holding it till commit,
+ * we ensure that whichever updater goes second will see the other
+ * updater's changes as committed, and thus the final state of the file
+ * will include all updates.
+ *
+ * We use a lock on "database 0" to protect writing the pg_database flat
+ * file, and a lock on "role 0" to protect the auth file. This is a bit
+ * ugly but it's not worth inventing any more-general convention. (Any
+ * two locktags that are never used for anything else would do.)
+ *
+ * This is safe against deadlock as long as these are the very last locks
+ * acquired during the transaction.
+ */
+ if (database_file_update_subid != InvalidSubTransactionId)
+ LockSharedObject(DatabaseRelationId, InvalidOid, 0,
+ AccessExclusiveLock);
+
+ if (auth_file_update_subid != InvalidSubTransactionId)
+ LockSharedObject(AuthIdRelationId, InvalidOid, 0,
+ AccessExclusiveLock);
+
/* Okay to write the files */
if (database_file_update_subid != InvalidSubTransactionId)
{