📄 flatfiles.c
字号:
* rolvaliduntil is timestamptz, which we assume is double * alignment and pass-by-reference. */ off = att_align(off, 'd'); datum = PointerGetDatum(tp + off); auth_info[curr_role].rolvaliduntil = DatumGetCString(DirectFunctionCall1(timestamptz_out, datum)); } /* * 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; /* We can skip this for non-login roles */ if (!auth_info[curr_role].rolcanlogin) continue; /* * 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]; if (arole->rolcanlogin) { 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. */voidBuildFlatFiles(bool database_only){ ResourceOwner owner; RelFileNode rnode; Relation rel_db, rel_authid, rel_authmem; /* * We don't have any hope of running a real relcache, but we can use the * same fake-relcache facility that WAL replay uses. */ XLogInitRelationCache(); /* 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; /* No locking is needed because no one else is alive yet */ rel_db = XLogOpenRelation(rnode); write_database_file(rel_db); if (!database_only) { /* hard-wired path to pg_authid */ rnode.spcNode = GLOBALTABLESPACE_OID; rnode.dbNode = 0; rnode.relNode = AuthIdRelationId; rel_authid = XLogOpenRelation(rnode); /* hard-wired path to pg_auth_members */ rnode.spcNode = GLOBALTABLESPACE_OID; rnode.dbNode = 0; rnode.relNode = AuthMemRelationId; rel_authmem = XLogOpenRelation(rnode); write_auth_file(rel_authid, rel_authmem); } CurrentResourceOwner = NULL; ResourceOwnerDelete(owner); XLogCloseRelationCache();}/* * 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. */voidAtEOXact_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(); /* * We use ExclusiveLock to ensure that only one backend writes the flat * file(s) at a time. That's sufficient because it's okay to allow plain * reads of the tables in parallel. There is some chance of a deadlock * here (if we were triggered by a user update of one of the tables, which * likely won't have gotten a strong enough lock), so get the locks we * need before writing anything. * * For writing the auth file, it's sufficient to ExclusiveLock pg_authid; * we take just regular AccessShareLock on pg_auth_members. */ if (database_file_update_subid != InvalidSubTransactionId) drel = heap_open(DatabaseRelationId, ExclusiveLock); if (auth_file_update_subid != InvalidSubTransactionId) { arel = heap_open(AuthIdRelationId, ExclusiveLock); mrel = heap_open(AuthMemRelationId, AccessShareLock); } /* 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); } /* * Signal the postmaster to reload its caches. */ SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE);}/* * 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. */voidAtPrepare_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. */voidAtEOSubXact_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. */Datumflatfile_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.) */voidflatfile_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();}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -