+++ /dev/null
-/*-------------------------------------------------------------------------
- *
- * flatfiles.c
- *       Routines for maintaining "flat file" images of the shared catalogs.
- *
- * We use flat files so that the postmaster and not-yet-fully-started
- * backends can look at the contents of pg_database, pg_authid, and
- * pg_auth_members for authentication purposes.  This module is
- * responsible for keeping the flat-file images as nearly in sync with
- * database reality as possible.
- *
- * The tricky part of the write_xxx_file() routines in this module is that
- * they need to be able to operate in the context of the database startup
- * process (which calls BuildFlatFiles()) as well as a normal backend.
- * This means for example that we can't assume a fully functional relcache
- * and we can't use syscaches at all.  The major restriction imposed by
- * all that is that there's no way to read an out-of-line-toasted datum,
- * because the tuptoaster.c code is not prepared to cope with such an
- * environment.  Fortunately we can design the shared catalogs in such
- * a way that this is OK.
- *
- *
- * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.39 2009/08/31 02:23:22 tgl Exp $
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include "access/heapam.h"
-#include "access/transam.h"
-#include "access/twophase_rmgr.h"
-#include "access/xact.h"
-#include "access/xlogutils.h"
-#include "catalog/pg_auth_members.h"
-#include "catalog/pg_authid.h"
-#include "catalog/pg_database.h"
-#include "catalog/pg_namespace.h"
-#include "catalog/pg_tablespace.h"
-#include "commands/trigger.h"
-#include "miscadmin.h"
-#include "storage/bufmgr.h"
-#include "storage/fd.h"
-#include "storage/lmgr.h"
-#include "storage/pmsignal.h"
-#include "utils/builtins.h"
-#include "utils/flatfiles.h"
-#include "utils/resowner.h"
-#include "utils/tqual.h"
-
-
-/* Actual names of the flat files (within $PGDATA) */
-#define DATABASE_FLAT_FILE     "global/pg_database"
-#define AUTH_FLAT_FILE         "global/pg_auth"
-
-/* Info bits in a flatfiles 2PC record */
-#define FF_BIT_DATABASE 1
-#define FF_BIT_AUTH            2
-
-
-/*
- * The need-to-update-files flags are 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 seen at top-level commit.
- */
-static SubTransactionId database_file_update_subid = InvalidSubTransactionId;
-static SubTransactionId auth_file_update_subid = InvalidSubTransactionId;
-
-
-/*
- * Mark flat database file as needing an update (because pg_database changed)
- */
-void
-database_file_update_needed(void)
-{
-       if (database_file_update_subid == InvalidSubTransactionId)
-               database_file_update_subid = GetCurrentSubTransactionId();
-}
-
-/*
- * Mark flat auth file as needing an update (because pg_authid or
- * pg_auth_members changed)
- */
-void
-auth_file_update_needed(void)
-{
-       if (auth_file_update_subid == InvalidSubTransactionId)
-               auth_file_update_subid = GetCurrentSubTransactionId();
-}
-
-
-/*
- * database_getflatfilename --- get pathname of database file
- *
- * Note that result string is palloc'd, and should be freed by the caller.
- * (This convention is not really needed anymore, since the relative path
- * is fixed.)
- */
-char *
-database_getflatfilename(void)
-{
-       return pstrdup(DATABASE_FLAT_FILE);
-}
-
-/*
- * auth_getflatfilename --- get pathname of auth file
- *
- * Note that result string is palloc'd, and should be freed by the caller.
- * (This convention is not really needed anymore, since the relative path
- * is fixed.)
- */
-char *
-auth_getflatfilename(void)
-{
-       return pstrdup(AUTH_FLAT_FILE);
-}
-
-
-/*
- *     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(const char *str, FILE *fp)
-{
-       fputc('"', fp);
-       while (*str)
-       {
-               fputc(*str, fp);
-               if (*str == '"')
-                       fputc('"', fp);
-               str++;
-       }
-       fputc('"', fp);
-}
-
-/*
- * name_okay
- *
- * We must disallow newlines in role names because
- * hba.c's parser won't handle fields split across lines, even if quoted.
- */
-static bool
-name_okay(const char *str)
-{
-       int                     i;
-
-       i = strcspn(str, "\r\n");
-       return (str[i] == '\0');
-}
-
-
-/*
- * write_database_file: update the flat database file
- */
-static void
-write_database_file(Relation drel)
-{
-       char       *filename,
-                          *tempname;
-       int                     bufsize;
-       FILE       *fp;
-       mode_t          oumask;
-       HeapScanDesc scan;
-       HeapTuple       tuple;
-
-       /*
-        * Create a temporary filename to be renamed later.  This prevents the
-        * backend from clobbering the flat file while the postmaster might be
-        * reading from it.
-        */
-       filename = database_getflatfilename();
-       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_database and write the file.
-        */
-       scan = heap_beginscan(drel, SnapshotNow, 0, NULL);
-       while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
-       {
-               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;
-
-               /*
-                * Check for illegal characters in the database name.
-                */
-               if (!name_okay(datname))
-               {
-                       ereport(LOG,
-                                       (errmsg("invalid database name \"%s\"", datname)));
-                       continue;
-               }
-
-               /*
-                * The file format is: "dbname" oid tablespace frozenxid
-                *
-                * The xids are not needed for backend startup, but are of use to
-                * autovacuum, and might also be helpful for forensic purposes.
-                */
-               fputs_quote(datname, fp);
-               fprintf(fp, " %u %u %u\n",
-                               datoid, dattablespace, datfrozenxid);
-       }
-       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 flat file. 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)));
-}
-
-
-/*
- * Support for write_auth_file
- *
- * The format for the flat auth file is
- *             "rolename" "password" "validuntil" "memberof" "memberof" ...
- * Each role's line lists all the roles (groups) of which it is directly
- * or indirectly a member, except for itself.
- *
- * The postmaster expects the file to be sorted by rolename.  There is not
- * any special ordering of the membership lists.
- *
- * To construct this information, we scan pg_authid and pg_auth_members,
- * and build data structures in-memory before writing the file.
- */
-
-typedef struct
-{
-       Oid                     roleid;
-       char       *rolname;
-       char       *rolpassword;
-       char       *rolvaliduntil;
-       List       *member_of;
-} auth_entry;
-
-typedef struct
-{
-       Oid                     roleid;
-       Oid                     memberid;
-} authmem_entry;
-
-
-/* qsort comparator for sorting auth_entry array by roleid */
-static int
-oid_compar(const void *a, const void *b)
-{
-       const auth_entry *a_auth = (const auth_entry *) a;
-       const auth_entry *b_auth = (const auth_entry *) b;
-
-       if (a_auth->roleid < b_auth->roleid)
-               return -1;
-       if (a_auth->roleid > b_auth->roleid)
-               return 1;
-       return 0;
-}
-
-/* qsort comparator for sorting auth_entry array by rolname */
-static int
-name_compar(const void *a, const void *b)
-{
-       const auth_entry *a_auth = (const auth_entry *) a;
-       const auth_entry *b_auth = (const auth_entry *) b;
-
-       return strcmp(a_auth->rolname, b_auth->rolname);
-}
-
-/* qsort comparator for sorting authmem_entry array by memberid */
-static int
-mem_compar(const void *a, const void *b)
-{
-       const authmem_entry *a_auth = (const authmem_entry *) a;
-       const authmem_entry *b_auth = (const authmem_entry *) b;
-
-       if (a_auth->memberid < b_auth->memberid)
-               return -1;
-       if (a_auth->memberid > b_auth->memberid)
-               return 1;
-       return 0;
-}
-
-
-/*
- * write_auth_file: update the flat auth file
- */
-static void
-write_auth_file(Relation rel_authid, Relation rel_authmem)
-{
-       char       *filename,
-                          *tempname;
-       int                     bufsize;
-       BlockNumber totalblocks;
-       FILE       *fp;
-       mode_t          oumask;
-       HeapScanDesc scan;
-       HeapTuple       tuple;
-       int                     curr_role = 0;
-       int                     total_roles = 0;
-       int                     curr_mem = 0;
-       int                     total_mem = 0;
-       int                     est_rows;
-       auth_entry *auth_info;
-       authmem_entry *authmem_info;
-
-       /*
-        * Create a temporary filename to be renamed later.  This prevents the
-        * backend from clobbering the flat file while the postmaster might be
-        * reading from it.
-        */
-       filename = auth_getflatfilename();
-       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_authid and fill temporary data structures.
-        */
-       totalblocks = RelationGetNumberOfBlocks(rel_authid);
-       totalblocks = totalblocks ? totalblocks : 1;
-       est_rows = totalblocks * (BLCKSZ / (sizeof(HeapTupleHeaderData) + sizeof(FormData_pg_authid)));
-       auth_info = (auth_entry *) palloc(est_rows * sizeof(auth_entry));
-
-       scan = heap_beginscan(rel_authid, SnapshotNow, 0, NULL);
-       while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
-       {
-               Form_pg_authid aform = (Form_pg_authid) GETSTRUCT(tuple);
-               HeapTupleHeader tup = tuple->t_data;
-               char       *tp;                 /* ptr to tuple data */
-               long            off;            /* offset in tuple data */
-               bits8      *bp = tup->t_bits;   /* ptr to null bitmask in tuple */
-               Datum           datum;
-
-               if (curr_role >= est_rows)
-               {
-                       est_rows *= 2;
-                       auth_info = (auth_entry *)
-                               repalloc(auth_info, est_rows * sizeof(auth_entry));
-               }
-
-               auth_info[curr_role].roleid = HeapTupleGetOid(tuple);
-               auth_info[curr_role].rolname = pstrdup(NameStr(aform->rolname));
-               auth_info[curr_role].member_of = NIL;
-
-               /*
-                * We can't use heap_getattr() here because during startup we will not
-                * have any tupdesc for pg_authid.      Fortunately it's not too hard to
-                * work around this.  rolpassword is the first possibly-null field so
-                * we can compute its offset directly.  Note that this only works
-                * reliably because the preceding field (rolconnlimit) is int4, and
-                * therefore rolpassword is always 4-byte-aligned, and will be at the
-                * same offset no matter whether it uses 1-byte or 4-byte header.
-                */
-               tp = (char *) tup + tup->t_hoff;
-               off = offsetof(FormData_pg_authid, rolpassword);
-
-               if (HeapTupleHasNulls(tuple) &&
-                       att_isnull(Anum_pg_authid_rolpassword - 1, bp))
-               {
-                       /* passwd is null, emit as an empty string */
-                       auth_info[curr_role].rolpassword = pstrdup("");
-               }
-               else
-               {
-                       /* assume passwd is pass-by-ref */
-                       datum = PointerGetDatum(tp + off);
-
-                       /*
-                        * The password probably shouldn't ever be out-of-line toasted; if
-                        * it is, ignore it, since we can't handle that in startup mode.
-                        *
-                        * It is entirely likely that it's 1-byte format not 4-byte, and
-                        * theoretically possible that it's compressed inline, but
-                        * text_to_cstring should be able to handle those cases even in
-                        * startup mode.
-                        */
-                       if (VARATT_IS_EXTERNAL(DatumGetPointer(datum)))
-                               auth_info[curr_role].rolpassword = pstrdup("");
-                       else
-                               auth_info[curr_role].rolpassword = TextDatumGetCString(datum);
-
-                       /* assume passwd has attlen -1 */
-                       off = att_addlength_pointer(off, -1, tp + off);
-               }
-
-               if (HeapTupleHasNulls(tuple) &&
-                       att_isnull(Anum_pg_authid_rolvaliduntil - 1, bp))
-               {
-                       /* rolvaliduntil is null, emit as an empty string */
-                       auth_info[curr_role].rolvaliduntil = pstrdup("");
-               }
-               else
-               {
-                       TimestampTz *rvup;
-
-                       /* Assume timestamptz has double alignment */
-                       off = att_align_nominal(off, 'd');
-                       rvup = (TimestampTz *) (tp + off);
-                       auth_info[curr_role].rolvaliduntil =
-                               DatumGetCString(DirectFunctionCall1(timestamptz_out,
-                                                                                               TimestampTzGetDatum(*rvup)));
-               }
-
-               /*
-                * Check for illegal characters in the user name and password.
-                */
-               if (!name_okay(auth_info[curr_role].rolname))
-               {
-                       ereport(LOG,
-                                       (errmsg("invalid role name \"%s\"",
-                                                       auth_info[curr_role].rolname)));
-                       continue;
-               }
-               if (!name_okay(auth_info[curr_role].rolpassword))
-               {
-                       ereport(LOG,
-                                       (errmsg("invalid role password \"%s\"",
-                                                       auth_info[curr_role].rolpassword)));
-                       continue;
-               }
-
-               curr_role++;
-               total_roles++;
-       }
-       heap_endscan(scan);
-
-       /*
-        * Read pg_auth_members into temporary data structure, too
-        */
-       totalblocks = RelationGetNumberOfBlocks(rel_authmem);
-       totalblocks = totalblocks ? totalblocks : 1;
-       est_rows = totalblocks * (BLCKSZ / (sizeof(HeapTupleHeaderData) + sizeof(FormData_pg_auth_members)));
-       authmem_info = (authmem_entry *) palloc(est_rows * sizeof(authmem_entry));
-
-       scan = heap_beginscan(rel_authmem, SnapshotNow, 0, NULL);
-       while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
-       {
-               Form_pg_auth_members memform = (Form_pg_auth_members) GETSTRUCT(tuple);
-
-               if (curr_mem >= est_rows)
-               {
-                       est_rows *= 2;
-                       authmem_info = (authmem_entry *)
-                               repalloc(authmem_info, est_rows * sizeof(authmem_entry));
-               }
-
-               authmem_info[curr_mem].roleid = memform->roleid;
-               authmem_info[curr_mem].memberid = memform->member;
-               curr_mem++;
-               total_mem++;
-       }
-       heap_endscan(scan);
-
-       /*
-        * Search for memberships.      We can skip all this if pg_auth_members is
-        * empty.
-        */
-       if (total_mem > 0)
-       {
-               /*
-                * Sort auth_info by roleid and authmem_info by memberid.
-                */
-               qsort(auth_info, total_roles, sizeof(auth_entry), oid_compar);
-               qsort(authmem_info, total_mem, sizeof(authmem_entry), mem_compar);
-
-               /*
-                * For each role, find what it belongs to.
-                */
-               for (curr_role = 0; curr_role < total_roles; curr_role++)
-               {
-                       List       *roles_list;
-                       List       *roles_names_list = NIL;
-                       ListCell   *mem;
-
-                       /*
-                        * This search algorithm is the same as in is_member_of_role; we
-                        * are just working with a different input data structure.
-                        */
-                       roles_list = list_make1_oid(auth_info[curr_role].roleid);
-
-                       foreach(mem, roles_list)
-                       {
-                               authmem_entry key;
-                               authmem_entry *found_mem;
-                               int                     first_found,
-                                                       last_found,
-                                                       i;
-
-                               key.memberid = lfirst_oid(mem);
-                               found_mem = bsearch(&key, authmem_info, total_mem,
-                                                                       sizeof(authmem_entry), mem_compar);
-                               if (!found_mem)
-                                       continue;
-
-                               /*
-                                * bsearch found a match for us; but if there were multiple
-                                * matches it could have found any one of them. Locate first
-                                * and last match.
-                                */
-                               first_found = last_found = (found_mem - authmem_info);
-                               while (first_found > 0 &&
-                                          mem_compar(&key, &authmem_info[first_found - 1]) == 0)
-                                       first_found--;
-                               while (last_found + 1 < total_mem &&
-                                          mem_compar(&key, &authmem_info[last_found + 1]) == 0)
-                                       last_found++;
-
-                               /*
-                                * Now add all the new roles to roles_list.
-                                */
-                               for (i = first_found; i <= last_found; i++)
-                                       roles_list = list_append_unique_oid(roles_list,
-                                                                                                        authmem_info[i].roleid);
-                       }
-
-                       /*
-                        * Convert list of role Oids to list of role names. We must do
-                        * this before re-sorting auth_info.
-                        *
-                        * We skip the first list element (curr_role itself) since there
-                        * is no point in writing that a role is a member of itself.
-                        */
-                       for_each_cell(mem, lnext(list_head(roles_list)))
-                       {
-                               auth_entry      key_auth;
-                               auth_entry *found_role;
-
-                               key_auth.roleid = lfirst_oid(mem);
-                               found_role = bsearch(&key_auth, auth_info, total_roles,
-                                                                        sizeof(auth_entry), oid_compar);
-                               if (found_role) /* paranoia */
-                                       roles_names_list = lappend(roles_names_list,
-                                                                                          found_role->rolname);
-                       }
-                       auth_info[curr_role].member_of = roles_names_list;
-                       list_free(roles_list);
-               }
-       }
-
-       /*
-        * Now sort auth_info into rolname order for output, and write the file.
-        */
-       qsort(auth_info, total_roles, sizeof(auth_entry), name_compar);
-
-       for (curr_role = 0; curr_role < total_roles; curr_role++)
-       {
-               auth_entry *arole = &auth_info[curr_role];
-               ListCell   *mem;
-
-               fputs_quote(arole->rolname, fp);
-               fputs(" ", fp);
-               fputs_quote(arole->rolpassword, fp);
-               fputs(" ", fp);
-               fputs_quote(arole->rolvaliduntil, fp);
-
-               foreach(mem, arole->member_of)
-               {
-                       fputs(" ", fp);
-                       fputs_quote((char *) lfirst(mem), fp);
-               }
-
-               fputs("\n", fp);
-       }
-
-       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 flat file. 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)));
-}
-
-
-/*
- * This routine is called once during database startup, after completing
- * WAL replay if needed.  Its purpose is to sync the flat files with the
- * current state of the database tables.  This is particularly important
- * during PITR operation, since the flat files will come from the
- * base backup which may be far out of sync with the current state.
- *
- * In theory we could skip rebuilding the flat files if no WAL replay
- * occurred, but it seems best to just do it always.  We have to
- * scan pg_database to compute the XID wrap limit anyway.  Also, this
- * policy means we need not force initdb to change the format of the
- * flat files.
- *
- * In a standalone backend we pass database_only = true to skip processing
- * the auth file.  We won't need it, and building it could fail if there's
- * something corrupt in the authid/authmem catalogs.
- */
-void
-BuildFlatFiles(bool database_only)
-{
-       ResourceOwner owner;
-       RelFileNode rnode;
-       Relation        rel_db,
-                               rel_authid,
-                               rel_authmem;
-
-       /* Need a resowner to keep the heapam and buffer code happy */
-       owner = ResourceOwnerCreate(NULL, "BuildFlatFiles");
-       CurrentResourceOwner = owner;
-
-       /* hard-wired path to pg_database */
-       rnode.spcNode = GLOBALTABLESPACE_OID;
-       rnode.dbNode = 0;
-       rnode.relNode = DatabaseRelationId;
-
-       /*
-        * We don't have any hope of running a real relcache, but we can use the
-        * same fake-relcache facility that WAL replay uses.
-        *
-        * No locking is needed because no one else is alive yet.
-        */
-       rel_db = CreateFakeRelcacheEntry(rnode);
-       write_database_file(rel_db);
-       FreeFakeRelcacheEntry(rel_db);
-
-       if (!database_only)
-       {
-               /* hard-wired path to pg_authid */
-               rnode.spcNode = GLOBALTABLESPACE_OID;
-               rnode.dbNode = 0;
-               rnode.relNode = AuthIdRelationId;
-               rel_authid = CreateFakeRelcacheEntry(rnode);
-
-               /* hard-wired path to pg_auth_members */
-               rnode.spcNode = GLOBALTABLESPACE_OID;
-               rnode.dbNode = 0;
-               rnode.relNode = AuthMemRelationId;
-               rel_authmem = CreateFakeRelcacheEntry(rnode);
-
-               write_auth_file(rel_authid, rel_authmem);
-               FreeFakeRelcacheEntry(rel_authid);
-               FreeFakeRelcacheEntry(rel_authmem);
-       }
-
-       CurrentResourceOwner = NULL;
-       ResourceOwnerDelete(owner);
-}
-
-
-/*
- * This routine is called during transaction commit or abort.
- *
- * On commit, if we've written any of the critical database tables 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_UpdateFlatFiles(bool isCommit)
-{
-       Relation        drel = NULL;
-       Relation        arel = NULL;
-       Relation        mrel = NULL;
-
-       if (database_file_update_subid == InvalidSubTransactionId &&
-               auth_file_update_subid == InvalidSubTransactionId)
-               return;                                 /* nothing to do */
-
-       if (!isCommit)
-       {
-               database_file_update_subid = InvalidSubTransactionId;
-               auth_file_update_subid = InvalidSubTransactionId;
-               return;
-       }
-
-       /*
-        * Advance command counter to be certain we see all effects of the current
-        * transaction.
-        */
-       CommandCounterIncrement();
-
-       /*
-        * Open and lock the needed catalog(s).
-        *
-        * Even though we only need AccessShareLock, this could theoretically fail
-        * due to deadlock.  In practice, however, our transaction already holds
-        * RowExclusiveLock or better (it couldn't have updated the catalog
-        * without such a lock).  This implies that dbcommands.c and other places
-        * that force flat-file updates must not follow the common practice of
-        * dropping catalog locks before commit.
-        */
-       if (database_file_update_subid != InvalidSubTransactionId)
-               drel = heap_open(DatabaseRelationId, AccessShareLock);
-
-       if (auth_file_update_subid != InvalidSubTransactionId)
-       {
-               arel = heap_open(AuthIdRelationId, AccessShareLock);
-               mrel = heap_open(AuthMemRelationId, AccessShareLock);
-       }
-
-       /*
-        * Obtain special locks to ensure that two transactions don't try to write
-        * the same flat file concurrently.  Quite aside from any direct risks of
-        * corrupted output, the winning writer probably wouldn't have seen the
-        * other writer's updates.  By taking a lock and holding it till commit,
-        * we ensure that whichever updater goes second will see the other
-        * updater's changes as committed, and thus the final state of the file
-        * will include all updates.
-        *
-        * We use a lock on "database 0" to protect writing the pg_database flat
-        * file, and a lock on "role 0" to protect the auth file.  This is a bit
-        * ugly but it's not worth inventing any more-general convention.  (Any
-        * two locktags that are never used for anything else would do.)
-        *
-        * This is safe against deadlock as long as these are the very last locks
-        * acquired during the transaction.
-        */
-       if (database_file_update_subid != InvalidSubTransactionId)
-               LockSharedObject(DatabaseRelationId, InvalidOid, 0,
-                                                AccessExclusiveLock);
-
-       if (auth_file_update_subid != InvalidSubTransactionId)
-               LockSharedObject(AuthIdRelationId, InvalidOid, 0,
-                                                AccessExclusiveLock);
-
-       /* Okay to write the files */
-       if (database_file_update_subid != InvalidSubTransactionId)
-       {
-               database_file_update_subid = InvalidSubTransactionId;
-               write_database_file(drel);
-               heap_close(drel, NoLock);
-       }
-
-       if (auth_file_update_subid != InvalidSubTransactionId)
-       {
-               auth_file_update_subid = InvalidSubTransactionId;
-               write_auth_file(arel, mrel);
-               heap_close(arel, NoLock);
-               heap_close(mrel, NoLock);
-       }
-
-       /*
-        * Force synchronous commit, to minimize the window between changing the
-        * flat files on-disk and marking the transaction committed.  It's not
-        * great that there is any window at all, but definitely we don't want to
-        * make it larger than necessary.
-        */
-       ForceSyncCommit();
-}
-
-
-/*
- * This routine is called during transaction prepare.
- *
- * Record which files need to be refreshed if this transaction later
- * commits.
- *
- * Note: it's OK to clear the flags immediately, since if the PREPARE fails
- * further on, we'd only reset the flags anyway. So there's no need for a
- * separate PostPrepare call.
- */
-void
-AtPrepare_UpdateFlatFiles(void)
-{
-       uint16          info = 0;
-
-       if (database_file_update_subid != InvalidSubTransactionId)
-       {
-               database_file_update_subid = InvalidSubTransactionId;
-               info |= FF_BIT_DATABASE;
-       }
-       if (auth_file_update_subid != InvalidSubTransactionId)
-       {
-               auth_file_update_subid = InvalidSubTransactionId;
-               info |= FF_BIT_AUTH;
-       }
-       if (info != 0)
-               RegisterTwoPhaseRecord(TWOPHASE_RM_FLATFILES_ID, info,
-                                                          NULL, 0);
-}
-
-
-/*
- * AtEOSubXact_UpdateFlatFiles
- *
- * Called at subtransaction end, this routine resets or updates the
- * need-to-update-files flags.
- */
-void
-AtEOSubXact_UpdateFlatFiles(bool isCommit,
-                                                       SubTransactionId mySubid,
-                                                       SubTransactionId parentSubid)
-{
-       if (isCommit)
-       {
-               if (database_file_update_subid == mySubid)
-                       database_file_update_subid = parentSubid;
-
-               if (auth_file_update_subid == mySubid)
-                       auth_file_update_subid = parentSubid;
-       }
-       else
-       {
-               if (database_file_update_subid == mySubid)
-                       database_file_update_subid = InvalidSubTransactionId;
-
-               if (auth_file_update_subid == mySubid)
-                       auth_file_update_subid = InvalidSubTransactionId;
-       }
-}
-
-
-/*
- * This trigger is fired whenever someone modifies pg_database, pg_authid
- * or pg_auth_members via general-purpose INSERT/UPDATE/DELETE commands.
- *
- * It is sufficient for this to be a STATEMENT trigger since we don't
- * care which individual rows changed. It doesn't much matter whether
- * it's a BEFORE or AFTER trigger.
- */
-Datum
-flatfile_update_trigger(PG_FUNCTION_ARGS)
-{
-       TriggerData *trigdata = (TriggerData *) fcinfo->context;
-
-       if (!CALLED_AS_TRIGGER(fcinfo))
-               elog(ERROR,
-                        "flatfile_update_trigger was not called by trigger manager");
-
-       if (RelationGetNamespace(trigdata->tg_relation) != PG_CATALOG_NAMESPACE)
-               elog(ERROR, "flatfile_update_trigger was called for wrong table");
-
-       switch (RelationGetRelid(trigdata->tg_relation))
-       {
-               case DatabaseRelationId:
-                       database_file_update_needed();
-                       break;
-               case AuthIdRelationId:
-               case AuthMemRelationId:
-                       auth_file_update_needed();
-                       break;
-               default:
-                       elog(ERROR, "flatfile_update_trigger was called for wrong table");
-                       break;
-       }
-
-       return PointerGetDatum(NULL);
-}
-
-
-/*
- * 2PC processing routine for COMMIT PREPARED case.
- *
- * (We don't have to do anything for ROLLBACK PREPARED.)
- */
-void
-flatfile_twophase_postcommit(TransactionId xid, uint16 info,
-                                                        void *recdata, uint32 len)
-{
-       /*
-        * Set flags to do the needed file updates at the end of my own current
-        * transaction.  (XXX this has some issues if my own transaction later
-        * rolls back, or if there is any significant delay before I commit.  OK
-        * for now because we disallow COMMIT PREPARED inside a transaction
-        * block.)
-        */
-       if (info & FF_BIT_DATABASE)
-               database_file_update_needed();
-       if (info & FF_BIT_AUTH)
-               auth_file_update_needed();
-}