Another go-round on making GetRawDatabaseInfo behave as well as it can,
authorTom Lane
Sun, 14 Jan 2001 22:21:05 +0000 (22:21 +0000)
committerTom Lane
Sun, 14 Jan 2001 22:21:05 +0000 (22:21 +0000)
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.

src/backend/utils/misc/database.c

index 364075c8bed80b2a6c888c92ee26491a5a779053..28ff5bdb2856f05a8a5304348d9fff736210b20f 100644 (file)
@@ -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;
+}