]> granicus.if.org Git - postgresql/blob - src/backend/utils/init/flatfiles.c
Improve vacuum code to track minimum Xids per table instead of per database.
[postgresql] / src / backend / utils / init / flatfiles.c
1 /*-------------------------------------------------------------------------
2  *
3  * flatfiles.c
4  *        Routines for maintaining "flat file" images of the shared catalogs.
5  *
6  * We use flat files so that the postmaster and not-yet-fully-started
7  * backends can look at the contents of pg_database, pg_authid, and
8  * pg_auth_members for authentication purposes.  This module is
9  * responsible for keeping the flat-file images as nearly in sync with
10  * database reality as possible.
11  *
12  * The tricky part of the write_xxx_file() routines in this module is that
13  * they need to be able to operate in the context of the database startup
14  * process (which calls BuildFlatFiles()) as well as a normal backend.
15  * This means for example that we can't assume a fully functional relcache
16  * and we can't use syscaches at all.  The major restriction imposed by
17  * all that is that there's no way to read an out-of-line-toasted datum,
18  * because the tuptoaster.c code is not prepared to cope with such an
19  * environment.  Fortunately we can design the shared catalogs in such
20  * a way that this is OK.
21  *
22  *
23  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
24  * Portions Copyright (c) 1994, Regents of the University of California
25  *
26  * $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.19 2006/07/10 16:20:51 alvherre Exp $
27  *
28  *-------------------------------------------------------------------------
29  */
30 #include "postgres.h"
31
32 #include <sys/stat.h>
33 #include <unistd.h>
34
35 #include "access/heapam.h"
36 #include "access/twophase_rmgr.h"
37 #include "catalog/pg_auth_members.h"
38 #include "catalog/pg_authid.h"
39 #include "catalog/pg_database.h"
40 #include "catalog/pg_namespace.h"
41 #include "catalog/pg_tablespace.h"
42 #include "commands/trigger.h"
43 #include "miscadmin.h"
44 #include "storage/fd.h"
45 #include "storage/pmsignal.h"
46 #include "utils/acl.h"
47 #include "utils/builtins.h"
48 #include "utils/flatfiles.h"
49 #include "utils/resowner.h"
50 #include "utils/syscache.h"
51
52
53 /* Actual names of the flat files (within $PGDATA) */
54 #define DATABASE_FLAT_FILE      "global/pg_database"
55 #define AUTH_FLAT_FILE          "global/pg_auth"
56
57 /* Info bits in a flatfiles 2PC record */
58 #define FF_BIT_DATABASE 1
59 #define FF_BIT_AUTH             2
60
61
62 /*
63  * The need-to-update-files flags are SubTransactionIds that show
64  * what level of the subtransaction tree requested the update. To register
65  * an update, the subtransaction saves its own SubTransactionId in the flag,
66  * unless the value was already set to a valid SubTransactionId (which implies
67  * that it or a parent level has already requested the same).  If it aborts
68  * and the value is its SubTransactionId, it resets the flag to
69  * InvalidSubTransactionId. If it commits, it changes the value to its
70  * parent's SubTransactionId.  This way the value is propagated up to the
71  * top-level transaction, which will update the files if a valid
72  * SubTransactionId is seen at top-level commit.
73  */
74 static SubTransactionId database_file_update_subid = InvalidSubTransactionId;
75 static SubTransactionId auth_file_update_subid = InvalidSubTransactionId;
76
77
78 /*
79  * Mark flat database file as needing an update (because pg_database changed)
80  */
81 void
82 database_file_update_needed(void)
83 {
84         if (database_file_update_subid == InvalidSubTransactionId)
85                 database_file_update_subid = GetCurrentSubTransactionId();
86 }
87
88 /*
89  * Mark flat auth file as needing an update (because pg_authid or
90  * pg_auth_members changed)
91  */
92 void
93 auth_file_update_needed(void)
94 {
95         if (auth_file_update_subid == InvalidSubTransactionId)
96                 auth_file_update_subid = GetCurrentSubTransactionId();
97 }
98
99
100 /*
101  * database_getflatfilename --- get pathname of database file
102  *
103  * Note that result string is palloc'd, and should be freed by the caller.
104  * (This convention is not really needed anymore, since the relative path
105  * is fixed.)
106  */
107 char *
108 database_getflatfilename(void)
109 {
110         return pstrdup(DATABASE_FLAT_FILE);
111 }
112
113 /*
114  * auth_getflatfilename --- get pathname of auth file
115  *
116  * Note that result string is palloc'd, and should be freed by the caller.
117  * (This convention is not really needed anymore, since the relative path
118  * is fixed.)
119  */
120 char *
121 auth_getflatfilename(void)
122 {
123         return pstrdup(AUTH_FLAT_FILE);
124 }
125
126
127 /*
128  *      fputs_quote
129  *
130  *      Outputs string in quotes, with double-quotes duplicated.
131  *      We could use quote_ident(), but that expects a TEXT argument.
132  */
133 static void
134 fputs_quote(const char *str, FILE *fp)
135 {
136         fputc('"', fp);
137         while (*str)
138         {
139                 fputc(*str, fp);
140                 if (*str == '"')
141                         fputc('"', fp);
142                 str++;
143         }
144         fputc('"', fp);
145 }
146
147 /*
148  * name_okay
149  *
150  * We must disallow newlines in role names because
151  * hba.c's parser won't handle fields split across lines, even if quoted.
152  */
153 static bool
154 name_okay(const char *str)
155 {
156         int                     i;
157
158         i = strcspn(str, "\r\n");
159         return (str[i] == '\0');
160 }
161
162
163 /*
164  * write_database_file: update the flat database file
165  *
166  * A side effect is to determine the oldest database's datminxid
167  * so we can set or update the XID wrap limit.
168  */
169 static void
170 write_database_file(Relation drel)
171 {
172         char       *filename,
173                            *tempname;
174         int                     bufsize;
175         FILE       *fp;
176         mode_t          oumask;
177         HeapScanDesc scan;
178         HeapTuple       tuple;
179         NameData        oldest_datname;
180         TransactionId oldest_datminxid = InvalidTransactionId;
181
182         /*
183          * Create a temporary filename to be renamed later.  This prevents the
184          * backend from clobbering the flat file while the postmaster might be
185          * reading from it.
186          */
187         filename = database_getflatfilename();
188         bufsize = strlen(filename) + 12;
189         tempname = (char *) palloc(bufsize);
190         snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid);
191
192         oumask = umask((mode_t) 077);
193         fp = AllocateFile(tempname, "w");
194         umask(oumask);
195         if (fp == NULL)
196                 ereport(ERROR,
197                                 (errcode_for_file_access(),
198                                  errmsg("could not write to temporary file \"%s\": %m",
199                                                 tempname)));
200
201         /*
202          * Read pg_database and write the file.
203          */
204         scan = heap_beginscan(drel, SnapshotNow, 0, NULL);
205         while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
206         {
207                 Form_pg_database dbform = (Form_pg_database) GETSTRUCT(tuple);
208                 char       *datname;
209                 Oid                     datoid;
210                 Oid                     dattablespace;
211                 TransactionId datminxid,
212                                         datvacuumxid;
213
214                 datname = NameStr(dbform->datname);
215                 datoid = HeapTupleGetOid(tuple);
216                 dattablespace = dbform->dattablespace;
217                 datminxid = dbform->datminxid;
218                 datvacuumxid = dbform->datvacuumxid;
219
220                 /*
221                  * Identify the oldest datminxid, ignoring databases that are not
222                  * connectable (we assume they are safely frozen).      This must match
223                  * the logic in vac_truncate_clog() in vacuum.c.
224                  */
225                 if (dbform->datallowconn &&
226                         TransactionIdIsNormal(datminxid))
227                 {
228                         if (oldest_datminxid == InvalidTransactionId ||
229                                 TransactionIdPrecedes(datminxid, oldest_datminxid))
230                         {
231                                 oldest_datminxid = datminxid;
232                                 namestrcpy(&oldest_datname, datname);
233                         }
234                 }
235
236                 /*
237                  * Check for illegal characters in the database name.
238                  */
239                 if (!name_okay(datname))
240                 {
241                         ereport(LOG,
242                                         (errmsg("invalid database name \"%s\"", datname)));
243                         continue;
244                 }
245
246                 /*
247                  * The file format is: "dbname" oid tablespace minxid vacuumxid
248                  *
249                  * The xids are not needed for backend startup, but are of use to
250                  * autovacuum, and might also be helpful for forensic purposes.
251                  */
252                 fputs_quote(datname, fp);
253                 fprintf(fp, " %u %u %u %u\n",
254                                 datoid, dattablespace, datminxid, datvacuumxid);
255         }
256         heap_endscan(scan);
257
258         if (FreeFile(fp))
259                 ereport(ERROR,
260                                 (errcode_for_file_access(),
261                                  errmsg("could not write to temporary file \"%s\": %m",
262                                                 tempname)));
263
264         /*
265          * Rename the temp file to its final name, deleting the old flat file. We
266          * expect that rename(2) is an atomic action.
267          */
268         if (rename(tempname, filename))
269                 ereport(ERROR,
270                                 (errcode_for_file_access(),
271                                  errmsg("could not rename file \"%s\" to \"%s\": %m",
272                                                 tempname, filename)));
273
274         /*
275          * Set the transaction ID wrap limit using the oldest datminxid
276          */
277         if (oldest_datminxid != InvalidTransactionId)
278                 SetTransactionIdLimit(oldest_datminxid, &oldest_datname);
279 }
280
281
282 /*
283  * Support for write_auth_file
284  *
285  * The format for the flat auth file is
286  *              "rolename" "password" "validuntil" "memberof" "memberof" ...
287  * Only roles that are marked rolcanlogin are entered into the auth file.
288  * Each role's line lists all the roles (groups) of which it is directly
289  * or indirectly a member, except for itself.
290  *
291  * The postmaster expects the file to be sorted by rolename.  There is not
292  * any special ordering of the membership lists.
293  *
294  * To construct this information, we scan pg_authid and pg_auth_members,
295  * and build data structures in-memory before writing the file.
296  */
297
298 typedef struct
299 {
300         Oid                     roleid;
301         bool            rolcanlogin;
302         char       *rolname;
303         char       *rolpassword;
304         char       *rolvaliduntil;
305         List       *member_of;
306 } auth_entry;
307
308 typedef struct
309 {
310         Oid                     roleid;
311         Oid                     memberid;
312 } authmem_entry;
313
314
315 /* qsort comparator for sorting auth_entry array by roleid */
316 static int
317 oid_compar(const void *a, const void *b)
318 {
319         const auth_entry *a_auth = (const auth_entry *) a;
320         const auth_entry *b_auth = (const auth_entry *) b;
321
322         if (a_auth->roleid < b_auth->roleid)
323                 return -1;
324         if (a_auth->roleid > b_auth->roleid)
325                 return 1;
326         return 0;
327 }
328
329 /* qsort comparator for sorting auth_entry array by rolname */
330 static int
331 name_compar(const void *a, const void *b)
332 {
333         const auth_entry *a_auth = (const auth_entry *) a;
334         const auth_entry *b_auth = (const auth_entry *) b;
335
336         return strcmp(a_auth->rolname, b_auth->rolname);
337 }
338
339 /* qsort comparator for sorting authmem_entry array by memberid */
340 static int
341 mem_compar(const void *a, const void *b)
342 {
343         const authmem_entry *a_auth = (const authmem_entry *) a;
344         const authmem_entry *b_auth = (const authmem_entry *) b;
345
346         if (a_auth->memberid < b_auth->memberid)
347                 return -1;
348         if (a_auth->memberid > b_auth->memberid)
349                 return 1;
350         return 0;
351 }
352
353
354 /*
355  * write_auth_file: update the flat auth file
356  */
357 static void
358 write_auth_file(Relation rel_authid, Relation rel_authmem)
359 {
360         char       *filename,
361                            *tempname;
362         int                     bufsize;
363         BlockNumber totalblocks;
364         FILE       *fp;
365         mode_t          oumask;
366         HeapScanDesc scan;
367         HeapTuple       tuple;
368         int                     curr_role = 0;
369         int                     total_roles = 0;
370         int                     curr_mem = 0;
371         int                     total_mem = 0;
372         int                     est_rows;
373         auth_entry *auth_info;
374         authmem_entry *authmem_info;
375
376         /*
377          * Create a temporary filename to be renamed later.  This prevents the
378          * backend from clobbering the flat file while the postmaster might be
379          * reading from it.
380          */
381         filename = auth_getflatfilename();
382         bufsize = strlen(filename) + 12;
383         tempname = (char *) palloc(bufsize);
384         snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid);
385
386         oumask = umask((mode_t) 077);
387         fp = AllocateFile(tempname, "w");
388         umask(oumask);
389         if (fp == NULL)
390                 ereport(ERROR,
391                                 (errcode_for_file_access(),
392                                  errmsg("could not write to temporary file \"%s\": %m",
393                                                 tempname)));
394
395         /*
396          * Read pg_authid and fill temporary data structures.  Note we must read
397          * all roles, even those without rolcanlogin.
398          */
399         totalblocks = RelationGetNumberOfBlocks(rel_authid);
400         totalblocks = totalblocks ? totalblocks : 1;
401         est_rows = totalblocks * (BLCKSZ / (sizeof(HeapTupleHeaderData) + sizeof(FormData_pg_authid)));
402         auth_info = (auth_entry *) palloc(est_rows * sizeof(auth_entry));
403
404         scan = heap_beginscan(rel_authid, SnapshotNow, 0, NULL);
405         while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
406         {
407                 Form_pg_authid aform = (Form_pg_authid) GETSTRUCT(tuple);
408                 HeapTupleHeader tup = tuple->t_data;
409                 char       *tp;                 /* ptr to tuple data */
410                 long            off;            /* offset in tuple data */
411                 bits8      *bp = tup->t_bits;   /* ptr to null bitmask in tuple */
412                 Datum           datum;
413
414                 if (curr_role >= est_rows)
415                 {
416                         est_rows *= 2;
417                         auth_info = (auth_entry *)
418                                 repalloc(auth_info, est_rows * sizeof(auth_entry));
419                 }
420
421                 auth_info[curr_role].roleid = HeapTupleGetOid(tuple);
422                 auth_info[curr_role].rolcanlogin = aform->rolcanlogin;
423                 auth_info[curr_role].rolname = pstrdup(NameStr(aform->rolname));
424                 auth_info[curr_role].member_of = NIL;
425
426                 /*
427                  * We can't use heap_getattr() here because during startup we will not
428                  * have any tupdesc for pg_authid.      Fortunately it's not too hard to
429                  * work around this.  rolpassword is the first possibly-null field so
430                  * we can compute its offset directly.
431                  */
432                 tp = (char *) tup + tup->t_hoff;
433                 off = offsetof(FormData_pg_authid, rolpassword);
434
435                 if (HeapTupleHasNulls(tuple) &&
436                         att_isnull(Anum_pg_authid_rolpassword - 1, bp))
437                 {
438                         /* passwd is null, emit as an empty string */
439                         auth_info[curr_role].rolpassword = pstrdup("");
440                 }
441                 else
442                 {
443                         /* assume passwd is pass-by-ref */
444                         datum = PointerGetDatum(tp + off);
445
446                         /*
447                          * The password probably shouldn't ever be out-of-line toasted; if
448                          * it is, ignore it, since we can't handle that in startup mode.
449                          */
450                         if (VARATT_IS_EXTERNAL(DatumGetPointer(datum)))
451                                 auth_info[curr_role].rolpassword = pstrdup("");
452                         else
453                                 auth_info[curr_role].rolpassword = DatumGetCString(DirectFunctionCall1(textout, datum));
454
455                         /* assume passwd has attlen -1 */
456                         off = att_addlength(off, -1, tp + off);
457                 }
458
459                 if (HeapTupleHasNulls(tuple) &&
460                         att_isnull(Anum_pg_authid_rolvaliduntil - 1, bp))
461                 {
462                         /* rolvaliduntil is null, emit as an empty string */
463                         auth_info[curr_role].rolvaliduntil = pstrdup("");
464                 }
465                 else
466                 {
467                         /*
468                          * rolvaliduntil is timestamptz, which we assume is double
469                          * alignment and pass-by-reference.
470                          */
471                         off = att_align(off, 'd');
472                         datum = PointerGetDatum(tp + off);
473                         auth_info[curr_role].rolvaliduntil = DatumGetCString(DirectFunctionCall1(timestamptz_out, datum));
474                 }
475
476                 /*
477                  * Check for illegal characters in the user name and password.
478                  */
479                 if (!name_okay(auth_info[curr_role].rolname))
480                 {
481                         ereport(LOG,
482                                         (errmsg("invalid role name \"%s\"",
483                                                         auth_info[curr_role].rolname)));
484                         continue;
485                 }
486                 if (!name_okay(auth_info[curr_role].rolpassword))
487                 {
488                         ereport(LOG,
489                                         (errmsg("invalid role password \"%s\"",
490                                                         auth_info[curr_role].rolpassword)));
491                         continue;
492                 }
493
494                 curr_role++;
495                 total_roles++;
496         }
497         heap_endscan(scan);
498
499         /*
500          * Read pg_auth_members into temporary data structure, too
501          */
502         totalblocks = RelationGetNumberOfBlocks(rel_authmem);
503         totalblocks = totalblocks ? totalblocks : 1;
504         est_rows = totalblocks * (BLCKSZ / (sizeof(HeapTupleHeaderData) + sizeof(FormData_pg_auth_members)));
505         authmem_info = (authmem_entry *) palloc(est_rows * sizeof(authmem_entry));
506
507         scan = heap_beginscan(rel_authmem, SnapshotNow, 0, NULL);
508         while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
509         {
510                 Form_pg_auth_members memform = (Form_pg_auth_members) GETSTRUCT(tuple);
511
512                 if (curr_mem >= est_rows)
513                 {
514                         est_rows *= 2;
515                         authmem_info = (authmem_entry *)
516                                 repalloc(authmem_info, est_rows * sizeof(authmem_entry));
517                 }
518
519                 authmem_info[curr_mem].roleid = memform->roleid;
520                 authmem_info[curr_mem].memberid = memform->member;
521                 curr_mem++;
522                 total_mem++;
523         }
524         heap_endscan(scan);
525
526         /*
527          * Search for memberships.      We can skip all this if pg_auth_members is
528          * empty.
529          */
530         if (total_mem > 0)
531         {
532                 /*
533                  * Sort auth_info by roleid and authmem_info by memberid.
534                  */
535                 qsort(auth_info, total_roles, sizeof(auth_entry), oid_compar);
536                 qsort(authmem_info, total_mem, sizeof(authmem_entry), mem_compar);
537
538                 /*
539                  * For each role, find what it belongs to.
540                  */
541                 for (curr_role = 0; curr_role < total_roles; curr_role++)
542                 {
543                         List       *roles_list;
544                         List       *roles_names_list = NIL;
545                         ListCell   *mem;
546
547                         /* We can skip this for non-login roles */
548                         if (!auth_info[curr_role].rolcanlogin)
549                                 continue;
550
551                         /*
552                          * This search algorithm is the same as in is_member_of_role; we
553                          * are just working with a different input data structure.
554                          */
555                         roles_list = list_make1_oid(auth_info[curr_role].roleid);
556
557                         foreach(mem, roles_list)
558                         {
559                                 authmem_entry key;
560                                 authmem_entry *found_mem;
561                                 int                     first_found,
562                                                         last_found,
563                                                         i;
564
565                                 key.memberid = lfirst_oid(mem);
566                                 found_mem = bsearch(&key, authmem_info, total_mem,
567                                                                         sizeof(authmem_entry), mem_compar);
568                                 if (!found_mem)
569                                         continue;
570
571                                 /*
572                                  * bsearch found a match for us; but if there were multiple
573                                  * matches it could have found any one of them. Locate first
574                                  * and last match.
575                                  */
576                                 first_found = last_found = (found_mem - authmem_info);
577                                 while (first_found > 0 &&
578                                            mem_compar(&key, &authmem_info[first_found - 1]) == 0)
579                                         first_found--;
580                                 while (last_found + 1 < total_mem &&
581                                            mem_compar(&key, &authmem_info[last_found + 1]) == 0)
582                                         last_found++;
583
584                                 /*
585                                  * Now add all the new roles to roles_list.
586                                  */
587                                 for (i = first_found; i <= last_found; i++)
588                                         roles_list = list_append_unique_oid(roles_list,
589                                                                                                          authmem_info[i].roleid);
590                         }
591
592                         /*
593                          * Convert list of role Oids to list of role names. We must do
594                          * this before re-sorting auth_info.
595                          *
596                          * We skip the first list element (curr_role itself) since there
597                          * is no point in writing that a role is a member of itself.
598                          */
599                         for_each_cell(mem, lnext(list_head(roles_list)))
600                         {
601                                 auth_entry      key_auth;
602                                 auth_entry *found_role;
603
604                                 key_auth.roleid = lfirst_oid(mem);
605                                 found_role = bsearch(&key_auth, auth_info, total_roles,
606                                                                          sizeof(auth_entry), oid_compar);
607                                 if (found_role) /* paranoia */
608                                         roles_names_list = lappend(roles_names_list,
609                                                                                            found_role->rolname);
610                         }
611                         auth_info[curr_role].member_of = roles_names_list;
612                         list_free(roles_list);
613                 }
614         }
615
616         /*
617          * Now sort auth_info into rolname order for output, and write the file.
618          */
619         qsort(auth_info, total_roles, sizeof(auth_entry), name_compar);
620
621         for (curr_role = 0; curr_role < total_roles; curr_role++)
622         {
623                 auth_entry *arole = &auth_info[curr_role];
624
625                 if (arole->rolcanlogin)
626                 {
627                         ListCell   *mem;
628
629                         fputs_quote(arole->rolname, fp);
630                         fputs(" ", fp);
631                         fputs_quote(arole->rolpassword, fp);
632                         fputs(" ", fp);
633                         fputs_quote(arole->rolvaliduntil, fp);
634
635                         foreach(mem, arole->member_of)
636                         {
637                                 fputs(" ", fp);
638                                 fputs_quote((char *) lfirst(mem), fp);
639                         }
640
641                         fputs("\n", fp);
642                 }
643         }
644
645         if (FreeFile(fp))
646                 ereport(ERROR,
647                                 (errcode_for_file_access(),
648                                  errmsg("could not write to temporary file \"%s\": %m",
649                                                 tempname)));
650
651         /*
652          * Rename the temp file to its final name, deleting the old flat file. We
653          * expect that rename(2) is an atomic action.
654          */
655         if (rename(tempname, filename))
656                 ereport(ERROR,
657                                 (errcode_for_file_access(),
658                                  errmsg("could not rename file \"%s\" to \"%s\": %m",
659                                                 tempname, filename)));
660 }
661
662
663 /*
664  * This routine is called once during database startup, after completing
665  * WAL replay if needed.  Its purpose is to sync the flat files with the
666  * current state of the database tables.  This is particularly important
667  * during PITR operation, since the flat files will come from the
668  * base backup which may be far out of sync with the current state.
669  *
670  * In theory we could skip rebuilding the flat files if no WAL replay
671  * occurred, but it seems best to just do it always.  We have to
672  * scan pg_database to compute the XID wrap limit anyway.  Also, this
673  * policy means we need not force initdb to change the format of the
674  * flat files.
675  *
676  * In a standalone backend we pass database_only = true to skip processing
677  * the auth file.  We won't need it, and building it could fail if there's
678  * something corrupt in the authid/authmem catalogs.
679  */
680 void
681 BuildFlatFiles(bool database_only)
682 {
683         ResourceOwner owner;
684         RelFileNode rnode;
685         Relation        rel_db,
686                                 rel_authid,
687                                 rel_authmem;
688
689         /*
690          * We don't have any hope of running a real relcache, but we can use the
691          * same fake-relcache facility that WAL replay uses.
692          */
693         XLogInitRelationCache();
694
695         /* Need a resowner to keep the heapam and buffer code happy */
696         owner = ResourceOwnerCreate(NULL, "BuildFlatFiles");
697         CurrentResourceOwner = owner;
698
699         /* hard-wired path to pg_database */
700         rnode.spcNode = GLOBALTABLESPACE_OID;
701         rnode.dbNode = 0;
702         rnode.relNode = DatabaseRelationId;
703
704         /* No locking is needed because no one else is alive yet */
705         rel_db = XLogOpenRelation(rnode);
706         write_database_file(rel_db);
707
708         if (!database_only)
709         {
710                 /* hard-wired path to pg_authid */
711                 rnode.spcNode = GLOBALTABLESPACE_OID;
712                 rnode.dbNode = 0;
713                 rnode.relNode = AuthIdRelationId;
714                 rel_authid = XLogOpenRelation(rnode);
715
716                 /* hard-wired path to pg_auth_members */
717                 rnode.spcNode = GLOBALTABLESPACE_OID;
718                 rnode.dbNode = 0;
719                 rnode.relNode = AuthMemRelationId;
720                 rel_authmem = XLogOpenRelation(rnode);
721
722                 write_auth_file(rel_authid, rel_authmem);
723         }
724
725         CurrentResourceOwner = NULL;
726         ResourceOwnerDelete(owner);
727
728         XLogCloseRelationCache();
729 }
730
731
732 /*
733  * This routine is called during transaction commit or abort.
734  *
735  * On commit, if we've written any of the critical database tables during
736  * the current transaction, update the flat files and signal the postmaster.
737  *
738  * On abort, just reset the static flags so we don't try to do it on the
739  * next successful commit.
740  *
741  * NB: this should be the last step before actual transaction commit.
742  * If any error aborts the transaction after we run this code, the postmaster
743  * will still have received and cached the changed data; so minimize the
744  * window for such problems.
745  */
746 void
747 AtEOXact_UpdateFlatFiles(bool isCommit)
748 {
749         Relation        drel = NULL;
750         Relation        arel = NULL;
751         Relation        mrel = NULL;
752
753         if (database_file_update_subid == InvalidSubTransactionId &&
754                 auth_file_update_subid == InvalidSubTransactionId)
755                 return;                                 /* nothing to do */
756
757         if (!isCommit)
758         {
759                 database_file_update_subid = InvalidSubTransactionId;
760                 auth_file_update_subid = InvalidSubTransactionId;
761                 return;
762         }
763
764         /*
765          * Advance command counter to be certain we see all effects of the current
766          * transaction.
767          */
768         CommandCounterIncrement();
769
770         /*
771          * Open and lock the needed catalog(s).
772          *
773          * Even though we only need AccessShareLock, this could theoretically fail
774          * due to deadlock.  In practice, however, our transaction already holds
775          * RowExclusiveLock or better (it couldn't have updated the catalog
776          * without such a lock).  This implies that dbcommands.c and other places
777          * that force flat-file updates must not follow the common practice of
778          * dropping catalog locks before commit.
779          */
780         if (database_file_update_subid != InvalidSubTransactionId)
781                 drel = heap_open(DatabaseRelationId, AccessShareLock);
782
783         if (auth_file_update_subid != InvalidSubTransactionId)
784         {
785                 arel = heap_open(AuthIdRelationId, AccessShareLock);
786                 mrel = heap_open(AuthMemRelationId, AccessShareLock);
787         }
788
789         /*
790          * Obtain special locks to ensure that two transactions don't try to write
791          * the same flat file concurrently.  Quite aside from any direct risks of
792          * corrupted output, the winning writer probably wouldn't have seen the
793          * other writer's updates.  By taking a lock and holding it till commit,
794          * we ensure that whichever updater goes second will see the other
795          * updater's changes as committed, and thus the final state of the file
796          * will include all updates.
797          *
798          * We use a lock on "database 0" to protect writing the pg_database flat
799          * file, and a lock on "role 0" to protect the auth file.  This is a bit
800          * ugly but it's not worth inventing any more-general convention.  (Any
801          * two locktags that are never used for anything else would do.)
802          *
803          * This is safe against deadlock as long as these are the very last locks
804          * acquired during the transaction.
805          */
806         if (database_file_update_subid != InvalidSubTransactionId)
807                 LockSharedObject(DatabaseRelationId, InvalidOid, 0,
808                                                  AccessExclusiveLock);
809
810         if (auth_file_update_subid != InvalidSubTransactionId)
811                 LockSharedObject(AuthIdRelationId, InvalidOid, 0,
812                                                  AccessExclusiveLock);
813
814         /* Okay to write the files */
815         if (database_file_update_subid != InvalidSubTransactionId)
816         {
817                 database_file_update_subid = InvalidSubTransactionId;
818                 write_database_file(drel);
819                 heap_close(drel, NoLock);
820         }
821
822         if (auth_file_update_subid != InvalidSubTransactionId)
823         {
824                 auth_file_update_subid = InvalidSubTransactionId;
825                 write_auth_file(arel, mrel);
826                 heap_close(arel, NoLock);
827                 heap_close(mrel, NoLock);
828         }
829
830         /*
831          * Signal the postmaster to reload its caches.
832          */
833         SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE);
834 }
835
836
837 /*
838  * This routine is called during transaction prepare.
839  *
840  * Record which files need to be refreshed if this transaction later
841  * commits.
842  *
843  * Note: it's OK to clear the flags immediately, since if the PREPARE fails
844  * further on, we'd only reset the flags anyway. So there's no need for a
845  * separate PostPrepare call.
846  */
847 void
848 AtPrepare_UpdateFlatFiles(void)
849 {
850         uint16          info = 0;
851
852         if (database_file_update_subid != InvalidSubTransactionId)
853         {
854                 database_file_update_subid = InvalidSubTransactionId;
855                 info |= FF_BIT_DATABASE;
856         }
857         if (auth_file_update_subid != InvalidSubTransactionId)
858         {
859                 auth_file_update_subid = InvalidSubTransactionId;
860                 info |= FF_BIT_AUTH;
861         }
862         if (info != 0)
863                 RegisterTwoPhaseRecord(TWOPHASE_RM_FLATFILES_ID, info,
864                                                            NULL, 0);
865 }
866
867
868 /*
869  * AtEOSubXact_UpdateFlatFiles
870  *
871  * Called at subtransaction end, this routine resets or updates the
872  * need-to-update-files flags.
873  */
874 void
875 AtEOSubXact_UpdateFlatFiles(bool isCommit,
876                                                         SubTransactionId mySubid,
877                                                         SubTransactionId parentSubid)
878 {
879         if (isCommit)
880         {
881                 if (database_file_update_subid == mySubid)
882                         database_file_update_subid = parentSubid;
883
884                 if (auth_file_update_subid == mySubid)
885                         auth_file_update_subid = parentSubid;
886         }
887         else
888         {
889                 if (database_file_update_subid == mySubid)
890                         database_file_update_subid = InvalidSubTransactionId;
891
892                 if (auth_file_update_subid == mySubid)
893                         auth_file_update_subid = InvalidSubTransactionId;
894         }
895 }
896
897
898 /*
899  * This trigger is fired whenever someone modifies pg_database, pg_authid
900  * or pg_auth_members via general-purpose INSERT/UPDATE/DELETE commands.
901  *
902  * It is sufficient for this to be a STATEMENT trigger since we don't
903  * care which individual rows changed.  It doesn't much matter whether
904  * it's a BEFORE or AFTER trigger.
905  */
906 Datum
907 flatfile_update_trigger(PG_FUNCTION_ARGS)
908 {
909         TriggerData *trigdata = (TriggerData *) fcinfo->context;
910
911         if (!CALLED_AS_TRIGGER(fcinfo))
912                 elog(ERROR,
913                          "flatfile_update_trigger was not called by trigger manager");
914
915         if (RelationGetNamespace(trigdata->tg_relation) != PG_CATALOG_NAMESPACE)
916                 elog(ERROR, "flatfile_update_trigger was called for wrong table");
917
918         switch (RelationGetRelid(trigdata->tg_relation))
919         {
920                 case DatabaseRelationId:
921                         database_file_update_needed();
922                         break;
923                 case AuthIdRelationId:
924                 case AuthMemRelationId:
925                         auth_file_update_needed();
926                         break;
927                 default:
928                         elog(ERROR, "flatfile_update_trigger was called for wrong table");
929                         break;
930         }
931
932         return PointerGetDatum(NULL);
933 }
934
935
936 /*
937  * 2PC processing routine for COMMIT PREPARED case.
938  *
939  * (We don't have to do anything for ROLLBACK PREPARED.)
940  */
941 void
942 flatfile_twophase_postcommit(TransactionId xid, uint16 info,
943                                                          void *recdata, uint32 len)
944 {
945         /*
946          * Set flags to do the needed file updates at the end of my own current
947          * transaction.  (XXX this has some issues if my own transaction later
948          * rolls back, or if there is any significant delay before I commit.  OK
949          * for now because we disallow COMMIT PREPARED inside a transaction
950          * block.)
951          */
952         if (info & FF_BIT_DATABASE)
953                 database_file_update_needed();
954         if (info & FF_BIT_AUTH)
955                 auth_file_update_needed();
956 }