Another go-round on making GetRawDatabaseInfo behave as well as it can,
given the fundamental restriction of not looking at transaction commit data in pg_log. Use code that is actually based on tqual.c rather than ad-hoc tests. Also write the tuple fetch loop using standard access macros rather than ad-hoc code.
This commit is contained in:
parent
8a0c9b17a5
commit
a904181a46
@ -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 <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user