flatfiles.c

来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 977 行 · 第 1/2 页

C
977
字号
			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;			/*			 * 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. * * We also cause relcache init files to be flushed, for largely the same * reasons. * * 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, true);	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();	/*	 * 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, false);		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);	/*	 * 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. */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 + =
减小字号Ctrl + -
显示快捷键?