diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c index 60f005c..87cd9ea 100644 --- a/src/backend/access/gin/ginget.c +++ b/src/backend/access/gin/ginget.c @@ -120,7 +120,7 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack, Form_pg_attribute attr; /* Initialize empty bitmap result */ - scanEntry->matchBitmap = tbm_create(work_mem * 1024L); + scanEntry->matchBitmap = tbm_create(work_mem * 1024L, NULL); /* Null query cannot partial-match anything */ if (scanEntry->isPartialMatch && diff --git a/src/backend/executor/nodeBitmapIndexscan.c b/src/backend/executor/nodeBitmapIndexscan.c index 4274e9a..94bb289 100644 --- a/src/backend/executor/nodeBitmapIndexscan.c +++ b/src/backend/executor/nodeBitmapIndexscan.c @@ -78,7 +78,7 @@ MultiExecBitmapIndexScan(BitmapIndexScanState *node) else { /* XXX should we use less than work_mem for this? */ - tbm = tbm_create(work_mem * 1024L); + tbm = tbm_create(work_mem * 1024L, NULL); } /* diff --git a/src/backend/executor/nodeBitmapOr.c b/src/backend/executor/nodeBitmapOr.c index 3c6155b..1d280be 100644 --- a/src/backend/executor/nodeBitmapOr.c +++ b/src/backend/executor/nodeBitmapOr.c @@ -129,7 +129,7 @@ MultiExecBitmapOr(BitmapOrState *node) if (result == NULL) /* first subplan */ { /* XXX should we use less than work_mem for this? */ - result = tbm_create(work_mem * 1024L); + result = tbm_create(work_mem * 1024L, NULL); } ((BitmapIndexScanState *) subnode)->biss_result = result; diff --git a/src/backend/nodes/tidbitmap.c b/src/backend/nodes/tidbitmap.c index 0885812..ac6bb7b 100644 --- a/src/backend/nodes/tidbitmap.c +++ b/src/backend/nodes/tidbitmap.c @@ -43,6 +43,8 @@ #include "access/htup_details.h" #include "nodes/bitmapset.h" #include "nodes/tidbitmap.h" +#include "storage/lwlock.h" +#include "utils/dsa.h" /* * The maximum number of tuples per page is not large (typically 256 with @@ -121,6 +123,16 @@ typedef enum } TBMStatus; /* + * Current iterating state of the TBM. + */ +typedef enum +{ + 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; + +/* * Here is the representation for a whole TIDBitMap: */ struct TIDBitmap @@ -133,12 +145,17 @@ struct TIDBitmap int maxentries; /* limit on same to meet maxbytes */ int npages; /* number of exact entries in pagetable */ int nchunks; /* number of lossy entries in pagetable */ - bool iterating; /* tbm_begin_iterate called? */ + 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_area *dsa; /* reference to per-query dsa area */ + dsa_pointer ptpages; /* dsa_pointer to the page array */ + dsa_pointer ptchunks; /* dsa_pointer to the chunk array */ }; /* @@ -156,6 +173,40 @@ struct TBMIterator TBMIterateResult output; /* MUST BE LAST (because variable-size) */ }; +/* + * This stores the shared members of TBMSharedIterator so that multiple + * workers can operate on the same state. It also stores the TBMSharedInfo, + * in order to share relptrs of the chunk and the pages arrays and other + * TBM related information with other workers. + */ +typedef struct TBMSharedIteratorState +{ + int spageptr; /* next spages index */ + int schunkptr; /* next schunks index */ + int schunkbit; /* next bit to check in current schunk */ + LWLock lock; /* lock to protect above members */ + 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 */ + 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 */ +} TBMSharedIteratorState; + +/* + * same as TBMIterator except that it holds a reference to the shared + * memory state so that multiple workers could operate on the same state. + */ +struct TBMSharedIterator +{ + PagetableEntry *ptbase; /* pointer to the pagetable element array */ + dsa_area *dsa; /* reference to per-query dsa area */ + int *ptpages; /* sorted exact page index list */ + int *ptchunks; /* sorted lossy page index list */ + TBMSharedIteratorState *state; /* shared members */ + TBMIterateResult output; /* MUST BE LAST (because variable-size) */ +}; /* Local function prototypes */ static void tbm_union_page(TIDBitmap *a, const PagetableEntry *bpage); @@ -168,6 +219,8 @@ static bool tbm_page_is_lossy(const TIDBitmap *tbm, BlockNumber pageno); static void tbm_mark_page_lossy(TIDBitmap *tbm, BlockNumber pageno); static void tbm_lossify(TIDBitmap *tbm); static int tbm_comparator(const void *left, const void *right); +static int tbm_shared_comparator(const void *left, const void *right, + void *arg); /* * Simple inline murmur hash implementation for the exact width required, for @@ -187,6 +240,7 @@ hash_blockno(BlockNumber b) } /* define hashtable mapping block numbers to PagetableEntry's */ +#define SH_USE_NONDEFAULT_ALLOCATOR #define SH_PREFIX pagetable #define SH_ELEMENT_TYPE PagetableEntry #define SH_KEY_TYPE BlockNumber @@ -204,10 +258,12 @@ hash_blockno(BlockNumber b) * * The bitmap will live in the memory context that is CurrentMemoryContext * at the time of this call. It will be limited to (approximately) maxbytes - * total memory consumption. + * total memory consumption. If dsa passed to this function is not NULL + * then the memory for storing elements of the underlying page table will + * be allocated from the DSA. */ TIDBitmap * -tbm_create(long maxbytes) +tbm_create(long maxbytes, dsa_area *dsa) { TIDBitmap *tbm; long nbuckets; @@ -230,6 +286,7 @@ tbm_create(long maxbytes) nbuckets = Max(nbuckets, 16); /* sanity limit */ tbm->maxentries = (int) nbuckets; tbm->lossify_start = 0; + tbm->dsa = dsa; return tbm; } @@ -244,7 +301,7 @@ tbm_create_pagetable(TIDBitmap *tbm) Assert(tbm->status != TBM_HASH); Assert(tbm->pagetable == NULL); - tbm->pagetable = pagetable_create(tbm->mcxt, 128, NULL); + tbm->pagetable = pagetable_create(tbm->mcxt, 128, tbm); /* If entry1 is valid, push it into the hashtable */ if (tbm->status == TBM_ONE_PAGE) @@ -294,7 +351,7 @@ tbm_add_tuples(TIDBitmap *tbm, const ItemPointer tids, int ntids, PagetableEntry *page = NULL; /* only valid when currblk is valid */ int i; - Assert(!tbm->iterating); + Assert(tbm->iterating == TBM_NOT_ITERATING); for (i = 0; i < ntids; i++) { BlockNumber blk = ItemPointerGetBlockNumber(tids + i); @@ -624,7 +681,7 @@ tbm_begin_iterate(TIDBitmap *tbm) * attached to the bitmap not the iterator, so they can be used by more * than one iterator. */ - if (tbm->status == TBM_HASH && !tbm->iterating) + if (tbm->status == TBM_HASH && (tbm->iterating == TBM_NOT_ITERATING)) { pagetable_iterator i; PagetableEntry *page; @@ -659,12 +716,189 @@ tbm_begin_iterate(TIDBitmap *tbm) tbm_comparator); } - tbm->iterating = true; + tbm->iterating = TBM_ITERATING_PRIVATE; return iterator; } /* + * tbm_prepare_shared_iterate - prepare to iterator through a TIDBitmap + * by multiple workers using shared iterator. + * + * The TBMSharedIteratorState will be allocated from DSA so that multiple + * worker can attach to it and iterate jointly. + * + * This will convert the pagetable hash into page and chunk array of the index + * into pagetable array so that multiple workers can use it to get the actual + * page entry. + */ +dsa_pointer +tbm_prepare_shared_iterate(TIDBitmap *tbm) +{ + dsa_pointer iterator; + TBMSharedIteratorState *iterator_state; + + /* + * Allocate TBMSharedIteratorState from DSA to hold the shared members and + * lock, this will also be used by multiple worker for shared iterate. + */ + iterator = dsa_allocate(tbm->dsa, sizeof(TBMSharedIteratorState)); + iterator_state = dsa_get_address(tbm->dsa, iterator); + + /* + * If we have a hashtable, create and fill the sorted page lists, unless + * we already did that for a previous iterator. Note that the lists are + * attached to the bitmap not the iterator, so they can be used by more + * than one iterator. However, we keep dsa_pointer to these in the shared + * iterator so that other workers can access them directly. + */ + if (tbm->iterating == TBM_NOT_ITERATING) + { + pagetable_iterator i; + PagetableEntry *ptbase; + PagetableEntry *page; + int idx; + int npages; + int nchunks; + int *ptpages; + int *ptchunks; + + /* + * Create page list and chunk list using relptr so that we can share + * this information across multiple workers. + */ + if (tbm->npages) + { + tbm->ptpages = dsa_allocate(tbm->dsa, tbm->npages * (sizeof(int))); + ptpages = dsa_get_address(tbm->dsa, tbm->ptpages); + } + if (tbm->nchunks) + { + tbm->ptchunks = dsa_allocate(tbm->dsa, + tbm->nchunks * (sizeof(int))); + ptchunks = dsa_get_address(tbm->dsa, tbm->ptchunks); + } + + /* + * If TBM status is TBM_HASH then iterate over the pagetable and + * convert it to page and chunk arrays. But if it's in TBM_ONE_PAGE + * mode then directly allocate the space for one entry from the DSA. + */ + npages = nchunks = 0; + if (tbm->status == TBM_HASH) + { + ptbase = dsa_get_address(tbm->dsa, tbm->dsapagetable); + + pagetable_start_iterate(tbm->pagetable, &i); + while ((page = pagetable_iterate(tbm->pagetable, &i)) != NULL) + { + idx = page - ptbase; + if (page->ischunk) + ptchunks[nchunks++] = idx; + else + ptpages[npages++] = idx; + } + + Assert(npages == tbm->npages); + Assert(nchunks == tbm->nchunks); + } + else + { + /* + * In one page mode allocate the space for one pagetable entry and + * directly store it's index i.e. 0 in page array + */ + tbm->dsapagetable = dsa_allocate(tbm->dsa, sizeof(PagetableEntry)); + ptbase = dsa_get_address(tbm->dsa, tbm->dsapagetable); + ptpages[0] = 0; + } + + if (npages > 1) + qsort_arg((void *) ptpages, npages, sizeof(int), + tbm_shared_comparator, (void *) ptbase); + if (nchunks > 1) + qsort_arg((void *) ptchunks, nchunks, sizeof(int), + tbm_shared_comparator, (void *) ptbase); + } + + /* + * Store the TBM member in the shared state so that we can share them + * across multiple workers. + */ + iterator_state->maxentries = tbm->maxentries; + iterator_state->nchunks = tbm->nchunks; + iterator_state->nentries = tbm->nentries; + iterator_state->npages = tbm->npages; + iterator_state->pagetable = tbm->dsapagetable; + iterator_state->spages = tbm->ptpages; + iterator_state->schunks = tbm->ptchunks; + + /* Initialize the shared iterator state */ + iterator_state->schunkbit = 0; + iterator_state->schunkptr = 0; + iterator_state->spageptr = 0; + + /* Initialize the iterator lock */ + LWLockInitialize(&iterator_state->lock, LWTRANCHE_PARALLEL_TBM_ITERATOR); + + tbm->iterating = TBM_ITERATING_SHARED; + + return iterator; +} + +/* + * tbm_extract_page_tuple - extract the tuple offsets from the page + * + * Process the page bits to extract the tuple offsets and store them into + * TBMIterateResult. + */ +static inline int +tbm_extract_page_tuple(PagetableEntry *page, TBMIterateResult *output) +{ + int wordnum; + int 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; + } + } + } + + return ntuples; +} + +/* + * tbm_advance_schunkbit + * + * Advance the chunkbit to get the next page entry from the chunk + */ +static inline void +tbm_advance_schunkbit(PagetableEntry *chunk, int *schunkbit) +{ + while (*schunkbit < PAGES_PER_CHUNK) + { + int wordnum = WORDNUM(*schunkbit); + int bitnum = BITNUM(*schunkbit); + + if ((chunk->words[wordnum] & ((bitmapword) 1 << bitnum)) != 0) + break; + (*schunkbit)++; + } +} + +/* * tbm_iterate - scan through next page of a TIDBitmap * * Returns a TBMIterateResult representing one page, or NULL if there are @@ -682,7 +916,7 @@ tbm_iterate(TBMIterator *iterator) TIDBitmap *tbm = iterator->tbm; TBMIterateResult *output = &(iterator->output); - Assert(tbm->iterating); + Assert(tbm->iterating == TBM_ITERATING_PRIVATE); /* * If lossy chunk pages remain, make sure we've advanced schunkptr/ @@ -693,15 +927,7 @@ tbm_iterate(TBMIterator *iterator) PagetableEntry *chunk = tbm->schunks[iterator->schunkptr]; int schunkbit = iterator->schunkbit; - while (schunkbit < PAGES_PER_CHUNK) - { - int wordnum = WORDNUM(schunkbit); - int bitnum = BITNUM(schunkbit); - - if ((chunk->words[wordnum] & ((bitmapword) 1 << bitnum)) != 0) - break; - schunkbit++; - } + tbm_advance_schunkbit(chunk, &schunkbit); if (schunkbit < PAGES_PER_CHUNK) { iterator->schunkbit = schunkbit; @@ -738,7 +964,6 @@ tbm_iterate(TBMIterator *iterator) { PagetableEntry *page; int ntuples; - int wordnum; /* In ONE_PAGE state, we don't allocate an spages[] array */ if (tbm->status == TBM_ONE_PAGE) @@ -747,24 +972,7 @@ tbm_iterate(TBMIterator *iterator) page = tbm->spages[iterator->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; - } - } - } + ntuples = tbm_extract_page_tuple(page, output); output->blockno = page->blockno; output->ntuples = ntuples; output->recheck = page->recheck; @@ -777,6 +985,95 @@ tbm_iterate(TBMIterator *iterator) } /* + *------------------------ + * tbm_shared_iterate + * + * As above, but this will iterate using shared iterator which is shared + * across multiple workers. We need to acquire the iterator LWLock, before + * accessing the shared members. + *---------------------- + */ +TBMIterateResult * +tbm_shared_iterate(TBMSharedIterator *iterator) +{ + TBMIterateResult *output = &iterator->output; + TBMSharedIteratorState *state = iterator->state; + PagetableEntry *ptbase = iterator->ptbase; + int *ptpages = iterator->ptpages; + int *ptchunks = iterator->ptchunks; + + /* Acquire the LWLock before accessing the shared members */ + LWLockAcquire(&state->lock, LW_EXCLUSIVE); + + /* + * If lossy chunk pages remain, make sure we've advanced schunkptr/ + * schunkbit to the next set bit. + */ + while (state->schunkptr < state->nchunks) + { + PagetableEntry *chunk = ptbase + ptchunks[state->schunkptr]; + int schunkbit = state->schunkbit; + + tbm_advance_schunkbit(chunk, &schunkbit); + if (schunkbit < PAGES_PER_CHUNK) + { + state->schunkbit = schunkbit; + break; + } + /* advance to next chunk */ + state->schunkptr++; + state->schunkbit = 0; + } + + /* + * If both chunk and per-page data remain, must output the numerically + * earlier page. + */ + if (state->schunkptr < state->nchunks) + { + PagetableEntry *chunk = ptbase + ptchunks[state->schunkptr]; + PagetableEntry *page = ptbase + ptpages[state->spageptr]; + BlockNumber chunk_blockno; + + chunk_blockno = chunk->blockno + state->schunkbit; + + if (state->spageptr >= state->npages || chunk_blockno < page->blockno) + { + /* Return a lossy page indicator from the chunk */ + output->blockno = chunk_blockno; + output->ntuples = -1; + output->recheck = true; + state->schunkbit++; + + LWLockRelease(&state->lock); + return output; + } + } + + if (state->spageptr < state->npages) + { + PagetableEntry *page = ptbase + ptpages[state->spageptr]; + int ntuples; + + /* scan bitmap to extract individual offset numbers */ + ntuples = tbm_extract_page_tuple(page, output); + output->blockno = page->blockno; + output->ntuples = ntuples; + output->recheck = page->recheck; + state->spageptr++; + + LWLockRelease(&state->lock); + + return output; + } + + LWLockRelease(&state->lock); + + /* Nothing more in the bitmap */ + return NULL; +} + +/* * tbm_end_iterate - finish an iteration over a TIDBitmap * * Currently this is just a pfree, but it might do more someday. (For @@ -790,6 +1087,17 @@ tbm_end_iterate(TBMIterator *iterator) } /* + * tbm_end_shared_iterate - finish an iteration over a TIDBitmap + * + * As above, but it frees the memory of TBMSharedIterator. + */ +void +tbm_end_shared_iterate(TBMSharedIterator *iterator) +{ + pfree(iterator); +} + +/* * tbm_find_pageentry - find a PagetableEntry for the pageno * * Returns NULL if there is no non-lossy entry for the pageno. @@ -995,7 +1303,7 @@ tbm_lossify(TIDBitmap *tbm) * push nentries down to significantly less than maxentries, or else we'll * just end up doing this again very soon. We shoot for maxentries/2. */ - Assert(!tbm->iterating); + Assert(tbm->iterating == TBM_NOT_ITERATING); Assert(tbm->status == TBM_HASH); pagetable_start_iterate_at(tbm->pagetable, &i, tbm->lossify_start); @@ -1061,3 +1369,113 @@ tbm_comparator(const void *left, const void *right) return 1; return 0; } + +/* + * As above, but this will get index into PagetableEntry array. Therefore, it + * needs to get actual PagetableEntry using the index before comparing the + * blockno. + */ +static int +tbm_shared_comparator(const void *left, const void *right, void *arg) +{ + PagetableEntry *lpage = ((PagetableEntry *) arg) + *((int *) left); + PagetableEntry *rpage = ((PagetableEntry *) arg) + *((int *) right); + + if (lpage->blockno < rpage->blockno) + return -1; + else if (lpage->blockno > rpage->blockno) + return 1; + return 0; +} + +/* + * tbm_attach_shared_iterate + * + * Allocate an iterator and attach shared iterator state to it so that + * multiple workers can access the same memory. We also need to convert + * some of the dsa_ponter from shared state to local pointers so that + * shared_iterate can operate on those. + */ +TBMSharedIterator * +tbm_attach_shared_iterate(dsa_area *dsa, dsa_pointer iterator) +{ + TBMSharedIterator *shared_iterator; + TBMSharedIteratorState *shared_state; + + /* + * Create the TBMSharedIterator struct, with enough trailing space to + * serve the needs of the TBMIterateResult sub-struct. + */ + shared_iterator = (TBMSharedIterator *) palloc(sizeof(TBMIterator) + + MAX_TUPLES_PER_PAGE * sizeof(OffsetNumber)); + + shared_state = shared_iterator->state = + (TBMSharedIteratorState *) dsa_get_address(dsa, iterator); + + shared_iterator->ptbase = dsa_get_address(dsa, shared_state->pagetable); + + /* Convert dsa pointers to the local pointers */ + if (shared_state->npages) + shared_iterator->ptpages = dsa_get_address(dsa, shared_state->spages); + if (shared_state->nchunks) + shared_iterator->ptchunks = dsa_get_address(dsa, + shared_state->schunks); + + shared_iterator->dsa = dsa; + + return shared_iterator; +} + +/* + * pagetable_allocate + * + * Callback function for allocating the memory for hashtable elements. + * It allocates memory from DSA if tbm holds a reference to a dsa. + */ +static inline void * +pagetable_allocate(pagetable_hash *pagetable, Size size) +{ + TIDBitmap *tbm = (TIDBitmap *) pagetable->private_data; + dsa_pointer dsaptr; + + if (tbm->dsa == NULL) + return MemoryContextAllocExtended(pagetable->ctx, size, + MCXT_ALLOC_HUGE | MCXT_ALLOC_ZERO); + + dsaptr = dsa_allocate0(tbm->dsa, size); + + /* + * If we already have the dsapagetable then save this reference in + * dsapagetableold so that pagetable_free can free the old entry. + */ + if (tbm->dsapagetable) + tbm->dsapagetableold = tbm->dsapagetable; + + tbm->dsapagetable = dsaptr; + + return dsa_get_address(tbm->dsa, dsaptr); +} + +/* + * pagetable_free + * + * Callback function for freeing hash table elements. + */ +static inline void +pagetable_free(pagetable_hash *pagetable, void *pointer) +{ + TIDBitmap *tbm = (TIDBitmap *) pagetable->private_data; + + /* + * If dsa pointer is NULL then we have to pfree the local pointer + * otherwise we need to free the dsa pointer. + */ + if (tbm->dsa == NULL) + pfree(pointer); + else + { + /* Free the dsapagetableold and make it same as dsapagetable */ + dsa_free(tbm->dsa, tbm->dsapagetableold); + tbm->dsapagetableold = tbm->dsapagetable; + } +} diff --git a/src/include/nodes/tidbitmap.h b/src/include/nodes/tidbitmap.h index 14992e0..ef598d0 100644 --- a/src/include/nodes/tidbitmap.h +++ b/src/include/nodes/tidbitmap.h @@ -23,6 +23,7 @@ #define TIDBITMAP_H #include "storage/itemptr.h" +#include "utils/dsa.h" /* @@ -33,6 +34,7 @@ typedef struct TIDBitmap TIDBitmap; /* Likewise, TBMIterator is private */ typedef struct TBMIterator TBMIterator; +typedef struct TBMSharedIterator TBMSharedIterator; /* Result structure for tbm_iterate */ typedef struct @@ -46,7 +48,7 @@ typedef struct /* function prototypes in nodes/tidbitmap.c */ -extern TIDBitmap *tbm_create(long maxbytes); +extern TIDBitmap *tbm_create(long maxbytes, dsa_area *dsa); extern void tbm_free(TIDBitmap *tbm); extern void tbm_add_tuples(TIDBitmap *tbm, @@ -62,5 +64,10 @@ extern bool tbm_is_empty(const TIDBitmap *tbm); extern TBMIterator *tbm_begin_iterate(TIDBitmap *tbm); extern TBMIterateResult *tbm_iterate(TBMIterator *iterator); extern void tbm_end_iterate(TBMIterator *iterator); +extern void tbm_end_shared_iterate(TBMSharedIterator *iterator); +extern dsa_pointer tbm_prepare_shared_iterate(TIDBitmap *tbm); +extern TBMSharedIterator *tbm_attach_shared_iterate(dsa_area *dsa, + dsa_pointer iterator); +extern TBMIterateResult *tbm_shared_iterate(TBMSharedIterator *iterator); #endif /* TIDBITMAP_H */ diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h index 8bd93c3..4d392e0 100644 --- a/src/include/storage/lwlock.h +++ b/src/include/storage/lwlock.h @@ -212,6 +212,7 @@ typedef enum BuiltinTrancheIds LWTRANCHE_LOCK_MANAGER, LWTRANCHE_PREDICATE_LOCK_MANAGER, LWTRANCHE_PARALLEL_QUERY_DSA, + LWTRANCHE_PARALLEL_TBM_ITERATOR, LWTRANCHE_FIRST_USER_DEFINED } BuiltinTrancheIds;