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

📄 vacuum.c

📁 关系型数据库 Postgresql 6.5.2
💻 C
📖 第 1 页 / 共 5 页
字号:
/*------------------------------------------------------------------------- * * vacuum.c *	  the postgres vacuum cleaner * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $Header: /usr/local/cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.110.2.3 1999/08/25 12:01:45 ishii Exp $ * *------------------------------------------------------------------------- */#include <sys/types.h>#include <sys/file.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include "postgres.h"#include "access/genam.h"#include "access/heapam.h"#include "catalog/catalog.h"#include "catalog/catname.h"#include "catalog/index.h"#include "catalog/pg_operator.h"#include "catalog/pg_statistic.h"#include "catalog/pg_type.h"#include "commands/vacuum.h"#include "miscadmin.h"#include "parser/parse_oper.h"#include "storage/smgr.h"#include "utils/builtins.h"#include "utils/inval.h"#include "utils/portal.h"#include "utils/relcache.h"#include "utils/syscache.h"#ifndef HAVE_GETRUSAGE#include "rusagestub.h"#else#include <sys/time.h>#include <sys/resource.h>#endif /* #include <port-protos.h> *//* Why? */extern int	BlowawayRelationBuffers(Relation rel, BlockNumber block);bool		VacuumRunning = false;static Portal vc_portal;static int	MESSAGE_LEVEL;		/* message level */static TransactionId XmaxRecent;#define swapLong(a,b)	{long tmp; tmp=a; a=b; b=tmp;}#define swapInt(a,b)	{int tmp; tmp=a; a=b; b=tmp;}#define swapDatum(a,b)	{Datum tmp; tmp=a; a=b; b=tmp;}#define VacAttrStatsEqValid(stats) ( stats->f_cmpeq.fn_addr != NULL )#define VacAttrStatsLtGtValid(stats) ( stats->f_cmplt.fn_addr != NULL && \								   stats->f_cmpgt.fn_addr != NULL && \								   RegProcedureIsValid(stats->outfunc) )/* non-export function prototypes */static void vc_init(void);static void vc_shutdown(void);static void vc_vacuum(NameData *VacRelP, bool analyze, List *va_cols);static VRelList vc_getrels(NameData *VacRelP);static void vc_vacone(Oid relid, bool analyze, List *va_cols);static void vc_scanheap(VRelStats *vacrelstats, Relation onerel, VPageList vacuum_pages, VPageList fraged_pages);static void vc_rpfheap(VRelStats *vacrelstats, Relation onerel, VPageList vacuum_pages, VPageList fraged_pages, int nindices, Relation *Irel);static void vc_vacheap(VRelStats *vacrelstats, Relation onerel, VPageList vpl);static void vc_vacpage(Page page, VPageDescr vpd);static void vc_vaconeind(VPageList vpl, Relation indrel, int num_tuples, int keep_tuples);static void vc_scanoneind(Relation indrel, int num_tuples);static void vc_attrstats(Relation onerel, VRelStats *vacrelstats, HeapTuple tuple);static void vc_bucketcpy(Form_pg_attribute attr, Datum value, Datum *bucket, int16 *bucket_len);static void vc_updstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats *vacrelstats);static void vc_delhilowstats(Oid relid, int attcnt, int *attnums);static VPageDescr vc_tidreapped(ItemPointer itemptr, VPageList vpl);static void vc_reappage(VPageList vpl, VPageDescr vpc);static void vc_vpinsert(VPageList vpl, VPageDescr vpnew);static void vc_free(VRelList vrl);static void vc_getindices(Oid relid, int *nindices, Relation **Irel);static void vc_clsindices(int nindices, Relation *Irel);static void vc_mkindesc(Relation onerel, int nindices, Relation *Irel, IndDesc **Idesc);static void *vc_find_eq(void *bot, int nelem, int size, void *elm,		   int (*compar) (const void *, const void *));static int	vc_cmp_blk(const void *left, const void *right);static int	vc_cmp_offno(const void *left, const void *right);static int	vc_cmp_vtlinks(const void *left, const void *right);static bool vc_enough_space(VPageDescr vpd, Size len);voidvacuum(char *vacrel, bool verbose, bool analyze, List *va_spec){	char	   *pname;	MemoryContext old;	PortalVariableMemory pmem;	NameData	VacRel;	List	   *le;	List	   *va_cols = NIL;	/*	 * Create a portal for safe memory across transctions.	We need to	 * palloc the name space for it because our hash function expects the	 * name to be on a longword boundary.  CreatePortal copies the name to	 * safe storage for us.	 */	pname = (char *) palloc(strlen(VACPNAME) + 1);	strcpy(pname, VACPNAME);	vc_portal = CreatePortal(pname);	pfree(pname);	if (verbose)		MESSAGE_LEVEL = NOTICE;	else		MESSAGE_LEVEL = DEBUG;	/* vacrel gets de-allocated on transaction commit */	if (vacrel)		strcpy(VacRel.data, vacrel);	pmem = PortalGetVariableMemory(vc_portal);	old = MemoryContextSwitchTo((MemoryContext) pmem);	if (va_spec != NIL && !analyze)		elog(ERROR, "Can't vacuum columns, only tables.  You can 'vacuum analyze' columns.");	foreach(le, va_spec)	{		char	   *col = (char *) lfirst(le);		char	   *dest;		dest = (char *) palloc(strlen(col) + 1);		strcpy(dest, col);		va_cols = lappend(va_cols, dest);	}	MemoryContextSwitchTo(old);	/* initialize vacuum cleaner */	vc_init();	/* vacuum the database */	if (vacrel)		vc_vacuum(&VacRel, analyze, va_cols);	else		vc_vacuum(NULL, analyze, NIL);	PortalDestroy(&vc_portal);	/* clean up */	vc_shutdown();}/* *	vc_init(), vc_shutdown() -- start up and shut down the vacuum cleaner. * *		We run exactly one vacuum cleaner at a time.  We use the file system *		to guarantee an exclusive lock on vacuuming, since a single vacuum *		cleaner instantiation crosses transaction boundaries, and we'd lose *		postgres-style locks at the end of every transaction. * *		The strangeness with committing and starting transactions in the *		init and shutdown routines is due to the fact that the vacuum cleaner *		is invoked via a sql command, and so is already executing inside *		a transaction.	We need to leave ourselves in a predictable state *		on entry and exit to the vacuum cleaner.  We commit the transaction *		started in PostgresMain() inside vc_init(), and start one in *		vc_shutdown() to match the commit waiting for us back in *		PostgresMain(). */static voidvc_init(){	int			fd;#ifndef __CYGWIN32__	if ((fd = open("pg_vlock", O_CREAT | O_EXCL, 0600)) < 0)#else	if ((fd = open("pg_vlock", O_CREAT | O_EXCL | O_BINARY, 0600)) < 0)#endif	{		elog(ERROR, "Can't create lock file.  Is another vacuum cleaner running?\n\\tIf not, you may remove the pg_vlock file in the %s\n\\tdirectory", DatabasePath);	}	close(fd);	/*	 * By here, exclusive open on the lock file succeeded.	If we abort	 * for any reason during vacuuming, we need to remove the lock file.	 * This global variable is checked in the transaction manager on xact	 * abort, and the routine vc_abort() is called if necessary.	 */	VacuumRunning = true;	/* matches the StartTransaction in PostgresMain() */	CommitTransactionCommand();}static voidvc_shutdown(){	/* on entry, we are not in a transaction */	/*	 * Flush the init file that relcache.c uses to save startup time. The	 * next backend startup will rebuild the init file with up-to-date	 * information from pg_class.  This lets the optimizer see the stats	 * that we've collected for certain critical system indexes.  See	 * relcache.c for more details.	 *	 * Ignore any failure to unlink the file, since it might not be there if	 * no backend has been started since the last vacuum...	 */	unlink(RELCACHE_INIT_FILENAME);	/* remove the vacuum cleaner lock file */	if (unlink("pg_vlock") < 0)		elog(ERROR, "vacuum: can't destroy lock file!");	/* okay, we're done */	VacuumRunning = false;	/* matches the CommitTransaction in PostgresMain() */	StartTransactionCommand();}voidvc_abort(){	/* on abort, remove the vacuum cleaner lock file */	unlink("pg_vlock");	VacuumRunning = false;}/* *	vc_vacuum() -- vacuum the database. * *		This routine builds a list of relations to vacuum, and then calls *		code that vacuums them one at a time.  We are careful to vacuum each *		relation in a separate transaction in order to avoid holding too many *		locks at one time. */static voidvc_vacuum(NameData *VacRelP, bool analyze, List *va_cols){	VRelList	vrl,				cur;	/* get list of relations */	vrl = vc_getrels(VacRelP);	if (analyze && VacRelP == NULL && vrl != NULL)		vc_delhilowstats(InvalidOid, 0, NULL);	/* vacuum each heap relation */	for (cur = vrl; cur != (VRelList) NULL; cur = cur->vrl_next)		vc_vacone(cur->vrl_relid, analyze, va_cols);	vc_free(vrl);}static VRelListvc_getrels(NameData *VacRelP){	Relation	rel;	TupleDesc	tupdesc;	HeapScanDesc scan;	HeapTuple	tuple;	PortalVariableMemory portalmem;	MemoryContext old;	VRelList	vrl,				cur;	Datum		d;	char	   *rname;	char		rkind;	bool		n;	bool		found = false;	ScanKeyData key;	StartTransactionCommand();	if (VacRelP->data)	{		ScanKeyEntryInitialize(&key, 0x0, Anum_pg_class_relname,							   F_NAMEEQ,							   PointerGetDatum(VacRelP->data));	}	else	{		ScanKeyEntryInitialize(&key, 0x0, Anum_pg_class_relkind,							   F_CHAREQ, CharGetDatum('r'));	}	portalmem = PortalGetVariableMemory(vc_portal);	vrl = cur = (VRelList) NULL;	rel = heap_openr(RelationRelationName);	tupdesc = RelationGetDescr(rel);	scan = heap_beginscan(rel, false, SnapshotNow, 1, &key);	while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))	{		found = true;		d = heap_getattr(tuple, Anum_pg_class_relname, tupdesc, &n);		rname = (char *) d;		d = heap_getattr(tuple, Anum_pg_class_relkind, tupdesc, &n);		rkind = DatumGetChar(d);		if (rkind != RELKIND_RELATION)		{			elog(NOTICE, "Vacuum: can not process index and certain system tables");			continue;		}		/* get a relation list entry for this guy */		old = MemoryContextSwitchTo((MemoryContext) portalmem);		if (vrl == (VRelList) NULL)			vrl = cur = (VRelList) palloc(sizeof(VRelListData));		else		{			cur->vrl_next = (VRelList) palloc(sizeof(VRelListData));			cur = cur->vrl_next;		}		MemoryContextSwitchTo(old);		cur->vrl_relid = tuple->t_data->t_oid;		cur->vrl_next = (VRelList) NULL;	}	if (found == false)		elog(NOTICE, "Vacuum: table not found");	heap_endscan(scan);	heap_close(rel);	CommitTransactionCommand();	return vrl;}/* *	vc_vacone() -- vacuum one heap relation * *		This routine vacuums a single heap, cleans out its indices, and *		updates its statistics num_pages and num_tuples statistics. * *		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. */static voidvc_vacone(Oid relid, bool analyze, List *va_cols){	HeapTuple	tuple,				typetuple;	Relation	onerel;	VPageListData vacuum_pages; /* List of pages to vacuum and/or clean								 * indices */	VPageListData fraged_pages; /* List of pages with space enough for								 * re-using */	VPageDescr *vpp;	Relation   *Irel;	int32		nindices,				i;	VRelStats  *vacrelstats;	StartTransactionCommand();	/*	 * Race condition -- if the pg_class tuple has gone away since the	 * last time we saw it, we don't need to vacuum it.	 */	tuple = SearchSysCacheTuple(RELOID,								ObjectIdGetDatum(relid),								0, 0, 0);	if (!HeapTupleIsValid(tuple))	{		CommitTransactionCommand();		return;	}	/* now open the class and vacuum it */	onerel = heap_open(relid);	vacrelstats = (VRelStats *) palloc(sizeof(VRelStats));	vacrelstats->relid = relid;	vacrelstats->num_pages = vacrelstats->num_tuples = 0;	vacrelstats->hasindex = false;	if (analyze && !IsSystemRelationName((RelationGetRelationName(onerel))->data))	{		int			attr_cnt,				   *attnums = NULL;		Form_pg_attribute *attr;		attr_cnt = onerel->rd_att->natts;		attr = onerel->rd_att->attrs;		if (va_cols != NIL)		{			int			tcnt = 0;			List	   *le;			if (length(va_cols) > attr_cnt)				elog(ERROR, "vacuum: too many attributes specified for relation %s",					 (RelationGetRelationName(onerel))->data);			attnums = (int *) palloc(attr_cnt * sizeof(int));			foreach(le, va_cols)			{				char	   *col = (char *) lfirst(le);				for (i = 0; i < attr_cnt; i++)				{					if (namestrcmp(&(attr[i]->attname), col) == 0)						break;				}				if (i < attr_cnt)		/* found */					attnums[tcnt++] = i;				else				{					elog(ERROR, "vacuum: there is no attribute %s in %s",						 col, (RelationGetRelationName(onerel))->data);				}			}			attr_cnt = tcnt;		}		vacrelstats->vacattrstats = (VacAttrStats *) palloc(attr_cnt * sizeof(VacAttrStats));		for (i = 0; i < attr_cnt; i++)		{			Operator	func_operator;			Form_pg_operator pgopform;			VacAttrStats *stats;			stats = &vacrelstats->vacattrstats[i];			stats->attr = palloc(ATTRIBUTE_TUPLE_SIZE);			memmove(stats->attr, attr[((attnums) ? attnums[i] : i)], ATTRIBUTE_TUPLE_SIZE);			stats->best = stats->guess1 = stats->guess2 = 0;			stats->max = stats->min = 0;			stats->best_len = stats->guess1_len = stats->guess2_len = 0;			stats->max_len = stats->min_len = 0;			stats->initialized = false;			stats->best_cnt = stats->guess1_cnt = stats->guess1_hits = stats->guess2_hits = 0;			stats->max_cnt = stats->min_cnt = stats->null_cnt = stats->nonnull_cnt = 0;

⌨️ 快捷键说明

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