aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands')
-rw-r--r--src/backend/commands/dbcommands.c28
-rw-r--r--src/backend/commands/user.c492
-rw-r--r--src/backend/commands/vacuum.c52
3 files changed, 66 insertions, 506 deletions
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index e5a2a0bcd31..0962e32c5e1 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.149 2005/01/27 23:23:55 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.150 2005/02/20 02:21:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -39,6 +39,7 @@
#include "utils/acl.h"
#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/flatfiles.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
@@ -506,6 +507,11 @@ createdb(const CreatedbStmt *stmt)
/* Close pg_database, but keep exclusive lock till commit */
heap_close(pg_database_rel, NoLock);
+
+ /*
+ * Set flag to update flat database file at commit.
+ */
+ database_file_update_needed();
}
@@ -642,6 +648,11 @@ dropdb(const char *dbname)
/* Close pg_database, but keep exclusive lock till commit */
heap_close(pgdbrel, NoLock);
+
+ /*
+ * Set flag to update flat database file at commit.
+ */
+ database_file_update_needed();
}
@@ -741,6 +752,11 @@ RenameDatabase(const char *oldname, const char *newname)
/* Close pg_database, but keep exclusive lock till commit */
heap_close(rel, NoLock);
+
+ /*
+ * Set flag to update flat database file at commit.
+ */
+ database_file_update_needed();
}
@@ -834,6 +850,11 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt)
/* Close pg_database, but keep exclusive lock till commit */
heap_close(rel, NoLock);
+
+ /*
+ * We don't bother updating the flat file since ALTER DATABASE SET
+ * doesn't affect it.
+ */
}
@@ -933,6 +954,11 @@ AlterDatabaseOwner(const char *dbname, AclId newOwnerSysId)
/* Close pg_database, but keep exclusive lock till commit */
heap_close(rel, NoLock);
+
+ /*
+ * We don't bother updating the flat file since ALTER DATABASE OWNER
+ * doesn't affect it.
+ */
}
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index db4f903d144..6136e99519e 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -6,17 +6,12 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.148 2005/01/27 23:23:56 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.149 2005/02/20 02:21:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <unistd.h>
-
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/indexing.h"
@@ -27,50 +22,17 @@
#include "commands/user.h"
#include "libpq/crypt.h"
#include "miscadmin.h"
-#include "storage/fd.h"
-#include "storage/pmsignal.h"
#include "utils/acl.h"
-#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/flatfiles.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
-#define PWD_FILE "pg_pwd"
-#define USER_GROUP_FILE "pg_group"
-
-
extern bool Password_encryption;
-/*
- * The need-to-update-files flags are a pair of SubTransactionIds that show
- * what level of the subtransaction tree requested the update. To register
- * an update, the subtransaction saves its own SubTransactionId in the flag,
- * unless the value was already set to a valid SubTransactionId (which implies
- * that it or a parent level has already requested the same). If it aborts
- * and the value is its SubTransactionId, it resets the flag to
- * InvalidSubTransactionId. If it commits, it changes the value to its
- * parent's SubTransactionId. This way the value is propagated up to the
- * top-level transaction, which will update the files if a valid
- * SubTransactionId is detected.
- */
-static SubTransactionId user_file_update_subid = InvalidSubTransactionId;
-static SubTransactionId group_file_update_subid = InvalidSubTransactionId;
-
-#define user_file_update_needed() \
- do { \
- if (user_file_update_subid == InvalidSubTransactionId) \
- user_file_update_subid = GetCurrentSubTransactionId(); \
- } while (0)
-
-#define group_file_update_needed() \
- do { \
- if (group_file_update_subid == InvalidSubTransactionId) \
- group_file_update_subid = GetCurrentSubTransactionId(); \
- } while (0)
-
static void CheckPgUserAclNotNull(void);
static void UpdateGroupMembership(Relation group_rel, HeapTuple group_tuple,
@@ -80,454 +42,6 @@ static List *IdArrayToList(IdList *oldarray);
/*
- * fputs_quote
- *
- * Outputs string in quotes, with double-quotes duplicated.
- * We could use quote_ident(), but that expects a TEXT argument.
- */
-static void
-fputs_quote(char *str, FILE *fp)
-{
- fputc('"', fp);
- while (*str)
- {
- fputc(*str, fp);
- if (*str == '"')
- fputc('"', fp);
- str++;
- }
- fputc('"', fp);
-}
-
-
-/*
- * group_getfilename --- get full pathname of group file
- *
- * Note that result string is palloc'd, and should be freed by the caller.
- */
-char *
-group_getfilename(void)
-{
- int bufsize;
- char *pfnam;
-
- bufsize = strlen(DataDir) + strlen("/global/") +
- strlen(USER_GROUP_FILE) + 1;
- pfnam = (char *) palloc(bufsize);
- snprintf(pfnam, bufsize, "%s/global/%s", DataDir, USER_GROUP_FILE);
-
- return pfnam;
-}
-
-
-/*
- * Get full pathname of password file.
- *
- * Note that result string is palloc'd, and should be freed by the caller.
- */
-char *
-user_getfilename(void)
-{
- int bufsize;
- char *pfnam;
-
- bufsize = strlen(DataDir) + strlen("/global/") +
- strlen(PWD_FILE) + 1;
- pfnam = (char *) palloc(bufsize);
- snprintf(pfnam, bufsize, "%s/global/%s", DataDir, PWD_FILE);
-
- return pfnam;
-}
-
-
-/*
- * write_group_file: update the flat group file
- */
-static void
-write_group_file(Relation grel)
-{
- char *filename,
- *tempname;
- int bufsize;
- FILE *fp;
- mode_t oumask;
- HeapScanDesc scan;
- HeapTuple tuple;
- TupleDesc dsc = RelationGetDescr(grel);
-
- /*
- * Create a temporary filename to be renamed later. This prevents the
- * backend from clobbering the pg_group file while the postmaster
- * might be reading from it.
- */
- filename = group_getfilename();
- bufsize = strlen(filename) + 12;
- tempname = (char *) palloc(bufsize);
- snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid);
-
- oumask = umask((mode_t) 077);
- fp = AllocateFile(tempname, "w");
- umask(oumask);
- if (fp == NULL)
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not write to temporary file \"%s\": %m", tempname)));
-
- /*
- * Read pg_group and write the file. Note we use SnapshotSelf to
- * ensure we see all effects of current transaction. (Perhaps could
- * do a CommandCounterIncrement beforehand, instead?)
- */
- scan = heap_beginscan(grel, SnapshotSelf, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
- {
- Datum datum,
- grolist_datum;
- bool isnull;
- char *groname;
- IdList *grolist_p;
- AclId *aidp;
- int i,
- j,
- num;
- char *usename;
- bool first_user = true;
-
- datum = heap_getattr(tuple, Anum_pg_group_groname, dsc, &isnull);
- /* ignore NULL groupnames --- shouldn't happen */
- if (isnull)
- continue;
- groname = NameStr(*DatumGetName(datum));
-
- /*
- * Check for invalid characters in the group name.
- */
- i = strcspn(groname, "\n");
- if (groname[i] != '\0')
- {
- ereport(LOG,
- (errmsg("invalid group name \"%s\"", groname)));
- continue;
- }
-
- grolist_datum = heap_getattr(tuple, Anum_pg_group_grolist, dsc, &isnull);
- /* Ignore NULL group lists */
- if (isnull)
- continue;
-
- /* be sure the IdList is not toasted */
- grolist_p = DatumGetIdListP(grolist_datum);
-
- /* scan grolist */
- num = IDLIST_NUM(grolist_p);
- aidp = IDLIST_DAT(grolist_p);
- for (i = 0; i < num; ++i)
- {
- tuple = SearchSysCache(SHADOWSYSID,
- PointerGetDatum(aidp[i]),
- 0, 0, 0);
- if (HeapTupleIsValid(tuple))
- {
- usename = NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename);
-
- /*
- * Check for illegal characters in the user name.
- */
- j = strcspn(usename, "\n");
- if (usename[j] != '\0')
- {
- ereport(LOG,
- (errmsg("invalid user name \"%s\"", usename)));
- continue;
- }
-
- /*
- * File format is: "dbname" "user1" "user2" "user3"
- */
- if (first_user)
- {
- fputs_quote(groname, fp);
- fputs("\t", fp);
- }
- else
- fputs(" ", fp);
-
- first_user = false;
- fputs_quote(usename, fp);
-
- ReleaseSysCache(tuple);
- }
- }
- if (!first_user)
- fputs("\n", fp);
- /* if IdList was toasted, free detoasted copy */
- if ((Pointer) grolist_p != DatumGetPointer(grolist_datum))
- pfree(grolist_p);
- }
- heap_endscan(scan);
-
- if (FreeFile(fp))
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not write to temporary file \"%s\": %m",
- tempname)));
-
- /*
- * Rename the temp file to its final name, deleting the old pg_pwd. We
- * expect that rename(2) is an atomic action.
- */
- if (rename(tempname, filename))
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not rename file \"%s\" to \"%s\": %m",
- tempname, filename)));
-
- pfree(tempname);
- pfree(filename);
-}
-
-
-/*
- * write_user_file: update the flat password file
- */
-static void
-write_user_file(Relation urel)
-{
- char *filename,
- *tempname;
- int bufsize;
- FILE *fp;
- mode_t oumask;
- HeapScanDesc scan;
- HeapTuple tuple;
- TupleDesc dsc = RelationGetDescr(urel);
-
- /*
- * Create a temporary filename to be renamed later. This prevents the
- * backend from clobbering the pg_pwd file while the postmaster might
- * be reading from it.
- */
- filename = user_getfilename();
- bufsize = strlen(filename) + 12;
- tempname = (char *) palloc(bufsize);
- snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid);
-
- oumask = umask((mode_t) 077);
- fp = AllocateFile(tempname, "w");
- umask(oumask);
- if (fp == NULL)
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not write to temporary file \"%s\": %m", tempname)));
-
- /*
- * Read pg_shadow and write the file. Note we use SnapshotSelf to
- * ensure we see all effects of current transaction. (Perhaps could
- * do a CommandCounterIncrement beforehand, instead?)
- */
- scan = heap_beginscan(urel, SnapshotSelf, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
- {
- Datum datum;
- bool isnull;
- char *usename,
- *passwd,
- *valuntil;
- int i;
-
- datum = heap_getattr(tuple, Anum_pg_shadow_usename, dsc, &isnull);
- /* ignore NULL usernames (shouldn't happen) */
- if (isnull)
- continue;
- usename = NameStr(*DatumGetName(datum));
-
- datum = heap_getattr(tuple, Anum_pg_shadow_passwd, dsc, &isnull);
-
- /*
- * It can be argued that people having a null password shouldn't
- * be allowed to connect under password authentication, because
- * they need to have a password set up first. If you think
- * assuming an empty password in that case is better, change this
- * logic to look something like the code for valuntil.
- */
- if (isnull)
- continue;
-
- passwd = DatumGetCString(DirectFunctionCall1(textout, datum));
-
- datum = heap_getattr(tuple, Anum_pg_shadow_valuntil, dsc, &isnull);
- if (isnull)
- valuntil = pstrdup("");
- else
- valuntil = DatumGetCString(DirectFunctionCall1(abstimeout, datum));
-
- /*
- * Check for illegal characters in the username and password.
- */
- i = strcspn(usename, "\n");
- if (usename[i] != '\0')
- {
- ereport(LOG,
- (errmsg("invalid user name \"%s\"", usename)));
- continue;
- }
- i = strcspn(passwd, "\n");
- if (passwd[i] != '\0')
- {
- ereport(LOG,
- (errmsg("invalid user password \"%s\"", passwd)));
- continue;
- }
-
- /*
- * The extra columns we emit here are not really necessary. To
- * remove them, the parser in backend/libpq/crypt.c would need to
- * be adjusted.
- */
- fputs_quote(usename, fp);
- fputs(" ", fp);
- fputs_quote(passwd, fp);
- fputs(" ", fp);
- fputs_quote(valuntil, fp);
- fputs("\n", fp);
-
- pfree(passwd);
- pfree(valuntil);
- }
- heap_endscan(scan);
-
- if (FreeFile(fp))
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not write to temporary file \"%s\": %m",
- tempname)));
-
- /*
- * Rename the temp file to its final name, deleting the old pg_pwd. We
- * expect that rename(2) is an atomic action.
- */
- if (rename(tempname, filename))
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not rename file \"%s\" to \"%s\": %m",
- tempname, filename)));
-
- pfree(tempname);
- pfree(filename);
-}
-
-
-/*
- * This trigger is fired whenever someone modifies pg_shadow or pg_group
- * via general-purpose INSERT/UPDATE/DELETE commands.
- *
- * XXX should probably have two separate triggers.
- */
-Datum
-update_pg_pwd_and_pg_group(PG_FUNCTION_ARGS)
-{
- user_file_update_needed();
- group_file_update_needed();
-
- return PointerGetDatum(NULL);
-}
-
-
-/*
- * This routine is called during transaction commit or abort.
- *
- * On commit, if we've written pg_shadow or pg_group during the current
- * transaction, update the flat files and signal the postmaster.
- *
- * On abort, just reset the static flags so we don't try to do it on the
- * next successful commit.
- *
- * NB: this should be the last step before actual transaction commit.
- * If any error aborts the transaction after we run this code, the postmaster
- * will still have received and cached the changed data; so minimize the
- * window for such problems.
- */
-void
-AtEOXact_UpdatePasswordFile(bool isCommit)
-{
- Relation urel = NULL;
- Relation grel = NULL;
-
- if (user_file_update_subid == InvalidSubTransactionId &&
- group_file_update_subid == InvalidSubTransactionId)
- return;
-
- if (!isCommit)
- {
- user_file_update_subid = InvalidSubTransactionId;
- group_file_update_subid = InvalidSubTransactionId;
- return;
- }
-
- /*
- * 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
- * pg_shadow or pg_group, which likely won't have gotten a strong
- * enough lock), so get the locks we need before writing anything.
- */
- if (user_file_update_subid != InvalidSubTransactionId)
- urel = heap_openr(ShadowRelationName, ExclusiveLock);
- if (group_file_update_subid != InvalidSubTransactionId)
- grel = heap_openr(GroupRelationName, ExclusiveLock);
-
- /* Okay to write the files */
- if (user_file_update_subid != InvalidSubTransactionId)
- {
- user_file_update_subid = InvalidSubTransactionId;
- write_user_file(urel);
- heap_close(urel, NoLock);
- }
-
- if (group_file_update_subid != InvalidSubTransactionId)
- {
- group_file_update_subid = InvalidSubTransactionId;
- write_group_file(grel);
- heap_close(grel, NoLock);
- }
-
- /*
- * Signal the postmaster to reload its password & group-file cache.
- */
- SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE);
-}
-
-/*
- * AtEOSubXact_UpdatePasswordFile
- *
- * Called at subtransaction end, this routine resets or updates the
- * need-to-update-files flags.
- */
-void
-AtEOSubXact_UpdatePasswordFile(bool isCommit,
- SubTransactionId mySubid,
- SubTransactionId parentSubid)
-{
- if (isCommit)
- {
- if (user_file_update_subid == mySubid)
- user_file_update_subid = parentSubid;
-
- if (group_file_update_subid == mySubid)
- group_file_update_subid = parentSubid;
- }
- else
- {
- if (user_file_update_subid == mySubid)
- user_file_update_subid = InvalidSubTransactionId;
-
- if (group_file_update_subid == mySubid)
- group_file_update_subid = InvalidSubTransactionId;
- }
-}
-
-/*
* CREATE USER
*/
void
@@ -1060,7 +574,6 @@ AlterUserSet(AlterUserSetStmt *stmt)
}
-
/*
* DROP USER
*/
@@ -1318,7 +831,6 @@ CheckPgUserAclNotNull(void)
}
-
/*
* CREATE GROUP
*/
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index d0ccf310cc4..c818d8f9667 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -13,7 +13,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.300 2005/02/15 03:50:07 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.301 2005/02/20 02:21:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -32,6 +32,7 @@
#include "catalog/namespace.h"
#include "catalog/pg_database.h"
#include "catalog/pg_index.h"
+#include "commands/dbcommands.h"
#include "commands/vacuum.h"
#include "executor/executor.h"
#include "miscadmin.h"
@@ -761,8 +762,13 @@ vac_update_dbstats(Oid dbid,
*
* Scan pg_database to determine the system-wide oldest datvacuumxid,
* and use it to truncate the transaction commit log (pg_clog).
- * Also generate a warning if the system-wide oldest datfrozenxid
- * seems to be in danger of wrapping around.
+ * Also update the XID wrap limit point maintained by varsup.c.
+ *
+ * We also generate a warning if the system-wide oldest datfrozenxid
+ * seems to be in danger of wrapping around. This is a long-in-advance
+ * warning; if we start getting uncomfortably close, GetNewTransactionId
+ * will generate more-annoying warnings, and ultimately refuse to issue
+ * any more new XIDs.
*
* The passed XIDs are simply the ones I just wrote into my pg_database
* entry. They're used to initialize the "min" calculations.
@@ -778,10 +784,17 @@ vac_truncate_clog(TransactionId vacuumXID, TransactionId frozenXID)
HeapScanDesc scan;
HeapTuple tuple;
int32 age;
+ NameData oldest_datname;
bool vacuumAlreadyWrapped = false;
bool frozenAlreadyWrapped = false;
+ /* init oldest_datname to sync with my frozenXID */
+ namestrcpy(&oldest_datname, get_database_name(MyDatabaseId));
+ /*
+ * Note: the "already wrapped" cases should now be impossible due to the
+ * defenses in GetNewTransactionId, but we keep them anyway.
+ */
relation = heap_openr(DatabaseRelationName, AccessShareLock);
scan = heap_beginscan(relation, SnapshotNow, 0, NULL);
@@ -807,7 +820,10 @@ vac_truncate_clog(TransactionId vacuumXID, TransactionId frozenXID)
if (TransactionIdPrecedes(myXID, dbform->datfrozenxid))
frozenAlreadyWrapped = true;
else if (TransactionIdPrecedes(dbform->datfrozenxid, frozenXID))
+ {
frozenXID = dbform->datfrozenxid;
+ namecpy(&oldest_datname, &dbform->datname);
+ }
}
}
@@ -830,24 +846,30 @@ vac_truncate_clog(TransactionId vacuumXID, TransactionId frozenXID)
/* Truncate CLOG to the oldest vacuumxid */
TruncateCLOG(vacuumXID);
- /* Give warning about impending wraparound problems */
+ /*
+ * Do not update varsup.c if we seem to have suffered wraparound
+ * already; the computed XID might be bogus.
+ */
if (frozenAlreadyWrapped)
{
ereport(WARNING,
(errmsg("some databases have not been vacuumed in over 1 billion transactions"),
errhint("Better vacuum them soon, or you may have a wraparound failure.")));
+ return;
}
- else
- {
- age = (int32) (myXID - frozenXID);
- if (age > (int32) ((MaxTransactionId >> 3) * 3))
- ereport(WARNING,
- (errmsg("some databases have not been vacuumed in %d transactions",
- age),
- errhint("Better vacuum them within %d transactions, "
- "or you may have a wraparound failure.",
- (int32) (MaxTransactionId >> 1) - age)));
- }
+
+ /* Update the wrap limit for GetNewTransactionId */
+ SetTransactionIdLimit(frozenXID, &oldest_datname);
+
+ /* Give warning about impending wraparound problems */
+ age = (int32) (myXID - frozenXID);
+ if (age > (int32) ((MaxTransactionId >> 3) * 3))
+ ereport(WARNING,
+ (errmsg("database \"%s\" must be vacuumed within %u transactions",
+ NameStr(oldest_datname),
+ (MaxTransactionId >> 1) - age),
+ errhint("To avoid a database shutdown, execute a full-database VACUUM in \"%s\".",
+ NameStr(oldest_datname))));
}