v8-alt-0005-Add-EXPLAIN-IO-instrumentation-for-SeqScan.patch
text/x-patch
Filename: v8-alt-0005-Add-EXPLAIN-IO-instrumentation-for-SeqScan.patch
Type: text/x-patch
Part: 4
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 v8-0005
Subject: Add EXPLAIN (IO) instrumentation for SeqScan
| File | + | − |
|---|---|---|
| contrib/postgres_fdw/expected/postgres_fdw.out | 4 | 4 |
| contrib/postgres_fdw/sql/postgres_fdw.sql | 4 | 4 |
| src/backend/commands/explain.c | 25 | 0 |
| src/backend/executor/execParallel.c | 11 | 0 |
| src/backend/executor/nodeSeqscan.c | 107 | 9 |
| src/include/executor/instrument_node.h | 19 | 0 |
| src/include/executor/nodeSeqscan.h | 9 | 0 |
| src/include/nodes/execnodes.h | 1 | 0 |
| src/test/isolation/expected/merge-update.out | 1 | 1 |
| src/test/isolation/specs/merge-update.spec | 4 | 1 |
| src/test/regress/expected/explain.out | 20 | 6 |
| src/test/regress/expected/partition_prune.out | 17 | 17 |
| src/test/regress/sql/explain.sql | 6 | 6 |
| src/test/regress/sql/partition_prune.sql | 17 | 17 |
| src/tools/pgindent/typedefs.list | 2 | 0 |
From 6a6bfb5ae89ae2399431f77a03d79961ded6d2df Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
Date: Sun, 5 Apr 2026 17:28:29 -0400
Subject: [PATCH v8-alt 5/7] Add EXPLAIN (IO) instrumentation for SeqScan
---
.../postgres_fdw/expected/postgres_fdw.out | 8 +-
contrib/postgres_fdw/sql/postgres_fdw.sql | 8 +-
src/backend/commands/explain.c | 25 ++++
src/backend/executor/execParallel.c | 11 ++
src/backend/executor/nodeSeqscan.c | 116 ++++++++++++++++--
src/include/executor/instrument_node.h | 19 +++
src/include/executor/nodeSeqscan.h | 9 ++
src/include/nodes/execnodes.h | 1 +
src/test/isolation/expected/merge-update.out | 2 +-
src/test/isolation/specs/merge-update.spec | 5 +-
src/test/regress/expected/explain.out | 26 +++-
src/test/regress/expected/partition_prune.out | 34 ++---
src/test/regress/sql/explain.sql | 12 +-
src/test/regress/sql/partition_prune.sql | 34 ++---
src/tools/pgindent/typedefs.list | 2 +
15 files changed, 247 insertions(+), 65 deletions(-)
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index cd22553236f..6b460a90158 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -11980,7 +11980,7 @@ SELECT * FROM local_tbl, async_pt WHERE local_tbl.a = async_pt.a AND local_tbl.c
Filter: (async_pt_3.a = local_tbl.a)
(15 rows)
-EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF)
+EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF, IO OFF)
SELECT * FROM local_tbl, async_pt WHERE local_tbl.a = async_pt.a AND local_tbl.c = 'bar';
QUERY PLAN
----------------------------------------------------------------------------------
@@ -12226,7 +12226,7 @@ SELECT * FROM local_tbl t1 LEFT JOIN (SELECT *, (SELECT count(*) FROM async_pt W
Remote SQL: SELECT a, b, c FROM public.base_tbl2 WHERE ((a < 3000))
(20 rows)
-EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF)
+EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF, IO OFF)
SELECT * FROM local_tbl t1 LEFT JOIN (SELECT *, (SELECT count(*) FROM async_pt WHERE a < 3000) FROM async_pt WHERE a < 3000) t2 ON t1.a = t2.a;
QUERY PLAN
--------------------------------------------------------------------------------------------
@@ -12270,7 +12270,7 @@ SELECT * FROM async_pt t1 WHERE t1.b === 505 LIMIT 1;
Filter: (t1_3.b === 505)
(14 rows)
-EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF)
+EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF, IO OFF)
SELECT * FROM async_pt t1 WHERE t1.b === 505 LIMIT 1;
QUERY PLAN
----------------------------------------------------------------------------
@@ -12430,7 +12430,7 @@ DELETE FROM async_pt WHERE b = 0 RETURNING *;
DELETE FROM async_p1;
DELETE FROM async_p2;
DELETE FROM async_p3;
-EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF)
+EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF, IO OFF)
SELECT * FROM async_pt;
QUERY PLAN
----------------------------------------------------------------------------
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 59963e298b8..e8b96a16849 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -4093,7 +4093,7 @@ ALTER FOREIGN TABLE async_p2 OPTIONS (use_remote_estimate 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM local_tbl, async_pt WHERE local_tbl.a = async_pt.a AND local_tbl.c = 'bar';
-EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF)
+EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF, IO OFF)
SELECT * FROM local_tbl, async_pt WHERE local_tbl.a = async_pt.a AND local_tbl.c = 'bar';
SELECT * FROM local_tbl, async_pt WHERE local_tbl.a = async_pt.a AND local_tbl.c = 'bar';
@@ -4168,13 +4168,13 @@ ANALYZE local_tbl;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM local_tbl t1 LEFT JOIN (SELECT *, (SELECT count(*) FROM async_pt WHERE a < 3000) FROM async_pt WHERE a < 3000) t2 ON t1.a = t2.a;
-EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF)
+EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF, IO OFF)
SELECT * FROM local_tbl t1 LEFT JOIN (SELECT *, (SELECT count(*) FROM async_pt WHERE a < 3000) FROM async_pt WHERE a < 3000) t2 ON t1.a = t2.a;
SELECT * FROM local_tbl t1 LEFT JOIN (SELECT *, (SELECT count(*) FROM async_pt WHERE a < 3000) FROM async_pt WHERE a < 3000) t2 ON t1.a = t2.a;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM async_pt t1 WHERE t1.b === 505 LIMIT 1;
-EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF)
+EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF, IO OFF)
SELECT * FROM async_pt t1 WHERE t1.b === 505 LIMIT 1;
SELECT * FROM async_pt t1 WHERE t1.b === 505 LIMIT 1;
@@ -4226,7 +4226,7 @@ DELETE FROM async_p1;
DELETE FROM async_p2;
DELETE FROM async_p3;
-EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF)
+EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF, IO OFF)
SELECT * FROM async_pt;
-- Clean up
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 0fab21a5cfa..56c749c0d86 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -2031,6 +2031,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
planstate, es);
if (IsA(plan, CteScan))
show_ctescan_info(castNode(CteScanState, planstate), es);
+ show_scan_io_usage((ScanState *) planstate, es);
break;
case T_Gather:
{
@@ -4085,6 +4086,30 @@ show_scan_io_usage(ScanState *planstate, ExplainState *es)
}
}
+ break;
+ }
+ case T_SeqScan:
+ {
+ SharedSeqScanInstrumentation *sinstrument
+ = ((SeqScanState *) planstate)->sinstrument;
+
+ if (sinstrument)
+ {
+ for (int i = 0; i < sinstrument->num_workers; ++i)
+ {
+ SeqScanInstrumentation *winstrument = &sinstrument->sinstrument[i];
+
+ AccumulateIOStats(&stats, &winstrument->stats.io);
+
+ if (!es->workers_state)
+ continue;
+
+ ExplainOpenWorker(i, es);
+ print_io_usage(es, &winstrument->stats.io);
+ ExplainCloseWorker(i, es);
+ }
+ }
+
break;
}
default:
diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c
index 8d5c69ec85e..8c2ee4cd463 100644
--- a/src/backend/executor/execParallel.c
+++ b/src/backend/executor/execParallel.c
@@ -253,6 +253,9 @@ ExecParallelEstimate(PlanState *planstate, ExecParallelEstimateContext *e)
if (planstate->plan->parallel_aware)
ExecSeqScanEstimate((SeqScanState *) planstate,
e->pcxt);
+ /* even when not parallel-aware, for EXPLAIN ANALYZE */
+ ExecSeqScanInstrumentEstimate((SeqScanState *) planstate,
+ e->pcxt);
break;
case T_IndexScanState:
if (planstate->plan->parallel_aware)
@@ -496,6 +499,9 @@ ExecParallelInitializeDSM(PlanState *planstate,
if (planstate->plan->parallel_aware)
ExecSeqScanInitializeDSM((SeqScanState *) planstate,
d->pcxt);
+ /* even when not parallel-aware, for EXPLAIN ANALYZE */
+ ExecSeqScanInstrumentInitDSM((SeqScanState *) planstate,
+ d->pcxt);
break;
case T_IndexScanState:
if (planstate->plan->parallel_aware)
@@ -1144,6 +1150,9 @@ ExecParallelRetrieveInstrumentation(PlanState *planstate,
case T_BitmapHeapScanState:
ExecBitmapHeapRetrieveInstrumentation((BitmapHeapScanState *) planstate);
break;
+ case T_SeqScanState:
+ ExecSeqScanRetrieveInstrumentation((SeqScanState *) planstate);
+ break;
default:
break;
}
@@ -1384,6 +1393,8 @@ ExecParallelInitializeWorker(PlanState *planstate, ParallelWorkerContext *pwcxt)
case T_SeqScanState:
if (planstate->plan->parallel_aware)
ExecSeqScanInitializeWorker((SeqScanState *) planstate, pwcxt);
+ /* even when not parallel-aware, for EXPLAIN ANALYZE */
+ ExecSeqScanInstrumentInitWorker((SeqScanState *) planstate, pwcxt);
break;
case T_IndexScanState:
if (planstate->plan->parallel_aware)
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 04803b0e37d..d169cb6b3c2 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -29,6 +29,7 @@
#include "access/relscan.h"
#include "access/tableam.h"
+#include "executor/execParallel.h"
#include "executor/execScan.h"
#include "executor/executor.h"
#include "executor/nodeSeqscan.h"
@@ -65,15 +66,21 @@ SeqNext(SeqScanState *node)
if (scandesc == NULL)
{
+ uint32 flags = SO_NONE;
+
+ if (ScanRelIsReadOnly(&node->ss))
+ flags |= SO_HINT_REL_READ_ONLY;
+
+ if (estate->es_instrument)
+ flags |= SO_SCAN_INSTRUMENT;
+
/*
* We reach here if the scan is not parallel, or if we're serially
* executing a scan that was planned to be parallel.
*/
scandesc = table_beginscan(node->ss.ss_currentRelation,
estate->es_snapshot,
- 0, NULL,
- ScanRelIsReadOnly(&node->ss) ?
- SO_HINT_REL_READ_ONLY : SO_NONE);
+ 0, NULL, flags);
node->ss.ss_currentScanDesc = scandesc;
}
@@ -297,6 +304,24 @@ ExecEndSeqScan(SeqScanState *node)
{
TableScanDesc scanDesc;
+ /*
+ * Collect IO stats for this process into shared instrumentation.
+ */
+ if (node->sinstrument != NULL && IsParallelWorker())
+ {
+ SeqScanInstrumentation *si;
+
+ Assert(ParallelWorkerNumber <= node->sinstrument->num_workers);
+ si = &node->sinstrument->sinstrument[ParallelWorkerNumber];
+
+ if (node->ss.ss_currentScanDesc &&
+ node->ss.ss_currentScanDesc->rs_instrument)
+ {
+ AccumulateIOStats(&si->stats.io,
+ &node->ss.ss_currentScanDesc->rs_instrument->io);
+ }
+ }
+
/*
* get information from node
*/
@@ -370,6 +395,13 @@ ExecSeqScanInitializeDSM(SeqScanState *node,
{
EState *estate = node->ss.ps.state;
ParallelTableScanDesc pscan;
+ uint32 flags = SO_NONE;
+
+ if (ScanRelIsReadOnly(&node->ss))
+ flags |= SO_HINT_REL_READ_ONLY;
+
+ if (estate->es_instrument)
+ flags |= SO_SCAN_INSTRUMENT;
pscan = shm_toc_allocate(pcxt->toc, node->pscan_len);
table_parallelscan_initialize(node->ss.ss_currentRelation,
@@ -378,9 +410,7 @@ ExecSeqScanInitializeDSM(SeqScanState *node,
shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, pscan);
node->ss.ss_currentScanDesc =
- table_beginscan_parallel(node->ss.ss_currentRelation, pscan,
- ScanRelIsReadOnly(&node->ss) ?
- SO_HINT_REL_READ_ONLY : SO_NONE);
+ table_beginscan_parallel(node->ss.ss_currentRelation, pscan, flags);
}
/* ----------------------------------------------------------------
@@ -410,10 +440,78 @@ ExecSeqScanInitializeWorker(SeqScanState *node,
ParallelWorkerContext *pwcxt)
{
ParallelTableScanDesc pscan;
+ uint32 flags = SO_NONE;
+
+ if (ScanRelIsReadOnly(&node->ss))
+ flags |= SO_HINT_REL_READ_ONLY;
+
+ if (node->ss.ps.state->es_instrument)
+ flags |= SO_SCAN_INSTRUMENT;
pscan = shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false);
node->ss.ss_currentScanDesc =
- table_beginscan_parallel(node->ss.ss_currentRelation, pscan,
- ScanRelIsReadOnly(&node->ss) ?
- SO_HINT_REL_READ_ONLY : SO_NONE);
+ table_beginscan_parallel(node->ss.ss_currentRelation, pscan, flags);
+}
+
+void
+ExecSeqScanInstrumentEstimate(SeqScanState *node, ParallelContext *pcxt)
+{
+ EState *estate = node->ss.ps.state;
+
+ if (!estate->es_instrument || pcxt->nworkers == 0)
+ return;
+
+ shm_toc_estimate_chunk(&pcxt->estimator,
+ offsetof(SharedSeqScanInstrumentation, sinstrument) +
+ sizeof(SeqScanInstrumentation) * pcxt->nworkers);
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+}
+
+void
+ExecSeqScanInstrumentInitDSM(SeqScanState *node, ParallelContext *pcxt)
+{
+ EState *estate = node->ss.ps.state;
+ SharedSeqScanInstrumentation *sinstrument;
+ Size size;
+
+ if (!estate->es_instrument || pcxt->nworkers == 0)
+ return;
+
+ size = offsetof(SharedSeqScanInstrumentation, sinstrument) +
+ sizeof(SeqScanInstrumentation) * pcxt->nworkers;
+ sinstrument = shm_toc_allocate(pcxt->toc, size);
+ memset(sinstrument, 0, size);
+ sinstrument->num_workers = pcxt->nworkers;
+ shm_toc_insert(pcxt->toc,
+ node->ss.ps.plan->plan_node_id + PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
+ sinstrument);
+ node->sinstrument = sinstrument;
+}
+
+void
+ExecSeqScanInstrumentInitWorker(SeqScanState *node,
+ ParallelWorkerContext *pwcxt)
+{
+ if (!node->ss.ps.state->es_instrument)
+ return;
+
+ node->sinstrument = shm_toc_lookup(pwcxt->toc,
+ node->ss.ps.plan->plan_node_id + PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
+ true);
+}
+
+void
+ExecSeqScanRetrieveInstrumentation(SeqScanState *node)
+{
+ SharedSeqScanInstrumentation *sinstrument = node->sinstrument;
+ Size size;
+
+ if (sinstrument == NULL)
+ return;
+
+ size = offsetof(SharedSeqScanInstrumentation, sinstrument)
+ + sinstrument->num_workers * sizeof(SeqScanInstrumentation);
+
+ node->sinstrument = palloc(size);
+ memcpy(node->sinstrument, sinstrument, size);
}
diff --git a/src/include/executor/instrument_node.h b/src/include/executor/instrument_node.h
index 56a695304dd..b7226df6840 100644
--- a/src/include/executor/instrument_node.h
+++ b/src/include/executor/instrument_node.h
@@ -258,4 +258,23 @@ typedef struct SharedIncrementalSortInfo
IncrementalSortInfo sinfo[FLEXIBLE_ARRAY_MEMBER];
} SharedIncrementalSortInfo;
+
+/* ---------------------
+ * Instrumentation information for sequential scans
+ * ---------------------
+ */
+typedef struct SeqScanInstrumentation
+{
+ TableScanInstrumentation stats;
+} SeqScanInstrumentation;
+
+/*
+ * Shared memory container for per-worker information
+ */
+typedef struct SharedSeqScanInstrumentation
+{
+ int num_workers;
+ SeqScanInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER];
+} SharedSeqScanInstrumentation;
+
#endif /* INSTRUMENT_NODE_H */
diff --git a/src/include/executor/nodeSeqscan.h b/src/include/executor/nodeSeqscan.h
index 7a1490596fb..9c0ad4879d7 100644
--- a/src/include/executor/nodeSeqscan.h
+++ b/src/include/executor/nodeSeqscan.h
@@ -28,4 +28,13 @@ extern void ExecSeqScanReInitializeDSM(SeqScanState *node, ParallelContext *pcxt
extern void ExecSeqScanInitializeWorker(SeqScanState *node,
ParallelWorkerContext *pwcxt);
+/* instrument support */
+extern void ExecSeqScanInstrumentEstimate(SeqScanState *node,
+ ParallelContext *pcxt);
+extern void ExecSeqScanInstrumentInitDSM(SeqScanState *node,
+ ParallelContext *pcxt);
+extern void ExecSeqScanInstrumentInitWorker(SeqScanState *node,
+ ParallelWorkerContext *pwcxt);
+extern void ExecSeqScanRetrieveInstrumentation(SeqScanState *node);
+
#endif /* NODESEQSCAN_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 090cfccf65f..ee9ca731f70 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1666,6 +1666,7 @@ typedef struct SeqScanState
{
ScanState ss; /* its first field is NodeTag */
Size pscan_len; /* size of parallel heap scan descriptor */
+ struct SharedSeqScanInstrumentation *sinstrument;
} SeqScanState;
/* ----------------
diff --git a/src/test/isolation/expected/merge-update.out b/src/test/isolation/expected/merge-update.out
index 821565b4303..dd9c67c4dc1 100644
--- a/src/test/isolation/expected/merge-update.out
+++ b/src/test/isolation/expected/merge-update.out
@@ -358,7 +358,7 @@ step pa_merge1:
step explain_pa_merge2a:
SELECT explain_filter($$
- EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF, BUFFERS OFF)
+ EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF, BUFFERS OFF, IO OFF)
MERGE INTO pa_target t
USING (SELECT 1 as key, 'pa_merge2a' as val) s
ON s.key = t.key
diff --git a/src/test/isolation/specs/merge-update.spec b/src/test/isolation/specs/merge-update.spec
index b902779edd6..cf387eccefc 100644
--- a/src/test/isolation/specs/merge-update.spec
+++ b/src/test/isolation/specs/merge-update.spec
@@ -33,6 +33,9 @@ setup
FOR ln IN EXECUTE $1 LOOP
-- Ignore hash memory usage because it varies depending on the system
CONTINUE WHEN (ln ~ 'Memory Usage');
+ -- Ignore prefetch and I/O because it varies depending on the system
+ CONTINUE WHEN (ln ~ 'Prefetch');
+ CONTINUE WHEN (ln ~ 'I/O');
RETURN NEXT ln;
END LOOP;
END;
@@ -165,7 +168,7 @@ step "pa_merge2a"
step "explain_pa_merge2a"
{
SELECT explain_filter($$
- EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF, BUFFERS OFF)
+ EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF, BUFFERS OFF, IO OFF)
MERGE INTO pa_target t
USING (SELECT 1 as key, 'pa_merge2a' as val) s
ON s.key = t.key
diff --git a/src/test/regress/expected/explain.out b/src/test/regress/expected/explain.out
index d7754c6f2cb..3e504911db4 100644
--- a/src/test/regress/expected/explain.out
+++ b/src/test/regress/expected/explain.out
@@ -85,7 +85,7 @@ select explain_filter('explain (analyze, buffers off, io off, verbose) select *
Execution Time: N.N ms
(4 rows)
-select explain_filter('explain (analyze, buffers, format text) select * from int8_tbl i8');
+select explain_filter('explain (analyze, buffers, io off, format text) select * from int8_tbl i8');
explain_filter
-------------------------------------------------------------------------------------------------
Seq Scan on int8_tbl i8 (cost=N.N..N.N rows=N width=N) (actual time=N.N..N.N rows=N.N loops=N)
@@ -119,6 +119,13 @@ explain_filter
<Actual-Rows>N.N</Actual-Rows>
<Actual-Loops>N</Actual-Loops>
<Disabled>false</Disabled>
+ <Average-Prefetch-Distance>N.N</Average-Prefetch-Distance>
+ <Max-Prefetch-Distance>N</Max-Prefetch-Distance>
+ <Prefetch-Capacity>N</Prefetch-Capacity>
+ <I-O-Count>N</I-O-Count>
+ <I-O-Waits>N</I-O-Waits>
+ <Average-I-O-Size>N.N</Average-I-O-Size>
+ <Average-I-Os-In-Progress>N.N</Average-I-Os-In-Progress>
<Shared-Hit-Blocks>N</Shared-Hit-Blocks>
<Shared-Read-Blocks>N</Shared-Read-Blocks>
<Shared-Dirtied-Blocks>N</Shared-Dirtied-Blocks>
@@ -166,6 +173,13 @@ explain_filter
Actual Rows: N.N
Actual Loops: N
Disabled: false
+ Average Prefetch Distance: N.N
+ Max Prefetch Distance: N
+ Prefetch Capacity: N
+ I/O Count: N
+ I/O Waits: N
+ Average I/O Size: N.N
+ Average I/Os In Progress: N.N
Shared Hit Blocks: N
Shared Read Blocks: N
Shared Dirtied Blocks: N
@@ -286,7 +300,7 @@ select explain_filter('explain verbose select sum(unique1) over w1, sum(unique2)
-- Check output including I/O timings. These fields are conditional
-- but always set in JSON format, so check them only in this case.
set track_io_timing = on;
-select explain_filter('explain (analyze, buffers, format json) select * from int8_tbl i8');
+select explain_filter('explain (analyze, buffers, io off, format json) select * from int8_tbl i8');
explain_filter
-------------------------------------
[ +
@@ -421,7 +435,7 @@ select explain_filter('explain (memory, summary, format yaml) select * from int8
Planning Time: N.N
(1 row)
-select explain_filter('explain (memory, analyze, format json) select * from int8_tbl i8');
+select explain_filter('explain (memory, analyze, io off, format json) select * from int8_tbl i8');
explain_filter
------------------------------------
[ +
@@ -524,7 +538,7 @@ set parallel_tuple_cost=0;
set min_parallel_table_scan_size=0;
set max_parallel_workers_per_gather=4;
select jsonb_pretty(
- explain_filter_to_json('explain (analyze, verbose, buffers, format json)
+ explain_filter_to_json('explain (analyze, verbose, buffers, io off, format json)
select * from tenk1 order by tenthous')
-- remove "Workers" node of the Seq Scan plan node
#- '{0,Plan,Plans,0,Plans,0,Workers}'
@@ -749,7 +763,7 @@ select explain_filter('explain (analyze,buffers off,io off,serialize) select * f
Execution Time: N.N ms
(4 rows)
-select explain_filter('explain (analyze,serialize text,buffers,timing off) select * from int8_tbl i8');
+select explain_filter('explain (analyze,serialize text,buffers,io off,timing off) select * from int8_tbl i8');
explain_filter
-----------------------------------------------------------------------------------
Seq Scan on int8_tbl i8 (cost=N.N..N.N rows=N width=N) (actual rows=N.N loops=N)
@@ -758,7 +772,7 @@ select explain_filter('explain (analyze,serialize text,buffers,timing off) selec
Execution Time: N.N ms
(4 rows)
-select explain_filter('explain (analyze,serialize binary,buffers,timing) select * from int8_tbl i8');
+select explain_filter('explain (analyze,serialize binary,buffers,io off,timing) select * from int8_tbl i8');
explain_filter
-------------------------------------------------------------------------------------------------
Seq Scan on int8_tbl i8 (cost=N.N..N.N rows=N width=N) (actual time=N.N..N.N rows=N.N loops=N)
diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out
index 1012be91689..6bd15f26aec 100644
--- a/src/test/regress/expected/partition_prune.out
+++ b/src/test/regress/expected/partition_prune.out
@@ -2315,7 +2315,7 @@ begin;
-- Test run-time pruning using stable functions
create function list_part_fn(int) returns int as $$ begin return $1; end;$$ language plpgsql stable;
-- Ensure pruning works using a stable function containing no Vars
-explain (analyze, costs off, summary off, timing off, buffers off) select * from list_part where a = list_part_fn(1);
+explain (analyze, costs off, summary off, timing off, buffers off, io off) select * from list_part where a = list_part_fn(1);
QUERY PLAN
---------------------------------------------------------------------
Append (actual rows=1.00 loops=1)
@@ -2325,7 +2325,7 @@ explain (analyze, costs off, summary off, timing off, buffers off) select * from
(4 rows)
-- Ensure pruning does not take place when the function has a Var parameter
-explain (analyze, costs off, summary off, timing off, buffers off) select * from list_part where a = list_part_fn(a);
+explain (analyze, costs off, summary off, timing off, buffers off, io off) select * from list_part where a = list_part_fn(a);
QUERY PLAN
---------------------------------------------------------------------
Append (actual rows=4.00 loops=1)
@@ -2340,7 +2340,7 @@ explain (analyze, costs off, summary off, timing off, buffers off) select * from
(9 rows)
-- Ensure pruning does not take place when the expression contains a Var.
-explain (analyze, costs off, summary off, timing off, buffers off) select * from list_part where a = list_part_fn(1) + a;
+explain (analyze, costs off, summary off, timing off, buffers off, io off) select * from list_part where a = list_part_fn(1) + a;
QUERY PLAN
---------------------------------------------------------------------
Append (actual rows=0.00 loops=1)
@@ -2376,7 +2376,7 @@ declare
ln text;
begin
for ln in
- execute format('explain (analyze, costs off, summary off, timing off, buffers off) %s',
+ execute format('explain (analyze, costs off, summary off, timing off, buffers off, io off) %s',
$1)
loop
ln := regexp_replace(ln, 'Workers Launched: \d+', 'Workers Launched: N');
@@ -2687,7 +2687,7 @@ reset parallel_tuple_cost;
reset min_parallel_table_scan_size;
reset max_parallel_workers_per_gather;
-- Test run-time partition pruning with an initplan
-explain (analyze, costs off, summary off, timing off, buffers off)
+explain (analyze, costs off, summary off, timing off, buffers off, io off)
select * from ab where a = (select max(a) from lprt_a) and b = (select max(a)-1 from lprt_a);
QUERY PLAN
----------------------------------------------------------------------------
@@ -2864,7 +2864,7 @@ union all
select tableoid::regclass,a,b from ab
) ab where a = $1 and b = (select -10);
-- Ensure the xy_1 subplan is not pruned.
-explain (analyze, costs off, summary off, timing off, buffers off) execute ab_q6(1);
+explain (analyze, costs off, summary off, timing off, buffers off, io off) execute ab_q6(1);
QUERY PLAN
-------------------------------------------------------------
Append (actual rows=0.00 loops=1)
@@ -3019,7 +3019,7 @@ create index tprt6_idx on tprt_6 (col1);
insert into tprt values (10), (20), (501), (502), (505), (1001), (4500);
set enable_hashjoin = off;
set enable_mergejoin = off;
-explain (analyze, costs off, summary off, timing off, buffers off)
+explain (analyze, costs off, summary off, timing off, buffers off, io off)
select * from tbl1 join tprt on tbl1.col1 > tprt.col1;
QUERY PLAN
-----------------------------------------------------------------------------
@@ -3046,7 +3046,7 @@ select * from tbl1 join tprt on tbl1.col1 > tprt.col1;
Index Searches: 0
(21 rows)
-explain (analyze, costs off, summary off, timing off, buffers off)
+explain (analyze, costs off, summary off, timing off, buffers off, io off)
select * from tbl1 join tprt on tbl1.col1 = tprt.col1;
QUERY PLAN
-----------------------------------------------------------------------------
@@ -3097,7 +3097,7 @@ order by tbl1.col1, tprt.col1;
-- Multiple partitions
insert into tbl1 values (1001), (1010), (1011);
-explain (analyze, costs off, summary off, timing off, buffers off)
+explain (analyze, costs off, summary off, timing off, buffers off, io off)
select * from tbl1 inner join tprt on tbl1.col1 > tprt.col1;
QUERY PLAN
-----------------------------------------------------------------------------
@@ -3124,7 +3124,7 @@ select * from tbl1 inner join tprt on tbl1.col1 > tprt.col1;
Index Searches: 0
(21 rows)
-explain (analyze, costs off, summary off, timing off, buffers off)
+explain (analyze, costs off, summary off, timing off, buffers off, io off)
select * from tbl1 inner join tprt on tbl1.col1 = tprt.col1;
QUERY PLAN
-----------------------------------------------------------------------------
@@ -3194,7 +3194,7 @@ order by tbl1.col1, tprt.col1;
-- Last partition
delete from tbl1;
insert into tbl1 values (4400);
-explain (analyze, costs off, summary off, timing off, buffers off)
+explain (analyze, costs off, summary off, timing off, buffers off, io off)
select * from tbl1 join tprt on tbl1.col1 < tprt.col1;
QUERY PLAN
-----------------------------------------------------------------------------
@@ -3232,7 +3232,7 @@ order by tbl1.col1, tprt.col1;
-- No matching partition
delete from tbl1;
insert into tbl1 values (10000);
-explain (analyze, costs off, summary off, timing off, buffers off)
+explain (analyze, costs off, summary off, timing off, buffers off, io off)
select * from tbl1 join tprt on tbl1.col1 = tprt.col1;
QUERY PLAN
-------------------------------------------------------------------
@@ -3478,7 +3478,7 @@ create table mc3p1 partition of mc3p
create table mc3p2 partition of mc3p
for values from (2, minvalue, minvalue) to (3, maxvalue, maxvalue);
insert into mc3p values (0, 1, 1), (1, 1, 1), (2, 1, 1);
-explain (analyze, costs off, summary off, timing off, buffers off)
+explain (analyze, costs off, summary off, timing off, buffers off, io off)
select * from mc3p where a < 3 and abs(b) = 1;
QUERY PLAN
-----------------------------------------------------------
@@ -3498,7 +3498,7 @@ select * from mc3p where a < 3 and abs(b) = 1;
--
prepare ps1 as
select * from mc3p where a = $1 and abs(b) < (select 3);
-explain (analyze, costs off, summary off, timing off, buffers off)
+explain (analyze, costs off, summary off, timing off, buffers off, io off)
execute ps1(1);
QUERY PLAN
------------------------------------------------------------------
@@ -3513,7 +3513,7 @@ execute ps1(1);
deallocate ps1;
prepare ps2 as
select * from mc3p where a <= $1 and abs(b) < (select 3);
-explain (analyze, costs off, summary off, timing off, buffers off)
+explain (analyze, costs off, summary off, timing off, buffers off, io off)
execute ps2(1);
QUERY PLAN
-------------------------------------------------------------------
@@ -3535,7 +3535,7 @@ insert into boolvalues values('t'),('f');
create table boolp (a bool) partition by list (a);
create table boolp_t partition of boolp for values in('t');
create table boolp_f partition of boolp for values in('f');
-explain (analyze, costs off, summary off, timing off, buffers off)
+explain (analyze, costs off, summary off, timing off, buffers off, io off)
select * from boolp where a = (select value from boolvalues where value);
QUERY PLAN
--------------------------------------------------------------
@@ -3550,7 +3550,7 @@ select * from boolp where a = (select value from boolvalues where value);
Filter: (a = (InitPlan expr_1).col1)
(9 rows)
-explain (analyze, costs off, summary off, timing off, buffers off)
+explain (analyze, costs off, summary off, timing off, buffers off, io off)
select * from boolp where a = (select value from boolvalues where not value);
QUERY PLAN
--------------------------------------------------------------
diff --git a/src/test/regress/sql/explain.sql b/src/test/regress/sql/explain.sql
index dbd210a07f2..b7907a228fa 100644
--- a/src/test/regress/sql/explain.sql
+++ b/src/test/regress/sql/explain.sql
@@ -65,7 +65,7 @@ explain (costs off) select 1 as a, 2 as b having false;
select explain_filter('explain select * from int8_tbl i8');
select explain_filter('explain (analyze, buffers off, io off) select * from int8_tbl i8');
select explain_filter('explain (analyze, buffers off, io off, verbose) select * from int8_tbl i8');
-select explain_filter('explain (analyze, buffers, format text) select * from int8_tbl i8');
+select explain_filter('explain (analyze, buffers, io off, format text) select * from int8_tbl i8');
select explain_filter('explain (buffers, format text) select * from int8_tbl i8');
\a
@@ -82,7 +82,7 @@ select explain_filter('explain verbose select sum(unique1) over w1, sum(unique2)
-- Check output including I/O timings. These fields are conditional
-- but always set in JSON format, so check them only in this case.
set track_io_timing = on;
-select explain_filter('explain (analyze, buffers, format json) select * from int8_tbl i8');
+select explain_filter('explain (analyze, buffers, io off, format json) select * from int8_tbl i8');
set track_io_timing = off;
-- SETTINGS option
@@ -107,7 +107,7 @@ select explain_filter('explain (analyze, generic_plan) select unique1 from tenk1
select explain_filter('explain (memory) select * from int8_tbl i8');
select explain_filter('explain (memory, analyze, buffers off, io off) select * from int8_tbl i8');
select explain_filter('explain (memory, summary, format yaml) select * from int8_tbl i8');
-select explain_filter('explain (memory, analyze, format json) select * from int8_tbl i8');
+select explain_filter('explain (memory, analyze, io off, format json) select * from int8_tbl i8');
prepare int8_query as select * from int8_tbl i8;
select explain_filter('explain (memory) execute int8_query');
@@ -147,7 +147,7 @@ set min_parallel_table_scan_size=0;
set max_parallel_workers_per_gather=4;
select jsonb_pretty(
- explain_filter_to_json('explain (analyze, verbose, buffers, format json)
+ explain_filter_to_json('explain (analyze, verbose, buffers, io off, format json)
select * from tenk1 order by tenthous')
-- remove "Workers" node of the Seq Scan plan node
#- '{0,Plan,Plans,0,Plans,0,Workers}'
@@ -178,8 +178,8 @@ select explain_filter('explain (verbose) create table test_ctas as select 1');
-- Test SERIALIZE option
select explain_filter('explain (analyze,buffers off,io off,serialize) select * from int8_tbl i8');
-select explain_filter('explain (analyze,serialize text,buffers,timing off) select * from int8_tbl i8');
-select explain_filter('explain (analyze,serialize binary,buffers,timing) select * from int8_tbl i8');
+select explain_filter('explain (analyze,serialize text,buffers,io off,timing off) select * from int8_tbl i8');
+select explain_filter('explain (analyze,serialize binary,buffers,io off,timing) select * from int8_tbl i8');
-- this tests an edge case where we have no data to return
select explain_filter('explain (analyze,buffers off,io off,serialize) create temp table explain_temp as select * from int8_tbl i8');
diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql
index 212de0e6285..c5968932aed 100644
--- a/src/test/regress/sql/partition_prune.sql
+++ b/src/test/regress/sql/partition_prune.sql
@@ -553,13 +553,13 @@ begin;
create function list_part_fn(int) returns int as $$ begin return $1; end;$$ language plpgsql stable;
-- Ensure pruning works using a stable function containing no Vars
-explain (analyze, costs off, summary off, timing off, buffers off) select * from list_part where a = list_part_fn(1);
+explain (analyze, costs off, summary off, timing off, buffers off, io off) select * from list_part where a = list_part_fn(1);
-- Ensure pruning does not take place when the function has a Var parameter
-explain (analyze, costs off, summary off, timing off, buffers off) select * from list_part where a = list_part_fn(a);
+explain (analyze, costs off, summary off, timing off, buffers off, io off) select * from list_part where a = list_part_fn(a);
-- Ensure pruning does not take place when the expression contains a Var.
-explain (analyze, costs off, summary off, timing off, buffers off) select * from list_part where a = list_part_fn(1) + a;
+explain (analyze, costs off, summary off, timing off, buffers off, io off) select * from list_part where a = list_part_fn(1) + a;
rollback;
@@ -582,7 +582,7 @@ declare
ln text;
begin
for ln in
- execute format('explain (analyze, costs off, summary off, timing off, buffers off) %s',
+ execute format('explain (analyze, costs off, summary off, timing off, buffers off, io off) %s',
$1)
loop
ln := regexp_replace(ln, 'Workers Launched: \d+', 'Workers Launched: N');
@@ -669,7 +669,7 @@ reset min_parallel_table_scan_size;
reset max_parallel_workers_per_gather;
-- Test run-time partition pruning with an initplan
-explain (analyze, costs off, summary off, timing off, buffers off)
+explain (analyze, costs off, summary off, timing off, buffers off, io off)
select * from ab where a = (select max(a) from lprt_a) and b = (select max(a)-1 from lprt_a);
-- Test run-time partition pruning with UNION ALL parents
@@ -697,7 +697,7 @@ union all
) ab where a = $1 and b = (select -10);
-- Ensure the xy_1 subplan is not pruned.
-explain (analyze, costs off, summary off, timing off, buffers off) execute ab_q6(1);
+explain (analyze, costs off, summary off, timing off, buffers off, io off) execute ab_q6(1);
-- Ensure we see just the xy_1 row.
execute ab_q6(100);
@@ -752,10 +752,10 @@ insert into tprt values (10), (20), (501), (502), (505), (1001), (4500);
set enable_hashjoin = off;
set enable_mergejoin = off;
-explain (analyze, costs off, summary off, timing off, buffers off)
+explain (analyze, costs off, summary off, timing off, buffers off, io off)
select * from tbl1 join tprt on tbl1.col1 > tprt.col1;
-explain (analyze, costs off, summary off, timing off, buffers off)
+explain (analyze, costs off, summary off, timing off, buffers off, io off)
select * from tbl1 join tprt on tbl1.col1 = tprt.col1;
select tbl1.col1, tprt.col1 from tbl1
@@ -768,10 +768,10 @@ order by tbl1.col1, tprt.col1;
-- Multiple partitions
insert into tbl1 values (1001), (1010), (1011);
-explain (analyze, costs off, summary off, timing off, buffers off)
+explain (analyze, costs off, summary off, timing off, buffers off, io off)
select * from tbl1 inner join tprt on tbl1.col1 > tprt.col1;
-explain (analyze, costs off, summary off, timing off, buffers off)
+explain (analyze, costs off, summary off, timing off, buffers off, io off)
select * from tbl1 inner join tprt on tbl1.col1 = tprt.col1;
select tbl1.col1, tprt.col1 from tbl1
@@ -785,7 +785,7 @@ order by tbl1.col1, tprt.col1;
-- Last partition
delete from tbl1;
insert into tbl1 values (4400);
-explain (analyze, costs off, summary off, timing off, buffers off)
+explain (analyze, costs off, summary off, timing off, buffers off, io off)
select * from tbl1 join tprt on tbl1.col1 < tprt.col1;
select tbl1.col1, tprt.col1 from tbl1
@@ -795,7 +795,7 @@ order by tbl1.col1, tprt.col1;
-- No matching partition
delete from tbl1;
insert into tbl1 values (10000);
-explain (analyze, costs off, summary off, timing off, buffers off)
+explain (analyze, costs off, summary off, timing off, buffers off, io off)
select * from tbl1 join tprt on tbl1.col1 = tprt.col1;
select tbl1.col1, tprt.col1 from tbl1
@@ -917,7 +917,7 @@ create table mc3p2 partition of mc3p
for values from (2, minvalue, minvalue) to (3, maxvalue, maxvalue);
insert into mc3p values (0, 1, 1), (1, 1, 1), (2, 1, 1);
-explain (analyze, costs off, summary off, timing off, buffers off)
+explain (analyze, costs off, summary off, timing off, buffers off, io off)
select * from mc3p where a < 3 and abs(b) = 1;
--
@@ -927,12 +927,12 @@ select * from mc3p where a < 3 and abs(b) = 1;
--
prepare ps1 as
select * from mc3p where a = $1 and abs(b) < (select 3);
-explain (analyze, costs off, summary off, timing off, buffers off)
+explain (analyze, costs off, summary off, timing off, buffers off, io off)
execute ps1(1);
deallocate ps1;
prepare ps2 as
select * from mc3p where a <= $1 and abs(b) < (select 3);
-explain (analyze, costs off, summary off, timing off, buffers off)
+explain (analyze, costs off, summary off, timing off, buffers off, io off)
execute ps2(1);
deallocate ps2;
@@ -946,10 +946,10 @@ create table boolp (a bool) partition by list (a);
create table boolp_t partition of boolp for values in('t');
create table boolp_f partition of boolp for values in('f');
-explain (analyze, costs off, summary off, timing off, buffers off)
+explain (analyze, costs off, summary off, timing off, buffers off, io off)
select * from boolp where a = (select value from boolvalues where value);
-explain (analyze, costs off, summary off, timing off, buffers off)
+explain (analyze, costs off, summary off, timing off, buffers off, io off)
select * from boolp where a = (select value from boolvalues where not value);
drop table boolp;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index fce46f79ac2..0a9e5247634 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2793,6 +2793,7 @@ SelfJoinCandidate
SemTPadded
SemiAntiJoinFactors
SeqScan
+SeqScanInstrumentation
SeqScanState
SeqTable
SeqTableData
@@ -2857,6 +2858,7 @@ SharedMemoizeInfo
SharedRecordTableEntry
SharedRecordTableKey
SharedRecordTypmodRegistry
+SharedSeqScanInstrumentation
SharedSortInfo
SharedTuplestore
SharedTuplestoreAccessor
--
2.43.0