From 0fc4ecf93543a0e293845bdc47712285a9165aa0 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sat, 26 Feb 2005 18:43:34 +0000 Subject: [PATCH] Finish up the flat-files project: get rid of GetRawDatabaseInfo() hack in favor of looking at the flat file copy of pg_database during backend startup. This should finally eliminate the various corner cases in which backend startup fails unexpectedly because it isn't able to distinguish live and dead tuples in pg_database. Simplify locking on pg_database to be similar to the rules used with pg_shadow and pg_group, and eliminate FlushRelationBuffers operations that were used only to reduce the odds of failure of GetRawDatabaseInfo. initdb forced due to addition of a trigger to pg_database. --- src/backend/commands/dbcommands.c | 89 +++++--------- src/backend/commands/vacuum.c | 7 +- src/backend/libpq/hba.c | 50 +++++++- src/backend/utils/init/flatfiles.c | 20 +-- src/backend/utils/init/postinit.c | 88 ++++++++++---- src/backend/utils/misc/Makefile | 14 ++- src/backend/utils/misc/database.c | 189 ----------------------------- src/bin/initdb/initdb.c | 17 +-- src/include/catalog/catversion.h | 4 +- src/include/catalog/pg_proc.h | 6 +- src/include/libpq/hba.h | 11 +- src/include/miscadmin.h | 5 +- 12 files changed, 184 insertions(+), 316 deletions(-) delete mode 100644 src/backend/utils/misc/database.c diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index 0962e32c5e..474701fa57 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -3,13 +3,19 @@ * dbcommands.c * Database management commands (create/drop database). * + * Note: database creation/destruction commands take ExclusiveLock on + * pg_database to ensure that no two proceed in parallel. We must use + * at least this level of locking to ensure that no two backends try to + * write the flat-file copy of pg_database at once. We avoid using + * AccessExclusiveLock since there's no need to lock out ordinary readers + * of pg_database. * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.150 2005/02/20 02:21:34 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.151 2005/02/26 18:43:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -446,13 +452,13 @@ createdb(const CreatedbStmt *stmt) /* * Now OK to grab exclusive lock on pg_database. */ - pg_database_rel = heap_openr(DatabaseRelationName, AccessExclusiveLock); + pg_database_rel = heap_openr(DatabaseRelationName, ExclusiveLock); /* Check to see if someone else created same DB name meanwhile. */ if (get_db_info(dbname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) { /* Don't hold lock while doing recursive remove */ - heap_close(pg_database_rel, AccessExclusiveLock); + heap_close(pg_database_rel, ExclusiveLock); remove_dbtablespaces(dboid); ereport(ERROR, (errcode(ERRCODE_DUPLICATE_DATABASE), @@ -498,13 +504,6 @@ createdb(const CreatedbStmt *stmt) /* Update indexes */ CatalogUpdateIndexes(pg_database_rel, tuple); - /* - * Force dirty buffers out to disk, so that newly-connecting backends - * will see the new database in pg_database right away. (They'll see - * an uncommitted tuple, but they don't care; see GetRawDatabaseInfo.) - */ - FlushRelationBuffers(pg_database_rel, MaxBlockNumber); - /* Close pg_database, but keep exclusive lock till commit */ heap_close(pg_database_rel, NoLock); @@ -542,12 +541,15 @@ dropdb(const char *dbname) * 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.) + * up, because it isn't able to lock pg_database while starting. But + * it will detect its error in ReverifyMyDatabase and shut down before + * any serious damage is done. See postinit.c.) + * + * An ExclusiveLock, rather than AccessExclusiveLock, is sufficient + * since ReverifyMyDatabase takes RowShareLock. This allows ordinary + * readers of pg_database to proceed in parallel. */ - pgdbrel = heap_openr(DatabaseRelationName, AccessExclusiveLock); + pgdbrel = heap_openr(DatabaseRelationName, ExclusiveLock); if (!get_db_info(dbname, &db_id, &db_owner, NULL, &db_istemplate, NULL, NULL, NULL, NULL)) @@ -638,14 +640,6 @@ dropdb(const char *dbname) */ remove_dbtablespaces(db_id); - /* - * Force dirty buffers out to disk, so that newly-connecting backends - * will see the database tuple marked dead in pg_database right away. - * (They'll see an uncommitted deletion, but they don't care; see - * GetRawDatabaseInfo.) - */ - FlushRelationBuffers(pgdbrel, MaxBlockNumber); - /* Close pg_database, but keep exclusive lock till commit */ heap_close(pgdbrel, NoLock); @@ -671,10 +665,10 @@ RenameDatabase(const char *oldname, const char *newname) key2; /* - * Obtain AccessExclusiveLock so that no new session gets started + * Obtain ExclusiveLock so that no new session gets started * while the rename is in progress. */ - rel = heap_openr(DatabaseRelationName, AccessExclusiveLock); + rel = heap_openr(DatabaseRelationName, ExclusiveLock); ScanKeyInit(&key, Anum_pg_database_datname, @@ -742,14 +736,6 @@ RenameDatabase(const char *oldname, const char *newname) systable_endscan(scan); - /* - * Force dirty buffers out to disk, so that newly-connecting backends - * will see the renamed database in pg_database right away. (They'll - * see an uncommitted tuple, but they don't care; see - * GetRawDatabaseInfo.) - */ - FlushRelationBuffers(rel, MaxBlockNumber); - /* Close pg_database, but keep exclusive lock till commit */ heap_close(rel, NoLock); @@ -779,9 +765,10 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt) valuestr = flatten_set_variable_args(stmt->variable, stmt->value); /* - * We need AccessExclusiveLock so we can safely do FlushRelationBuffers. + * We don't need ExclusiveLock since we aren't updating the + * flat file. */ - rel = heap_openr(DatabaseRelationName, AccessExclusiveLock); + rel = heap_openr(DatabaseRelationName, RowExclusiveLock); ScanKeyInit(&scankey, Anum_pg_database_datname, BTEqualStrategyNumber, F_NAMEEQ, @@ -840,15 +827,7 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt) systable_endscan(scan); - /* - * Force dirty buffers out to disk, so that newly-connecting backends - * will see the altered row in pg_database right away. (They'll - * see an uncommitted tuple, but they don't care; see - * GetRawDatabaseInfo.) - */ - FlushRelationBuffers(rel, MaxBlockNumber); - - /* Close pg_database, but keep exclusive lock till commit */ + /* Close pg_database, but keep lock till commit */ heap_close(rel, NoLock); /* @@ -871,9 +850,10 @@ AlterDatabaseOwner(const char *dbname, AclId newOwnerSysId) Form_pg_database datForm; /* - * We need AccessExclusiveLock so we can safely do FlushRelationBuffers. + * We don't need ExclusiveLock since we aren't updating the + * flat file. */ - rel = heap_openr(DatabaseRelationName, AccessExclusiveLock); + rel = heap_openr(DatabaseRelationName, RowExclusiveLock); ScanKeyInit(&scankey, Anum_pg_database_datname, BTEqualStrategyNumber, F_NAMEEQ, @@ -937,22 +917,11 @@ AlterDatabaseOwner(const char *dbname, AclId newOwnerSysId) CatalogUpdateIndexes(rel, newtuple); heap_freetuple(newtuple); - - /* must release buffer pins before FlushRelationBuffers */ - systable_endscan(scan); - - /* - * Force dirty buffers out to disk, so that newly-connecting backends - * will see the altered row in pg_database right away. (They'll - * see an uncommitted tuple, but they don't care; see - * GetRawDatabaseInfo.) - */ - FlushRelationBuffers(rel, MaxBlockNumber); } - else - systable_endscan(scan); - /* Close pg_database, but keep exclusive lock till commit */ + systable_endscan(scan); + + /* Close pg_database, but keep lock till commit */ heap_close(rel, NoLock); /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index c818d8f966..f4fbbae2ec 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.301 2005/02/20 02:21:34 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.302 2005/02/26 18:43:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -704,11 +704,12 @@ vac_update_relstats(Oid relid, BlockNumber num_pages, double num_tuples, * We violate no-overwrite semantics here by storing new values for the * statistics columns directly into the tuple that's already on the page. * As with vac_update_relstats, this avoids leaving dead tuples behind - * after a VACUUM; which is good since GetRawDatabaseInfo - * can get confused by finding dead tuples in pg_database. + * after a VACUUM. * * This routine is shared by full and lazy VACUUM. Note that it is only * applied after a database-wide VACUUM operation. + * + * Note that we don't bother to update the flat-file copy of pg_database. */ static void vac_update_dbstats(Oid dbid, diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index 9a65ee1610..658b42bbfa 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -10,12 +10,13 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.139 2005/02/20 04:45:57 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.140 2005/02/26 18:43:33 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include #include #include #include @@ -37,6 +38,8 @@ #include "utils/guc.h" +#define atooid(x) ((Oid) strtoul((x), NULL, 10)) + /* Max size of username ident server can return */ #define IDENT_USERNAME_MAX 512 @@ -1059,6 +1062,51 @@ load_hba(void) FreeFile(file); } +/* + * Read and parse one line from the flat pg_database file. + * + * Returns TRUE on success, FALSE if EOF; bad data causes elog(FATAL). + * + * Output parameters: + * dbname: gets database name (must be of size NAMEDATALEN bytes) + * dboid: gets database OID + * dbtablespace: gets database's default tablespace's OID + * + * This is not much related to the other functions in hba.c, but we put it + * here because it uses the next_token() infrastructure. + */ +bool +read_pg_database_line(FILE *fp, char *dbname, + Oid *dboid, Oid *dbtablespace) +{ + char buf[MAX_TOKEN]; + + if (feof(fp)) + return false; + next_token(fp, buf, sizeof(buf)); + if (!buf[0]) + return false; + if (strlen(buf) >= NAMEDATALEN) + elog(FATAL, "bad data in flat pg_database file"); + strcpy(dbname, buf); + next_token(fp, buf, sizeof(buf)); + if (!isdigit((unsigned char) buf[0])) + elog(FATAL, "bad data in flat pg_database file"); + *dboid = atooid(buf); + next_token(fp, buf, sizeof(buf)); + if (!isdigit((unsigned char) buf[0])) + elog(FATAL, "bad data in flat pg_database file"); + *dbtablespace = atooid(buf); + /* discard datfrozenxid */ + next_token(fp, buf, sizeof(buf)); + if (!isdigit((unsigned char) buf[0])) + elog(FATAL, "bad data in flat pg_database file"); + /* expect EOL next */ + next_token(fp, buf, sizeof(buf)); + if (buf[0]) + elog(FATAL, "bad data in flat pg_database file"); + return true; +} /* * Process one line from the ident config file. diff --git a/src/backend/utils/init/flatfiles.c b/src/backend/utils/init/flatfiles.c index 8968d572c8..e7ddd0a42c 100644 --- a/src/backend/utils/init/flatfiles.c +++ b/src/backend/utils/init/flatfiles.c @@ -22,7 +22,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.3 2005/02/20 22:02:19 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.4 2005/02/26 18:43:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -243,10 +243,12 @@ write_database_file(Relation drel) Form_pg_database dbform = (Form_pg_database) GETSTRUCT(tuple); char *datname; Oid datoid; + Oid dattablespace; TransactionId datfrozenxid; datname = NameStr(dbform->datname); datoid = HeapTupleGetOid(tuple); + dattablespace = dbform->dattablespace; datfrozenxid = dbform->datfrozenxid; /* @@ -276,13 +278,13 @@ write_database_file(Relation drel) } /* - * The file format is: "dbname" oid frozenxid + * The file format is: "dbname" oid tablespace frozenxid * * The xid is not needed for backend startup, but may be of use * for forensic purposes. */ fputs_quote(datname, fp); - fprintf(fp, " %u %u\n", datoid, datfrozenxid); + fprintf(fp, " %u %u %u\n", datoid, dattablespace, datfrozenxid); } heap_endscan(scan); @@ -830,15 +832,3 @@ flatfile_update_trigger(PG_FUNCTION_ARGS) return PointerGetDatum(NULL); } - - -/* - * Old version of trigger --- remove after we can force an initdb - */ -extern Datum update_pg_pwd_and_pg_group(PG_FUNCTION_ARGS); - -Datum -update_pg_pwd_and_pg_group(PG_FUNCTION_ARGS) -{ - return flatfile_update_trigger(fcinfo); -} diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index 53eb47a97e..d1479bbab7 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.140 2005/02/20 21:46:49 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.141 2005/02/26 18:43:33 tgl Exp $ * * *------------------------------------------------------------------------- @@ -27,14 +27,16 @@ #include "catalog/pg_database.h" #include "catalog/pg_shadow.h" #include "catalog/pg_tablespace.h" +#include "libpq/hba.h" #include "mb/pg_wchar.h" #include "miscadmin.h" #include "postmaster/postmaster.h" -#include "storage/backendid.h" +#include "storage/fd.h" #include "storage/ipc.h" #include "storage/proc.h" #include "storage/sinval.h" #include "storage/smgr.h" +#include "utils/flatfiles.h" #include "utils/fmgroids.h" #include "utils/guc.h" #include "utils/portal.h" @@ -42,6 +44,7 @@ #include "utils/syscache.h" +static bool FindMyDatabase(const char *name, Oid *db_id, Oid *db_tablespace); static void ReverifyMyDatabase(const char *name); static void InitCommunication(void); static void ShutdownPostgres(int code, Datum arg); @@ -51,18 +54,60 @@ static bool ThereIsAtLeastOneUser(void); /*** InitPostgres support ***/ -/* -------------------------------- - * ReverifyMyDatabase +/* + * FindMyDatabase -- get the critical info needed to locate my database * - * Since we are forced to fetch the database OID out of pg_database without - * benefit of locking or transaction ID checking (see utils/misc/database.c), - * we might have gotten a wrong answer. Or, we might have attached to a - * database that's in process of being destroyed by destroydb(). This - * routine is called after we have all the locking and other infrastructure - * running --- now we can check that we are really attached to a valid - * database. + * Find the named database in pg_database, return its database OID and the + * OID of its default tablespace. Return TRUE if found, FALSE if not. * - * In reality, if destroydb() is running in parallel with our startup, + * Since we are not yet up and running as a backend, we cannot look directly + * at pg_database (we can't obtain locks nor participate in transactions). + * So to get the info we need before starting up, we must look at the "flat + * file" copy of pg_database that is helpfully maintained by flatfiles.c. + * This is subject to various race conditions, so after we have the + * transaction infrastructure started, we have to recheck the information; + * see ReverifyMyDatabase. + */ +static bool +FindMyDatabase(const char *name, Oid *db_id, Oid *db_tablespace) +{ + bool result = false; + char *filename; + FILE *db_file; + char thisname[NAMEDATALEN]; + + filename = database_getflatfilename(); + db_file = AllocateFile(filename, "r"); + if (db_file == NULL) + ereport(FATAL, + (errcode_for_file_access(), + errmsg("could not open file \"%s\": %m", filename))); + + while (read_pg_database_line(db_file, thisname, db_id, db_tablespace)) + { + if (strcmp(thisname, name) == 0) + { + result = true; + break; + } + } + + FreeFile(db_file); + pfree(filename); + + return result; +} + +/* + * ReverifyMyDatabase -- recheck info obtained by FindMyDatabase + * + * Since FindMyDatabase cannot lock pg_database, the information it read + * could be stale; for example we might have attached to a database that's in + * process of being destroyed by dropdb(). This routine is called after + * we have all the locking and other infrastructure running --- now we can + * check that we are really attached to a valid database. + * + * In reality, if dropdb() is running in parallel with our startup, * it's pretty likely that we will have failed before now, due to being * unable to read some of the system tables within the doomed database. * This routine just exists to make *sure* we have not started up in an @@ -75,7 +120,6 @@ static bool ThereIsAtLeastOneUser(void); * To avoid having to read pg_database more times than necessary * during session startup, this place is also fitting to set up any * database-specific configuration variables. - * -------------------------------- */ static void ReverifyMyDatabase(const char *name) @@ -87,10 +131,10 @@ ReverifyMyDatabase(const char *name) Form_pg_database dbform; /* - * Because we grab AccessShareLock here, we can be sure that destroydb + * Because we grab RowShareLock here, we can be sure that dropdb() * is not running in parallel with us (any more). */ - pgdbrel = heap_openr(DatabaseRelationName, AccessShareLock); + pgdbrel = heap_openr(DatabaseRelationName, RowShareLock); ScanKeyInit(&key, Anum_pg_database_datname, @@ -104,7 +148,7 @@ ReverifyMyDatabase(const char *name) HeapTupleGetOid(tup) != MyDatabaseId) { /* OOPS */ - heap_close(pgdbrel, AccessShareLock); + heap_close(pgdbrel, RowShareLock); /* * The only real problem I could have created is to load dirty @@ -131,7 +175,7 @@ ReverifyMyDatabase(const char *name) name))); /* - * OK, we're golden. Only other to-do item is to save the encoding + * OK, we're golden. Next to-do item is to save the encoding * info out of the pg_database tuple. */ SetDatabaseEncoding(dbform->encoding); @@ -143,7 +187,7 @@ ReverifyMyDatabase(const char *name) PGC_BACKEND, PGC_S_DEFAULT); /* - * Set up database-specific configuration variables. + * Lastly, set up any database-specific configuration variables. */ if (IsUnderPostmaster) { @@ -161,7 +205,7 @@ ReverifyMyDatabase(const char *name) } heap_endscan(pgdbscan); - heap_close(pgdbrel, AccessShareLock); + heap_close(pgdbrel, RowShareLock); } @@ -261,11 +305,9 @@ InitPostgres(const char *dbname, const char *username) /* * Find oid and tablespace of the database we're about to open. * Since we're not yet up and running we have to use the hackish - * GetRawDatabaseInfo. + * FindMyDatabase. */ - GetRawDatabaseInfo(dbname, &MyDatabaseId, &MyDatabaseTableSpace); - - if (!OidIsValid(MyDatabaseId)) + if (!FindMyDatabase(dbname, &MyDatabaseId, &MyDatabaseTableSpace)) ereport(FATAL, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile index f907f22d16..afa3216558 100644 --- a/src/backend/utils/misc/Makefile +++ b/src/backend/utils/misc/Makefile @@ -1,4 +1,12 @@ -# $PostgreSQL: pgsql/src/backend/utils/misc/Makefile,v 1.22 2003/11/29 19:52:03 pgsql Exp $ +#------------------------------------------------------------------------- +# +# Makefile-- +# Makefile for utils/misc +# +# IDENTIFICATION +# $PostgreSQL: pgsql/src/backend/utils/misc/Makefile,v 1.23 2005/02/26 18:43:33 tgl Exp $ +# +#------------------------------------------------------------------------- subdir = src/backend/utils/misc top_builddir = ../../../.. @@ -6,10 +14,10 @@ include $(top_builddir)/src/Makefile.global override CPPFLAGS := -I$(srcdir) $(CPPFLAGS) -OBJS = database.o superuser.o guc.o help_config.o ps_status.o +OBJS = guc.o help_config.o ps_status.o superuser.o # This location might depend on the installation directories. Therefore -# we can't subsitute it into config.h. +# we can't subsitute it into pg_config.h. ifdef krb_srvtab override CPPFLAGS += -DPG_KRB_SRVTAB='"$(krb_srvtab)"' endif diff --git a/src/backend/utils/misc/database.c b/src/backend/utils/misc/database.c deleted file mode 100644 index 66ea6dbed7..0000000000 --- a/src/backend/utils/misc/database.c +++ /dev/null @@ -1,189 +0,0 @@ -/*------------------------------------------------------------------------- - * - * database.c - * miscellaneous initialization support stuff - * - * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/misc/database.c,v 1.63 2004/12/31 22:02:45 pgsql Exp $ - * - *------------------------------------------------------------------------- - */ -#include "postgres.h" - -#include -#include - -#include "access/xact.h" -#include "catalog/catname.h" -#include "catalog/catalog.h" -#include "catalog/pg_database.h" -#include "catalog/pg_tablespace.h" -#include "miscadmin.h" -#include "utils/syscache.h" - - -static bool PhonyHeapTupleSatisfiesNow(HeapTupleHeader tuple); - - -/* -------------------------------- - * GetRawDatabaseInfo() -- Find the OID and tablespace of the database. - * - * We need both the OID and the default tablespace in order to find - * the database's system catalogs. Moreover the database's OID forms - * half of the unique key for the system caches and lock tables, so - * we must have it before we can use any of the cache mechanisms. - * To get around these problems, this code opens and scans the - * pg_database relation by hand. - * - * This code knows way more than it should about the layout of - * tuples on disk, but there seems to be no help for that. - * We're pulling ourselves up by the bootstraps here... - * -------------------------------- - */ -void -GetRawDatabaseInfo(const char *name, Oid *db_id, Oid *db_tablespace) -{ - int dbfd; - int nbytes; - HeapTupleData tup; - Form_pg_database tup_db; - Page pg; - char *dbfname; - RelFileNode rnode; - - /* hard-wired path to pg_database */ - rnode.spcNode = GLOBALTABLESPACE_OID; - rnode.dbNode = 0; - rnode.relNode = RelOid_pg_database; - - dbfname = relpath(rnode); - - if ((dbfd = open(dbfname, O_RDONLY | PG_BINARY, 0)) < 0) - ereport(FATAL, - (errcode_for_file_access(), - errmsg("could not open file \"%s\": %m", dbfname))); - - pfree(dbfname); - - /* - * read and examine every page in pg_database - * - * Raw I/O! Read those tuples the hard way! Yow! - * - * Why don't we use the access methods or move this code someplace else? - * This is really pg_database schema dependent code. Perhaps it - * should go in lib/catalog/pg_database? -cim 10/3/90 - * - * mao replies 4 apr 91: yeah, maybe this should be moved to - * lib/catalog. however, we CANNOT use the access methods since those - * use the buffer cache, which uses the relation cache, which requires - * that the dbid be set, which is what we're trying to do here. - * - */ - pg = (Page) palloc(BLCKSZ); - - while ((nbytes = read(dbfd, pg, BLCKSZ)) == BLCKSZ) - { - OffsetNumber max = PageGetMaxOffsetNumber(pg); - OffsetNumber lineoff; - - /* look at each tuple on the page */ - for (lineoff = FirstOffsetNumber; lineoff <= max; lineoff++) - { - ItemId lpp = PageGetItemId(pg, lineoff); - - /* if it's a freed tuple, ignore it */ - if (!ItemIdIsUsed(lpp)) - continue; - - /* get a pointer to the tuple itself */ - tup.t_datamcxt = NULL; - tup.t_data = (HeapTupleHeader) PageGetItem(pg, lpp); - - /* - * Check to see if tuple is valid (committed). - * - * XXX warning, will robinson: violation of transaction semantics - * happens right here. We cannot really determine if the - * tuple is valid without checking transaction commit status, - * and the only way to do that at init time is to paw over - * pg_clog by hand, too. Instead of checking, we assume that - * the inserting transaction committed, and that any deleting - * transaction did also, unless shown otherwise by on-row - * commit status bits. - * - * All in all, this code is pretty shaky. We will cross-check - * our result in ReverifyMyDatabase() in postinit.c. - * - * NOTE: if a bogus tuple in pg_database prevents connection to a - * valid database, a fix is to connect to another database and - * do "select * from pg_database". That should cause - * committed and dead tuples to be marked with correct states. - * - * XXX wouldn't it be better to let new backends read the - * database info from a flat file, handled the same way we - * handle the password relation? - */ - if (!PhonyHeapTupleSatisfiesNow(tup.t_data)) - continue; - - /* - * Okay, see if this is the one we want. - */ - tup_db = (Form_pg_database) GETSTRUCT(&tup); - - if (strcmp(name, NameStr(tup_db->datname)) == 0) - { - /* Found it; extract the db's OID and tablespace. */ - *db_id = HeapTupleGetOid(&tup); - *db_tablespace = tup_db->dattablespace; - goto done; - } - } - } - - /* failed to find it... */ - *db_id = InvalidOid; - *db_tablespace = InvalidOid; - -done: - close(dbfd); - pfree(pg); -} - -/* - * PhonyHeapTupleSatisfiesNow --- cut-down tuple time qual test - * - * This is a simplified version of HeapTupleSatisfiesNow() that does not - * depend on having transaction commit info available. Any transaction - * that touched the tuple is assumed committed unless later marked invalid. - * (While we could think about more complex rules, this seems appropriate - * for examining pg_database, since both CREATE DATABASE and DROP DATABASE - * are non-roll-back-able.) - */ -static bool -PhonyHeapTupleSatisfiesNow(HeapTupleHeader tuple) -{ - if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED)) - { - if (tuple->t_infomask & HEAP_XMIN_INVALID) - return false; - - if (tuple->t_infomask & HEAP_MOVED_OFF) - return false; - /* else assume committed */ - } - - if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */ - return true; - - /* assume xmax transaction committed */ - if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) - return true; - - return false; -} diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index 5d8338f5ac..e6cfc05d20 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -39,7 +39,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * Portions taken from FreeBSD. * - * $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.75 2005/02/22 04:38:22 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.76 2005/02/26 18:43:34 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1319,15 +1319,18 @@ setup_shadow(void) char **line; static char *pg_shadow_setup[] = { /* - * Create a trigger so that direct updates to pg_shadow will be - * written to the flat password/group files pg_pwd and pg_group + * Create triggers to ensure manual updates to shared catalogs + * will be reflected into their "flat file" copies. */ - "CREATE TRIGGER pg_sync_pg_pwd " - " AFTER INSERT OR UPDATE OR DELETE ON pg_shadow " - " FOR EACH ROW EXECUTE PROCEDURE update_pg_pwd_and_pg_group();\n", + "CREATE TRIGGER pg_sync_pg_database " + " AFTER INSERT OR UPDATE OR DELETE ON pg_database " + " FOR EACH STATEMENT EXECUTE PROCEDURE flatfile_update_trigger();\n", "CREATE TRIGGER pg_sync_pg_group " " AFTER INSERT OR UPDATE OR DELETE ON pg_group " - " FOR EACH ROW EXECUTE PROCEDURE update_pg_pwd_and_pg_group();\n", + " FOR EACH STATEMENT EXECUTE PROCEDURE flatfile_update_trigger();\n", + "CREATE TRIGGER pg_sync_pg_pwd " + " AFTER INSERT OR UPDATE OR DELETE ON pg_shadow " + " FOR EACH STATEMENT EXECUTE PROCEDURE flatfile_update_trigger();\n", /* * needs to be done before alter user, because alter user checks diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index e4f6a74ea3..ca44b24b2c 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.254 2005/02/25 16:13:29 teodor Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.255 2005/02/26 18:43:34 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200502251 +#define CATALOG_VERSION_NO 200502261 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 16867cb992..e456e96bba 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.349 2004/12/31 22:03:25 pgsql Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.350 2005/02/26 18:43:34 tgl Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -2133,8 +2133,8 @@ DESCR("matches LIKE expression, case-insensitive"); DATA(insert OID = 1661 ( bpcharicnlike PGNSP PGUID 12 f f t f i 2 16 "1042 25" _null_ texticnlike - _null_ )); DESCR("does not match LIKE expression, case-insensitive"); -DATA(insert OID = 1689 ( update_pg_pwd_and_pg_group PGNSP PGUID 12 f f t f v 0 2279 "" _null_ update_pg_pwd_and_pg_group - _null_ )); -DESCR("update pg_pwd and pg_group files"); +DATA(insert OID = 1689 ( flatfile_update_trigger PGNSP PGUID 12 f f t f v 0 2279 "" _null_ flatfile_update_trigger - _null_ )); +DESCR("update flat-file copy of a shared catalog"); /* Oracle Compatibility Related Functions - By Edmund Mergl */ DATA(insert OID = 868 ( strpos PGNSP PGUID 12 f f t f i 2 23 "25 25" _null_ textpos - _null_ )); diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h index a284edf396..0656e6a4e8 100644 --- a/src/include/libpq/hba.h +++ b/src/include/libpq/hba.h @@ -4,19 +4,16 @@ * Interface to hba.c * * - * $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.35 2004/02/02 16:58:30 neilc Exp $ + * $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.36 2005/02/26 18:43:34 tgl Exp $ * *------------------------------------------------------------------------- */ #ifndef HBA_H #define HBA_H -#ifndef WIN32 -#include -#endif - #include "nodes/pg_list.h" + typedef enum UserAuth { uaReject, @@ -41,5 +38,7 @@ extern void load_user(void); extern void load_group(void); extern int hba_getauthmethod(hbaPort *port); extern int authident(hbaPort *port); +extern bool read_pg_database_line(FILE *fp, char *dbname, + Oid *dboid, Oid *dbtablespace); -#endif +#endif /* HBA_H */ diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index e5333977c0..3e9b7d912a 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -13,7 +13,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.174 2004/12/31 22:03:19 pgsql Exp $ + * $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.175 2005/02/26 18:43:34 tgl Exp $ * * NOTES * some of the information in this file should be moved to other files. @@ -225,9 +225,6 @@ extern void check_stack_depth(void); extern char *DatabasePath; -/* in utils/misc/database.c */ -extern void GetRawDatabaseInfo(const char *name, Oid *db_id, Oid *db_tablespace); - /* now in utils/init/miscinit.c */ extern void SetDatabasePath(const char *path);