From 61b491f0f43e29041ef750ea3d6e02d8268b73c9 Mon Sep 17 00:00:00 2001 From: Mehmet YILMAZ Date: Mon, 3 Nov 2025 15:34:28 +0300 Subject: [PATCH] PG18: Add _plan_json() test helper and switch columnar index tests to JSON plan checks (#8299) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fixes #8263 * Introduces `columnar_test_helpers._plan_json(q text) -> jsonb`, which runs `EXPLAIN (FORMAT JSON, COSTS OFF, ANALYZE OFF)` and returns the plan as `jsonb`. This lets us assert on plan structure instead of text matching. * Updates `columnar_indexes.sql` tests to detect whether any index-based scan is used by searching the JSON plan’s `"Node Type"` (e.g., `Index Scan`, `Index Only Scan`, `Bitmap Index Scan`). ## Notable changes * New helper in `src/test/regress/sql/columnar_test_helpers.sql`: * `EXECUTE format('EXPLAIN (FORMAT JSON, COSTS OFF, ANALYZE OFF) %s', q) INTO j;` * Returns the `jsonb` plan for downstream assertions. ```sql SELECT NOT jsonb_path_exists( columnar_test_helpers._plan_json('SELECT b FROM columnar_table WHERE b = 30000'), '$[*].Plan.** ? (@."Node Type" like_regex "^(Index|Bitmap Index).*Scan$")' ) AS uses_no_index_scan; ``` to verify no index scan occurs at the partial index boundary (`b = 30000`) and that an index scan is used where expected (`b = 30001`). --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/test/regress/expected/columnar_indexes.out | 13 ++++++++----- src/test/regress/expected/columnar_test_helpers.out | 8 ++++++++ src/test/regress/sql/columnar_indexes.sql | 6 +++++- src/test/regress/sql/columnar_test_helpers.sql | 9 +++++++++ 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/test/regress/expected/columnar_indexes.out b/src/test/regress/expected/columnar_indexes.out index d5e4b1cbb..341dc6b63 100644 --- a/src/test/regress/expected/columnar_indexes.out +++ b/src/test/regress/expected/columnar_indexes.out @@ -176,12 +176,15 @@ SELECT pg_total_relation_size('columnar_table_b_idx') * 5 < (1 row) -- can't use index scan due to partial index boundaries -EXPLAIN (COSTS OFF) SELECT b FROM columnar_table WHERE b = 30000; - QUERY PLAN +SELECT NOT jsonb_path_exists( + columnar_test_helpers._plan_json('SELECT b FROM columnar_table WHERE b = 30000'), + -- Regex matches any index-based scan: "Index Scan", "Index Only Scan", "Bitmap Index Scan". + '$[*].Plan.** ? (@."Node Type" like_regex "^(Index|Bitmap Index).*Scan$")' + ) AS uses_no_index_scan; -- expect: t + uses_no_index_scan --------------------------------------------------------------------- - Seq Scan on columnar_table - Filter: (b = 30000) -(2 rows) + t +(1 row) -- can use index scan EXPLAIN (COSTS OFF) SELECT b FROM columnar_table WHERE b = 30001; diff --git a/src/test/regress/expected/columnar_test_helpers.out b/src/test/regress/expected/columnar_test_helpers.out index f4f179e55..a0061688d 100644 --- a/src/test/regress/expected/columnar_test_helpers.out +++ b/src/test/regress/expected/columnar_test_helpers.out @@ -146,3 +146,11 @@ BEGIN RETURN NEXT; END LOOP; END; $$ language plpgsql; +CREATE OR REPLACE FUNCTION _plan_json(q text) +RETURNS jsonb +LANGUAGE plpgsql AS $$ +DECLARE j jsonb; +BEGIN + EXECUTE format('EXPLAIN (FORMAT JSON, COSTS OFF, ANALYZE OFF) %s', q) INTO j; + RETURN j; +END $$; diff --git a/src/test/regress/sql/columnar_indexes.sql b/src/test/regress/sql/columnar_indexes.sql index afb56e01c..6e54b8591 100644 --- a/src/test/regress/sql/columnar_indexes.sql +++ b/src/test/regress/sql/columnar_indexes.sql @@ -114,7 +114,11 @@ SELECT pg_total_relation_size('columnar_table_b_idx') * 5 < pg_total_relation_size('columnar_table_a_idx'); -- can't use index scan due to partial index boundaries -EXPLAIN (COSTS OFF) SELECT b FROM columnar_table WHERE b = 30000; +SELECT NOT jsonb_path_exists( + columnar_test_helpers._plan_json('SELECT b FROM columnar_table WHERE b = 30000'), + -- Regex matches any index-based scan: "Index Scan", "Index Only Scan", "Bitmap Index Scan". + '$[*].Plan.** ? (@."Node Type" like_regex "^(Index|Bitmap Index).*Scan$")' + ) AS uses_no_index_scan; -- expect: t -- can use index scan EXPLAIN (COSTS OFF) SELECT b FROM columnar_table WHERE b = 30001; diff --git a/src/test/regress/sql/columnar_test_helpers.sql b/src/test/regress/sql/columnar_test_helpers.sql index 9cff79bbe..09105c0e7 100644 --- a/src/test/regress/sql/columnar_test_helpers.sql +++ b/src/test/regress/sql/columnar_test_helpers.sql @@ -158,3 +158,12 @@ BEGIN RETURN NEXT; END LOOP; END; $$ language plpgsql; + +CREATE OR REPLACE FUNCTION _plan_json(q text) +RETURNS jsonb +LANGUAGE plpgsql AS $$ +DECLARE j jsonb; +BEGIN + EXECUTE format('EXPLAIN (FORMAT JSON, COSTS OFF, ANALYZE OFF) %s', q) INTO j; + RETURN j; +END $$;