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