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

📄 flatfiles.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 2 页
字号:
/*------------------------------------------------------------------------- * * flatfiles.c *	  Routines for maintaining "flat file" images of the shared catalogs. * * We use flat files so that the postmaster and not-yet-fully-started * backends can look at the contents of pg_database, pg_authid, and * pg_auth_members for authentication purposes.  This module is * responsible for keeping the flat-file images as nearly in sync with * database reality as possible. * * The tricky part of the write_xxx_file() routines in this module is that * they need to be able to operate in the context of the database startup * process (which calls BuildFlatFiles()) as well as a normal backend. * This means for example that we can't assume a fully functional relcache * and we can't use syscaches at all.  The major restriction imposed by * all that is that there's no way to read an out-of-line-toasted datum, * because the tuptoaster.c code is not prepared to cope with such an * environment.  Fortunately we can design the shared catalogs in such * a way that this is OK. * * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.15.2.1 2005/11/22 18:23:24 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <sys/stat.h>#include <unistd.h>#include "access/heapam.h"#include "access/twophase_rmgr.h"#include "catalog/pg_auth_members.h"#include "catalog/pg_authid.h"#include "catalog/pg_database.h"#include "catalog/pg_namespace.h"#include "catalog/pg_tablespace.h"#include "commands/trigger.h"#include "miscadmin.h"#include "storage/fd.h"#include "storage/pmsignal.h"#include "utils/acl.h"#include "utils/builtins.h"#include "utils/flatfiles.h"#include "utils/resowner.h"#include "utils/syscache.h"/* Actual names of the flat files (within $PGDATA) */#define DATABASE_FLAT_FILE	"global/pg_database"#define AUTH_FLAT_FILE		"global/pg_auth"/* Info bits in a flatfiles 2PC record */#define FF_BIT_DATABASE 1#define FF_BIT_AUTH		2/* * The need-to-update-files flags are SubTransactionIds that show * what level of the subtransaction tree requested the update. To register * an update, the subtransaction saves its own SubTransactionId in the flag, * unless the value was already set to a valid SubTransactionId (which implies * that it or a parent level has already requested the same).  If it aborts * and the value is its SubTransactionId, it resets the flag to * InvalidSubTransactionId. If it commits, it changes the value to its * parent's SubTransactionId.  This way the value is propagated up to the * top-level transaction, which will update the files if a valid * SubTransactionId is seen at top-level commit. */static SubTransactionId database_file_update_subid = InvalidSubTransactionId;static SubTransactionId auth_file_update_subid = InvalidSubTransactionId;/* * Mark flat database file as needing an update (because pg_database changed) */voiddatabase_file_update_needed(void){	if (database_file_update_subid == InvalidSubTransactionId)		database_file_update_subid = GetCurrentSubTransactionId();}/* * Mark flat auth file as needing an update (because pg_authid or * pg_auth_members changed) */voidauth_file_update_needed(void){	if (auth_file_update_subid == InvalidSubTransactionId)		auth_file_update_subid = GetCurrentSubTransactionId();}/* * database_getflatfilename --- get pathname of database file * * Note that result string is palloc'd, and should be freed by the caller. * (This convention is not really needed anymore, since the relative path * is fixed.) */char *database_getflatfilename(void){	return pstrdup(DATABASE_FLAT_FILE);}/* * auth_getflatfilename --- get pathname of auth file * * Note that result string is palloc'd, and should be freed by the caller. * (This convention is not really needed anymore, since the relative path * is fixed.) */char *auth_getflatfilename(void){	return pstrdup(AUTH_FLAT_FILE);}/* *	fputs_quote * *	Outputs string in quotes, with double-quotes duplicated. *	We could use quote_ident(), but that expects a TEXT argument. */static voidfputs_quote(const char *str, FILE *fp){	fputc('"', fp);	while (*str)	{		fputc(*str, fp);		if (*str == '"')			fputc('"', fp);		str++;	}	fputc('"', fp);}/* * name_okay * * We must disallow newlines in role names because * hba.c's parser won't handle fields split across lines, even if quoted. */static boolname_okay(const char *str){	int			i;	i = strcspn(str, "\r\n");	return (str[i] == '\0');}/* * write_database_file: update the flat database file * * A side effect is to determine the oldest database's datfrozenxid * so we can set or update the XID wrap limit. */static voidwrite_database_file(Relation drel){	char	   *filename,			   *tempname;	int			bufsize;	FILE	   *fp;	mode_t		oumask;	HeapScanDesc scan;	HeapTuple	tuple;	NameData	oldest_datname;	TransactionId oldest_datfrozenxid = InvalidTransactionId;	/*	 * Create a temporary filename to be renamed later.  This prevents the	 * backend from clobbering the flat file while the postmaster might be	 * reading from it.	 */	filename = database_getflatfilename();	bufsize = strlen(filename) + 12;	tempname = (char *) palloc(bufsize);	snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid);	oumask = umask((mode_t) 077);	fp = AllocateFile(tempname, "w");	umask(oumask);	if (fp == NULL)		ereport(ERROR,				(errcode_for_file_access(),				 errmsg("could not write to temporary file \"%s\": %m",						tempname)));	/*	 * Read pg_database and write the file.	 */	scan = heap_beginscan(drel, SnapshotNow, 0, NULL);	while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)	{		Form_pg_database dbform = (Form_pg_database) GETSTRUCT(tuple);		char	   *datname;		Oid			datoid;		Oid			dattablespace;		TransactionId datfrozenxid,					datvacuumxid;		datname = NameStr(dbform->datname);		datoid = HeapTupleGetOid(tuple);		dattablespace = dbform->dattablespace;		datfrozenxid = dbform->datfrozenxid;		datvacuumxid = dbform->datvacuumxid;		/*		 * Identify the oldest datfrozenxid, ignoring databases that are not		 * connectable (we assume they are safely frozen).	This must match		 * the logic in vac_truncate_clog() in vacuum.c.		 */		if (dbform->datallowconn &&			TransactionIdIsNormal(datfrozenxid))		{			if (oldest_datfrozenxid == InvalidTransactionId ||				TransactionIdPrecedes(datfrozenxid, oldest_datfrozenxid))			{				oldest_datfrozenxid = datfrozenxid;				namestrcpy(&oldest_datname, datname);			}		}		/*		 * Check for illegal characters in the database name.		 */		if (!name_okay(datname))		{			ereport(LOG,					(errmsg("invalid database name \"%s\"", datname)));			continue;		}		/*		 * The file format is: "dbname" oid tablespace frozenxid vacuumxid		 *		 * The xids are not needed for backend startup, but are of use to		 * autovacuum, and might also be helpful for forensic purposes.		 */		fputs_quote(datname, fp);		fprintf(fp, " %u %u %u %u\n",				datoid, dattablespace, datfrozenxid, datvacuumxid);	}	heap_endscan(scan);	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)));	/*	 * Set the transaction ID wrap limit using the oldest datfrozenxid	 */	if (oldest_datfrozenxid != InvalidTransactionId)		SetTransactionIdLimit(oldest_datfrozenxid, &oldest_datname);}/* * Support for write_auth_file * * The format for the flat auth file is *		"rolename" "password" "validuntil" "memberof" "memberof" ... * Only roles that are marked rolcanlogin are entered into the auth file. * Each role's line lists all the roles (groups) of which it is directly * or indirectly a member, except for itself. * * The postmaster expects the file to be sorted by rolename.  There is not * any special ordering of the membership lists. * * To construct this information, we scan pg_authid and pg_auth_members, * and build data structures in-memory before writing the file. */typedef struct{	Oid			roleid;	bool		rolcanlogin;	char	   *rolname;	char	   *rolpassword;	char	   *rolvaliduntil;	List	   *member_of;} auth_entry;typedef struct{	Oid			roleid;	Oid			memberid;} authmem_entry;/* qsort comparator for sorting auth_entry array by roleid */static intoid_compar(const void *a, const void *b){	const auth_entry *a_auth = (const auth_entry *) a;	const auth_entry *b_auth = (const auth_entry *) b;	if (a_auth->roleid < b_auth->roleid)		return -1;	if (a_auth->roleid > b_auth->roleid)		return 1;	return 0;}/* qsort comparator for sorting auth_entry array by rolname */static intname_compar(const void *a, const void *b){	const auth_entry *a_auth = (const auth_entry *) a;	const auth_entry *b_auth = (const auth_entry *) b;	return strcmp(a_auth->rolname, b_auth->rolname);}/* qsort comparator for sorting authmem_entry array by memberid */static intmem_compar(const void *a, const void *b){	const authmem_entry *a_auth = (const authmem_entry *) a;	const authmem_entry *b_auth = (const authmem_entry *) b;	if (a_auth->memberid < b_auth->memberid)		return -1;	if (a_auth->memberid > b_auth->memberid)		return 1;	return 0;}/* * write_auth_file: update the flat auth file */static voidwrite_auth_file(Relation rel_authid, Relation rel_authmem){	char	   *filename,			   *tempname;	int			bufsize;	BlockNumber totalblocks;	FILE	   *fp;	mode_t		oumask;	HeapScanDesc scan;	HeapTuple	tuple;	int			curr_role = 0;	int			total_roles = 0;	int			curr_mem = 0;	int			total_mem = 0;	int			est_rows;	auth_entry *auth_info;	authmem_entry *authmem_info;	/*	 * Create a temporary filename to be renamed later.  This prevents the	 * backend from clobbering the flat file while the postmaster might be	 * reading from it.	 */	filename = auth_getflatfilename();	bufsize = strlen(filename) + 12;	tempname = (char *) palloc(bufsize);	snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid);	oumask = umask((mode_t) 077);	fp = AllocateFile(tempname, "w");	umask(oumask);	if (fp == NULL)		ereport(ERROR,				(errcode_for_file_access(),				 errmsg("could not write to temporary file \"%s\": %m",						tempname)));	/*	 * Read pg_authid and fill temporary data structures.  Note we must read	 * all roles, even those without rolcanlogin.	 */	totalblocks = RelationGetNumberOfBlocks(rel_authid);	totalblocks = totalblocks ? totalblocks : 1;	est_rows = totalblocks * (BLCKSZ / (sizeof(HeapTupleHeaderData) + sizeof(FormData_pg_authid)));	auth_info = (auth_entry *) palloc(est_rows * sizeof(auth_entry));	scan = heap_beginscan(rel_authid, SnapshotNow, 0, NULL);	while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)	{		Form_pg_authid aform = (Form_pg_authid) GETSTRUCT(tuple);		HeapTupleHeader tup = tuple->t_data;		char	   *tp;			/* ptr to tuple data */		long		off;		/* offset in tuple data */		bits8	   *bp = tup->t_bits;	/* ptr to null bitmask in tuple */		Datum		datum;		if (curr_role >= est_rows)		{			est_rows *= 2;			auth_info = (auth_entry *)				repalloc(auth_info, est_rows * sizeof(auth_entry));		}		auth_info[curr_role].roleid = HeapTupleGetOid(tuple);		auth_info[curr_role].rolcanlogin = aform->rolcanlogin;		auth_info[curr_role].rolname = pstrdup(NameStr(aform->rolname));		auth_info[curr_role].member_of = NIL;		/*		 * We can't use heap_getattr() here because during startup we will not		 * have any tupdesc for pg_authid.	Fortunately it's not too hard to		 * work around this.  rolpassword is the first possibly-null field so		 * we can compute its offset directly.		 */		tp = (char *) tup + tup->t_hoff;		off = offsetof(FormData_pg_authid, rolpassword);		if (HeapTupleHasNulls(tuple) &&			att_isnull(Anum_pg_authid_rolpassword - 1, bp))		{			/* passwd is null, emit as an empty string */			auth_info[curr_role].rolpassword = pstrdup("");		}		else		{			/* assume passwd is pass-by-ref */			datum = PointerGetDatum(tp + off);			/*			 * The password probably shouldn't ever be out-of-line toasted; if			 * it is, ignore it, since we can't handle that in startup mode.			 */			if (VARATT_IS_EXTERNAL(DatumGetPointer(datum)))				auth_info[curr_role].rolpassword = pstrdup("");			else				auth_info[curr_role].rolpassword = DatumGetCString(DirectFunctionCall1(textout, datum));			/* assume passwd has attlen -1 */			off = att_addlength(off, -1, tp + off);		}		if (HeapTupleHasNulls(tuple) &&			att_isnull(Anum_pg_authid_rolvaliduntil - 1, bp))		{			/* rolvaliduntil is null, emit as an empty string */			auth_info[curr_role].rolvaliduntil = pstrdup("");		}		else		{			/*

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -