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 + -
显示快捷键?