⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 flatfiles.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 2 页
字号:
			 * 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 + -