From 4a792a3d864761741c7ce5fd0e10a9f1c9539094 Mon Sep 17 00:00:00 2001 From: Melanie Plageman Date: Sun, 5 Apr 2026 16:27:22 -0400 Subject: [PATCH v11 1/2] Fix BitmapHeapScan non-parallel-aware EXPLAIN ANALYZE Allocate shared bitmap table scan instrumentation in its own DSM chunk, separate from ParallelBitmapHeapState. Previously, shared instrumentation was only allocated for parallel-aware nodes, so a non-parallel-aware bitmap table scan in a parallel query had no shared instrumentation and EXPLAIN ANALYZE didn't report exact/lossy pages. This affected queries like bitmap table scans on the outside of a parallel join or those run with debug_parallel_query=regress. Fix by allocating a separate DSM chunk for shared instrumentation and doing so regardless of parallel-awareness. --- src/backend/commands/explain.c | 2 +- src/backend/executor/execParallel.c | 9 ++ src/backend/executor/nodeBitmapHeapscan.c | 111 +++++++++++++--------- src/include/executor/nodeBitmapHeapscan.h | 6 ++ 4 files changed, 83 insertions(+), 45 deletions(-) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 73eaaf176ac..f151f21f9b3 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -3948,7 +3948,7 @@ show_tidbitmap_info(BitmapHeapScanState *planstate, ExplainState *es) } /* Display stats for each parallel worker */ - if (planstate->pstate != NULL) + if (planstate->sinstrument != NULL) { for (int n = 0; n < planstate->sinstrument->num_workers; n++) { diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index 726aca230a4..1a5ec0c305f 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -303,6 +303,9 @@ ExecParallelEstimate(PlanState *planstate, ExecParallelEstimateContext *e) if (planstate->plan->parallel_aware) ExecBitmapHeapEstimate((BitmapHeapScanState *) planstate, e->pcxt); + /* even when not parallel-aware, for EXPLAIN ANALYZE */ + ExecBitmapHeapInstrumentEstimate((BitmapHeapScanState *) planstate, + e->pcxt); break; case T_HashJoinState: if (planstate->plan->parallel_aware) @@ -542,6 +545,9 @@ ExecParallelInitializeDSM(PlanState *planstate, if (planstate->plan->parallel_aware) ExecBitmapHeapInitializeDSM((BitmapHeapScanState *) planstate, d->pcxt); + /* even when not parallel-aware, for EXPLAIN ANALYZE */ + ExecBitmapHeapInstrumentInitDSM((BitmapHeapScanState *) planstate, + d->pcxt); break; case T_HashJoinState: if (planstate->plan->parallel_aware) @@ -1427,6 +1433,9 @@ ExecParallelInitializeWorker(PlanState *planstate, ParallelWorkerContext *pwcxt) if (planstate->plan->parallel_aware) ExecBitmapHeapInitializeWorker((BitmapHeapScanState *) planstate, pwcxt); + /* even when not parallel-aware, for EXPLAIN ANALYZE */ + ExecBitmapHeapInstrumentInitWorker((BitmapHeapScanState *) planstate, + pwcxt); break; case T_HashJoinState: if (planstate->plan->parallel_aware) diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index 73831aed451..d65e2a87b42 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -495,18 +495,8 @@ void ExecBitmapHeapEstimate(BitmapHeapScanState *node, ParallelContext *pcxt) { - Size size; - - size = MAXALIGN(sizeof(ParallelBitmapHeapState)); - - /* account for instrumentation, if required */ - if (node->ss.ps.instrument && pcxt->nworkers > 0) - { - size = add_size(size, offsetof(SharedBitmapHeapInstrumentation, sinstrument)); - size = add_size(size, mul_size(pcxt->nworkers, sizeof(BitmapHeapScanInstrumentation))); - } - - shm_toc_estimate_chunk(&pcxt->estimator, size); + shm_toc_estimate_chunk(&pcxt->estimator, + MAXALIGN(sizeof(ParallelBitmapHeapState))); shm_toc_estimate_keys(&pcxt->estimator, 1); } @@ -521,27 +511,15 @@ ExecBitmapHeapInitializeDSM(BitmapHeapScanState *node, ParallelContext *pcxt) { ParallelBitmapHeapState *pstate; - SharedBitmapHeapInstrumentation *sinstrument = NULL; dsa_area *dsa = node->ss.ps.state->es_query_dsa; - char *ptr; - Size size; /* If there's no DSA, there are no workers; initialize nothing. */ if (dsa == NULL) return; - size = MAXALIGN(sizeof(ParallelBitmapHeapState)); - if (node->ss.ps.instrument && pcxt->nworkers > 0) - { - size = add_size(size, offsetof(SharedBitmapHeapInstrumentation, sinstrument)); - size = add_size(size, mul_size(pcxt->nworkers, sizeof(BitmapHeapScanInstrumentation))); - } - - ptr = shm_toc_allocate(pcxt->toc, size); - pstate = (ParallelBitmapHeapState *) ptr; - ptr += MAXALIGN(sizeof(ParallelBitmapHeapState)); - if (node->ss.ps.instrument && pcxt->nworkers > 0) - sinstrument = (SharedBitmapHeapInstrumentation *) ptr; + pstate = (ParallelBitmapHeapState *) + shm_toc_allocate(pcxt->toc, + MAXALIGN(sizeof(ParallelBitmapHeapState))); pstate->tbmiterator = 0; @@ -551,18 +529,8 @@ ExecBitmapHeapInitializeDSM(BitmapHeapScanState *node, ConditionVariableInit(&pstate->cv); - if (sinstrument) - { - sinstrument->num_workers = pcxt->nworkers; - - /* ensure any unfilled slots will contain zeroes */ - memset(sinstrument->sinstrument, 0, - pcxt->nworkers * sizeof(BitmapHeapScanInstrumentation)); - } - shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, pstate); node->pstate = pstate; - node->sinstrument = sinstrument; } /* ---------------------------------------------------------------- @@ -600,17 +568,72 @@ void ExecBitmapHeapInitializeWorker(BitmapHeapScanState *node, ParallelWorkerContext *pwcxt) { - char *ptr; - Assert(node->ss.ps.state->es_query_dsa != NULL); - ptr = shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false); + node->pstate = (ParallelBitmapHeapState *) + shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false); +} + +/* + * Compute the amount of space we'll need for the shared instrumentation and + * inform pcxt->estimator. + */ +void +ExecBitmapHeapInstrumentEstimate(BitmapHeapScanState *node, + ParallelContext *pcxt) +{ + Size size; + + if (!node->ss.ps.instrument || pcxt->nworkers == 0) + return; + + size = add_size(offsetof(SharedBitmapHeapInstrumentation, sinstrument), + mul_size(pcxt->nworkers, sizeof(BitmapHeapScanInstrumentation))); + shm_toc_estimate_chunk(&pcxt->estimator, size); + shm_toc_estimate_keys(&pcxt->estimator, 1); +} + +/* + * Set up parallel bitmap heap scan instrumentation. + */ +void +ExecBitmapHeapInstrumentInitDSM(BitmapHeapScanState *node, + ParallelContext *pcxt) +{ + Size size; + + if (!node->ss.ps.instrument || pcxt->nworkers == 0) + return; + + size = add_size(offsetof(SharedBitmapHeapInstrumentation, sinstrument), + mul_size(pcxt->nworkers, sizeof(BitmapHeapScanInstrumentation))); + node->sinstrument = + (SharedBitmapHeapInstrumentation *) shm_toc_allocate(pcxt->toc, size); + + /* Each per-worker area must start out as zeroes */ + memset(node->sinstrument, 0, size); + node->sinstrument->num_workers = pcxt->nworkers; + shm_toc_insert(pcxt->toc, + node->ss.ps.plan->plan_node_id + + PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET, + node->sinstrument); +} - node->pstate = (ParallelBitmapHeapState *) ptr; - ptr += MAXALIGN(sizeof(ParallelBitmapHeapState)); +/* + * Look up and save the location of the shared instrumentation. + */ +void +ExecBitmapHeapInstrumentInitWorker(BitmapHeapScanState *node, + ParallelWorkerContext *pwcxt) +{ + if (!node->ss.ps.instrument) + return; - if (node->ss.ps.instrument) - node->sinstrument = (SharedBitmapHeapInstrumentation *) ptr; + node->sinstrument = (SharedBitmapHeapInstrumentation *) + shm_toc_lookup(pwcxt->toc, + node->ss.ps.plan->plan_node_id + + PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET, + false); } /* ---------------------------------------------------------------- diff --git a/src/include/executor/nodeBitmapHeapscan.h b/src/include/executor/nodeBitmapHeapscan.h index 5d82f71abff..5c0b6592a1f 100644 --- a/src/include/executor/nodeBitmapHeapscan.h +++ b/src/include/executor/nodeBitmapHeapscan.h @@ -28,6 +28,12 @@ extern void ExecBitmapHeapReInitializeDSM(BitmapHeapScanState *node, ParallelContext *pcxt); extern void ExecBitmapHeapInitializeWorker(BitmapHeapScanState *node, ParallelWorkerContext *pwcxt); +extern void ExecBitmapHeapInstrumentEstimate(BitmapHeapScanState *node, + ParallelContext *pcxt); +extern void ExecBitmapHeapInstrumentInitDSM(BitmapHeapScanState *node, + ParallelContext *pcxt); +extern void ExecBitmapHeapInstrumentInitWorker(BitmapHeapScanState *node, + ParallelWorkerContext *pwcxt); extern void ExecBitmapHeapRetrieveInstrumentation(BitmapHeapScanState *node); #endif /* NODEBITMAPHEAPSCAN_H */ -- 2.43.0