tidbitmap.c

来自「PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统」· C语言 代码 · 共 935 行 · 第 1/2 页

C
935
字号
		}		return candelete;	}	else if (tbm_page_is_lossy(b, apage->blockno))	{		/*		 * When the page is lossy in b, we have to mark it lossy in a too. We		 * know that no bits need be set in bitmap a, but we do not know which		 * ones should be cleared, and we have no API for "at most these		 * tuples need be checked".  (Perhaps it's worth adding that?)		 */		tbm_mark_page_lossy(a, apage->blockno);		/*		 * Note: tbm_mark_page_lossy will have removed apage from a, and may		 * have inserted a new lossy chunk instead.  We can continue the same		 * seq_search scan at the caller level, because it does not matter		 * whether we visit such a new chunk or not: it will have only the bit		 * for apage->blockno set, which is correct.		 *		 * We must return false here since apage was already deleted.		 */		return false;	}	else	{		bool		candelete = true;		bpage = tbm_find_pageentry(b, apage->blockno);		if (bpage != NULL)		{			/* Both pages are exact, merge at the bit level */			Assert(!bpage->ischunk);			for (wordnum = 0; wordnum < WORDS_PER_PAGE; wordnum++)			{				apage->words[wordnum] &= bpage->words[wordnum];				if (apage->words[wordnum] != 0)					candelete = false;			}		}		return candelete;	}}/* * tbm_is_empty - is a TIDBitmap completely empty? */booltbm_is_empty(const TIDBitmap *tbm){	return (tbm->nentries == 0);}/* * tbm_begin_iterate - prepare to iterate through a TIDBitmap * * NB: after this is called, it is no longer allowed to modify the contents * of the bitmap.  However, you can call this multiple times to scan the * contents repeatedly. */voidtbm_begin_iterate(TIDBitmap *tbm){	HASH_SEQ_STATUS status;	PagetableEntry *page;	int			npages;	int			nchunks;	tbm->iterating = true;	/*	 * Reset iteration pointers.	 */	tbm->spageptr = 0;	tbm->schunkptr = 0;	tbm->schunkbit = 0;	/*	 * Nothing else to do if no entries, nor if we don't have a hashtable.	 */	if (tbm->nentries == 0 || tbm->status != TBM_HASH)		return;	/*	 * Create and fill the sorted page lists if we didn't already.	 */	if (!tbm->spages && tbm->npages > 0)		tbm->spages = (PagetableEntry **)			MemoryContextAlloc(tbm->mcxt,							   tbm->npages * sizeof(PagetableEntry *));	if (!tbm->schunks && tbm->nchunks > 0)		tbm->schunks = (PagetableEntry **)			MemoryContextAlloc(tbm->mcxt,							   tbm->nchunks * sizeof(PagetableEntry *));	hash_seq_init(&status, tbm->pagetable);	npages = nchunks = 0;	while ((page = (PagetableEntry *) hash_seq_search(&status)) != NULL)	{		if (page->ischunk)			tbm->schunks[nchunks++] = page;		else			tbm->spages[npages++] = page;	}	Assert(npages == tbm->npages);	Assert(nchunks == tbm->nchunks);	if (npages > 1)		qsort(tbm->spages, npages, sizeof(PagetableEntry *), tbm_comparator);	if (nchunks > 1)		qsort(tbm->schunks, nchunks, sizeof(PagetableEntry *), tbm_comparator);}/* * tbm_iterate - scan through next page of a TIDBitmap * * Returns a TBMIterateResult representing one page, or NULL if there are * no more pages to scan.  Pages are guaranteed to be delivered in numerical * order.  If result->ntuples < 0, then the bitmap is "lossy" and failed to * remember the exact tuples to look at on this page --- the caller must * examine all tuples on the page and check if they meet the intended * condition. */TBMIterateResult *tbm_iterate(TIDBitmap *tbm){	TBMIterateResult *output = &(tbm->output);	Assert(tbm->iterating);	/*	 * If lossy chunk pages remain, make sure we've advanced schunkptr/	 * schunkbit to the next set bit.	 */	while (tbm->schunkptr < tbm->nchunks)	{		PagetableEntry *chunk = tbm->schunks[tbm->schunkptr];		int			schunkbit = tbm->schunkbit;		while (schunkbit < PAGES_PER_CHUNK)		{			int			wordnum = WORDNUM(schunkbit);			int			bitnum = BITNUM(schunkbit);			if ((chunk->words[wordnum] & ((bitmapword) 1 << bitnum)) != 0)				break;			schunkbit++;		}		if (schunkbit < PAGES_PER_CHUNK)		{			tbm->schunkbit = schunkbit;			break;		}		/* advance to next chunk */		tbm->schunkptr++;		tbm->schunkbit = 0;	}	/*	 * If both chunk and per-page data remain, must output the numerically	 * earlier page.	 */	if (tbm->schunkptr < tbm->nchunks)	{		PagetableEntry *chunk = tbm->schunks[tbm->schunkptr];		BlockNumber chunk_blockno;		chunk_blockno = chunk->blockno + tbm->schunkbit;		if (tbm->spageptr >= tbm->npages ||			chunk_blockno < tbm->spages[tbm->spageptr]->blockno)		{			/* Return a lossy page indicator from the chunk */			output->blockno = chunk_blockno;			output->ntuples = -1;			tbm->schunkbit++;			return output;		}	}	if (tbm->spageptr < tbm->npages)	{		PagetableEntry *page;		int			ntuples;		int			wordnum;		/* In ONE_PAGE state, we don't allocate an spages[] array */		if (tbm->status == TBM_ONE_PAGE)			page = &tbm->entry1;		else			page = tbm->spages[tbm->spageptr];		/* scan bitmap to extract individual offset numbers */		ntuples = 0;		for (wordnum = 0; wordnum < WORDS_PER_PAGE; wordnum++)		{			bitmapword	w = page->words[wordnum];			if (w != 0)			{				int			off = wordnum * BITS_PER_BITMAPWORD + 1;				while (w != 0)				{					if (w & 1)						output->offsets[ntuples++] = (OffsetNumber) off;					off++;					w >>= 1;				}			}		}		output->blockno = page->blockno;		output->ntuples = ntuples;		tbm->spageptr++;		return output;	}	/* Nothing more in the bitmap */	return NULL;}/* * tbm_find_pageentry - find a PagetableEntry for the pageno * * Returns NULL if there is no non-lossy entry for the pageno. */static const PagetableEntry *tbm_find_pageentry(const TIDBitmap *tbm, BlockNumber pageno){	const PagetableEntry *page;	if (tbm->nentries == 0)		/* in case pagetable doesn't exist */		return NULL;	if (tbm->status == TBM_ONE_PAGE)	{		page = &tbm->entry1;		if (page->blockno != pageno)			return NULL;		Assert(!page->ischunk);		return page;	}	page = (PagetableEntry *) hash_search(tbm->pagetable,										  (void *) &pageno,										  HASH_FIND, NULL);	if (page == NULL)		return NULL;	if (page->ischunk)		return NULL;			/* don't want a lossy chunk header */	return page;}/* * tbm_get_pageentry - find or create a PagetableEntry for the pageno * * If new, the entry is marked as an exact (non-chunk) entry. * * This may cause the table to exceed the desired memory size.	It is * up to the caller to call tbm_lossify() at the next safe point if so. */static PagetableEntry *tbm_get_pageentry(TIDBitmap *tbm, BlockNumber pageno){	PagetableEntry *page;	bool		found;	if (tbm->status == TBM_EMPTY)	{		/* Use the fixed slot */		page = &tbm->entry1;		found = false;		tbm->status = TBM_ONE_PAGE;	}	else	{		if (tbm->status == TBM_ONE_PAGE)		{			page = &tbm->entry1;			if (page->blockno == pageno)				return page;			/* Time to switch from one page to a hashtable */			tbm_create_pagetable(tbm);		}		/* Look up or create an entry */		page = (PagetableEntry *) hash_search(tbm->pagetable,											  (void *) &pageno,											  HASH_ENTER, &found);	}	/* Initialize it if not present before */	if (!found)	{		MemSet(page, 0, sizeof(PagetableEntry));		page->blockno = pageno;		/* must count it too */		tbm->nentries++;		tbm->npages++;	}	return page;}/* * tbm_page_is_lossy - is the page marked as lossily stored? */static booltbm_page_is_lossy(const TIDBitmap *tbm, BlockNumber pageno){	PagetableEntry *page;	BlockNumber chunk_pageno;	int			bitno;	/* we can skip the lookup if there are no lossy chunks */	if (tbm->nchunks == 0)		return false;	Assert(tbm->status == TBM_HASH);	bitno = pageno % PAGES_PER_CHUNK;	chunk_pageno = pageno - bitno;	page = (PagetableEntry *) hash_search(tbm->pagetable,										  (void *) &chunk_pageno,										  HASH_FIND, NULL);	if (page != NULL && page->ischunk)	{		int			wordnum = WORDNUM(bitno);		int			bitnum = BITNUM(bitno);		if ((page->words[wordnum] & ((bitmapword) 1 << bitnum)) != 0)			return true;	}	return false;}/* * tbm_mark_page_lossy - mark the page number as lossily stored * * This may cause the table to exceed the desired memory size.	It is * up to the caller to call tbm_lossify() at the next safe point if so. */static voidtbm_mark_page_lossy(TIDBitmap *tbm, BlockNumber pageno){	PagetableEntry *page;	bool		found;	BlockNumber chunk_pageno;	int			bitno;	int			wordnum;	int			bitnum;	/* We force the bitmap into hashtable mode whenever it's lossy */	if (tbm->status != TBM_HASH)		tbm_create_pagetable(tbm);	bitno = pageno % PAGES_PER_CHUNK;	chunk_pageno = pageno - bitno;	/*	 * Remove any extant non-lossy entry for the page.	If the page is its own	 * chunk header, however, we skip this and handle the case below.	 */	if (bitno != 0)	{		if (hash_search(tbm->pagetable,						(void *) &pageno,						HASH_REMOVE, NULL) != NULL)		{			/* It was present, so adjust counts */			tbm->nentries--;			tbm->npages--;		/* assume it must have been non-lossy */		}	}	/* Look up or create entry for chunk-header page */	page = (PagetableEntry *) hash_search(tbm->pagetable,										  (void *) &chunk_pageno,										  HASH_ENTER, &found);	/* Initialize it if not present before */	if (!found)	{		MemSet(page, 0, sizeof(PagetableEntry));		page->blockno = chunk_pageno;		page->ischunk = true;		/* must count it too */		tbm->nentries++;		tbm->nchunks++;	}	else if (!page->ischunk)	{		/* chunk header page was formerly non-lossy, make it lossy */		MemSet(page, 0, sizeof(PagetableEntry));		page->blockno = chunk_pageno;		page->ischunk = true;		/* we assume it had some tuple bit(s) set, so mark it lossy */		page->words[0] = ((bitmapword) 1 << 0);		/* adjust counts */		tbm->nchunks++;		tbm->npages--;	}	/* Now set the original target page's bit */	wordnum = WORDNUM(bitno);	bitnum = BITNUM(bitno);	page->words[wordnum] |= ((bitmapword) 1 << bitnum);}/* * tbm_lossify - lose some information to get back under the memory limit */static voidtbm_lossify(TIDBitmap *tbm){	HASH_SEQ_STATUS status;	PagetableEntry *page;	/*	 * XXX Really stupid implementation: this just lossifies pages in	 * essentially random order.  We should be paying some attention to the	 * number of bits set in each page, instead.  Also it might be a good idea	 * to lossify more than the minimum number of pages during each call.	 */	Assert(!tbm->iterating);	Assert(tbm->status == TBM_HASH);	hash_seq_init(&status, tbm->pagetable);	while ((page = (PagetableEntry *) hash_seq_search(&status)) != NULL)	{		if (page->ischunk)			continue;			/* already a chunk header */		/*		 * If the page would become a chunk header, we won't save anything by		 * converting it to lossy, so skip it.		 */		if ((page->blockno % PAGES_PER_CHUNK) == 0)			continue;		/* This does the dirty work ... */		tbm_mark_page_lossy(tbm, page->blockno);		if (tbm->nentries <= tbm->maxentries)			return;				/* we have done enough */		/*		 * Note: tbm_mark_page_lossy may have inserted a lossy chunk into the		 * hashtable.  We can continue the same seq_search scan since we do		 * not care whether we visit lossy chunks or not.		 */	}}/* * qsort comparator to handle PagetableEntry pointers. */static inttbm_comparator(const void *left, const void *right){	BlockNumber l = (*((const PagetableEntry **) left))->blockno;	BlockNumber r = (*((const PagetableEntry **) right))->blockno;	if (l < r)		return -1;	else if (l > r)		return 1;	return 0;}

⌨️ 快捷键说明

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