diff --git a/src/backend/utils/misc/database.c b/src/backend/utils/misc/database.c index 364075c8be..28ff5bdb28 100644 --- a/src/backend/utils/misc/database.c +++ b/src/backend/utils/misc/database.c @@ -8,13 +8,12 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/misc/Attic/database.c,v 1.41 2000/11/14 18:37:45 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/misc/Attic/database.c,v 1.42 2001/01/14 22:21:05 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" -#include #include #include #include @@ -28,6 +27,9 @@ #include "utils/syscache.h" +static bool PhonyHeapTupleSatisfiesNow(HeapTupleHeader tuple); + + /* * ExpandDatabasePath resolves a proposed database path (obtained from * pg_database.datpath) to a full absolute path for further consumption. @@ -136,11 +138,9 @@ GetRawDatabaseInfo(const char *name, Oid *db_id, char *path) { int dbfd; int nbytes; - int max, - i; + int pathlen; HeapTupleData tup; Page pg; - PageHeader ph; char *dbfname; Form_pg_database tup_db; @@ -157,7 +157,7 @@ GetRawDatabaseInfo(const char *name, Oid *db_id, char *path) #endif if ((dbfd = open(dbfname, O_RDONLY | PG_BINARY, 0)) < 0) - elog(FATAL, "cannot open %s: %s", dbfname, strerror(errno)); + elog(FATAL, "cannot open %s: %m", dbfname); pfree(dbfname); @@ -179,38 +179,38 @@ GetRawDatabaseInfo(const char *name, Oid *db_id, char *path) * ---------------- */ pg = (Page) palloc(BLCKSZ); - ph = (PageHeader) pg; while ((nbytes = read(dbfd, pg, BLCKSZ)) == BLCKSZ) { - max = PageGetMaxOffsetNumber(pg); + OffsetNumber max = PageGetMaxOffsetNumber(pg); + OffsetNumber lineoff; /* look at each tuple on the page */ - for (i = 0; i < max; i++) + for (lineoff = FirstOffsetNumber; lineoff <= max; lineoff++) { - int offset; + ItemId lpp = PageGetItemId(pg, lineoff); /* if it's a freed tuple, ignore it */ - if (!(ph->pd_linp[i].lp_flags & LP_USED)) + if (!ItemIdIsUsed(lpp)) continue; /* get a pointer to the tuple itself */ - offset = (int) ph->pd_linp[i].lp_off; tup.t_datamcxt = NULL; - tup.t_data = (HeapTupleHeader) (((char *) pg) + offset); + tup.t_data = (HeapTupleHeader) PageGetItem(pg, lpp); - /* - * if the tuple has been deleted (the database was destroyed), - * skip this tuple. XXX warning, will robinson: violation of - * transaction semantics happens right here. we should check - * to be sure that the xact that deleted this tuple actually - * committed. Only way to do that at init time is to paw over - * the log relation by hand, too. Instead we take the - * conservative assumption that if someone tried to delete it, - * it's gone. The other side of the coin is that we might - * accept a tuple that was stored and never committed. All in - * all, this code is pretty shaky. We will cross-check our - * result in ReverifyMyDatabase() in postinit.c. + /*-------------------- + * 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_log 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 @@ -220,12 +220,10 @@ GetRawDatabaseInfo(const char *name, Oid *db_id, char *path) * XXX wouldn't it be better to let new backends read the * database OID from a flat file, handled the same way we * handle the password relation? + *-------------------- */ - if (tup.t_data->t_infomask & HEAP_XMIN_INVALID) - continue; /* inserting xact known aborted */ - if (TransactionIdIsValid((TransactionId) tup.t_data->t_xmax) && - !(tup.t_data->t_infomask & HEAP_XMAX_INVALID)) - continue; /* deleting xact happened, not known aborted */ + if (! PhonyHeapTupleSatisfiesNow(tup.t_data)) + continue; /* * Okay, see if this is the one we want. @@ -236,9 +234,11 @@ GetRawDatabaseInfo(const char *name, Oid *db_id, char *path) { /* Found it; extract the OID and the database path. */ *db_id = tup.t_data->t_oid; - strncpy(path, VARDATA(&(tup_db->datpath)), - (VARSIZE(&(tup_db->datpath)) - VARHDRSZ)); - *(path + VARSIZE(&(tup_db->datpath)) - VARHDRSZ) = '\0'; + pathlen = VARSIZE(&(tup_db->datpath)) - VARHDRSZ; + if (pathlen >= MAXPGPATH) + pathlen = MAXPGPATH-1; /* pure paranoia */ + strncpy(path, VARDATA(&(tup_db->datpath)), pathlen); + path[pathlen] = '\0'; goto done; } } @@ -251,4 +251,37 @@ GetRawDatabaseInfo(const char *name, Oid *db_id, char *path) done: close(dbfd); pfree(pg); -} /* GetRawDatabaseInfo() */ +} + +/* + * 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; +}