/* number of active words for an exact page: */ #define WORDS_PER_PAGE ((TBM_MAX_TUPLES_PER_PAGE - 1) / BITS_PER_BITMAPWORD + 1) /* number of active words for a lossy chunk: */ #define WORDS_PER_CHUNK ((PAGES_PER_CHUNK - 1) / BITS_PER_BITMAPWORD + 1)
/* *Thehashtableentriesarerepresentedbythisdatastructure.For *anexactpage,blocknoisthepagenumberandbitkofthebitmap *representstupleoffsetk+1.Foralossychunk,blocknoisthefirst *pageinthechunk(thismustbeamultipleofPAGES_PER_CHUNK)and *bitkrepresentspageblockno+k.Notethatitisnotpossibleto *haveexactstorageforthefirstpageofachunkifweareusing *lossystorageforanypageinthechunk'srange,sincethesame *hashtableentryhastoservebothpurposes. * *recheckisusedonlyonexactpages---itindicatesthatalthough *onlythestatedtuplesneedbechecked,thefullindexqualcondition *mustbecheckedforeach(ie,thesearecandidatematches).
*/ typedefstruct PagetableEntry
{
BlockNumber blockno; /* page number (hashtable key) */ char status; /* hash entry status */ bool ischunk; /* T = lossy storage, F = exact */ bool recheck; /* should the tuples be rechecked? */
bitmapword words[Max(WORDS_PER_PAGE, WORDS_PER_CHUNK)];
} PagetableEntry;
/* *Wewanttoavoidtheoverheadofcreatingthehashtable,whichis *comparativelylarge,whennotnecessary.Particularlywhenweareusinga *bitmapscanontheinsideofanestloopjoin:abitmapmaywellliveonly *longenoughtoaccumulateoneentryinsuchcases.Wethereforeavoid *creatinganactualhashtableuntilweneedtwopagetableentries.When *justonepagetableentryisneeded,westoreitinafixedfieldof *TIDBitMap.(NOTE:wedon'tgetridofthehashtableifthebitmaplater *shrinksdowntozerooronepageagain.So,statuscanbeTBM_HASHeven *whennentriesiszeroorone.)
*/ typedefenum
{
TBM_EMPTY, /* no hashtable, nentries == 0 */
TBM_ONE_PAGE, /* entry1 contains the single entry */
TBM_HASH, /* pagetable is valid, entry1 is not */
} TBMStatus;
/* *CurrentiteratingstateoftheTBM.
*/ typedefenum
{
TBM_NOT_ITERATING, /* not yet converted to page and chunk array */
TBM_ITERATING_PRIVATE, /* converted to local page and chunk array */
TBM_ITERATING_SHARED, /* converted to shared page and chunk array */
} TBMIteratingState;
/* *HereistherepresentationforawholeTIDBitMap:
*/ struct TIDBitmap
{
NodeTag type; /* to make it a valid Node */
MemoryContext mcxt; /* memory context containing me */
TBMStatus status; /* see codes above */ struct pagetable_hash *pagetable; /* hash table of PagetableEntry's */ int nentries; /* number of entries in pagetable */ int maxentries; /* limit on same to meet maxbytes */ int npages; /* number of exact entries in pagetable */ int nchunks; /* number of lossy entries in pagetable */
TBMIteratingState iterating; /* tbm_begin_iterate called? */
uint32 lossify_start; /* offset to start lossifying hashtable at */
PagetableEntry entry1; /* used when status == TBM_ONE_PAGE */ /* these are valid when iterating is true: */
PagetableEntry **spages; /* sorted exact-page list, or NULL */
PagetableEntry **schunks; /* sorted lossy-chunk list, or NULL */
dsa_pointer dsapagetable; /* dsa_pointer to the element array */
dsa_pointer dsapagetableold; /* dsa_pointer to the old element array */
dsa_pointer ptpages; /* dsa_pointer to the page array */
dsa_pointer ptchunks; /* dsa_pointer to the chunk array */
dsa_area *dsa; /* reference to per-query dsa area */
};
/* *Wheniteratingoverabackend-localbitmapinsortedorder,a *TBMPrivateIteratorisusedtotrackourprogress.Therecanbeseveral *iteratorsscanningthesamebitmapconcurrently.Notethatthebitmap *becomesread-onlyassoonasanyiteratoriscreated.
*/ struct TBMPrivateIterator
{
TIDBitmap *tbm; /* TIDBitmap we're iterating over */ int spageptr; /* next spages index */ int schunkptr; /* next schunks index */ int schunkbit; /* next bit to check in current schunk */
};
/* *Holdsthesharedmembersoftheiteratorsothatmultipleprocesses *canjointlyiterate.
*/ typedefstruct TBMSharedIteratorState
{ int nentries; /* number of entries in pagetable */ int maxentries; /* limit on same to meet maxbytes */ int npages; /* number of exact entries in pagetable */ int nchunks; /* number of lossy entries in pagetable */
dsa_pointer pagetable; /* dsa pointers to head of pagetable data */
dsa_pointer spages; /* dsa pointer to page array */
dsa_pointer schunks; /* dsa pointer to chunk array */
LWLock lock; /* lock to protect below members */ int spageptr; /* next spages index */ int schunkptr; /* next schunks index */ int schunkbit; /* next bit to check in current schunk */
} TBMSharedIteratorState;
/* *pagetableiterationarray.
*/ typedefstruct PTIterationArray
{
pg_atomic_uint32 refcount; /* no. of iterator attached */ int index[FLEXIBLE_ARRAY_MEMBER]; /* index array */
} PTIterationArray;
/* *sameasTBMPrivateIterator,butitisusedforjointiteration,therefore *thisalsoholdsareferencetothesharedstate.
*/ struct TBMSharedIterator
{
TBMSharedIteratorState *state; /* shared state */
PTEntryArray *ptbase; /* pagetable element array */
PTIterationArray *ptpages; /* sorted exact page index list */
PTIterationArray *ptchunks; /* sorted lossy page index list */
};
if (DsaPointerIsValid(istate->pagetable))
{
ptbase = dsa_get_address(dsa, istate->pagetable); if (pg_atomic_sub_fetch_u32(&ptbase->refcount, 1) == 0)
dsa_free(dsa, istate->pagetable);
} if (DsaPointerIsValid(istate->spages))
{
ptpages = dsa_get_address(dsa, istate->spages); if (pg_atomic_sub_fetch_u32(&ptpages->refcount, 1) == 0)
dsa_free(dsa, istate->spages);
} if (DsaPointerIsValid(istate->schunks))
{
ptchunks = dsa_get_address(dsa, istate->schunks); if (pg_atomic_sub_fetch_u32(&ptchunks->refcount, 1) == 0)
dsa_free(dsa, istate->schunks);
}
dsa_free(dsa, dp);
}
/* *tbm_add_tuples-addsometupleIDstoaTIDBitmap * *Ifrecheckistrue,thentherecheckflagwillbesetinthe *TBMIterateResultwhenanyofthesetuplesarereportedout.
*/ void
tbm_add_tuples(TIDBitmap *tbm, const ItemPointer tids, int ntids, bool recheck)
{
BlockNumber currblk = InvalidBlockNumber;
PagetableEntry *page = NULL; /* only valid when currblk is valid */ int i;
Assert(tbm->iterating == TBM_NOT_ITERATING); for (i = 0; i < ntids; i++)
{
BlockNumber blk = ItemPointerGetBlockNumber(tids + i);
OffsetNumber off = ItemPointerGetOffsetNumber(tids + i); int wordnum,
bitnum;
/* safety check to ensure we don't overrun bit array bounds */ if (off < 1 || off > TBM_MAX_TUPLES_PER_PAGE)
elog(ERROR, "tuple offset out of range: %u", off);
if (page == NULL) continue; /* whole page is already marked */
if (page->ischunk)
{ /* The page is a lossy chunk header, set bit for itself */
wordnum = bitnum = 0;
} else
{ /* Page is exact, so set bit for individual tuple */
wordnum = WORDNUM(off - 1);
bitnum = BITNUM(off - 1);
}
page->words[wordnum] |= ((bitmapword) 1 << bitnum);
page->recheck |= recheck;
if (tbm->nentries > tbm->maxentries)
{
tbm_lossify(tbm); /* Page could have been converted to lossy, so force new lookup */
currblk = InvalidBlockNumber;
}
}
}
/* *tbm_add_page-addawholepagetoaTIDBitmap * *Thiscausesthewholepagetobereported(withtherecheckflag) *whentheTIDBitmapisscanned.
*/ void
tbm_add_page(TIDBitmap *tbm, BlockNumber pageno)
{ /* Enter the page in the bitmap, or mark it lossy if already present */
tbm_mark_page_lossy(tbm, pageno); /* If we went over the memory limit, lossify some more pages */ if (tbm->nentries > tbm->maxentries)
tbm_lossify(tbm);
}
/* *tbm_union-setunion * *aismodifiedin-place,bisnotchanged
*/ void
tbm_union(TIDBitmap *a, const TIDBitmap *b)
{
Assert(!a->iterating); /* Nothing to do if b is empty */ if (b->nentries == 0) return; /* Scan through chunks and pages in b, merge into a */ if (b->status == TBM_ONE_PAGE)
tbm_union_page(a, &b->entry1); else
{
pagetable_iterator i;
PagetableEntry *bpage;
/* Process one page of b during a union op */ staticvoid
tbm_union_page(TIDBitmap *a, const PagetableEntry *bpage)
{
PagetableEntry *apage; int wordnum;
if (bpage->ischunk)
{ /* Scan b's chunk, mark each indicated page lossy in a */ for (wordnum = 0; wordnum < WORDS_PER_CHUNK; wordnum++)
{
bitmapword w = bpage->words[wordnum];
if (w != 0)
{
BlockNumber pg;
pg = bpage->blockno + (wordnum * BITS_PER_BITMAPWORD); while (w != 0)
{ if (w & 1)
tbm_mark_page_lossy(a, pg);
pg++;
w >>= 1;
}
}
}
} elseif (tbm_page_is_lossy(a, bpage->blockno))
{ /* page is already lossy in a, nothing to do */ return;
} else
{
apage = tbm_get_pageentry(a, bpage->blockno); if (apage->ischunk)
{ /* The page is a lossy chunk header, set bit for itself */
apage->words[0] |= ((bitmapword) 1 << 0);
} else
{ /* Both pages are exact, merge at the bit level */ for (wordnum = 0; wordnum < WORDS_PER_PAGE; wordnum++)
apage->words[wordnum] |= bpage->words[wordnum];
apage->recheck |= bpage->recheck;
}
}
if (a->nentries > a->maxentries)
tbm_lossify(a);
}
/* *tbm_intersect-setintersection * *aismodifiedin-place,bisnotchanged
*/ void
tbm_intersect(TIDBitmap *a, const TIDBitmap *b)
{
Assert(!a->iterating); /* Nothing to do if a is empty */ if (a->nentries == 0) return; /* Scan through chunks and pages in a, try to match to b */ if (a->status == TBM_ONE_PAGE)
{ if (tbm_intersect_page(a, &a->entry1, b))
{ /* Page is now empty, remove it from a */
Assert(!a->entry1.ischunk);
a->npages--;
a->nentries--;
Assert(a->nentries == 0);
a->status = TBM_EMPTY;
}
} else
{
pagetable_iterator i;
PagetableEntry *apage;
Assert(a->status == TBM_HASH);
pagetable_start_iterate(a->pagetable, &i); while ((apage = pagetable_iterate(a->pagetable, &i)) != NULL)
{ if (tbm_intersect_page(a, apage, b))
{ /* Page or chunk is now empty, remove it from a */ if (apage->ischunk)
a->nchunks--; else
a->npages--;
a->nentries--; if (!pagetable_delete(a->pagetable, apage->blockno))
elog(ERROR, "hash table corrupted");
}
}
}
}
if (apage->ischunk)
{ /* Scan each bit in chunk, try to clear */ bool candelete = true;
for (wordnum = 0; wordnum < WORDS_PER_CHUNK; wordnum++)
{
bitmapword w = apage->words[wordnum];
if (w != 0)
{
bitmapword neww = w;
BlockNumber pg; int bitnum;
pg = apage->blockno + (wordnum * BITS_PER_BITMAPWORD);
bitnum = 0; while (w != 0)
{ if (w & 1)
{ if (!tbm_page_is_lossy(b, pg) &&
tbm_find_pageentry(b, pg) == NULL)
{ /* Page is not in b at all, lose lossy bit */
neww &= ~((bitmapword) 1 << bitnum);
}
}
pg++;
bitnum++;
w >>= 1;
}
apage->words[wordnum] = neww; if (neww != 0)
candelete = false;
}
} return candelete;
} elseif (tbm_page_is_lossy(b, apage->blockno))
{ /* *Someofthetuplesin'a'mightnotsatisfythequalsfor'b',but *becausethepage'b'islossy,wedon'tknowwhichones.Therefore *wemark'a'asrequiringrechecks,toindicatethatatmostthose *tuplessetin'a'arematches.
*/
apage->recheck = true; returnfalse;
} 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;
}
apage->recheck |= bpage->recheck;
} /* If there is no matching b page, we can just delete the a page */ return candelete;
}
}
/* *Ifwe'renotalreadyiterating,createandfillthesortedpagelists. *(Ifweare,thesortedpagelistsarealreadystoredintheTIDBitmap, *andwecanjustreusethem.)
*/ if (tbm->iterating == TBM_NOT_ITERATING)
{
pagetable_iterator i;
PagetableEntry *page; int idx; int npages; int nchunks;
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 = pagetable_insert(tbm->pagetable, pageno, &found);
}
/* Initialize it if not present before */ if (!found)
{ char oldstatus = page->status;
MemSet(page, 0, sizeof(PagetableEntry));
page->status = oldstatus;
page->blockno = pageno; /* must count it too */
tbm->nentries++;
tbm->npages++;
}
/* *tbm_mark_page_lossy-markthepagenumberaslossilystored * *Thismaycausethetabletoexceedthedesiredmemorysize.Itis *uptothecallertocalltbm_lossify()atthenextsafepointifso.
*/ staticvoid
tbm_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;
/* *Removeanyextantnon-lossyentryforthepage.Ifthepageisitsown *chunkheader,however,weskipthisandhandlethecasebelow.
*/ if (bitno != 0)
{ if (pagetable_delete(tbm->pagetable, pageno))
{ /* 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 = pagetable_insert(tbm->pagetable, chunk_pageno, &found);
/* Initialize it if not present before */ if (!found)
{ char oldstatus = page->status;
MemSet(page, 0, sizeof(PagetableEntry));
page->status = oldstatus;
page->blockno = chunk_pageno;
page->ischunk = true; /* must count it too */
tbm->nentries++;
tbm->nchunks++;
} elseif (!page->ischunk)
{ char oldstatus = page->status;
/* chunk header page was formerly non-lossy, make it lossy */
MemSet(page, 0, sizeof(PagetableEntry));
page->status = oldstatus;
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);
}
/* pfree the input pointer if DSA is not available */ if (tbm->dsa == NULL)
pfree(pointer); elseif (DsaPointerIsValid(tbm->dsapagetableold))
{
dsa_free(tbm->dsa, tbm->dsapagetableold);
tbm->dsapagetableold = InvalidDsaPointer;
}
}
/* Allocate a private iterator and attach the shared state to it */ if (DsaPointerIsValid(dsp))
{
iterator.shared = true;
iterator.i.shared_iterator = tbm_attach_shared_iterate(dsa, dsp);
} else
{
iterator.shared = false;
iterator.i.private_iterator = tbm_begin_private_iterate(tbm);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.