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

📄 vacuum.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 5 页
字号:
/*------------------------------------------------------------------------- * * vacuum.c *	  The postgres vacuum cleaner. * * This file includes the "full" version of VACUUM, as well as control code * used by all three of full VACUUM, lazy VACUUM, and ANALYZE.	See * vacuumlazy.c and analyze.c for the rest of the code for the latter two. * * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.317.2.3 2006/01/18 20:35:15 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <sys/time.h>#include <unistd.h>#include "access/clog.h"#include "access/genam.h"#include "access/heapam.h"#include "access/subtrans.h"#include "access/xlog.h"#include "catalog/catalog.h"#include "catalog/namespace.h"#include "catalog/pg_database.h"#include "catalog/pg_index.h"#include "commands/dbcommands.h"#include "commands/vacuum.h"#include "executor/executor.h"#include "miscadmin.h"#include "postmaster/autovacuum.h"#include "storage/freespace.h"#include "storage/procarray.h"#include "storage/smgr.h"#include "tcop/pquery.h"#include "utils/acl.h"#include "utils/builtins.h"#include "utils/flatfiles.h"#include "utils/fmgroids.h"#include "utils/inval.h"#include "utils/lsyscache.h"#include "utils/memutils.h"#include "utils/pg_rusage.h"#include "utils/relcache.h"#include "utils/syscache.h"#include "pgstat.h"/* * VacPage structures keep track of each page on which we find useful * amounts of free space. */typedef struct VacPageData{	BlockNumber blkno;			/* BlockNumber of this Page */	Size		free;			/* FreeSpace on this Page */	uint16		offsets_used;	/* Number of OffNums used by vacuum */	uint16		offsets_free;	/* Number of OffNums free or to be free */	OffsetNumber offsets[1];	/* Array of free OffNums */} VacPageData;typedef VacPageData *VacPage;typedef struct VacPageListData{	BlockNumber empty_end_pages;	/* Number of "empty" end-pages */	int			num_pages;		/* Number of pages in pagedesc */	int			num_allocated_pages;	/* Number of allocated pages in										 * pagedesc */	VacPage    *pagedesc;		/* Descriptions of pages */} VacPageListData;typedef VacPageListData *VacPageList;/* * The "vtlinks" array keeps information about each recently-updated tuple * ("recent" meaning its XMAX is too new to let us recycle the tuple). * We store the tuple's own TID as well as its t_ctid (its link to the next * newer tuple version).  Searching in this array allows us to follow update * chains backwards from newer to older tuples.  When we move a member of an * update chain, we must move *all* the live members of the chain, so that we * can maintain their t_ctid link relationships (we must not just overwrite * t_ctid in an existing tuple). * * Note: because t_ctid links can be stale (this would only occur if a prior * VACUUM crashed partway through), it is possible that new_tid points to an * empty slot or unrelated tuple.  We have to check the linkage as we follow * it, just as is done in EvalPlanQual. */typedef struct VTupleLinkData{	ItemPointerData new_tid;	/* t_ctid of an updated tuple */	ItemPointerData this_tid;	/* t_self of the tuple */} VTupleLinkData;typedef VTupleLinkData *VTupleLink;/* * We use an array of VTupleMoveData to plan a chain tuple move fully * before we do it. */typedef struct VTupleMoveData{	ItemPointerData tid;		/* tuple ID */	VacPage		vacpage;		/* where to move it to */	bool		cleanVpd;		/* clean vacpage before using? */} VTupleMoveData;typedef VTupleMoveData *VTupleMove;/* * VRelStats contains the data acquired by scan_heap for use later */typedef struct VRelStats{	/* miscellaneous statistics */	BlockNumber rel_pages;	double		rel_tuples;	Size		min_tlen;	Size		max_tlen;	bool		hasindex;	/* vtlinks array for tuple chain following - sorted by new_tid */	int			num_vtlinks;	VTupleLink	vtlinks;} VRelStats;/*---------------------------------------------------------------------- * ExecContext: * * As these variables always appear together, we put them into one struct * and pull initialization and cleanup into separate routines. * ExecContext is used by repair_frag() and move_xxx_tuple().  More * accurately:	It is *used* only in move_xxx_tuple(), but because this * routine is called many times, we initialize the struct just once in * repair_frag() and pass it on to move_xxx_tuple(). */typedef struct ExecContextData{	ResultRelInfo *resultRelInfo;	EState	   *estate;	TupleTableSlot *slot;} ExecContextData;typedef ExecContextData *ExecContext;static voidExecContext_Init(ExecContext ec, Relation rel){	TupleDesc	tupdesc = RelationGetDescr(rel);	/*	 * We need a ResultRelInfo and an EState so we can use the regular	 * executor's index-entry-making machinery.	 */	ec->estate = CreateExecutorState();	ec->resultRelInfo = makeNode(ResultRelInfo);	ec->resultRelInfo->ri_RangeTableIndex = 1;	/* dummy */	ec->resultRelInfo->ri_RelationDesc = rel;	ec->resultRelInfo->ri_TrigDesc = NULL;		/* we don't fire triggers */	ExecOpenIndices(ec->resultRelInfo);	ec->estate->es_result_relations = ec->resultRelInfo;	ec->estate->es_num_result_relations = 1;	ec->estate->es_result_relation_info = ec->resultRelInfo;	/* Set up a tuple slot too */	ec->slot = MakeSingleTupleTableSlot(tupdesc);}static voidExecContext_Finish(ExecContext ec){	ExecDropSingleTupleTableSlot(ec->slot);	ExecCloseIndices(ec->resultRelInfo);	FreeExecutorState(ec->estate);}/* * End of ExecContext Implementation *---------------------------------------------------------------------- */static MemoryContext vac_context = NULL;static int	elevel = -1;static TransactionId OldestXmin;static TransactionId FreezeLimit;/* non-export function prototypes */static List *get_rel_oids(List *relids, const RangeVar *vacrel,			 const char *stmttype);static void vac_update_dbstats(Oid dbid,				   TransactionId vacuumXID,				   TransactionId frozenXID);static void vac_truncate_clog(TransactionId vacuumXID,				  TransactionId frozenXID);static bool vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind);static void full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt);static void scan_heap(VRelStats *vacrelstats, Relation onerel,		  VacPageList vacuum_pages, VacPageList fraged_pages);static void repair_frag(VRelStats *vacrelstats, Relation onerel,			VacPageList vacuum_pages, VacPageList fraged_pages,			int nindexes, Relation *Irel);static void move_chain_tuple(Relation rel,				 Buffer old_buf, Page old_page, HeapTuple old_tup,				 Buffer dst_buf, Page dst_page, VacPage dst_vacpage,				 ExecContext ec, ItemPointer ctid, bool cleanVpd);static void move_plain_tuple(Relation rel,				 Buffer old_buf, Page old_page, HeapTuple old_tup,				 Buffer dst_buf, Page dst_page, VacPage dst_vacpage,				 ExecContext ec);static void update_hint_bits(Relation rel, VacPageList fraged_pages,				 int num_fraged_pages, BlockNumber last_move_dest_block,				 int num_moved);static void vacuum_heap(VRelStats *vacrelstats, Relation onerel,			VacPageList vacpagelist);static void vacuum_page(Relation onerel, Buffer buffer, VacPage vacpage);static void vacuum_index(VacPageList vacpagelist, Relation indrel,			 double num_tuples, int keep_tuples);static void scan_index(Relation indrel, double num_tuples);static bool tid_reaped(ItemPointer itemptr, void *state);static bool dummy_tid_reaped(ItemPointer itemptr, void *state);static void vac_update_fsm(Relation onerel, VacPageList fraged_pages,			   BlockNumber rel_pages);static VacPage copy_vac_page(VacPage vacpage);static void vpage_insert(VacPageList vacpagelist, VacPage vpnew);static void *vac_bsearch(const void *key, const void *base,			size_t nelem, size_t size,			int (*compar) (const void *, const void *));static int	vac_cmp_blk(const void *left, const void *right);static int	vac_cmp_offno(const void *left, const void *right);static int	vac_cmp_vtlinks(const void *left, const void *right);static bool enough_space(VacPage vacpage, Size len);/**************************************************************************** *																			* *			Code common to all flavors of VACUUM and ANALYZE				* *																			* **************************************************************************** *//* * Primary entry point for VACUUM and ANALYZE commands. * * relids is normally NIL; if it is not, then it provides the list of * relation OIDs to be processed, and vacstmt->relation is ignored. * (The non-NIL case is currently only used by autovacuum.) * * It is the caller's responsibility that both vacstmt and relids * (if given) be allocated in a memory context that won't disappear * at transaction commit.  In fact this context must be QueryContext * to avoid complaints from PreventTransactionChain. */voidvacuum(VacuumStmt *vacstmt, List *relids){	const char *stmttype = vacstmt->vacuum ? "VACUUM" : "ANALYZE";	TransactionId initialOldestXmin = InvalidTransactionId;	TransactionId initialFreezeLimit = InvalidTransactionId;	volatile MemoryContext anl_context = NULL;	volatile bool all_rels,				in_outer_xact,				use_own_xacts;	List	   *relations;	if (vacstmt->verbose)		elevel = INFO;	else		elevel = DEBUG2;	/*	 * We cannot run VACUUM inside a user transaction block; if we were inside	 * a transaction, then our commit- and start-transaction-command calls	 * would not have the intended effect! Furthermore, the forced commit that	 * occurs before truncating the relation's file would have the effect of	 * committing the rest of the user's transaction too, which would	 * certainly not be the desired behavior.  (This only applies to VACUUM	 * FULL, though.  We could in theory run lazy VACUUM inside a transaction	 * block, but we choose to disallow that case because we'd rather commit	 * as soon as possible after finishing the vacuum.	This is mainly so that	 * we can let go the AccessExclusiveLock that we may be holding.)	 *	 * ANALYZE (without VACUUM) can run either way.	 */	if (vacstmt->vacuum)	{		PreventTransactionChain((void *) vacstmt, stmttype);		in_outer_xact = false;	}	else		in_outer_xact = IsInTransactionChain((void *) vacstmt);	/*	 * Disallow the combination VACUUM FULL FREEZE; although it would mostly	 * work, VACUUM FULL's ability to move tuples around means that it is	 * injecting its own XID into tuple visibility checks.	We'd have to	 * guarantee that every moved tuple is properly marked XMIN_COMMITTED or	 * XMIN_INVALID before the end of the operation.  There are corner cases	 * where this does not happen, and getting rid of them all seems hard (not	 * to mention fragile to maintain).  On the whole it's not worth it	 * compared to telling people to use two operations.  See pgsql-hackers	 * discussion of 27-Nov-2004, and comments below for update_hint_bits().	 *	 * Note: this is enforced here, and not in the grammar, since (a) we can	 * give a better error message, and (b) we might want to allow it again	 * someday.	 */	if (vacstmt->vacuum && vacstmt->full && vacstmt->freeze)		ereport(ERROR,				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),				 errmsg("VACUUM FULL FREEZE is not supported"),				 errhint("Use VACUUM FULL, then VACUUM FREEZE.")));	/*	 * Send info about dead objects to the statistics collector, unless	 * we are in autovacuum --- autovacuum.c does this for itself.	 */	if (vacstmt->vacuum && !IsAutoVacuumProcess())		pgstat_vacuum_tabstat();	/*	 * Create special memory context for cross-transaction storage.	 *	 * Since it is a child of PortalContext, it will go away eventually even	 * if we suffer an error; there's no need for special abort cleanup logic.	 */	vac_context = AllocSetContextCreate(PortalContext,										"Vacuum",										ALLOCSET_DEFAULT_MINSIZE,										ALLOCSET_DEFAULT_INITSIZE,										ALLOCSET_DEFAULT_MAXSIZE);	/* Remember whether we are processing everything in the DB */	all_rels = (relids == NIL && vacstmt->relation == NULL);	/*	 * Build list of relations to process, unless caller gave us one. (If we	 * build one, we put it in vac_context for safekeeping.)	 */	relations = get_rel_oids(relids, vacstmt->relation, stmttype);	if (vacstmt->vacuum && all_rels)	{		/*		 * It's a database-wide VACUUM.		 *		 * Compute the initially applicable OldestXmin and FreezeLimit XIDs,		 * so that we can record these values at the end of the VACUUM. Note		 * that individual tables may well be processed with newer values, but		 * we can guarantee that no (non-shared) relations are processed with		 * older ones.		 *		 * It is okay to record non-shared values in pg_database, even though		 * we may vacuum shared relations with older cutoffs, because only the		 * minimum of the values present in pg_database matters.  We can be		 * sure that shared relations have at some time been vacuumed with		 * cutoffs no worse than the global minimum; for, if there is a		 * backend in some other DB with xmin = OLDXMIN that's determining the		 * cutoff with which we vacuum shared relations, it is not possible		 * for that database to have a cutoff newer than OLDXMIN recorded in		 * pg_database.		 */		vacuum_set_xid_limits(vacstmt, false,							  &initialOldestXmin,							  &initialFreezeLimit);	}	/*	 * Decide whether we need to start/commit our own transactions.	 *	 * For VACUUM (with or without ANALYZE): always do so, so that we can	 * release locks as soon as possible.  (We could possibly use the outer	 * transaction for a one-table VACUUM, but handling TOAST tables would be	 * problematic.)	 *	 * For ANALYZE (no VACUUM): if inside a transaction block, we cannot	 * start/commit our own transactions.  Also, there's no need to do so if	 * only processing one relation.  For multiple relations when not within a	 * transaction block, use own transactions so we can release locks sooner.	 */	if (vacstmt->vacuum)		use_own_xacts = true;	else	{		Assert(vacstmt->analyze);		if (in_outer_xact)			use_own_xacts = false;		else if (list_length(relations) > 1)			use_own_xacts = true;		else			use_own_xacts = false;	}	/*	 * If we are running ANALYZE without per-table transactions, we'll need a	 * memory context with table lifetime.	 */	if (!use_own_xacts)		anl_context = AllocSetContextCreate(PortalContext,											"Analyze",											ALLOCSET_DEFAULT_MINSIZE,											ALLOCSET_DEFAULT_INITSIZE,											ALLOCSET_DEFAULT_MAXSIZE);	/*	 * vacuum_rel expects to be entered with no transaction active; it will	 * start and commit its own transaction.  But we are called by an SQL	 * command, and so we are executing inside a transaction already. We	 * commit the transaction started in PostgresMain() here, and start	 * another one before exiting to match the commit waiting for us back in	 * PostgresMain().

⌨️ 快捷键说明

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