| /*------------------------------------------------------------------------- |
| * |
| * blscan.c |
| * Bloom index scan functions. |
| * |
| * Copyright (c) 2016-2023, PostgreSQL Global Development Group |
| * |
| * IDENTIFICATION |
| * contrib/bloom/blscan.c |
| * |
| *------------------------------------------------------------------------- |
| */ |
| #include "postgres.h" |
| |
| #include "access/relscan.h" |
| #include "bloom.h" |
| #include "miscadmin.h" |
| #include "pgstat.h" |
| #include "storage/bufmgr.h" |
| #include "storage/lmgr.h" |
| #include "utils/memutils.h" |
| #include "utils/rel.h" |
| |
| /* |
| * Begin scan of bloom index. |
| */ |
| IndexScanDesc |
| blbeginscan(Relation r, int nkeys, int norderbys) |
| { |
| IndexScanDesc scan; |
| BloomScanOpaque so; |
| |
| scan = RelationGetIndexScan(r, nkeys, norderbys); |
| |
| so = (BloomScanOpaque) palloc(sizeof(BloomScanOpaqueData)); |
| initBloomState(&so->state, scan->indexRelation); |
| so->sign = NULL; |
| |
| scan->opaque = so; |
| |
| return scan; |
| } |
| |
| /* |
| * Rescan a bloom index. |
| */ |
| void |
| blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys, |
| ScanKey orderbys, int norderbys) |
| { |
| BloomScanOpaque so = (BloomScanOpaque) scan->opaque; |
| |
| if (so->sign) |
| pfree(so->sign); |
| so->sign = NULL; |
| |
| if (scankey && scan->numberOfKeys > 0) |
| { |
| memmove(scan->keyData, scankey, |
| scan->numberOfKeys * sizeof(ScanKeyData)); |
| } |
| } |
| |
| /* |
| * End scan of bloom index. |
| */ |
| void |
| blendscan(IndexScanDesc scan) |
| { |
| BloomScanOpaque so = (BloomScanOpaque) scan->opaque; |
| |
| if (so->sign) |
| pfree(so->sign); |
| so->sign = NULL; |
| } |
| |
| /* |
| * Insert all matching tuples into a bitmap. |
| */ |
| int64 |
| blgetbitmap(IndexScanDesc scan, Node **bmNodeP) |
| { |
| int64 ntids = 0; |
| BlockNumber blkno = BLOOM_HEAD_BLKNO, |
| npages; |
| int i; |
| TIDBitmap *tbm; |
| BufferAccessStrategy bas; |
| BloomScanOpaque so = (BloomScanOpaque) scan->opaque; |
| |
| /* |
| * GPDB specific code. Since GPDB also support StreamBitmap |
| * in bitmap index. So normally we need to create specific bitmap |
| * node in the amgetbitmap AM. |
| */ |
| Assert(bmNodeP); |
| if (*bmNodeP == NULL) |
| { |
| /* XXX should we use less than work_mem for this? */ |
| tbm = tbm_create(work_mem * 1024L, scan->dsa); |
| *bmNodeP = (Node *) tbm; |
| } |
| else if (!IsA(*bmNodeP, TIDBitmap)) |
| elog(ERROR, "non bloom bitmap"); |
| else |
| tbm = (TIDBitmap *)*bmNodeP; |
| |
| if (so->sign == NULL) |
| { |
| /* New search: have to calculate search signature */ |
| ScanKey skey = scan->keyData; |
| |
| so->sign = palloc0(sizeof(BloomSignatureWord) * so->state.opts.bloomLength); |
| |
| for (i = 0; i < scan->numberOfKeys; i++) |
| { |
| /* |
| * Assume bloom-indexable operators to be strict, so nothing could |
| * be found for NULL key. |
| */ |
| if (skey->sk_flags & SK_ISNULL) |
| { |
| pfree(so->sign); |
| so->sign = NULL; |
| return 0; |
| } |
| |
| /* Add next value to the signature */ |
| signValue(&so->state, so->sign, skey->sk_argument, |
| skey->sk_attno - 1); |
| |
| skey++; |
| } |
| } |
| |
| /* |
| * We're going to read the whole index. This is why we use appropriate |
| * buffer access strategy. |
| */ |
| bas = GetAccessStrategy(BAS_BULKREAD); |
| npages = RelationGetNumberOfBlocks(scan->indexRelation); |
| pgstat_count_index_scan(scan->indexRelation); |
| |
| for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++) |
| { |
| Buffer buffer; |
| Page page; |
| |
| buffer = ReadBufferExtended(scan->indexRelation, MAIN_FORKNUM, |
| blkno, RBM_NORMAL, bas); |
| |
| LockBuffer(buffer, BUFFER_LOCK_SHARE); |
| page = BufferGetPage(buffer); |
| TestForOldSnapshot(scan->xs_snapshot, scan->indexRelation, page); |
| |
| if (!PageIsNew(page) && !BloomPageIsDeleted(page)) |
| { |
| OffsetNumber offset, |
| maxOffset = BloomPageGetMaxOffset(page); |
| |
| for (offset = 1; offset <= maxOffset; offset++) |
| { |
| BloomTuple *itup = BloomPageGetTuple(&so->state, page, offset); |
| bool res = true; |
| |
| /* Check index signature with scan signature */ |
| for (i = 0; i < so->state.opts.bloomLength; i++) |
| { |
| if ((itup->sign[i] & so->sign[i]) != so->sign[i]) |
| { |
| res = false; |
| break; |
| } |
| } |
| |
| /* Add matching tuples to bitmap */ |
| if (res) |
| { |
| tbm_add_tuples(tbm, &itup->heapPtr, 1, true); |
| ntids++; |
| } |
| } |
| } |
| |
| UnlockReleaseBuffer(buffer); |
| CHECK_FOR_INTERRUPTS(); |
| } |
| FreeAccessStrategy(bas); |
| |
| return ntids; |
| } |