From eb3be81df13b3b7ded84db0019bb68105ce3163a Mon Sep 17 00:00:00 2001 From: Lukas Fittl Date: Sun, 5 Apr 2026 03:48:22 -0700 Subject: [PATCH v13 06/12] Add regression test coverage for EXPLAIN of Parallel Index Only Scans The functions dealing with copying back parallel worker instrumentation such as ExecIndexOnlyScanRetrieveInstrumentation were not exercised at all in the regression tests, leading to a gap in coverage. Add a query that verifies we correctly copy back "Index Searches" for EXPLAIN ANALYZE of a Parallel Index Only Scan. Reported-by: Andres Freund Author: Lukas Fittl Discussion: --- src/test/regress/expected/explain.out | 34 +++++++++++++++++++++++++++ src/test/regress/sql/explain.sql | 32 +++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/src/test/regress/expected/explain.out b/src/test/regress/expected/explain.out index 58c5a512d74..b307e810ca5 100644 --- a/src/test/regress/expected/explain.out +++ b/src/test/regress/expected/explain.out @@ -855,3 +855,37 @@ SELECT check_parallel_bitmap_heap_scan() AS parallel_bitmap_instrumentation; (1 row) DROP FUNCTION check_parallel_bitmap_heap_scan; +-- Test parallel index-only scan reports per-worker index search stats. +CREATE FUNCTION check_parallel_indexonly_scan() RETURNS boolean AS $$ +DECLARE + plan_json json; + node json; +BEGIN + SET LOCAL enable_seqscan = off; + SET LOCAL enable_bitmapscan = off; + SET LOCAL parallel_setup_cost = 0; + SET LOCAL parallel_tuple_cost = 0; + SET LOCAL min_parallel_index_scan_size = 0; + SET LOCAL min_parallel_table_scan_size = 0; + SET LOCAL max_parallel_workers_per_gather = 2; + SET LOCAL parallel_leader_participation = off; + + EXECUTE 'EXPLAIN (ANALYZE, BUFFERS, COSTS OFF, FORMAT JSON) + SELECT count(*) FROM tenk1 WHERE thousand > 95' INTO plan_json; + + -- Drill down to the Index Only Scan node + node := plan_json->0->'Plan'; + WHILE node->'Plans' IS NOT NULL AND node->>'Node Type' != 'Index Only Scan' LOOP + node := node->'Plans'->0; + END LOOP; + + RETURN COALESCE((node->>'Index Searches')::int, 0) > 0; +END; +$$ LANGUAGE plpgsql; +SELECT check_parallel_indexonly_scan() AS parallel_indexonly_instrumentation; + parallel_indexonly_instrumentation +------------------------------------ + t +(1 row) + +DROP FUNCTION check_parallel_indexonly_scan; diff --git a/src/test/regress/sql/explain.sql b/src/test/regress/sql/explain.sql index bac97522053..3a13fa6ca69 100644 --- a/src/test/regress/sql/explain.sql +++ b/src/test/regress/sql/explain.sql @@ -219,3 +219,35 @@ $$ LANGUAGE plpgsql; SELECT check_parallel_bitmap_heap_scan() AS parallel_bitmap_instrumentation; DROP FUNCTION check_parallel_bitmap_heap_scan; + +-- Test parallel index-only scan reports per-worker index search stats. +CREATE FUNCTION check_parallel_indexonly_scan() RETURNS boolean AS $$ +DECLARE + plan_json json; + node json; +BEGIN + SET LOCAL enable_seqscan = off; + SET LOCAL enable_bitmapscan = off; + SET LOCAL parallel_setup_cost = 0; + SET LOCAL parallel_tuple_cost = 0; + SET LOCAL min_parallel_index_scan_size = 0; + SET LOCAL min_parallel_table_scan_size = 0; + SET LOCAL max_parallel_workers_per_gather = 2; + SET LOCAL parallel_leader_participation = off; + + EXECUTE 'EXPLAIN (ANALYZE, BUFFERS, COSTS OFF, FORMAT JSON) + SELECT count(*) FROM tenk1 WHERE thousand > 95' INTO plan_json; + + -- Drill down to the Index Only Scan node + node := plan_json->0->'Plan'; + WHILE node->'Plans' IS NOT NULL AND node->>'Node Type' != 'Index Only Scan' LOOP + node := node->'Plans'->0; + END LOOP; + + RETURN COALESCE((node->>'Index Searches')::int, 0) > 0; +END; +$$ LANGUAGE plpgsql; + +SELECT check_parallel_indexonly_scan() AS parallel_indexonly_instrumentation; + +DROP FUNCTION check_parallel_indexonly_scan; -- 2.47.1