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 15941 : BitmapTableScanSetup(BitmapHeapScanState *node)
103 : {
104 15941 : TBMIterator tbmiterator = {0};
105 15941 : ParallelBitmapHeapState *pstate = node->pstate;
106 15941 : dsa_area *dsa = node->ss.ps.state->es_query_dsa;
107 :
108 15941 : if (!pstate)
109 : {
110 15713 : node->tbm = (TIDBitmap *) MultiExecProcNode(outerPlanState(node));
111 :
112 15713 : 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 15941 : 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 15941 : if (!node->ss.ss_currentScanDesc)
146 : {
147 13637 : uint32 flags = SO_NONE;
148 :
149 13637 : if (ScanRelIsReadOnly(&node->ss))
150 10078 : flags |= SO_HINT_REL_READ_ONLY;
151 :
152 13637 : if (node->ss.ps.state->es_instrument & INSTRUMENT_IO)
153 0 : flags |= SO_SCAN_INSTRUMENT;
154 :
155 13637 : node->ss.ss_currentScanDesc =
156 13637 : table_beginscan_bm(node->ss.ss_currentRelation,
157 13637 : node->ss.ps.state->es_snapshot,
158 : 0,
159 : NULL,
160 : flags);
161 : }
162 :
163 15941 : node->ss.ss_currentScanDesc->st.rs_tbmiterator = tbmiterator;
164 15941 : node->initialized = true;
165 15941 : }
166 :
167 : /* ----------------------------------------------------------------
168 : * BitmapHeapNext
169 : *
170 : * Retrieve next tuple from the BitmapHeapScan node's currentRelation
171 : * ----------------------------------------------------------------
172 : */
173 : static TupleTableSlot *
174 3638308 : BitmapHeapNext(BitmapHeapScanState *node)
175 : {
176 3638308 : ExprContext *econtext = node->ss.ps.ps_ExprContext;
177 3638308 : TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
178 :
179 : /*
180 : * If we haven't yet performed the underlying index scan, do it, and begin
181 : * the iteration over the bitmap.
182 : */
183 3638308 : if (!node->initialized)
184 15941 : BitmapTableScanSetup(node);
185 :
186 4138933 : while (table_scan_bitmap_next_tuple(node->ss.ss_currentScanDesc,
187 : slot, &node->recheck,
188 : &node->stats.lossy_pages,
189 : &node->stats.exact_pages))
190 : {
191 : /*
192 : * Continuing in previously obtained page.
193 : */
194 4123325 : CHECK_FOR_INTERRUPTS();
195 :
196 : /*
197 : * If we are using lossy info, we have to recheck the qual conditions
198 : * at every tuple.
199 : */
200 4123325 : if (node->recheck)
201 : {
202 1889241 : econtext->ecxt_scantuple = slot;
203 1889241 : if (!ExecQualAndReset(node->bitmapqualorig, econtext))
204 : {
205 : /* Fails recheck, so drop it and loop back for another */
206 500625 : InstrCountFiltered2(node, 1);
207 500625 : ExecClearTuple(slot);
208 500625 : continue;
209 : }
210 : }
211 :
212 : /* OK to return this tuple */
213 3622700 : return slot;
214 : }
215 :
216 : /*
217 : * if we get here it means we are at the end of the scan..
218 : */
219 15605 : return ExecClearTuple(slot);
220 : }
221 :
222 : /*
223 : * BitmapDoneInitializingSharedState - Shared state is initialized
224 : *
225 : * By this time the leader has already populated the TBM and initialized the
226 : * shared state so wake up other processes.
227 : */
228 : static inline void
229 48 : BitmapDoneInitializingSharedState(ParallelBitmapHeapState *pstate)
230 : {
231 48 : SpinLockAcquire(&pstate->mutex);
232 48 : pstate->state = BM_FINISHED;
233 48 : SpinLockRelease(&pstate->mutex);
234 48 : ConditionVariableBroadcast(&pstate->cv);
235 48 : }
236 :
237 : /*
238 : * BitmapHeapRecheck -- access method routine to recheck a tuple in EvalPlanQual
239 : */
240 : static bool
241 0 : BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
242 : {
243 : ExprContext *econtext;
244 :
245 : /*
246 : * extract necessary information from index scan node
247 : */
248 0 : econtext = node->ss.ps.ps_ExprContext;
249 :
250 : /* Does the tuple meet the original qual conditions? */
251 0 : econtext->ecxt_scantuple = slot;
252 0 : return ExecQualAndReset(node->bitmapqualorig, econtext);
253 : }
254 :
255 : /* ----------------------------------------------------------------
256 : * ExecBitmapHeapScan(node)
257 : * ----------------------------------------------------------------
258 : */
259 : static TupleTableSlot *
260 3410453 : ExecBitmapHeapScan(PlanState *pstate)
261 : {
262 3410453 : BitmapHeapScanState *node = castNode(BitmapHeapScanState, pstate);
263 :
264 3410453 : return ExecScan(&node->ss,
265 : (ExecScanAccessMtd) BitmapHeapNext,
266 : (ExecScanRecheckMtd) BitmapHeapRecheck);
267 : }
268 :
269 : /* ----------------------------------------------------------------
270 : * ExecReScanBitmapHeapScan(node)
271 : * ----------------------------------------------------------------
272 : */
273 : void
274 2946 : ExecReScanBitmapHeapScan(BitmapHeapScanState *node)
275 : {
276 2946 : PlanState *outerPlan = outerPlanState(node);
277 :
278 2946 : TableScanDesc scan = node->ss.ss_currentScanDesc;
279 :
280 2946 : if (scan)
281 : {
282 : /*
283 : * End iteration on iterators saved in scan descriptor if they have
284 : * not already been cleaned up.
285 : */
286 2308 : if (!tbm_exhausted(&scan->st.rs_tbmiterator))
287 2304 : tbm_end_iterate(&scan->st.rs_tbmiterator);
288 :
289 : /* rescan to release any page pin */
290 2308 : table_rescan(node->ss.ss_currentScanDesc, NULL);
291 : }
292 :
293 : /* release bitmaps and buffers if any */
294 2946 : if (node->tbm)
295 2304 : tbm_free(node->tbm);
296 2946 : node->tbm = NULL;
297 2946 : node->initialized = false;
298 2946 : node->recheck = true;
299 :
300 2946 : ExecScanReScan(&node->ss);
301 :
302 : /*
303 : * if chgParam of subnode is not null then plan will be re-scanned by
304 : * first ExecProcNode.
305 : */
306 2946 : if (outerPlan->chgParam == NULL)
307 209 : ExecReScan(outerPlan);
308 2946 : }
309 :
310 : /* ----------------------------------------------------------------
311 : * ExecEndBitmapHeapScan
312 : * ----------------------------------------------------------------
313 : */
314 : void
315 16707 : ExecEndBitmapHeapScan(BitmapHeapScanState *node)
316 : {
317 : TableScanDesc scanDesc;
318 :
319 : /*
320 : * When ending a parallel worker, copy the statistics gathered by the
321 : * worker back into shared memory so that it can be picked up by the main
322 : * process to report in EXPLAIN ANALYZE.
323 : */
324 16707 : if (node->sinstrument != NULL && IsParallelWorker())
325 : {
326 : BitmapHeapScanInstrumentation *si;
327 :
328 : Assert(ParallelWorkerNumber < node->sinstrument->num_workers);
329 0 : si = &node->sinstrument->sinstrument[ParallelWorkerNumber];
330 :
331 : /*
332 : * Here we accumulate the stats rather than performing memcpy on
333 : * node->stats into si. When a Gather/GatherMerge node finishes it
334 : * will perform planner shutdown on the workers. On rescan it will
335 : * spin up new workers which will have a new BitmapHeapScanState and
336 : * zeroed stats.
337 : */
338 0 : si->exact_pages += node->stats.exact_pages;
339 0 : si->lossy_pages += node->stats.lossy_pages;
340 :
341 : /* collect I/O instrumentation for this process */
342 0 : if (node->ss.ss_currentScanDesc &&
343 0 : node->ss.ss_currentScanDesc->rs_instrument)
344 : {
345 0 : AccumulateIOStats(&si->stats.io,
346 0 : &node->ss.ss_currentScanDesc->rs_instrument->io);
347 : }
348 : }
349 :
350 : /*
351 : * extract information from the node
352 : */
353 16707 : scanDesc = node->ss.ss_currentScanDesc;
354 :
355 : /*
356 : * close down subplans
357 : */
358 16707 : ExecEndNode(outerPlanState(node));
359 :
360 16707 : if (scanDesc)
361 : {
362 : /*
363 : * End iteration on iterators saved in scan descriptor if they have
364 : * not already been cleaned up.
365 : */
366 13556 : if (!tbm_exhausted(&scanDesc->st.rs_tbmiterator))
367 13556 : tbm_end_iterate(&scanDesc->st.rs_tbmiterator);
368 :
369 : /*
370 : * close table scan
371 : */
372 13556 : table_endscan(scanDesc);
373 : }
374 :
375 : /*
376 : * release bitmaps and buffers if any
377 : */
378 16707 : if (node->tbm)
379 13376 : tbm_free(node->tbm);
380 16707 : }
381 :
382 : /* ----------------------------------------------------------------
383 : * ExecInitBitmapHeapScan
384 : *
385 : * Initializes the scan's state information.
386 : * ----------------------------------------------------------------
387 : */
388 : BitmapHeapScanState *
389 16788 : ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
390 : {
391 : BitmapHeapScanState *scanstate;
392 : Relation currentRelation;
393 :
394 : /* check for unsupported flags */
395 : Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
396 :
397 : /*
398 : * Assert caller didn't ask for an unsafe snapshot --- see comments at
399 : * head of file.
400 : */
401 : Assert(IsMVCCSnapshot(estate->es_snapshot));
402 :
403 : /*
404 : * create state structure
405 : */
406 16788 : scanstate = makeNode(BitmapHeapScanState);
407 16788 : scanstate->ss.ps.plan = (Plan *) node;
408 16788 : scanstate->ss.ps.state = estate;
409 16788 : scanstate->ss.ps.ExecProcNode = ExecBitmapHeapScan;
410 :
411 16788 : scanstate->tbm = NULL;
412 :
413 : /* Zero the statistics counters */
414 16788 : memset(&scanstate->stats, 0, sizeof(BitmapHeapScanInstrumentation));
415 :
416 16788 : scanstate->initialized = false;
417 16788 : scanstate->pstate = NULL;
418 16788 : scanstate->recheck = true;
419 :
420 : /*
421 : * Miscellaneous initialization
422 : *
423 : * create expression context for node
424 : */
425 16788 : ExecAssignExprContext(estate, &scanstate->ss.ps);
426 :
427 : /*
428 : * open the scan relation
429 : */
430 16788 : currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
431 :
432 : /*
433 : * initialize child nodes
434 : */
435 16788 : outerPlanState(scanstate) = ExecInitNode(outerPlan(node), estate, eflags);
436 :
437 : /*
438 : * get the scan type from the relation descriptor.
439 : */
440 16788 : ExecInitScanTupleSlot(estate, &scanstate->ss,
441 : RelationGetDescr(currentRelation),
442 : table_slot_callbacks(currentRelation),
443 : TTS_FLAG_OBEYS_NOT_NULL_CONSTRAINTS);
444 :
445 : /*
446 : * Initialize result type and projection.
447 : */
448 16788 : ExecInitResultTypeTL(&scanstate->ss.ps);
449 16788 : ExecAssignScanProjectionInfo(&scanstate->ss);
450 :
451 : /*
452 : * initialize child expressions
453 : */
454 16788 : scanstate->ss.ps.qual =
455 16788 : ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
456 16788 : scanstate->bitmapqualorig =
457 16788 : ExecInitQual(node->bitmapqualorig, (PlanState *) scanstate);
458 :
459 16788 : scanstate->ss.ss_currentRelation = currentRelation;
460 :
461 : /*
462 : * all done.
463 : */
464 16788 : return scanstate;
465 : }
466 :
467 : /*----------------
468 : * BitmapShouldInitializeSharedState
469 : *
470 : * The first process to come here and see the state to the BM_INITIAL
471 : * will become the leader for the parallel bitmap scan and will be
472 : * responsible for populating the TIDBitmap. The other processes will
473 : * be blocked by the condition variable until the leader wakes them up.
474 : * ---------------
475 : */
476 : static bool
477 228 : BitmapShouldInitializeSharedState(ParallelBitmapHeapState *pstate)
478 : {
479 : SharedBitmapState state;
480 :
481 : while (1)
482 : {
483 252 : SpinLockAcquire(&pstate->mutex);
484 252 : state = pstate->state;
485 252 : if (pstate->state == BM_INITIAL)
486 48 : pstate->state = BM_INPROGRESS;
487 252 : SpinLockRelease(&pstate->mutex);
488 :
489 : /* Exit if bitmap is done, or if we're the leader. */
490 252 : if (state != BM_INPROGRESS)
491 228 : break;
492 :
493 : /* Wait for the leader to wake us up. */
494 24 : ConditionVariableSleep(&pstate->cv, WAIT_EVENT_PARALLEL_BITMAP_SCAN);
495 : }
496 :
497 228 : ConditionVariableCancelSleep();
498 :
499 228 : return (state == BM_INITIAL);
500 : }
501 :
502 : /* ----------------------------------------------------------------
503 : * ExecBitmapHeapEstimate
504 : *
505 : * Compute the amount of space we'll need in the parallel
506 : * query DSM, and inform pcxt->estimator about our needs.
507 : * ----------------------------------------------------------------
508 : */
509 : void
510 12 : ExecBitmapHeapEstimate(BitmapHeapScanState *node,
511 : ParallelContext *pcxt)
512 : {
513 12 : shm_toc_estimate_chunk(&pcxt->estimator,
514 : MAXALIGN(sizeof(ParallelBitmapHeapState)));
515 12 : shm_toc_estimate_keys(&pcxt->estimator, 1);
516 12 : }
517 :
518 : /* ----------------------------------------------------------------
519 : * ExecBitmapHeapInitializeDSM
520 : *
521 : * Set up a parallel bitmap heap scan descriptor.
522 : * ----------------------------------------------------------------
523 : */
524 : void
525 12 : ExecBitmapHeapInitializeDSM(BitmapHeapScanState *node,
526 : ParallelContext *pcxt)
527 : {
528 : ParallelBitmapHeapState *pstate;
529 12 : dsa_area *dsa = node->ss.ps.state->es_query_dsa;
530 :
531 : /* If there's no DSA, there are no workers; initialize nothing. */
532 12 : if (dsa == NULL)
533 0 : return;
534 :
535 : pstate = (ParallelBitmapHeapState *)
536 12 : shm_toc_allocate(pcxt->toc,
537 : MAXALIGN(sizeof(ParallelBitmapHeapState)));
538 :
539 12 : pstate->tbmiterator = 0;
540 :
541 : /* Initialize the mutex */
542 12 : SpinLockInit(&pstate->mutex);
543 12 : pstate->state = BM_INITIAL;
544 :
545 12 : ConditionVariableInit(&pstate->cv);
546 :
547 12 : shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, pstate);
548 12 : node->pstate = pstate;
549 : }
550 :
551 : /* ----------------------------------------------------------------
552 : * ExecBitmapHeapReInitializeDSM
553 : *
554 : * Reset shared state before beginning a fresh scan.
555 : * ----------------------------------------------------------------
556 : */
557 : void
558 36 : ExecBitmapHeapReInitializeDSM(BitmapHeapScanState *node,
559 : ParallelContext *pcxt)
560 : {
561 36 : ParallelBitmapHeapState *pstate = node->pstate;
562 36 : dsa_area *dsa = node->ss.ps.state->es_query_dsa;
563 :
564 : /* If there's no DSA, there are no workers; do nothing. */
565 36 : if (dsa == NULL)
566 0 : return;
567 :
568 36 : pstate->state = BM_INITIAL;
569 :
570 36 : if (DsaPointerIsValid(pstate->tbmiterator))
571 36 : tbm_free_shared_area(dsa, pstate->tbmiterator);
572 :
573 36 : pstate->tbmiterator = InvalidDsaPointer;
574 : }
575 :
576 : /* ----------------------------------------------------------------
577 : * ExecBitmapHeapInitializeWorker
578 : *
579 : * Copy relevant information from TOC into planstate.
580 : * ----------------------------------------------------------------
581 : */
582 : void
583 180 : ExecBitmapHeapInitializeWorker(BitmapHeapScanState *node,
584 : ParallelWorkerContext *pwcxt)
585 : {
586 : Assert(node->ss.ps.state->es_query_dsa != NULL);
587 :
588 180 : node->pstate = (ParallelBitmapHeapState *)
589 180 : shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false);
590 180 : }
591 :
592 : /*
593 : * Compute the amount of space we'll need for the shared instrumentation and
594 : * inform pcxt->estimator.
595 : */
596 : void
597 13 : ExecBitmapHeapInstrumentEstimate(BitmapHeapScanState *node,
598 : ParallelContext *pcxt)
599 : {
600 : Size size;
601 :
602 13 : if (!node->ss.ps.instrument || pcxt->nworkers == 0)
603 13 : return;
604 :
605 0 : size = add_size(offsetof(SharedBitmapHeapInstrumentation, sinstrument),
606 0 : mul_size(pcxt->nworkers, sizeof(BitmapHeapScanInstrumentation)));
607 0 : shm_toc_estimate_chunk(&pcxt->estimator, size);
608 0 : shm_toc_estimate_keys(&pcxt->estimator, 1);
609 : }
610 :
611 : /*
612 : * Set up parallel bitmap heap scan instrumentation.
613 : */
614 : void
615 13 : ExecBitmapHeapInstrumentInitDSM(BitmapHeapScanState *node,
616 : ParallelContext *pcxt)
617 : {
618 : Size size;
619 :
620 13 : if (!node->ss.ps.instrument || pcxt->nworkers == 0)
621 13 : return;
622 :
623 0 : size = add_size(offsetof(SharedBitmapHeapInstrumentation, sinstrument),
624 0 : mul_size(pcxt->nworkers, sizeof(BitmapHeapScanInstrumentation)));
625 0 : node->sinstrument =
626 0 : (SharedBitmapHeapInstrumentation *) shm_toc_allocate(pcxt->toc, size);
627 :
628 : /* Each per-worker area must start out as zeroes */
629 0 : memset(node->sinstrument, 0, size);
630 0 : node->sinstrument->num_workers = pcxt->nworkers;
631 0 : shm_toc_insert(pcxt->toc,
632 0 : node->ss.ps.plan->plan_node_id +
633 : PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
634 0 : node->sinstrument);
635 : }
636 :
637 : /*
638 : * Look up and save the location of the shared instrumentation.
639 : */
640 : void
641 181 : ExecBitmapHeapInstrumentInitWorker(BitmapHeapScanState *node,
642 : ParallelWorkerContext *pwcxt)
643 : {
644 181 : if (!node->ss.ps.instrument)
645 181 : return;
646 :
647 0 : node->sinstrument = (SharedBitmapHeapInstrumentation *)
648 0 : shm_toc_lookup(pwcxt->toc,
649 0 : node->ss.ps.plan->plan_node_id +
650 : PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
651 : false);
652 : }
653 :
654 : /* ----------------------------------------------------------------
655 : * ExecBitmapHeapRetrieveInstrumentation
656 : *
657 : * Transfer bitmap heap scan statistics from DSM to private memory.
658 : * ----------------------------------------------------------------
659 : */
660 : void
661 0 : ExecBitmapHeapRetrieveInstrumentation(BitmapHeapScanState *node)
662 : {
663 0 : SharedBitmapHeapInstrumentation *sinstrument = node->sinstrument;
664 : Size size;
665 :
666 0 : if (sinstrument == NULL)
667 0 : return;
668 :
669 0 : size = offsetof(SharedBitmapHeapInstrumentation, sinstrument)
670 0 : + sinstrument->num_workers * sizeof(BitmapHeapScanInstrumentation);
671 :
672 0 : node->sinstrument = palloc(size);
673 0 : memcpy(node->sinstrument, sinstrument, size);
674 : }
|