From 27b92895be48cbe8bd90d78efb7586d784aa52ed Mon Sep 17 00:00:00 2001 From: Tomas Vondra Date: Thu, 19 Mar 2026 14:29:52 +0100 Subject: [PATCH v4 3/4] show prefetch stats for SeqScan - enable show_scan_io_usage for SeqScan - add infrastructure to allocate/collect instrumentation from parallel workers --- .../postgres_fdw/expected/postgres_fdw.out | 8 +- contrib/postgres_fdw/sql/postgres_fdw.sql | 8 +- src/backend/commands/explain.c | 27 +++++ src/backend/executor/execParallel.c | 3 + src/backend/executor/nodeSeqscan.c | 110 +++++++++++++++++- src/include/executor/instrument_node.h | 16 ++- src/include/executor/nodeSeqscan.h | 1 + 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 | 12 +- src/test/regress/expected/partition_prune.out | 34 +++--- src/test/regress/sql/explain.sql | 12 +- src/test/regress/sql/partition_prune.sql | 34 +++--- 14 files changed, 213 insertions(+), 60 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 0f5271d476e..3f178eda913 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -11937,7 +11937,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 ---------------------------------------------------------------------------------- @@ -12183,7 +12183,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 -------------------------------------------------------------------------------------------- @@ -12227,7 +12227,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 ---------------------------------------------------------------------------- @@ -12387,7 +12387,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 49ed797e8ef..2aee5bb3fe9 100644 --- a/contrib/postgres_fdw/sql/postgres_fdw.sql +++ b/contrib/postgres_fdw/sql/postgres_fdw.sql @@ -4059,7 +4059,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'; @@ -4134,13 +4134,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; @@ -4192,7 +4192,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 d98962f50b3..07a418e50cc 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -4082,6 +4082,23 @@ show_scan_io_usage(ScanState *planstate, ExplainState *es) /* Initialize counters with stats from the local process first */ switch (nodeTag(plan)) { + case T_SeqScan: + { + SharedSeqScanInstrumentation *sinstrument + = ((SeqScanState *) planstate)->sinstrument; + + /* get the sum of the counters set within each and every process */ + if (sinstrument) + { + for (int i = 0; i < sinstrument->num_workers; ++i) + { + SeqScanInstrumentation *winstrument = &sinstrument->sinstrument[i]; + ACCUMULATE_IO_STATS(&stats, &winstrument->io); + } + } + + break; + } case T_BitmapHeapScan: { SharedBitmapHeapInstrumentation *sinstrument @@ -4125,6 +4142,16 @@ show_io_usage(PlanState *planstate, ExplainState *es, int worker) /* get instrumentation for the given worker */ switch (nodeTag(plan)) { + case T_SeqScan: + { + SeqScanState *state = ((SeqScanState *) planstate); + SharedSeqScanInstrumentation *sinstrument = state->sinstrument; + SeqScanInstrumentation *instrument = &sinstrument->sinstrument[worker]; + + stats = &instrument->io; + + break; + } case T_BitmapHeapScan: { BitmapHeapScanState *state = ((BitmapHeapScanState *) planstate); diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index ac84af294c9..ffc708ed6be 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -1119,6 +1119,9 @@ ExecParallelRetrieveInstrumentation(PlanState *planstate, case T_BitmapHeapScanState: ExecBitmapHeapRetrieveInstrumentation((BitmapHeapScanState *) planstate); break; + case T_SeqScanState: + ExecSeqScanRetrieveInstrumentation((SeqScanState *) planstate); + break; default: break; } diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 8f219f60a93..2ecc0e3054d 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -295,6 +295,30 @@ ExecEndSeqScan(SeqScanState *node) { TableScanDesc scanDesc; + /* + * When ending a parallel worker, copy the statistics gathered by the + * worker back into shared memory so that it can be picked up by the main + * process to report in EXPLAIN ANALYZE. + */ + if (node->sinstrument != NULL && IsParallelWorker()) + { + SeqScanInstrumentation *si; + + Assert(ParallelWorkerNumber < node->sinstrument->num_workers); + si = &node->sinstrument->sinstrument[ParallelWorkerNumber]; + + /* + * Here we accumulate the stats rather than performing memcpy on + * node->stats into si. When a Gather/GatherMerge node finishes it + * will perform planner shutdown on the workers. On rescan it will + * spin up new workers which will have a new SeqScanState and + * zeroed stats. + */ + + /* collect prefetch info for this process from the read_stream */ + ACCUMULATE_IO_STATS(&si->io, &node->ss.ss_currentScanDesc->rs_iostats); + } + /* * get information from node */ @@ -349,10 +373,23 @@ ExecSeqScanEstimate(SeqScanState *node, ParallelContext *pcxt) { EState *estate = node->ss.ps.state; + Size size; - node->pscan_len = table_parallelscan_estimate(node->ss.ss_currentRelation, + size = table_parallelscan_estimate(node->ss.ss_currentRelation, estate->es_snapshot); - shm_toc_estimate_chunk(&pcxt->estimator, node->pscan_len); + node->pscan_len = size; + + /* make sure the instrumentation is properly aligned */ + size = MAXALIGN(size); + + /* account for instrumentation, if required */ + if (node->ss.ps.instrument && pcxt->nworkers > 0) + { + size = add_size(size, offsetof(SharedSeqScanInstrumentation, sinstrument)); + size = add_size(size, mul_size(pcxt->nworkers, sizeof(SeqScanInstrumentation))); + } + + shm_toc_estimate_chunk(&pcxt->estimator, size); shm_toc_estimate_keys(&pcxt->estimator, 1); } @@ -368,14 +405,42 @@ ExecSeqScanInitializeDSM(SeqScanState *node, { EState *estate = node->ss.ps.state; ParallelTableScanDesc pscan; + SharedSeqScanInstrumentation *sinstrument = NULL; + Size size; + char *ptr; + + /* Recalculate the size. This needs to match ExecSeqScanEstimate. */ + size = MAXALIGN(node->pscan_len); + if (node->ss.ps.instrument && pcxt->nworkers > 0) + { + size = add_size(size, offsetof(SharedSeqScanInstrumentation, sinstrument)); + size = add_size(size, mul_size(pcxt->nworkers, sizeof(SeqScanInstrumentation))); + } - pscan = shm_toc_allocate(pcxt->toc, node->pscan_len); + pscan = shm_toc_allocate(pcxt->toc, size); table_parallelscan_initialize(node->ss.ss_currentRelation, pscan, estate->es_snapshot); 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); + + /* initialize the shared instrumentation (with correct alignment) */ + ptr = (char *) pscan; + ptr += MAXALIGN(node->pscan_len); + if (node->ss.ps.instrument && pcxt->nworkers > 0) + sinstrument = (SharedSeqScanInstrumentation *) ptr; + + if (sinstrument) + { + sinstrument->num_workers = pcxt->nworkers; + + /* ensure any unfilled slots will contain zeroes */ + memset(sinstrument->sinstrument, 0, + pcxt->nworkers * sizeof(SeqScanInstrumentation)); + } + + node->sinstrument = sinstrument; } /* ---------------------------------------------------------------- @@ -404,9 +469,48 @@ void ExecSeqScanInitializeWorker(SeqScanState *node, ParallelWorkerContext *pwcxt) { + EState *estate = node->ss.ps.state; ParallelTableScanDesc pscan; + char *ptr; + Size size; 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); + + /* + * Workers don't get the pscan_len value in scan descriptor, so use the + * TAM callback again. The result has to match the earlier result in + * ExecSeqScanEstimate. + */ + size = table_parallelscan_estimate(node->ss.ss_currentRelation, + estate->es_snapshot); + + ptr = (char *) pscan; + ptr += MAXALIGN(size); + + if (node->ss.ps.instrument) + node->sinstrument = (SharedSeqScanInstrumentation *) ptr; +} + +/* ---------------------------------------------------------------- + * ExecSeqScanRetrieveInstrumentation + * + * Transfer seq scan statistics from DSM to private memory. + * ---------------------------------------------------------------- + */ +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 e6c714aa662..ce643ef0635 100644 --- a/src/include/executor/instrument_node.h +++ b/src/include/executor/instrument_node.h @@ -82,9 +82,23 @@ typedef struct IOStats } while (0) /* --------------------- - * Instrumentation information for indexscans (amgettuple and amgetbitmap) + * Instrumentation information for sequential scans * --------------------- */ +typedef struct SeqScanInstrumentation +{ + IOStats io; +} SeqScanInstrumentation; + +/* + * Shared memory container for per-worker information + */ +typedef struct SharedSeqScanInstrumentation +{ + int num_workers; + SeqScanInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER]; +} SharedSeqScanInstrumentation; + typedef struct IndexScanInstrumentation { /* Index search count (incremented with pgstat_count_index_scan call) */ diff --git a/src/include/executor/nodeSeqscan.h b/src/include/executor/nodeSeqscan.h index 7a1490596fb..e2122bfffe3 100644 --- a/src/include/executor/nodeSeqscan.h +++ b/src/include/executor/nodeSeqscan.h @@ -27,5 +27,6 @@ extern void ExecSeqScanInitializeDSM(SeqScanState *node, ParallelContext *pcxt); extern void ExecSeqScanReInitializeDSM(SeqScanState *node, ParallelContext *pcxt); extern void ExecSeqScanInitializeWorker(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 0716c5a9aed..f287f8fc813 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1644,6 +1644,7 @@ typedef struct SeqScanState { ScanState ss; /* its first field is NodeTag */ Size pscan_len; /* size of parallel heap scan descriptor */ + 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 bb9fbe4a407..97cb7206676 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) @@ -290,7 +290,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 ------------------------------------- [ + @@ -425,7 +425,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 ------------------------------------ [ + @@ -528,7 +528,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}' @@ -753,7 +753,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) @@ -762,7 +762,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 0124c37ef15..5ee453a0a96 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 (analyze, buffers, io off, format xml) select * from int8_tbl i8'); select explain_filter('explain (analyze, serialize, buffers, io off, format yaml) select * from int8_tbl i8'); select explain_filter('explain (buffers, format text) select * from int8_tbl i8'); @@ -79,7 +79,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 @@ -104,7 +104,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'); @@ -144,7 +144,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}' @@ -175,8 +175,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; -- 2.53.0