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

📄 vacuum.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 5 页
字号:
	 * defenses in GetNewTransactionId, but we keep them anyway.	 */	relation = heap_open(DatabaseRelationId, AccessShareLock);	scan = heap_beginscan(relation, SnapshotNow, 0, NULL);	while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)	{		Form_pg_database dbform = (Form_pg_database) GETSTRUCT(tuple);		/* Ignore non-connectable databases (eg, template0) */		/* It's assumed that these have been frozen correctly */		if (!dbform->datallowconn)			continue;		if (TransactionIdIsNormal(dbform->datvacuumxid))		{			if (TransactionIdPrecedes(myXID, dbform->datvacuumxid))				vacuumAlreadyWrapped = true;			else if (TransactionIdPrecedes(dbform->datvacuumxid, vacuumXID))				vacuumXID = dbform->datvacuumxid;		}		if (TransactionIdIsNormal(dbform->datfrozenxid))		{			if (TransactionIdPrecedes(myXID, dbform->datfrozenxid))				frozenAlreadyWrapped = true;			else if (TransactionIdPrecedes(dbform->datfrozenxid, frozenXID))			{				frozenXID = dbform->datfrozenxid;				namecpy(&oldest_datname, &dbform->datname);			}		}	}	heap_endscan(scan);	heap_close(relation, AccessShareLock);	/*	 * Do not truncate CLOG if we seem to have suffered wraparound already;	 * the computed minimum XID might be bogus.	 */	if (vacuumAlreadyWrapped)	{		ereport(WARNING,				(errmsg("some databases have not been vacuumed in over 2 billion transactions"),				 errdetail("You may have already suffered transaction-wraparound data loss.")));		return;	}	/* Truncate CLOG to the oldest vacuumxid */	TruncateCLOG(vacuumXID);	/*	 * Do not update varsup.c if we seem to have suffered wraparound already;	 * the computed XID might be bogus.	 */	if (frozenAlreadyWrapped)	{		ereport(WARNING,				(errmsg("some databases have not been vacuumed in over 1 billion transactions"),				 errhint("Better vacuum them soon, or you may have a wraparound failure.")));		return;	}	/* Update the wrap limit for GetNewTransactionId */	SetTransactionIdLimit(frozenXID, &oldest_datname);	/* Give warning about impending wraparound problems */	age = (int32) (myXID - frozenXID);	if (age > (int32) ((MaxTransactionId >> 3) * 3))		ereport(WARNING,		   (errmsg("database \"%s\" must be vacuumed within %u transactions",				   NameStr(oldest_datname),				   (MaxTransactionId >> 1) - age),			errhint("To avoid a database shutdown, execute a full-database VACUUM in \"%s\".",					NameStr(oldest_datname))));}/**************************************************************************** *																			* *			Code common to both flavors of VACUUM							* *																			* **************************************************************************** *//* *	vacuum_rel() -- vacuum one heap relation * *		Returns TRUE if we actually processed the relation (or can ignore it *		for some reason), FALSE if we failed to process it due to permissions *		or other reasons.  (A FALSE result really means that some data *		may have been left unvacuumed, so we can't update XID stats.) * *		Doing one heap at a time incurs extra overhead, since we need to *		check that the heap exists again just before we vacuum it.	The *		reason that we do this is so that vacuuming can be spread across *		many small transactions.  Otherwise, two-phase locking would require *		us to lock the entire database during one pass of the vacuum cleaner. * *		At entry and exit, we are not inside a transaction. */static boolvacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind){	LOCKMODE	lmode;	Relation	onerel;	LockRelId	onerelid;	Oid			toast_relid;	bool		result;	/* Begin a transaction for vacuuming this relation */	StartTransactionCommand();	/* functions in indexes may want a snapshot set */	ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());	/*	 * Tell the cache replacement strategy that vacuum is causing all	 * following IO	 */	StrategyHintVacuum(true);	/*	 * Check for user-requested abort.	Note we want this to be inside a	 * transaction, so xact.c doesn't issue useless WARNING.	 */	CHECK_FOR_INTERRUPTS();	/*	 * Race condition -- if the pg_class tuple has gone away since the last	 * time we saw it, we don't need to vacuum it.	 */	if (!SearchSysCacheExists(RELOID,							  ObjectIdGetDatum(relid),							  0, 0, 0))	{		StrategyHintVacuum(false);		CommitTransactionCommand();		return true;			/* okay 'cause no data there */	}	/*	 * Determine the type of lock we want --- hard exclusive lock for a FULL	 * vacuum, but just ShareUpdateExclusiveLock for concurrent vacuum. Either	 * way, we can be sure that no other backend is vacuuming the same table.	 */	lmode = vacstmt->full ? AccessExclusiveLock : ShareUpdateExclusiveLock;	/*	 * Open the class, get an appropriate lock on it, and check permissions.	 *	 * We allow the user to vacuum a table if he is superuser, the table	 * owner, or the database owner (but in the latter case, only if it's not	 * a shared relation).	pg_class_ownercheck includes the superuser case.	 *	 * Note we choose to treat permissions failure as a WARNING and keep	 * trying to vacuum the rest of the DB --- is this appropriate?	 */	onerel = relation_open(relid, lmode);	if (!(pg_class_ownercheck(RelationGetRelid(onerel), GetUserId()) ||		  (pg_database_ownercheck(MyDatabaseId, GetUserId()) && !onerel->rd_rel->relisshared)))	{		ereport(WARNING,				(errmsg("skipping \"%s\" --- only table or database owner can vacuum it",						RelationGetRelationName(onerel))));		relation_close(onerel, lmode);		StrategyHintVacuum(false);		CommitTransactionCommand();		return false;	}	/*	 * Check that it's a plain table; we used to do this in get_rel_oids() but	 * seems safer to check after we've locked the relation.	 */	if (onerel->rd_rel->relkind != expected_relkind)	{		ereport(WARNING,				(errmsg("skipping \"%s\" --- cannot vacuum indexes, views, or special system tables",						RelationGetRelationName(onerel))));		relation_close(onerel, lmode);		StrategyHintVacuum(false);		CommitTransactionCommand();		return false;	}	/*	 * Silently ignore tables that are temp tables of other backends ---	 * trying to vacuum these will lead to great unhappiness, since their	 * contents are probably not up-to-date on disk.  (We don't throw a	 * warning here; it would just lead to chatter during a database-wide	 * VACUUM.)	 */	if (isOtherTempNamespace(RelationGetNamespace(onerel)))	{		relation_close(onerel, lmode);		StrategyHintVacuum(false);		CommitTransactionCommand();		return true;			/* assume no long-lived data in temp tables */	}	/*	 * Get a session-level lock too. This will protect our access to the	 * relation across multiple transactions, so that we can vacuum the	 * relation's TOAST table (if any) secure in the knowledge that no one is	 * deleting the parent relation.	 *	 * NOTE: this cannot block, even if someone else is waiting for access,	 * because the lock manager knows that both lock requests are from the	 * same process.	 */	onerelid = onerel->rd_lockInfo.lockRelId;	LockRelationForSession(&onerelid, onerel->rd_istemp, lmode);	/*	 * Remember the relation's TOAST relation for later	 */	toast_relid = onerel->rd_rel->reltoastrelid;	/*	 * Do the actual work --- either FULL or "lazy" vacuum	 */	if (vacstmt->full)		full_vacuum_rel(onerel, vacstmt);	else		lazy_vacuum_rel(onerel, vacstmt);	result = true;				/* did the vacuum */	/* all done with this class, but hold lock until commit */	relation_close(onerel, NoLock);	/*	 * Complete the transaction and free all temporary memory used.	 */	StrategyHintVacuum(false);	CommitTransactionCommand();	/*	 * If the relation has a secondary toast rel, vacuum that too while we	 * still hold the session lock on the master table.  Note however that	 * "analyze" will not get done on the toast table.	This is good, because	 * the toaster always uses hardcoded index access and statistics are	 * totally unimportant for toast relations.	 */	if (toast_relid != InvalidOid)	{		if (!vacuum_rel(toast_relid, vacstmt, RELKIND_TOASTVALUE))			result = false;		/* failed to vacuum the TOAST table? */	}	/*	 * Now release the session-level lock on the master table.	 */	UnlockRelationForSession(&onerelid, lmode);	return result;}/**************************************************************************** *																			* *			Code for VACUUM FULL (only)										* *																			* **************************************************************************** *//* *	full_vacuum_rel() -- perform FULL VACUUM for one heap relation * *		This routine vacuums a single heap, cleans out its indexes, and *		updates its num_pages and num_tuples statistics. * *		At entry, we have already established a transaction and opened *		and locked the relation. */static voidfull_vacuum_rel(Relation onerel, VacuumStmt *vacstmt){	VacPageListData vacuum_pages;		/* List of pages to vacuum and/or										 * clean indexes */	VacPageListData fraged_pages;		/* List of pages with space enough for										 * re-using */	Relation   *Irel;	int			nindexes,				i;	VRelStats  *vacrelstats;	vacuum_set_xid_limits(vacstmt, onerel->rd_rel->relisshared,						  &OldestXmin, &FreezeLimit);	/*	 * Set up statistics-gathering machinery.	 */	vacrelstats = (VRelStats *) palloc(sizeof(VRelStats));	vacrelstats->rel_pages = 0;	vacrelstats->rel_tuples = 0;	vacrelstats->hasindex = false;	/* scan the heap */	vacuum_pages.num_pages = fraged_pages.num_pages = 0;	scan_heap(vacrelstats, onerel, &vacuum_pages, &fraged_pages);	/* Now open all indexes of the relation */	vac_open_indexes(onerel, AccessExclusiveLock, &nindexes, &Irel);	if (nindexes > 0)		vacrelstats->hasindex = true;	/* Clean/scan index relation(s) */	if (Irel != NULL)	{		if (vacuum_pages.num_pages > 0)		{			for (i = 0; i < nindexes; i++)				vacuum_index(&vacuum_pages, Irel[i],							 vacrelstats->rel_tuples, 0);		}		else		{			/* just scan indexes to update statistic */			for (i = 0; i < nindexes; i++)				scan_index(Irel[i], vacrelstats->rel_tuples);		}	}	if (fraged_pages.num_pages > 0)	{		/* Try to shrink heap */		repair_frag(vacrelstats, onerel, &vacuum_pages, &fraged_pages,					nindexes, Irel);		vac_close_indexes(nindexes, Irel, NoLock);	}	else	{		vac_close_indexes(nindexes, Irel, NoLock);		if (vacuum_pages.num_pages > 0)		{			/* Clean pages from vacuum_pages list */			vacuum_heap(vacrelstats, onerel, &vacuum_pages);		}	}	/* update shared free space map with final free space info */	vac_update_fsm(onerel, &fraged_pages, vacrelstats->rel_pages);	/* update statistics in pg_class */	vac_update_relstats(RelationGetRelid(onerel), vacrelstats->rel_pages,						vacrelstats->rel_tuples, vacrelstats->hasindex);	/* report results to the stats collector, too */	pgstat_report_vacuum(RelationGetRelid(onerel), onerel->rd_rel->relisshared,						 vacstmt->analyze, vacrelstats->rel_tuples);}/* *	scan_heap() -- scan an open heap relation * *		This routine sets commit status bits, constructs vacuum_pages (list *		of pages we need to compact free space on and/or clean indexes of *		deleted tuples), constructs fraged_pages (list of pages with free *		space that tuples could be moved into), and calculates statistics *		on the number of live tuples in the heap. */static voidscan_heap(VRelStats *vacrelstats, Relation onerel,		  VacPageList vacuum_pages, VacPageList fraged_pages){	BlockNumber nblocks,				blkno;	HeapTupleData tuple;	char	   *relname;	VacPage		vacpage;	BlockNumber empty_pages,				empty_end_pages;	double		num_tuples,				tups_vacuumed,				nkeep,				nunused;	double		free_space,				usable_free_space;	Size		min_tlen = MaxTupleSize;	Size		max_tlen = 0;	bool		do_shrinking = true;	VTupleLink	vtlinks = (VTupleLink) palloc(100 * sizeof(VTupleLinkData));	int			num_vtlinks = 0;	int			free_vtlinks = 100;	PGRUsage	ru0;	pg_rusage_init(&ru0);	relname = RelationGetRelationName(onerel);	ereport(elevel,			(errmsg("vacuuming \"%s.%s\"",					get_namespace_name(RelationGetNamespace(onerel)),					relname)));	empty_pages = empty_end_pages = 0;	num_tuples = tups_vacuumed = nkeep = nunused = 0;	free_space = 0;	nblocks = RelationGetNumberOfBlocks(onerel);	/*	 * We initially create each VacPage item in a maximal-sized workspace,	 * then copy the workspace into a just-large-enough copy.	 */	vacpage = (VacPage) palloc(sizeof(VacPageData) + MaxOffsetNumber * sizeof(OffsetNumber));	for (blkno = 0; blkno < nblocks; blkno++)	{		Page		page,					tempPage = NULL;		bool		do_reap,					do_frag;		Buffer		buf;		OffsetNumber offnum,					maxoff;		bool		pgchanged,					notup;

⌨️ 快捷键说明

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