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

📄 vacuum.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 5 页
字号:
		 * Also note that we don't change the stored fraged_pages list, only		 * our local variable num_fraged_pages; so the forgotten pages are		 * still available to be loaded into the free space map later.		 */		while (num_fraged_pages > 0 &&			   fraged_pages->pagedesc[num_fraged_pages - 1]->blkno >= blkno)		{			Assert(fraged_pages->pagedesc[num_fraged_pages - 1]->offsets_used == 0);			--num_fraged_pages;		}		/*		 * Process this page of relation.		 */		buf = ReadBuffer(onerel, blkno);		page = BufferGetPage(buf);		vacpage->offsets_free = 0;		isempty = PageIsEmpty(page);		dowrite = false;		/* Is the page in the vacuum_pages list? */		if (blkno == last_vacuum_block)		{			if (last_vacuum_page->offsets_free > 0)			{				/* there are dead tuples on this page - clean them */				Assert(!isempty);				LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);				vacuum_page(onerel, buf, last_vacuum_page);				LockBuffer(buf, BUFFER_LOCK_UNLOCK);				dowrite = true;			}			else				Assert(isempty);			--vacuumed_pages;			if (vacuumed_pages > 0)			{				/* get prev reaped page from vacuum_pages */				last_vacuum_page = vacuum_pages->pagedesc[vacuumed_pages - 1];				last_vacuum_block = last_vacuum_page->blkno;			}			else			{				last_vacuum_page = NULL;				last_vacuum_block = InvalidBlockNumber;			}			if (isempty)			{				ReleaseBuffer(buf);				continue;			}		}		else			Assert(!isempty);		chain_tuple_moved = false;		/* no one chain-tuple was moved off										 * this page, yet */		vacpage->blkno = blkno;		maxoff = PageGetMaxOffsetNumber(page);		for (offnum = FirstOffsetNumber;			 offnum <= maxoff;			 offnum = OffsetNumberNext(offnum))		{			Size		tuple_len;			HeapTupleData tuple;			ItemId		itemid = PageGetItemId(page, offnum);			if (!ItemIdIsUsed(itemid))				continue;			tuple.t_datamcxt = NULL;			tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);			tuple_len = tuple.t_len = ItemIdGetLength(itemid);			ItemPointerSet(&(tuple.t_self), blkno, offnum);			/* ---			 * VACUUM FULL has an exclusive lock on the relation.  So			 * normally no other transaction can have pending INSERTs or			 * DELETEs in this relation.  A tuple is either:			 *		(a) a tuple in a system catalog, inserted or deleted			 *			by a not yet committed transaction			 *		(b) known dead (XMIN_INVALID, or XMAX_COMMITTED and xmax			 *			is visible to all active transactions)			 *		(c) inserted by a committed xact (XMIN_COMMITTED)			 *		(d) moved by the currently running VACUUM.			 *		(e) deleted (XMAX_COMMITTED) but at least one active			 *			transaction does not see the deleting transaction			 * In case (a) we wouldn't be in repair_frag() at all.			 * In case (b) we cannot be here, because scan_heap() has			 * already marked the item as unused, see continue above. Case			 * (c) is what normally is to be expected. Case (d) is only			 * possible, if a whole tuple chain has been moved while			 * processing this or a higher numbered block.			 * ---			 */			if (!(tuple.t_data->t_infomask & HEAP_XMIN_COMMITTED))			{				if (tuple.t_data->t_infomask & HEAP_MOVED_IN)					elog(ERROR, "HEAP_MOVED_IN was not expected");				if (!(tuple.t_data->t_infomask & HEAP_MOVED_OFF))					elog(ERROR, "HEAP_MOVED_OFF was expected");				/*				 * MOVED_OFF by another VACUUM would have caused the				 * visibility check to set XMIN_COMMITTED or XMIN_INVALID.				 */				if (HeapTupleHeaderGetXvac(tuple.t_data) != myXID)					elog(ERROR, "invalid XVAC in tuple header");				/*				 * If this (chain) tuple is moved by me already then I have to				 * check is it in vacpage or not - i.e. is it moved while				 * cleaning this page or some previous one.				 */				/* Can't we Assert(keep_tuples > 0) here? */				if (keep_tuples == 0)					continue;				if (chain_tuple_moved)				{					/* some chains were moved while cleaning this page */					Assert(vacpage->offsets_free > 0);					for (i = 0; i < vacpage->offsets_free; i++)					{						if (vacpage->offsets[i] == offnum)							break;					}					if (i >= vacpage->offsets_free)		/* not found */					{						vacpage->offsets[vacpage->offsets_free++] = offnum;						keep_tuples--;					}				}				else				{					vacpage->offsets[vacpage->offsets_free++] = offnum;					keep_tuples--;				}				continue;			}			/*			 * If this tuple is in a chain of tuples created in updates by			 * "recent" transactions then we have to move the whole chain of			 * tuples to other places, so that we can write new t_ctid links			 * that preserve the chain relationship.			 *			 * This test is complicated.  Read it as "if tuple is a recently			 * created updated version, OR if it is an obsoleted version". (In			 * the second half of the test, we needn't make any check on XMAX			 * --- it must be recently obsoleted, else scan_heap would have			 * deemed it removable.)			 *			 * NOTE: this test is not 100% accurate: it is possible for a			 * tuple to be an updated one with recent xmin, and yet not match			 * any new_tid entry in the vtlinks list.  Presumably there was			 * once a parent tuple with xmax matching the xmin, but it's			 * possible that that tuple has been removed --- for example, if			 * it had xmin = xmax and wasn't itself an updated version, then			 * HeapTupleSatisfiesVacuum would deem it removable as soon as the			 * xmin xact completes.			 *			 * To be on the safe side, we abandon the repair_frag process if			 * we cannot find the parent tuple in vtlinks.	This may be overly			 * conservative; AFAICS it would be safe to move the chain.			 */			if (((tuple.t_data->t_infomask & HEAP_UPDATED) &&				 !TransactionIdPrecedes(HeapTupleHeaderGetXmin(tuple.t_data),										OldestXmin)) ||				(!(tuple.t_data->t_infomask & (HEAP_XMAX_INVALID |											   HEAP_IS_LOCKED)) &&				 !(ItemPointerEquals(&(tuple.t_self),									 &(tuple.t_data->t_ctid)))))			{				Buffer		Cbuf = buf;				bool		freeCbuf = false;				bool		chain_move_failed = false;				ItemPointerData Ctid;				HeapTupleData tp = tuple;				Size		tlen = tuple_len;				VTupleMove	vtmove;				int			num_vtmove;				int			free_vtmove;				VacPage		to_vacpage = NULL;				int			to_item = 0;				int			ti;				if (dst_buffer != InvalidBuffer)				{					WriteBuffer(dst_buffer);					dst_buffer = InvalidBuffer;				}				/* Quick exit if we have no vtlinks to search in */				if (vacrelstats->vtlinks == NULL)				{					elog(DEBUG2, "parent item in update-chain not found --- can't continue repair_frag");					break;		/* out of walk-along-page loop */				}				/*				 * If this tuple is in the begin/middle of the chain then we				 * have to move to the end of chain.  As with any t_ctid				 * chase, we have to verify that each new tuple is really the				 * descendant of the tuple we came from.				 */				while (!(tp.t_data->t_infomask & (HEAP_XMAX_INVALID |												  HEAP_IS_LOCKED)) &&					   !(ItemPointerEquals(&(tp.t_self),										   &(tp.t_data->t_ctid))))				{					ItemPointerData nextTid;					TransactionId priorXmax;					Buffer		nextBuf;					Page		nextPage;					OffsetNumber nextOffnum;					ItemId		nextItemid;					HeapTupleHeader nextTdata;					nextTid = tp.t_data->t_ctid;					priorXmax = HeapTupleHeaderGetXmax(tp.t_data);					/* assume block# is OK (see heap_fetch comments) */					nextBuf = ReadBuffer(onerel,										 ItemPointerGetBlockNumber(&nextTid));					nextPage = BufferGetPage(nextBuf);					/* If bogus or unused slot, assume tp is end of chain */					nextOffnum = ItemPointerGetOffsetNumber(&nextTid);					if (nextOffnum < FirstOffsetNumber ||						nextOffnum > PageGetMaxOffsetNumber(nextPage))					{						ReleaseBuffer(nextBuf);						break;					}					nextItemid = PageGetItemId(nextPage, nextOffnum);					if (!ItemIdIsUsed(nextItemid))					{						ReleaseBuffer(nextBuf);						break;					}					/* if not matching XMIN, assume tp is end of chain */					nextTdata = (HeapTupleHeader) PageGetItem(nextPage,															  nextItemid);					if (!TransactionIdEquals(HeapTupleHeaderGetXmin(nextTdata),											 priorXmax))					{						ReleaseBuffer(nextBuf);						break;					}					/* OK, switch our attention to the next tuple in chain */					tp.t_datamcxt = NULL;					tp.t_data = nextTdata;					tp.t_self = nextTid;					tlen = tp.t_len = ItemIdGetLength(nextItemid);					if (freeCbuf)						ReleaseBuffer(Cbuf);					Cbuf = nextBuf;					freeCbuf = true;				}				/* Set up workspace for planning the chain move */				vtmove = (VTupleMove) palloc(100 * sizeof(VTupleMoveData));				num_vtmove = 0;				free_vtmove = 100;				/*				 * Now, walk backwards up the chain (towards older tuples) and				 * check if all items in chain can be moved.  We record all				 * the moves that need to be made in the vtmove array.				 */				for (;;)				{					Buffer		Pbuf;					Page		Ppage;					ItemId		Pitemid;					HeapTupleHeader PTdata;					VTupleLinkData vtld,							   *vtlp;					/* Identify a target page to move this tuple to */					if (to_vacpage == NULL ||						!enough_space(to_vacpage, tlen))					{						for (i = 0; i < num_fraged_pages; i++)						{							if (enough_space(fraged_pages->pagedesc[i], tlen))								break;						}						if (i == num_fraged_pages)						{							/* can't move item anywhere */							chain_move_failed = true;							break;		/* out of check-all-items loop */						}						to_item = i;						to_vacpage = fraged_pages->pagedesc[to_item];					}					to_vacpage->free -= MAXALIGN(tlen);					if (to_vacpage->offsets_used >= to_vacpage->offsets_free)						to_vacpage->free -= sizeof(ItemIdData);					(to_vacpage->offsets_used)++;					/* Add an entry to vtmove list */					if (free_vtmove == 0)					{						free_vtmove = 1000;						vtmove = (VTupleMove)							repalloc(vtmove,									 (free_vtmove + num_vtmove) *									 sizeof(VTupleMoveData));					}					vtmove[num_vtmove].tid = tp.t_self;					vtmove[num_vtmove].vacpage = to_vacpage;					if (to_vacpage->offsets_used == 1)						vtmove[num_vtmove].cleanVpd = true;					else						vtmove[num_vtmove].cleanVpd = false;					free_vtmove--;					num_vtmove++;					/* Done if at beginning of chain */					if (!(tp.t_data->t_infomask & HEAP_UPDATED) ||					 TransactionIdPrecedes(HeapTupleHeaderGetXmin(tp.t_data),										   OldestXmin))						break;	/* out of check-all-items loop */					/* Move to tuple with prior row version */					vtld.new_tid = tp.t_self;					vtlp = (VTupleLink)						vac_bsearch((void *) &vtld,									(void *) (vacrelstats->vtlinks),									vacrelstats->num_vtlinks,									sizeof(VTupleLinkData),									vac_cmp_vtlinks);					if (vtlp == NULL)					{						/* see discussion above */						elog(DEBUG2, "parent item in update-chain not found --- can't continue repair_frag");						chain_move_failed = true;						break;	/* out of check-all-items loop */					}					tp.t_self = vtlp->this_tid;					Pbuf = ReadBuffer(onerel,									ItemPointerGetBlockNumber(&(tp.t_self)));					Ppage = BufferGetPage(Pbuf);					Pitemid = PageGetItemId(Ppage,								   ItemPointerGetOffsetNumber(&(tp.t_self)));					/* this can't happen since we saw tuple earlier: */					if (!ItemIdIsUsed(Pitemid))						elog(ERROR, "parent itemid marked as unused");					PTdata = (HeapTupleHeader) PageGetItem(Ppage, Pitemid);					/* ctid should not have changed since we saved it */					Assert(ItemPointerEquals(&(vtld.new_tid),											 &(PTdata->t_ctid)));					/*					 * Read above about cases when !ItemIdIsUsed(nextItemid)					 * (child item is removed)... Due to the fact that at the					 * moment we don't remove unuseful part of update-chain,					 * it's possible to get non-matching parent row here. Like					 * as in the case which caused this problem, we stop					 * shrinking here. I could try to find real parent row but					 * want not to do it because of real solution will be					 * implemented anyway, later, and we are too close to 6.5					 * release. - vadim 06/11/99					 */					if ((PTdata->t_infomask & HEAP_XMAX_IS_MULTI) ||						!(TransactionIdEquals(HeapTupleHeaderGetXmax(PTdata),										 HeapTupleHeaderGetXmin(tp.t_data))))					{						ReleaseBuffer(Pbuf);						elog(DEBUG2, "too old parent tuple found --- can't continue repair_frag");						chain_move_failed = true;						break;	/* out of check-all-items loop */					}					tp.t_datamcxt = NULL;					tp.t_data = PTdata;					tlen = tp.t_len = ItemIdGetLength(Pitemid);					if (freeCbuf)						ReleaseBuffer(Cbuf);					Cbuf = Pbuf;					freeCbuf = true;				}				/* end of check-all-items loop */				if (freeCbuf)					ReleaseBuffer(Cbuf);				freeCbuf = false;				if (chain_move_failed)				{					/*					 * Undo changes to offsets_used state.	We don't bother					 * cleaning up the amount-free state, since we're not					 * going to do any further tuple motion.					 */					for (i = 0; i < num_vtmove; i++)					{						Assert(vtmove[i].vacpage->offsets_used > 0);						(vtmove[i].vacpage->offsets_used)--;					}					pfree(vtmove);					break;		/* out of walk-along-page loop */				}				/*				 * Okay, move the whole tuple chain in reverse order.				 *				 * Ctid tracks the new location of the previously-moved tuple.				 */				ItemPointerSetInvalid(&Ctid);				for (ti = 0; ti < num_vtmove; ti++)				{					VacPage		destvacpage = vtmove[ti].vacpage;					Page		Cpage;					ItemId		Citemid;					/* Get page to move from */					tuple.t_self = vtmove[ti].tid;					Cbuf = ReadBuffer(onerel,								 ItemPointerGetBlockNumber(&(tuple.t_

⌨️ 快捷键说明

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