Query in SQL function still not schema-safe; add a couple
[PostgreSQL.git] / src / backend / utils / init / flatfiles.c
blob9dbc53c159d4883d902b30dd7abe2442647cf8eb
1 /*-------------------------------------------------------------------------
3 * flatfiles.c
4 * Routines for maintaining "flat file" images of the shared catalogs.
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.
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.
23 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
24 * Portions Copyright (c) 1994, Regents of the University of California
26 * $PostgreSQL$
28 *-------------------------------------------------------------------------
30 #include "postgres.h"
32 #include <sys/stat.h>
33 #include <unistd.h>
35 #include "access/heapam.h"
36 #include "access/transam.h"
37 #include "access/twophase_rmgr.h"
38 #include "access/xact.h"
39 #include "access/xlogutils.h"
40 #include "catalog/catalog.h"
41 #include "catalog/pg_auth_members.h"
42 #include "catalog/pg_authid.h"
43 #include "catalog/pg_database.h"
44 #include "catalog/pg_namespace.h"
45 #include "catalog/pg_tablespace.h"
46 #include "commands/trigger.h"
47 #include "miscadmin.h"
48 #include "storage/bufmgr.h"
49 #include "storage/fd.h"
50 #include "storage/lmgr.h"
51 #include "storage/pmsignal.h"
52 #include "utils/builtins.h"
53 #include "utils/flatfiles.h"
54 #include "utils/relcache.h"
55 #include "utils/resowner.h"
56 #include "utils/tqual.h"
59 /* Actual names of the flat files (within $PGDATA) */
60 #define DATABASE_FLAT_FILE "global/pg_database"
61 #define AUTH_FLAT_FILE "global/pg_auth"
63 /* Info bits in a flatfiles 2PC record */
64 #define FF_BIT_DATABASE 1
65 #define FF_BIT_AUTH 2
69 * The need-to-update-files flags are SubTransactionIds that show
70 * what level of the subtransaction tree requested the update. To register
71 * an update, the subtransaction saves its own SubTransactionId in the flag,
72 * unless the value was already set to a valid SubTransactionId (which implies
73 * that it or a parent level has already requested the same). If it aborts
74 * and the value is its SubTransactionId, it resets the flag to
75 * InvalidSubTransactionId. If it commits, it changes the value to its
76 * parent's SubTransactionId. This way the value is propagated up to the
77 * top-level transaction, which will update the files if a valid
78 * SubTransactionId is seen at top-level commit.
80 static SubTransactionId database_file_update_subid = InvalidSubTransactionId;
81 static SubTransactionId auth_file_update_subid = InvalidSubTransactionId;
85 * Mark flat database file as needing an update (because pg_database changed)
87 void
88 database_file_update_needed(void)
90 if (database_file_update_subid == InvalidSubTransactionId)
91 database_file_update_subid = GetCurrentSubTransactionId();
95 * Mark flat auth file as needing an update (because pg_authid or
96 * pg_auth_members changed)
98 void
99 auth_file_update_needed(void)
101 if (auth_file_update_subid == InvalidSubTransactionId)
102 auth_file_update_subid = GetCurrentSubTransactionId();
107 * database_getflatfilename --- get pathname of database file
109 * Note that result string is palloc'd, and should be freed by the caller.
110 * (This convention is not really needed anymore, since the relative path
111 * is fixed.)
113 char *
114 database_getflatfilename(void)
116 return pstrdup(DATABASE_FLAT_FILE);
120 * auth_getflatfilename --- get pathname of auth file
122 * Note that result string is palloc'd, and should be freed by the caller.
123 * (This convention is not really needed anymore, since the relative path
124 * is fixed.)
126 char *
127 auth_getflatfilename(void)
129 return pstrdup(AUTH_FLAT_FILE);
134 * fputs_quote
136 * Outputs string in quotes, with double-quotes duplicated.
137 * We could use quote_ident(), but that expects a TEXT argument.
139 static void
140 fputs_quote(const char *str, FILE *fp)
142 fputc('"', fp);
143 while (*str)
145 fputc(*str, fp);
146 if (*str == '"')
147 fputc('"', fp);
148 str++;
150 fputc('"', fp);
154 * name_okay
156 * We must disallow newlines in role names because
157 * hba.c's parser won't handle fields split across lines, even if quoted.
159 static bool
160 name_okay(const char *str)
162 int i;
164 i = strcspn(str, "\r\n");
165 return (str[i] == '\0');
170 * write_database_file: update the flat database file
172 * A side effect is to determine the oldest database's datfrozenxid
173 * so we can set or update the XID wrap limit.
175 * Also, if "startup" is true, we tell relcache.c to clear out the relcache
176 * init file in each database. That's a bit nonmodular, but scanning
177 * pg_database twice during system startup seems too high a price for keeping
178 * things better separated.
180 static void
181 write_database_file(Relation drel, bool startup)
183 char *filename,
184 *tempname;
185 int bufsize;
186 FILE *fp;
187 mode_t oumask;
188 HeapScanDesc scan;
189 HeapTuple tuple;
190 NameData oldest_datname;
191 TransactionId oldest_datfrozenxid = InvalidTransactionId;
194 * Create a temporary filename to be renamed later. This prevents the
195 * backend from clobbering the flat file while the postmaster might be
196 * reading from it.
198 filename = database_getflatfilename();
199 bufsize = strlen(filename) + 12;
200 tempname = (char *) palloc(bufsize);
201 snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid);
203 oumask = umask((mode_t) 077);
204 fp = AllocateFile(tempname, "w");
205 umask(oumask);
206 if (fp == NULL)
207 ereport(ERROR,
208 (errcode_for_file_access(),
209 errmsg("could not write to temporary file \"%s\": %m",
210 tempname)));
213 * Read pg_database and write the file.
215 scan = heap_beginscan(drel, SnapshotNow, 0, NULL);
216 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
218 Form_pg_database dbform = (Form_pg_database) GETSTRUCT(tuple);
219 char *datname;
220 Oid datoid;
221 Oid dattablespace;
222 TransactionId datfrozenxid;
224 datname = NameStr(dbform->datname);
225 datoid = HeapTupleGetOid(tuple);
226 dattablespace = dbform->dattablespace;
227 datfrozenxid = dbform->datfrozenxid;
230 * Identify the oldest datfrozenxid. This must match the logic in
231 * vac_truncate_clog() in vacuum.c.
233 if (TransactionIdIsNormal(datfrozenxid))
235 if (oldest_datfrozenxid == InvalidTransactionId ||
236 TransactionIdPrecedes(datfrozenxid, oldest_datfrozenxid))
238 oldest_datfrozenxid = datfrozenxid;
239 namestrcpy(&oldest_datname, datname);
244 * Check for illegal characters in the database name.
246 if (!name_okay(datname))
248 ereport(LOG,
249 (errmsg("invalid database name \"%s\"", datname)));
250 continue;
254 * The file format is: "dbname" oid tablespace frozenxid
256 * The xids are not needed for backend startup, but are of use to
257 * autovacuum, and might also be helpful for forensic purposes.
259 fputs_quote(datname, fp);
260 fprintf(fp, " %u %u %u\n",
261 datoid, dattablespace, datfrozenxid);
264 * Also clear relcache init file for each DB if starting up.
266 if (startup)
268 char *dbpath = GetDatabasePath(datoid, dattablespace);
270 RelationCacheInitFileRemove(dbpath);
271 pfree(dbpath);
274 heap_endscan(scan);
276 if (FreeFile(fp))
277 ereport(ERROR,
278 (errcode_for_file_access(),
279 errmsg("could not write to temporary file \"%s\": %m",
280 tempname)));
283 * Rename the temp file to its final name, deleting the old flat file. We
284 * expect that rename(2) is an atomic action.
286 if (rename(tempname, filename))
287 ereport(ERROR,
288 (errcode_for_file_access(),
289 errmsg("could not rename file \"%s\" to \"%s\": %m",
290 tempname, filename)));
293 * Set the transaction ID wrap limit using the oldest datfrozenxid
295 if (oldest_datfrozenxid != InvalidTransactionId)
296 SetTransactionIdLimit(oldest_datfrozenxid, &oldest_datname);
301 * Support for write_auth_file
303 * The format for the flat auth file is
304 * "rolename" "password" "validuntil" "memberof" "memberof" ...
305 * Each role's line lists all the roles (groups) of which it is directly
306 * or indirectly a member, except for itself.
308 * The postmaster expects the file to be sorted by rolename. There is not
309 * any special ordering of the membership lists.
311 * To construct this information, we scan pg_authid and pg_auth_members,
312 * and build data structures in-memory before writing the file.
315 typedef struct
317 Oid roleid;
318 char *rolname;
319 char *rolpassword;
320 char *rolvaliduntil;
321 List *member_of;
322 } auth_entry;
324 typedef struct
326 Oid roleid;
327 Oid memberid;
328 } authmem_entry;
331 /* qsort comparator for sorting auth_entry array by roleid */
332 static int
333 oid_compar(const void *a, const void *b)
335 const auth_entry *a_auth = (const auth_entry *) a;
336 const auth_entry *b_auth = (const auth_entry *) b;
338 if (a_auth->roleid < b_auth->roleid)
339 return -1;
340 if (a_auth->roleid > b_auth->roleid)
341 return 1;
342 return 0;
345 /* qsort comparator for sorting auth_entry array by rolname */
346 static int
347 name_compar(const void *a, const void *b)
349 const auth_entry *a_auth = (const auth_entry *) a;
350 const auth_entry *b_auth = (const auth_entry *) b;
352 return strcmp(a_auth->rolname, b_auth->rolname);
355 /* qsort comparator for sorting authmem_entry array by memberid */
356 static int
357 mem_compar(const void *a, const void *b)
359 const authmem_entry *a_auth = (const authmem_entry *) a;
360 const authmem_entry *b_auth = (const authmem_entry *) b;
362 if (a_auth->memberid < b_auth->memberid)
363 return -1;
364 if (a_auth->memberid > b_auth->memberid)
365 return 1;
366 return 0;
371 * write_auth_file: update the flat auth file
373 static void
374 write_auth_file(Relation rel_authid, Relation rel_authmem)
376 char *filename,
377 *tempname;
378 int bufsize;
379 BlockNumber totalblocks;
380 FILE *fp;
381 mode_t oumask;
382 HeapScanDesc scan;
383 HeapTuple tuple;
384 int curr_role = 0;
385 int total_roles = 0;
386 int curr_mem = 0;
387 int total_mem = 0;
388 int est_rows;
389 auth_entry *auth_info;
390 authmem_entry *authmem_info;
393 * Create a temporary filename to be renamed later. This prevents the
394 * backend from clobbering the flat file while the postmaster might be
395 * reading from it.
397 filename = auth_getflatfilename();
398 bufsize = strlen(filename) + 12;
399 tempname = (char *) palloc(bufsize);
400 snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid);
402 oumask = umask((mode_t) 077);
403 fp = AllocateFile(tempname, "w");
404 umask(oumask);
405 if (fp == NULL)
406 ereport(ERROR,
407 (errcode_for_file_access(),
408 errmsg("could not write to temporary file \"%s\": %m",
409 tempname)));
412 * Read pg_authid and fill temporary data structures.
414 totalblocks = RelationGetNumberOfBlocks(rel_authid);
415 totalblocks = totalblocks ? totalblocks : 1;
416 est_rows = totalblocks * (BLCKSZ / (sizeof(HeapTupleHeaderData) + sizeof(FormData_pg_authid)));
417 auth_info = (auth_entry *) palloc(est_rows * sizeof(auth_entry));
419 scan = heap_beginscan(rel_authid, SnapshotNow, 0, NULL);
420 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
422 Form_pg_authid aform = (Form_pg_authid) GETSTRUCT(tuple);
423 HeapTupleHeader tup = tuple->t_data;
424 char *tp; /* ptr to tuple data */
425 long off; /* offset in tuple data */
426 bits8 *bp = tup->t_bits; /* ptr to null bitmask in tuple */
427 Datum datum;
429 if (curr_role >= est_rows)
431 est_rows *= 2;
432 auth_info = (auth_entry *)
433 repalloc(auth_info, est_rows * sizeof(auth_entry));
436 auth_info[curr_role].roleid = HeapTupleGetOid(tuple);
437 auth_info[curr_role].rolname = pstrdup(NameStr(aform->rolname));
438 auth_info[curr_role].member_of = NIL;
441 * We can't use heap_getattr() here because during startup we will not
442 * have any tupdesc for pg_authid. Fortunately it's not too hard to
443 * work around this. rolpassword is the first possibly-null field so
444 * we can compute its offset directly. Note that this only works
445 * reliably because the preceding field (rolconnlimit) is int4, and
446 * therefore rolpassword is always 4-byte-aligned, and will be at the
447 * same offset no matter whether it uses 1-byte or 4-byte header.
449 tp = (char *) tup + tup->t_hoff;
450 off = offsetof(FormData_pg_authid, rolpassword);
452 if (HeapTupleHasNulls(tuple) &&
453 att_isnull(Anum_pg_authid_rolpassword - 1, bp))
455 /* passwd is null, emit as an empty string */
456 auth_info[curr_role].rolpassword = pstrdup("");
458 else
460 /* assume passwd is pass-by-ref */
461 datum = PointerGetDatum(tp + off);
464 * The password probably shouldn't ever be out-of-line toasted; if
465 * it is, ignore it, since we can't handle that in startup mode.
467 * It is entirely likely that it's 1-byte format not 4-byte, and
468 * theoretically possible that it's compressed inline, but
469 * text_to_cstring should be able to handle those cases even in
470 * startup mode.
472 if (VARATT_IS_EXTERNAL(DatumGetPointer(datum)))
473 auth_info[curr_role].rolpassword = pstrdup("");
474 else
475 auth_info[curr_role].rolpassword = TextDatumGetCString(datum);
477 /* assume passwd has attlen -1 */
478 off = att_addlength_pointer(off, -1, tp + off);
481 if (HeapTupleHasNulls(tuple) &&
482 att_isnull(Anum_pg_authid_rolvaliduntil - 1, bp))
484 /* rolvaliduntil is null, emit as an empty string */
485 auth_info[curr_role].rolvaliduntil = pstrdup("");
487 else
489 TimestampTz *rvup;
491 /* Assume timestamptz has double alignment */
492 off = att_align_nominal(off, 'd');
493 rvup = (TimestampTz *) (tp + off);
494 auth_info[curr_role].rolvaliduntil =
495 DatumGetCString(DirectFunctionCall1(timestamptz_out,
496 TimestampTzGetDatum(*rvup)));
500 * Check for illegal characters in the user name and password.
502 if (!name_okay(auth_info[curr_role].rolname))
504 ereport(LOG,
505 (errmsg("invalid role name \"%s\"",
506 auth_info[curr_role].rolname)));
507 continue;
509 if (!name_okay(auth_info[curr_role].rolpassword))
511 ereport(LOG,
512 (errmsg("invalid role password \"%s\"",
513 auth_info[curr_role].rolpassword)));
514 continue;
517 curr_role++;
518 total_roles++;
520 heap_endscan(scan);
523 * Read pg_auth_members into temporary data structure, too
525 totalblocks = RelationGetNumberOfBlocks(rel_authmem);
526 totalblocks = totalblocks ? totalblocks : 1;
527 est_rows = totalblocks * (BLCKSZ / (sizeof(HeapTupleHeaderData) + sizeof(FormData_pg_auth_members)));
528 authmem_info = (authmem_entry *) palloc(est_rows * sizeof(authmem_entry));
530 scan = heap_beginscan(rel_authmem, SnapshotNow, 0, NULL);
531 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
533 Form_pg_auth_members memform = (Form_pg_auth_members) GETSTRUCT(tuple);
535 if (curr_mem >= est_rows)
537 est_rows *= 2;
538 authmem_info = (authmem_entry *)
539 repalloc(authmem_info, est_rows * sizeof(authmem_entry));
542 authmem_info[curr_mem].roleid = memform->roleid;
543 authmem_info[curr_mem].memberid = memform->member;
544 curr_mem++;
545 total_mem++;
547 heap_endscan(scan);
550 * Search for memberships. We can skip all this if pg_auth_members is
551 * empty.
553 if (total_mem > 0)
556 * Sort auth_info by roleid and authmem_info by memberid.
558 qsort(auth_info, total_roles, sizeof(auth_entry), oid_compar);
559 qsort(authmem_info, total_mem, sizeof(authmem_entry), mem_compar);
562 * For each role, find what it belongs to.
564 for (curr_role = 0; curr_role < total_roles; curr_role++)
566 List *roles_list;
567 List *roles_names_list = NIL;
568 ListCell *mem;
571 * This search algorithm is the same as in is_member_of_role; we
572 * are just working with a different input data structure.
574 roles_list = list_make1_oid(auth_info[curr_role].roleid);
576 foreach(mem, roles_list)
578 authmem_entry key;
579 authmem_entry *found_mem;
580 int first_found,
581 last_found,
584 key.memberid = lfirst_oid(mem);
585 found_mem = bsearch(&key, authmem_info, total_mem,
586 sizeof(authmem_entry), mem_compar);
587 if (!found_mem)
588 continue;
591 * bsearch found a match for us; but if there were multiple
592 * matches it could have found any one of them. Locate first
593 * and last match.
595 first_found = last_found = (found_mem - authmem_info);
596 while (first_found > 0 &&
597 mem_compar(&key, &authmem_info[first_found - 1]) == 0)
598 first_found--;
599 while (last_found + 1 < total_mem &&
600 mem_compar(&key, &authmem_info[last_found + 1]) == 0)
601 last_found++;
604 * Now add all the new roles to roles_list.
606 for (i = first_found; i <= last_found; i++)
607 roles_list = list_append_unique_oid(roles_list,
608 authmem_info[i].roleid);
612 * Convert list of role Oids to list of role names. We must do
613 * this before re-sorting auth_info.
615 * We skip the first list element (curr_role itself) since there
616 * is no point in writing that a role is a member of itself.
618 for_each_cell(mem, lnext(list_head(roles_list)))
620 auth_entry key_auth;
621 auth_entry *found_role;
623 key_auth.roleid = lfirst_oid(mem);
624 found_role = bsearch(&key_auth, auth_info, total_roles,
625 sizeof(auth_entry), oid_compar);
626 if (found_role) /* paranoia */
627 roles_names_list = lappend(roles_names_list,
628 found_role->rolname);
630 auth_info[curr_role].member_of = roles_names_list;
631 list_free(roles_list);
636 * Now sort auth_info into rolname order for output, and write the file.
638 qsort(auth_info, total_roles, sizeof(auth_entry), name_compar);
640 for (curr_role = 0; curr_role < total_roles; curr_role++)
642 auth_entry *arole = &auth_info[curr_role];
643 ListCell *mem;
645 fputs_quote(arole->rolname, fp);
646 fputs(" ", fp);
647 fputs_quote(arole->rolpassword, fp);
648 fputs(" ", fp);
649 fputs_quote(arole->rolvaliduntil, fp);
651 foreach(mem, arole->member_of)
653 fputs(" ", fp);
654 fputs_quote((char *) lfirst(mem), fp);
657 fputs("\n", fp);
660 if (FreeFile(fp))
661 ereport(ERROR,
662 (errcode_for_file_access(),
663 errmsg("could not write to temporary file \"%s\": %m",
664 tempname)));
667 * Rename the temp file to its final name, deleting the old flat file. We
668 * expect that rename(2) is an atomic action.
670 if (rename(tempname, filename))
671 ereport(ERROR,
672 (errcode_for_file_access(),
673 errmsg("could not rename file \"%s\" to \"%s\": %m",
674 tempname, filename)));
679 * This routine is called once during database startup, after completing
680 * WAL replay if needed. Its purpose is to sync the flat files with the
681 * current state of the database tables. This is particularly important
682 * during PITR operation, since the flat files will come from the
683 * base backup which may be far out of sync with the current state.
685 * In theory we could skip rebuilding the flat files if no WAL replay
686 * occurred, but it seems best to just do it always. We have to
687 * scan pg_database to compute the XID wrap limit anyway. Also, this
688 * policy means we need not force initdb to change the format of the
689 * flat files.
691 * We also cause relcache init files to be flushed, for largely the same
692 * reasons.
694 * In a standalone backend we pass database_only = true to skip processing
695 * the auth file. We won't need it, and building it could fail if there's
696 * something corrupt in the authid/authmem catalogs.
698 void
699 BuildFlatFiles(bool database_only)
701 ResourceOwner owner;
702 RelFileNode rnode;
703 Relation rel_db,
704 rel_authid,
705 rel_authmem;
707 /* Need a resowner to keep the heapam and buffer code happy */
708 owner = ResourceOwnerCreate(NULL, "BuildFlatFiles");
709 CurrentResourceOwner = owner;
711 /* hard-wired path to pg_database */
712 rnode.spcNode = GLOBALTABLESPACE_OID;
713 rnode.dbNode = 0;
714 rnode.relNode = DatabaseRelationId;
717 * We don't have any hope of running a real relcache, but we can use the
718 * same fake-relcache facility that WAL replay uses.
720 * No locking is needed because no one else is alive yet.
722 rel_db = CreateFakeRelcacheEntry(rnode);
723 write_database_file(rel_db, true);
724 FreeFakeRelcacheEntry(rel_db);
726 if (!database_only)
728 /* hard-wired path to pg_authid */
729 rnode.spcNode = GLOBALTABLESPACE_OID;
730 rnode.dbNode = 0;
731 rnode.relNode = AuthIdRelationId;
732 rel_authid = CreateFakeRelcacheEntry(rnode);
734 /* hard-wired path to pg_auth_members */
735 rnode.spcNode = GLOBALTABLESPACE_OID;
736 rnode.dbNode = 0;
737 rnode.relNode = AuthMemRelationId;
738 rel_authmem = CreateFakeRelcacheEntry(rnode);
740 write_auth_file(rel_authid, rel_authmem);
741 FreeFakeRelcacheEntry(rel_authid);
742 FreeFakeRelcacheEntry(rel_authmem);
745 CurrentResourceOwner = NULL;
746 ResourceOwnerDelete(owner);
751 * This routine is called during transaction commit or abort.
753 * On commit, if we've written any of the critical database tables during
754 * the current transaction, update the flat files and signal the postmaster.
756 * On abort, just reset the static flags so we don't try to do it on the
757 * next successful commit.
759 * NB: this should be the last step before actual transaction commit.
760 * If any error aborts the transaction after we run this code, the postmaster
761 * will still have received and cached the changed data; so minimize the
762 * window for such problems.
764 void
765 AtEOXact_UpdateFlatFiles(bool isCommit)
767 Relation drel = NULL;
768 Relation arel = NULL;
769 Relation mrel = NULL;
771 if (database_file_update_subid == InvalidSubTransactionId &&
772 auth_file_update_subid == InvalidSubTransactionId)
773 return; /* nothing to do */
775 if (!isCommit)
777 database_file_update_subid = InvalidSubTransactionId;
778 auth_file_update_subid = InvalidSubTransactionId;
779 return;
783 * Advance command counter to be certain we see all effects of the current
784 * transaction.
786 CommandCounterIncrement();
789 * Open and lock the needed catalog(s).
791 * Even though we only need AccessShareLock, this could theoretically fail
792 * due to deadlock. In practice, however, our transaction already holds
793 * RowExclusiveLock or better (it couldn't have updated the catalog
794 * without such a lock). This implies that dbcommands.c and other places
795 * that force flat-file updates must not follow the common practice of
796 * dropping catalog locks before commit.
798 if (database_file_update_subid != InvalidSubTransactionId)
799 drel = heap_open(DatabaseRelationId, AccessShareLock);
801 if (auth_file_update_subid != InvalidSubTransactionId)
803 arel = heap_open(AuthIdRelationId, AccessShareLock);
804 mrel = heap_open(AuthMemRelationId, AccessShareLock);
808 * Obtain special locks to ensure that two transactions don't try to write
809 * the same flat file concurrently. Quite aside from any direct risks of
810 * corrupted output, the winning writer probably wouldn't have seen the
811 * other writer's updates. By taking a lock and holding it till commit,
812 * we ensure that whichever updater goes second will see the other
813 * updater's changes as committed, and thus the final state of the file
814 * will include all updates.
816 * We use a lock on "database 0" to protect writing the pg_database flat
817 * file, and a lock on "role 0" to protect the auth file. This is a bit
818 * ugly but it's not worth inventing any more-general convention. (Any
819 * two locktags that are never used for anything else would do.)
821 * This is safe against deadlock as long as these are the very last locks
822 * acquired during the transaction.
824 if (database_file_update_subid != InvalidSubTransactionId)
825 LockSharedObject(DatabaseRelationId, InvalidOid, 0,
826 AccessExclusiveLock);
828 if (auth_file_update_subid != InvalidSubTransactionId)
829 LockSharedObject(AuthIdRelationId, InvalidOid, 0,
830 AccessExclusiveLock);
832 /* Okay to write the files */
833 if (database_file_update_subid != InvalidSubTransactionId)
835 database_file_update_subid = InvalidSubTransactionId;
836 write_database_file(drel, false);
837 heap_close(drel, NoLock);
840 if (auth_file_update_subid != InvalidSubTransactionId)
842 auth_file_update_subid = InvalidSubTransactionId;
843 write_auth_file(arel, mrel);
844 heap_close(arel, NoLock);
845 heap_close(mrel, NoLock);
849 * Signal the postmaster to reload its caches.
851 SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE);
854 * Force synchronous commit, to minimize the window between changing the
855 * flat files on-disk and marking the transaction committed. It's not
856 * great that there is any window at all, but definitely we don't want to
857 * make it larger than necessary.
859 ForceSyncCommit();
864 * This routine is called during transaction prepare.
866 * Record which files need to be refreshed if this transaction later
867 * commits.
869 * Note: it's OK to clear the flags immediately, since if the PREPARE fails
870 * further on, we'd only reset the flags anyway. So there's no need for a
871 * separate PostPrepare call.
873 void
874 AtPrepare_UpdateFlatFiles(void)
876 uint16 info = 0;
878 if (database_file_update_subid != InvalidSubTransactionId)
880 database_file_update_subid = InvalidSubTransactionId;
881 info |= FF_BIT_DATABASE;
883 if (auth_file_update_subid != InvalidSubTransactionId)
885 auth_file_update_subid = InvalidSubTransactionId;
886 info |= FF_BIT_AUTH;
888 if (info != 0)
889 RegisterTwoPhaseRecord(TWOPHASE_RM_FLATFILES_ID, info,
890 NULL, 0);
895 * AtEOSubXact_UpdateFlatFiles
897 * Called at subtransaction end, this routine resets or updates the
898 * need-to-update-files flags.
900 void
901 AtEOSubXact_UpdateFlatFiles(bool isCommit,
902 SubTransactionId mySubid,
903 SubTransactionId parentSubid)
905 if (isCommit)
907 if (database_file_update_subid == mySubid)
908 database_file_update_subid = parentSubid;
910 if (auth_file_update_subid == mySubid)
911 auth_file_update_subid = parentSubid;
913 else
915 if (database_file_update_subid == mySubid)
916 database_file_update_subid = InvalidSubTransactionId;
918 if (auth_file_update_subid == mySubid)
919 auth_file_update_subid = InvalidSubTransactionId;
925 * This trigger is fired whenever someone modifies pg_database, pg_authid
926 * or pg_auth_members via general-purpose INSERT/UPDATE/DELETE commands.
928 * It is sufficient for this to be a STATEMENT trigger since we don't
929 * care which individual rows changed. It doesn't much matter whether
930 * it's a BEFORE or AFTER trigger.
932 Datum
933 flatfile_update_trigger(PG_FUNCTION_ARGS)
935 TriggerData *trigdata = (TriggerData *) fcinfo->context;
937 if (!CALLED_AS_TRIGGER(fcinfo))
938 elog(ERROR,
939 "flatfile_update_trigger was not called by trigger manager");
941 if (RelationGetNamespace(trigdata->tg_relation) != PG_CATALOG_NAMESPACE)
942 elog(ERROR, "flatfile_update_trigger was called for wrong table");
944 switch (RelationGetRelid(trigdata->tg_relation))
946 case DatabaseRelationId:
947 database_file_update_needed();
948 break;
949 case AuthIdRelationId:
950 case AuthMemRelationId:
951 auth_file_update_needed();
952 break;
953 default:
954 elog(ERROR, "flatfile_update_trigger was called for wrong table");
955 break;
958 return PointerGetDatum(NULL);
963 * 2PC processing routine for COMMIT PREPARED case.
965 * (We don't have to do anything for ROLLBACK PREPARED.)
967 void
968 flatfile_twophase_postcommit(TransactionId xid, uint16 info,
969 void *recdata, uint32 len)
972 * Set flags to do the needed file updates at the end of my own current
973 * transaction. (XXX this has some issues if my own transaction later
974 * rolls back, or if there is any significant delay before I commit. OK
975 * for now because we disallow COMMIT PREPARED inside a transaction
976 * block.)
978 if (info & FF_BIT_DATABASE)
979 database_file_update_needed();
980 if (info & FF_BIT_AUTH)
981 auth_file_update_needed();