v11-0001-Fix-BitmapHeapScan-non-parallel-aware-EXPLAIN-AN.patch
text/x-patch
Filename: v11-0001-Fix-BitmapHeapScan-non-parallel-aware-EXPLAIN-AN.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v11-0001
Subject: Fix BitmapHeapScan non-parallel-aware EXPLAIN ANALYZE
| File | + | − |
|---|---|---|
| src/backend/commands/explain.c | 1 | 1 |
| src/backend/executor/execParallel.c | 9 | 0 |
| src/backend/executor/nodeBitmapHeapscan.c | 67 | 44 |
| src/include/executor/nodeBitmapHeapscan.h | 6 | 0 |
From 4a792a3d864761741c7ce5fd0e10a9f1c9539094 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
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