From cd8c8958b424b1c6799020787bfc238b8730a637 Mon Sep 17 00:00:00 2001 From: Tomas Vondra Date: Tue, 31 Mar 2026 13:50:57 +0200 Subject: [PATCH v6 4/5] 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 | 31 ++++ src/backend/executor/execParallel.c | 3 + src/backend/executor/nodeSeqscan.c | 142 ++++++++++++++++-- src/include/access/relscan.h | 1 + src/include/executor/instrument_node.h | 15 +- 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 | 26 +++- src/test/regress/expected/partition_prune.out | 34 ++--- src/test/regress/sql/explain.sql | 12 +- src/test/regress/sql/partition_prune.sql | 34 ++--- 15 files changed, 255 insertions(+), 68 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 8e015c2de33..ac881000788 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -4068,6 +4068,27 @@ 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; + + /* collect prefetch statistics from the read stream */ + stats = planstate->ss_currentScanDesc->rs_instrument->io; + + /* 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]; + + AccumulateIOStats(&stats, &winstrument->stats.io); + } + } + + break; + } case T_BitmapHeapScan: { SharedBitmapHeapInstrumentation *sinstrument @@ -4118,6 +4139,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->stats.io; + + break; + } case T_BitmapHeapScan: { BitmapHeapScanState *state = ((BitmapHeapScanState *) planstate); diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index 755191b51ef..d1747993df0 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -1125,6 +1125,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 04803b0e37d..032f3dc5efc 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -65,6 +65,14 @@ 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. @@ -72,8 +80,7 @@ SeqNext(SeqScanState *node) scandesc = table_beginscan(node->ss.ss_currentRelation, estate->es_snapshot, 0, NULL, - ScanRelIsReadOnly(&node->ss) ? - SO_HINT_REL_READ_ONLY : SO_NONE); + flags); node->ss.ss_currentScanDesc = scandesc; } @@ -297,6 +304,35 @@ 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 */ + 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 */ @@ -351,10 +387,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); } @@ -370,17 +419,52 @@ ExecSeqScanInitializeDSM(SeqScanState *node, { EState *estate = node->ss.ps.state; ParallelTableScanDesc pscan; + SharedSeqScanInstrumentation *sinstrument = NULL; + Size size; + char *ptr; + uint32 flags = SO_NONE; + + /* 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, size); + pscan->phs_len = node->pscan_len; - pscan = shm_toc_allocate(pcxt->toc, node->pscan_len); table_parallelscan_initialize(node->ss.ss_currentRelation, pscan, estate->es_snapshot); shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, pscan); + if (ScanRelIsReadOnly(&node->ss)) + flags |= SO_HINT_REL_READ_ONLY; + + if (estate->es_instrument) + flags |= SO_SCAN_INSTRUMENT; + 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); + + /* 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; } /* ---------------------------------------------------------------- @@ -409,11 +493,47 @@ void ExecSeqScanInitializeWorker(SeqScanState *node, ParallelWorkerContext *pwcxt) { + EState *estate = node->ss.ps.state; ParallelTableScanDesc pscan; + char *ptr; + 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_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); + + /* set pointer to the shared instrumentation */ + ptr = (char *) pscan; + ptr += MAXALIGN(pscan->phs_len); + + 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/access/relscan.h b/src/include/access/relscan.h index a9c334e633d..1c3e2f27aa7 100644 --- a/src/include/access/relscan.h +++ b/src/include/access/relscan.h @@ -89,6 +89,7 @@ typedef struct ParallelTableScanDescData bool phs_syncscan; /* report location to syncscan logic? */ bool phs_snapshot_any; /* SnapshotAny, not phs_snapshot_data? */ Size phs_snapshot_off; /* data for snapshot */ + Size phs_len; /* length of the scan descriptor */ } ParallelTableScanDescData; typedef struct ParallelTableScanDescData *ParallelTableScanDesc; diff --git a/src/include/executor/instrument_node.h b/src/include/executor/instrument_node.h index 6d1554fbc46..dba3ad7f599 100644 --- a/src/include/executor/instrument_node.h +++ b/src/include/executor/instrument_node.h @@ -89,9 +89,22 @@ AccumulateIOStats(IOStats *dst, IOStats *src) } /* --------------------- - * Instrumentation information for indexscans (amgettuple and amgetbitmap) + * 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; typedef struct IndexScanInstrumentation { 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 684e398f824..90da865518c 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 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 N.N N false + N.N + N + N + N + N + N.N + N.N N N N @@ -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; -- 2.53.0