Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * nodeBitmapHeapscan.c
4 : * Routines to support bitmapped scans of relations
5 : *
6 : * NOTE: it is critical that this plan type only be used with MVCC-compliant
7 : * snapshots (ie, regular snapshots, not SnapshotAny or one of the other
8 : * special snapshots). The reason is that since index and heap scans are
9 : * decoupled, there can be no assurance that the index tuple prompting a
10 : * visit to a particular heap TID still exists when the visit is made.
11 : * Therefore the tuple might not exist anymore either (which is OK because
12 : * heap_fetch will cope) --- but worse, the tuple slot could have been
13 : * re-used for a newer tuple. With an MVCC snapshot the newer tuple is
14 : * certain to fail the time qual and so it will not be mistakenly returned,
15 : * but with anything else we might return a tuple that doesn't meet the
16 : * required index qual conditions.
17 : *
18 : *
19 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
20 : * Portions Copyright (c) 1994, Regents of the University of California
21 : *
22 : *
23 : * IDENTIFICATION
24 : * src/backend/executor/nodeBitmapHeapscan.c
25 : *
26 : *-------------------------------------------------------------------------
27 : */
28 : /*
29 : * INTERFACE ROUTINES
30 : * ExecBitmapHeapScan scans a relation using bitmap info
31 : * ExecBitmapHeapNext workhorse for above
32 : * ExecInitBitmapHeapScan creates and initializes state info.
33 : * ExecReScanBitmapHeapScan prepares to rescan the plan.
34 : * ExecEndBitmapHeapScan releases all storage.
35 : */
36 : #include "postgres.h"
37 :
38 : #include "access/relscan.h"
39 : #include "access/tableam.h"
40 : #include "access/visibilitymap.h"
41 : #include "executor/executor.h"
42 : #include "executor/instrument.h"
43 : #include "executor/nodeBitmapHeapscan.h"
44 : #include "miscadmin.h"
45 : #include "pgstat.h"
46 : #include "storage/bufmgr.h"
47 : #include "storage/condition_variable.h"
48 : #include "utils/dsa.h"
49 : #include "utils/rel.h"
50 : #include "utils/spccache.h"
51 : #include "utils/wait_event.h"
52 :
53 : static void BitmapTableScanSetup(BitmapHeapScanState *node);
54 : static TupleTableSlot *BitmapHeapNext(BitmapHeapScanState *node);
55 : static inline void BitmapDoneInitializingSharedState(ParallelBitmapHeapState *pstate);
56 : static bool BitmapShouldInitializeSharedState(ParallelBitmapHeapState *pstate);
57 :
58 :
59 : /* ----------------
60 : * SharedBitmapState information
61 : *
62 : * BM_INITIAL TIDBitmap creation is not yet started, so first worker
63 : * to see this state will set the state to BM_INPROGRESS
64 : * and that process will be responsible for creating
65 : * TIDBitmap.
66 : * BM_INPROGRESS TIDBitmap creation is in progress; workers need to
67 : * sleep until it's finished.
68 : * BM_FINISHED TIDBitmap creation is done, so now all workers can
69 : * proceed to iterate over TIDBitmap.
70 : * ----------------
71 : */
72 : typedef enum
73 : {
74 : BM_INITIAL,
75 : BM_INPROGRESS,
76 : BM_FINISHED,
77 : } SharedBitmapState;
78 :
79 : /* ----------------
80 : * ParallelBitmapHeapState information
81 : * tbmiterator iterator for scanning current pages
82 : * mutex mutual exclusion for state
83 : * state current state of the TIDBitmap
84 : * cv conditional wait variable
85 : * ----------------
86 : */
87 : typedef struct ParallelBitmapHeapState
88 : {
89 : dsa_pointer tbmiterator;
90 : slock_t mutex;
91 : SharedBitmapState state;
92 : ConditionVariable cv;
93 : } ParallelBitmapHeapState;
94 :
95 :
96 : /*
97 : * Do the underlying index scan, build the bitmap, set up the parallel state
98 : * needed for parallel workers to iterate through the bitmap, and set up the
99 : * underlying table scan descriptor.
100 : */
101 : static void
102 15949 : BitmapTableScanSetup(BitmapHeapScanState *node)
103 : {
104 15949 : TBMIterator tbmiterator = {0};
105 15949 : ParallelBitmapHeapState *pstate = node->pstate;
106 15949 : dsa_area *dsa = node->ss.ps.state->es_query_dsa;
107 :
108 15949 : if (!pstate)
109 : {
110 15721 : node->tbm = (TIDBitmap *) MultiExecProcNode(outerPlanState(node));
111 :
112 15721 : if (!node->tbm || !IsA(node->tbm, TIDBitmap))
113 0 : elog(ERROR, "unrecognized result from subplan");
114 : }
115 228 : else if (BitmapShouldInitializeSharedState(pstate))
116 : {
117 : /*
118 : * The leader will immediately come out of the function, but others
119 : * will be blocked until leader populates the TBM and wakes them up.
120 : */
121 48 : node->tbm = (TIDBitmap *) MultiExecProcNode(outerPlanState(node));
122 48 : if (!node->tbm || !IsA(node->tbm, TIDBitmap))
123 0 : elog(ERROR, "unrecognized result from subplan");
124 :
125 : /*
126 : * Prepare to iterate over the TBM. This will return the dsa_pointer
127 : * of the iterator state which will be used by multiple processes to
128 : * iterate jointly.
129 : */
130 48 : pstate->tbmiterator = tbm_prepare_shared_iterate(node->tbm);
131 :
132 : /* We have initialized the shared state so wake up others. */
133 48 : BitmapDoneInitializingSharedState(pstate);
134 : }
135 :
136 15949 : tbmiterator = tbm_begin_iterate(node->tbm, dsa,
137 : pstate ?
138 : pstate->tbmiterator :
139 : InvalidDsaPointer);
140 :
141 : /*
142 : * If this is the first scan of the underlying table, create the table
143 : * scan descriptor and begin the scan.
144 : */
145 15949 : if (!node->ss.ss_currentScanDesc)
146 : {
147 13627 : node->ss.ss_currentScanDesc =
148 13627 : table_beginscan_bm(node->ss.ss_currentRelation,
149 13627 : node->ss.ps.state->es_snapshot,
150 : 0,
151 : NULL,
152 13627 : ScanRelIsReadOnly(&node->ss) ?
153 : SO_HINT_REL_READ_ONLY : SO_NONE);
154 : }
155 :
156 15949 : node->ss.ss_currentScanDesc->st.rs_tbmiterator = tbmiterator;
157 15949 : node->initialized = true;
158 15949 : }
159 :
160 : /* ----------------------------------------------------------------
161 : * BitmapHeapNext
162 : *
163 : * Retrieve next tuple from the BitmapHeapScan node's currentRelation
164 : * ----------------------------------------------------------------
165 : */
166 : static TupleTableSlot *
167 3641597 : BitmapHeapNext(BitmapHeapScanState *node)
168 : {
169 3641597 : ExprContext *econtext = node->ss.ps.ps_ExprContext;
170 3641597 : TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
171 :
172 : /*
173 : * If we haven't yet performed the underlying index scan, do it, and begin
174 : * the iteration over the bitmap.
175 : */
176 3641597 : if (!node->initialized)
177 15949 : BitmapTableScanSetup(node);
178 :
179 4142132 : while (table_scan_bitmap_next_tuple(node->ss.ss_currentScanDesc,
180 : slot, &node->recheck,
181 : &node->stats.lossy_pages,
182 : &node->stats.exact_pages))
183 : {
184 : /*
185 : * Continuing in previously obtained page.
186 : */
187 4126512 : CHECK_FOR_INTERRUPTS();
188 :
189 : /*
190 : * If we are using lossy info, we have to recheck the qual conditions
191 : * at every tuple.
192 : */
193 4126512 : if (node->recheck)
194 : {
195 1889151 : econtext->ecxt_scantuple = slot;
196 1889151 : if (!ExecQualAndReset(node->bitmapqualorig, econtext))
197 : {
198 : /* Fails recheck, so drop it and loop back for another */
199 500535 : InstrCountFiltered2(node, 1);
200 500535 : ExecClearTuple(slot);
201 500535 : continue;
202 : }
203 : }
204 :
205 : /* OK to return this tuple */
206 3625977 : return slot;
207 : }
208 :
209 : /*
210 : * if we get here it means we are at the end of the scan..
211 : */
212 15617 : return ExecClearTuple(slot);
213 : }
214 :
215 : /*
216 : * BitmapDoneInitializingSharedState - Shared state is initialized
217 : *
218 : * By this time the leader has already populated the TBM and initialized the
219 : * shared state so wake up other processes.
220 : */
221 : static inline void
222 48 : BitmapDoneInitializingSharedState(ParallelBitmapHeapState *pstate)
223 : {
224 48 : SpinLockAcquire(&pstate->mutex);
225 48 : pstate->state = BM_FINISHED;
226 48 : SpinLockRelease(&pstate->mutex);
227 48 : ConditionVariableBroadcast(&pstate->cv);
228 48 : }
229 :
230 : /*
231 : * BitmapHeapRecheck -- access method routine to recheck a tuple in EvalPlanQual
232 : */
233 : static bool
234 0 : BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
235 : {
236 : ExprContext *econtext;
237 :
238 : /*
239 : * extract necessary information from index scan node
240 : */
241 0 : econtext = node->ss.ps.ps_ExprContext;
242 :
243 : /* Does the tuple meet the original qual conditions? */
244 0 : econtext->ecxt_scantuple = slot;
245 0 : return ExecQualAndReset(node->bitmapqualorig, econtext);
246 : }
247 :
248 : /* ----------------------------------------------------------------
249 : * ExecBitmapHeapScan(node)
250 : * ----------------------------------------------------------------
251 : */
252 : static TupleTableSlot *
253 3411547 : ExecBitmapHeapScan(PlanState *pstate)
254 : {
255 3411547 : BitmapHeapScanState *node = castNode(BitmapHeapScanState, pstate);
256 :
257 3411547 : return ExecScan(&node->ss,
258 : (ExecScanAccessMtd) BitmapHeapNext,
259 : (ExecScanRecheckMtd) BitmapHeapRecheck);
260 : }
261 :
262 : /* ----------------------------------------------------------------
263 : * ExecReScanBitmapHeapScan(node)
264 : * ----------------------------------------------------------------
265 : */
266 : void
267 2976 : ExecReScanBitmapHeapScan(BitmapHeapScanState *node)
268 : {
269 2976 : PlanState *outerPlan = outerPlanState(node);
270 :
271 2976 : TableScanDesc scan = node->ss.ss_currentScanDesc;
272 :
273 2976 : if (scan)
274 : {
275 : /*
276 : * End iteration on iterators saved in scan descriptor if they have
277 : * not already been cleaned up.
278 : */
279 2326 : if (!tbm_exhausted(&scan->st.rs_tbmiterator))
280 2322 : tbm_end_iterate(&scan->st.rs_tbmiterator);
281 :
282 : /* rescan to release any page pin */
283 2326 : table_rescan(node->ss.ss_currentScanDesc, NULL);
284 : }
285 :
286 : /* release bitmaps and buffers if any */
287 2976 : if (node->tbm)
288 2322 : tbm_free(node->tbm);
289 2976 : node->tbm = NULL;
290 2976 : node->initialized = false;
291 2976 : node->recheck = true;
292 :
293 2976 : ExecScanReScan(&node->ss);
294 :
295 : /*
296 : * if chgParam of subnode is not null then plan will be re-scanned by
297 : * first ExecProcNode.
298 : */
299 2976 : if (outerPlan->chgParam == NULL)
300 198 : ExecReScan(outerPlan);
301 2976 : }
302 :
303 : /* ----------------------------------------------------------------
304 : * ExecEndBitmapHeapScan
305 : * ----------------------------------------------------------------
306 : */
307 : void
308 16609 : ExecEndBitmapHeapScan(BitmapHeapScanState *node)
309 : {
310 : TableScanDesc scanDesc;
311 :
312 : /*
313 : * When ending a parallel worker, copy the statistics gathered by the
314 : * worker back into shared memory so that it can be picked up by the main
315 : * process to report in EXPLAIN ANALYZE.
316 : */
317 16609 : if (node->sinstrument != NULL && IsParallelWorker())
318 : {
319 : BitmapHeapScanInstrumentation *si;
320 :
321 : Assert(ParallelWorkerNumber < node->sinstrument->num_workers);
322 0 : si = &node->sinstrument->sinstrument[ParallelWorkerNumber];
323 :
324 : /*
325 : * Here we accumulate the stats rather than performing memcpy on
326 : * node->stats into si. When a Gather/GatherMerge node finishes it
327 : * will perform planner shutdown on the workers. On rescan it will
328 : * spin up new workers which will have a new BitmapHeapScanState and
329 : * zeroed stats.
330 : */
331 0 : si->exact_pages += node->stats.exact_pages;
332 0 : si->lossy_pages += node->stats.lossy_pages;
333 : }
334 :
335 : /*
336 : * extract information from the node
337 : */
338 16609 : scanDesc = node->ss.ss_currentScanDesc;
339 :
340 : /*
341 : * close down subplans
342 : */
343 16609 : ExecEndNode(outerPlanState(node));
344 :
345 16609 : if (scanDesc)
346 : {
347 : /*
348 : * End iteration on iterators saved in scan descriptor if they have
349 : * not already been cleaned up.
350 : */
351 13546 : if (!tbm_exhausted(&scanDesc->st.rs_tbmiterator))
352 13546 : tbm_end_iterate(&scanDesc->st.rs_tbmiterator);
353 :
354 : /*
355 : * close table scan
356 : */
357 13546 : table_endscan(scanDesc);
358 : }
359 :
360 : /*
361 : * release bitmaps and buffers if any
362 : */
363 16609 : if (node->tbm)
364 13366 : tbm_free(node->tbm);
365 16609 : }
366 :
367 : /* ----------------------------------------------------------------
368 : * ExecInitBitmapHeapScan
369 : *
370 : * Initializes the scan's state information.
371 : * ----------------------------------------------------------------
372 : */
373 : BitmapHeapScanState *
374 16690 : ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
375 : {
376 : BitmapHeapScanState *scanstate;
377 : Relation currentRelation;
378 :
379 : /* check for unsupported flags */
380 : Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
381 :
382 : /*
383 : * Assert caller didn't ask for an unsafe snapshot --- see comments at
384 : * head of file.
385 : */
386 : Assert(IsMVCCSnapshot(estate->es_snapshot));
387 :
388 : /*
389 : * create state structure
390 : */
391 16690 : scanstate = makeNode(BitmapHeapScanState);
392 16690 : scanstate->ss.ps.plan = (Plan *) node;
393 16690 : scanstate->ss.ps.state = estate;
394 16690 : scanstate->ss.ps.ExecProcNode = ExecBitmapHeapScan;
395 :
396 16690 : scanstate->tbm = NULL;
397 :
398 : /* Zero the statistics counters */
399 16690 : memset(&scanstate->stats, 0, sizeof(BitmapHeapScanInstrumentation));
400 :
401 16690 : scanstate->initialized = false;
402 16690 : scanstate->pstate = NULL;
403 16690 : scanstate->recheck = true;
404 :
405 : /*
406 : * Miscellaneous initialization
407 : *
408 : * create expression context for node
409 : */
410 16690 : ExecAssignExprContext(estate, &scanstate->ss.ps);
411 :
412 : /*
413 : * open the scan relation
414 : */
415 16690 : currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
416 :
417 : /*
418 : * initialize child nodes
419 : */
420 16690 : outerPlanState(scanstate) = ExecInitNode(outerPlan(node), estate, eflags);
421 :
422 : /*
423 : * get the scan type from the relation descriptor.
424 : */
425 16690 : ExecInitScanTupleSlot(estate, &scanstate->ss,
426 : RelationGetDescr(currentRelation),
427 : table_slot_callbacks(currentRelation),
428 : TTS_FLAG_OBEYS_NOT_NULL_CONSTRAINTS);
429 :
430 : /*
431 : * Initialize result type and projection.
432 : */
433 16690 : ExecInitResultTypeTL(&scanstate->ss.ps);
434 16690 : ExecAssignScanProjectionInfo(&scanstate->ss);
435 :
436 : /*
437 : * initialize child expressions
438 : */
439 16690 : scanstate->ss.ps.qual =
440 16690 : ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
441 16690 : scanstate->bitmapqualorig =
442 16690 : ExecInitQual(node->bitmapqualorig, (PlanState *) scanstate);
443 :
444 16690 : scanstate->ss.ss_currentRelation = currentRelation;
445 :
446 : /*
447 : * all done.
448 : */
449 16690 : return scanstate;
450 : }
451 :
452 : /*----------------
453 : * BitmapShouldInitializeSharedState
454 : *
455 : * The first process to come here and see the state to the BM_INITIAL
456 : * will become the leader for the parallel bitmap scan and will be
457 : * responsible for populating the TIDBitmap. The other processes will
458 : * be blocked by the condition variable until the leader wakes them up.
459 : * ---------------
460 : */
461 : static bool
462 228 : BitmapShouldInitializeSharedState(ParallelBitmapHeapState *pstate)
463 : {
464 : SharedBitmapState state;
465 :
466 : while (1)
467 : {
468 256 : SpinLockAcquire(&pstate->mutex);
469 256 : state = pstate->state;
470 256 : if (pstate->state == BM_INITIAL)
471 48 : pstate->state = BM_INPROGRESS;
472 256 : SpinLockRelease(&pstate->mutex);
473 :
474 : /* Exit if bitmap is done, or if we're the leader. */
475 256 : if (state != BM_INPROGRESS)
476 228 : break;
477 :
478 : /* Wait for the leader to wake us up. */
479 28 : ConditionVariableSleep(&pstate->cv, WAIT_EVENT_PARALLEL_BITMAP_SCAN);
480 : }
481 :
482 228 : ConditionVariableCancelSleep();
483 :
484 228 : return (state == BM_INITIAL);
485 : }
486 :
487 : /* ----------------------------------------------------------------
488 : * ExecBitmapHeapEstimate
489 : *
490 : * Compute the amount of space we'll need in the parallel
491 : * query DSM, and inform pcxt->estimator about our needs.
492 : * ----------------------------------------------------------------
493 : */
494 : void
495 12 : ExecBitmapHeapEstimate(BitmapHeapScanState *node,
496 : ParallelContext *pcxt)
497 : {
498 12 : shm_toc_estimate_chunk(&pcxt->estimator,
499 : MAXALIGN(sizeof(ParallelBitmapHeapState)));
500 12 : shm_toc_estimate_keys(&pcxt->estimator, 1);
501 12 : }
502 :
503 : /* ----------------------------------------------------------------
504 : * ExecBitmapHeapInitializeDSM
505 : *
506 : * Set up a parallel bitmap heap scan descriptor.
507 : * ----------------------------------------------------------------
508 : */
509 : void
510 12 : ExecBitmapHeapInitializeDSM(BitmapHeapScanState *node,
511 : ParallelContext *pcxt)
512 : {
513 : ParallelBitmapHeapState *pstate;
514 12 : dsa_area *dsa = node->ss.ps.state->es_query_dsa;
515 :
516 : /* If there's no DSA, there are no workers; initialize nothing. */
517 12 : if (dsa == NULL)
518 0 : return;
519 :
520 : pstate = (ParallelBitmapHeapState *)
521 12 : shm_toc_allocate(pcxt->toc,
522 : MAXALIGN(sizeof(ParallelBitmapHeapState)));
523 :
524 12 : pstate->tbmiterator = 0;
525 :
526 : /* Initialize the mutex */
527 12 : SpinLockInit(&pstate->mutex);
528 12 : pstate->state = BM_INITIAL;
529 :
530 12 : ConditionVariableInit(&pstate->cv);
531 :
532 12 : shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, pstate);
533 12 : node->pstate = pstate;
534 : }
535 :
536 : /* ----------------------------------------------------------------
537 : * ExecBitmapHeapReInitializeDSM
538 : *
539 : * Reset shared state before beginning a fresh scan.
540 : * ----------------------------------------------------------------
541 : */
542 : void
543 36 : ExecBitmapHeapReInitializeDSM(BitmapHeapScanState *node,
544 : ParallelContext *pcxt)
545 : {
546 36 : ParallelBitmapHeapState *pstate = node->pstate;
547 36 : dsa_area *dsa = node->ss.ps.state->es_query_dsa;
548 :
549 : /* If there's no DSA, there are no workers; do nothing. */
550 36 : if (dsa == NULL)
551 0 : return;
552 :
553 36 : pstate->state = BM_INITIAL;
554 :
555 36 : if (DsaPointerIsValid(pstate->tbmiterator))
556 36 : tbm_free_shared_area(dsa, pstate->tbmiterator);
557 :
558 36 : pstate->tbmiterator = InvalidDsaPointer;
559 : }
560 :
561 : /* ----------------------------------------------------------------
562 : * ExecBitmapHeapInitializeWorker
563 : *
564 : * Copy relevant information from TOC into planstate.
565 : * ----------------------------------------------------------------
566 : */
567 : void
568 180 : ExecBitmapHeapInitializeWorker(BitmapHeapScanState *node,
569 : ParallelWorkerContext *pwcxt)
570 : {
571 : Assert(node->ss.ps.state->es_query_dsa != NULL);
572 :
573 180 : node->pstate = (ParallelBitmapHeapState *)
574 180 : shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false);
575 180 : }
576 :
577 : /*
578 : * Compute the amount of space we'll need for the shared instrumentation and
579 : * inform pcxt->estimator.
580 : */
581 : void
582 13 : ExecBitmapHeapInstrumentEstimate(BitmapHeapScanState *node,
583 : ParallelContext *pcxt)
584 : {
585 : Size size;
586 :
587 13 : if (!node->ss.ps.instrument || pcxt->nworkers == 0)
588 13 : return;
589 :
590 0 : size = add_size(offsetof(SharedBitmapHeapInstrumentation, sinstrument),
591 0 : mul_size(pcxt->nworkers, sizeof(BitmapHeapScanInstrumentation)));
592 0 : shm_toc_estimate_chunk(&pcxt->estimator, size);
593 0 : shm_toc_estimate_keys(&pcxt->estimator, 1);
594 : }
595 :
596 : /*
597 : * Set up parallel bitmap heap scan instrumentation.
598 : */
599 : void
600 13 : ExecBitmapHeapInstrumentInitDSM(BitmapHeapScanState *node,
601 : ParallelContext *pcxt)
602 : {
603 : Size size;
604 :
605 13 : if (!node->ss.ps.instrument || pcxt->nworkers == 0)
606 13 : return;
607 :
608 0 : size = add_size(offsetof(SharedBitmapHeapInstrumentation, sinstrument),
609 0 : mul_size(pcxt->nworkers, sizeof(BitmapHeapScanInstrumentation)));
610 0 : node->sinstrument =
611 0 : (SharedBitmapHeapInstrumentation *) shm_toc_allocate(pcxt->toc, size);
612 :
613 : /* Each per-worker area must start out as zeroes */
614 0 : memset(node->sinstrument, 0, size);
615 0 : node->sinstrument->num_workers = pcxt->nworkers;
616 0 : shm_toc_insert(pcxt->toc,
617 0 : node->ss.ps.plan->plan_node_id +
618 : PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
619 0 : node->sinstrument);
620 : }
621 :
622 : /*
623 : * Look up and save the location of the shared instrumentation.
624 : */
625 : void
626 181 : ExecBitmapHeapInstrumentInitWorker(BitmapHeapScanState *node,
627 : ParallelWorkerContext *pwcxt)
628 : {
629 181 : if (!node->ss.ps.instrument)
630 181 : return;
631 :
632 0 : node->sinstrument = (SharedBitmapHeapInstrumentation *)
633 0 : shm_toc_lookup(pwcxt->toc,
634 0 : node->ss.ps.plan->plan_node_id +
635 : PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
636 : false);
637 : }
638 :
639 : /* ----------------------------------------------------------------
640 : * ExecBitmapHeapRetrieveInstrumentation
641 : *
642 : * Transfer bitmap heap scan statistics from DSM to private memory.
643 : * ----------------------------------------------------------------
644 : */
645 : void
646 0 : ExecBitmapHeapRetrieveInstrumentation(BitmapHeapScanState *node)
647 : {
648 0 : SharedBitmapHeapInstrumentation *sinstrument = node->sinstrument;
649 : Size size;
650 :
651 0 : if (sinstrument == NULL)
652 0 : return;
653 :
654 0 : size = offsetof(SharedBitmapHeapInstrumentation, sinstrument)
655 0 : + sinstrument->num_workers * sizeof(BitmapHeapScanInstrumentation);
656 :
657 0 : node->sinstrument = palloc(size);
658 0 : memcpy(node->sinstrument, sinstrument, size);
659 : }
|