From 695215d4ada297ca31034c3c13f4d491f0c25a9a Mon Sep 17 00:00:00 2001 From: Melanie Plageman Date: Sun, 5 Apr 2026 14:10:33 -0400 Subject: [PATCH] Allow non-parallel-aware bitmap table scans to share instrumentation in the DSM EXPLAIN ANALYZE for non-parallel-aware bitmap table scans did not show exact/lossy pages because the DSM where the stats would be read from wasn't initialized. This affected queries like bitmap table scans on the outer side of a parallel join or bitmap table scans with debug_parallel_query=regress. Fix it by setting up the DSM if instrumentation is needed even if the node is not parallel aware. --- src/backend/commands/explain.c | 2 +- src/backend/executor/execParallel.c | 18 ++++---- src/backend/executor/nodeBitmapHeapscan.c | 50 +++++++++++++++-------- 3 files changed, 43 insertions(+), 27 deletions(-) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index e4b70166b0e..8275bb2af61 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -3949,7 +3949,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 755191b51ef..ce377a774f8 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -290,9 +290,9 @@ ExecParallelEstimate(PlanState *planstate, ExecParallelEstimateContext *e) e->pcxt); break; case T_BitmapHeapScanState: - if (planstate->plan->parallel_aware) - ExecBitmapHeapEstimate((BitmapHeapScanState *) planstate, - e->pcxt); + /* even when not parallel-aware, for EXPLAIN ANALYZE */ + ExecBitmapHeapEstimate((BitmapHeapScanState *) planstate, + e->pcxt); break; case T_HashJoinState: if (planstate->plan->parallel_aware) @@ -522,9 +522,9 @@ ExecParallelInitializeDSM(PlanState *planstate, d->pcxt); break; case T_BitmapHeapScanState: - if (planstate->plan->parallel_aware) - ExecBitmapHeapInitializeDSM((BitmapHeapScanState *) planstate, - d->pcxt); + /* even when not parallel-aware, for EXPLAIN ANALYZE */ + ExecBitmapHeapInitializeDSM((BitmapHeapScanState *) planstate, + d->pcxt); break; case T_HashJoinState: if (planstate->plan->parallel_aware) @@ -1400,9 +1400,9 @@ ExecParallelInitializeWorker(PlanState *planstate, ParallelWorkerContext *pwcxt) pwcxt); break; case T_BitmapHeapScanState: - if (planstate->plan->parallel_aware) - ExecBitmapHeapInitializeWorker((BitmapHeapScanState *) planstate, - pwcxt); + /* even when not parallel-aware, for EXPLAIN ANALYZE */ + ExecBitmapHeapInitializeWorker((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..7ae348ef1f1 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -103,9 +103,9 @@ BitmapTableScanSetup(BitmapHeapScanState *node) { TBMIterator tbmiterator = {0}; ParallelBitmapHeapState *pstate = node->pstate; - dsa_area *dsa = node->ss.ps.state->es_query_dsa; + bool parallel_aware = node->ss.ps.plan->parallel_aware; - if (!pstate) + if (!parallel_aware) { node->tbm = (TIDBitmap *) MultiExecProcNode(outerPlanState(node)); @@ -133,8 +133,9 @@ BitmapTableScanSetup(BitmapHeapScanState *node) BitmapDoneInitializingSharedState(pstate); } - tbmiterator = tbm_begin_iterate(node->tbm, dsa, - pstate ? + tbmiterator = tbm_begin_iterate(node->tbm, + node->ss.ps.state->es_query_dsa, + parallel_aware ? pstate->tbmiterator : InvalidDsaPointer); @@ -497,6 +498,12 @@ ExecBitmapHeapEstimate(BitmapHeapScanState *node, { Size size; + if (!node->ss.ps.instrument && !node->ss.ps.plan->parallel_aware) + { + /* No DSM required by the scan */ + return; + } + size = MAXALIGN(sizeof(ParallelBitmapHeapState)); /* account for instrumentation, if required */ @@ -522,13 +529,14 @@ ExecBitmapHeapInitializeDSM(BitmapHeapScanState *node, { 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) + if (!node->ss.ps.instrument && !node->ss.ps.plan->parallel_aware) + { + /* No DSM required by the scan */ return; + } size = MAXALIGN(sizeof(ParallelBitmapHeapState)); if (node->ss.ps.instrument && pcxt->nworkers > 0) @@ -543,13 +551,18 @@ ExecBitmapHeapInitializeDSM(BitmapHeapScanState *node, if (node->ss.ps.instrument && pcxt->nworkers > 0) sinstrument = (SharedBitmapHeapInstrumentation *) ptr; - pstate->tbmiterator = 0; - - /* Initialize the mutex */ - SpinLockInit(&pstate->mutex); - pstate->state = BM_INITIAL; + pstate->tbmiterator = InvalidDsaPointer; - ConditionVariableInit(&pstate->cv); + /* + * Only initialize these fields when parallel-aware as they are used to + * coordinate TBM iteration amongst parallel workers. + */ + if (node->ss.ps.plan->parallel_aware) + { + SpinLockInit(&pstate->mutex); + pstate->state = BM_INITIAL; + ConditionVariableInit(&pstate->cv); + } if (sinstrument) { @@ -578,9 +591,8 @@ ExecBitmapHeapReInitializeDSM(BitmapHeapScanState *node, ParallelBitmapHeapState *pstate = node->pstate; dsa_area *dsa = node->ss.ps.state->es_query_dsa; - /* If there's no DSA, there are no workers; do nothing. */ - if (dsa == NULL) - return; + Assert(node->ss.ps.plan->parallel_aware); + Assert(dsa != NULL); pstate->state = BM_INITIAL; @@ -602,7 +614,11 @@ ExecBitmapHeapInitializeWorker(BitmapHeapScanState *node, { char *ptr; - Assert(node->ss.ps.state->es_query_dsa != NULL); + if (!node->ss.ps.instrument && !node->ss.ps.plan->parallel_aware) + { + /* No DSM required by the scan */ + return; + } ptr = shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false); -- 2.43.0