From: Amit Langote Date: Thu, 16 Apr 2026 04:49:39 +0000 (+0900) Subject: Fix pg_overexplain to emit valid output with RANGE_TABLE option. X-Git-Tag: REL_18_4~65 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=6723d462db5a9ecea840b946f6b2843c20b1c866;p=thirdparty%2Fpostgresql.git Fix pg_overexplain to emit valid output with RANGE_TABLE option. overexplain_range_table() emitted the "Unprunable RTIs" and "Result RTIs" properties before closing the "Range Table" group. In the JSON and YAML formats the Range Table group is rendered as an array of RTE objects, so emitting key/value pairs inside it produced structurally invalid output. The XML format had a related oddity, with these elements nested inside rather than appearing as its siblings. These fields are properties of the PlannedStmt as a whole, not of any individual RTE, so close the Range Table group before emitting them. They now appear as siblings of "Range Table" in the parent Query object, which is what was intended. Also add a test exercising FORMAT JSON with RANGE_TABLE so that any future regression in the output structure is caught. Reported-by: Satyanarayana Narlapuram Author: Satyanarayana Narlapuram Reviewed-by: Amit Langote Reviewed-by: Chao Li Discussion: https://postgr.es/m/CAHg+QDdDrdqMr98a_OBYDYmK3RaT7XwCEShZfvDYKZpZTfOEjQ@mail.gmail.com Backpatch-through: 18 --- diff --git a/contrib/pg_overexplain/expected/pg_overexplain.out b/contrib/pg_overexplain/expected/pg_overexplain.out index 6de02323d7c..0c1d32d386e 100644 --- a/contrib/pg_overexplain/expected/pg_overexplain.out +++ b/contrib/pg_overexplain/expected/pg_overexplain.out @@ -291,13 +291,130 @@ $$); false + false + + - 1 3 4 + - none + + + 1 3 4 + + none + + (1 row) +-- Test JSON format with RANGE_TABLE to verify valid JSON structure. +SELECT explain_filter($$ +EXPLAIN (RANGE_TABLE, FORMAT JSON, COSTS OFF) +SELECT genus, array_agg(name ORDER BY name) FROM vegetables GROUP BY genus +$$); + explain_filter +---------------------------------------------------------------- + [ + + { + + "Plan": { + + "Node Type": "Aggregate", + + "Strategy": "Sorted", + + "Partial Mode": "Simple", + + "Parallel Aware": false, + + "Async Capable": false, + + "Disabled": false, + + "Group Key": ["vegetables.genus"], + + "Plans": [ + + { + + "Node Type": "Sort", + + "Parent Relationship": "Outer", + + "Parallel Aware": false, + + "Async Capable": false, + + "Disabled": false, + + "Sort Key": ["vegetables.genus", "vegetables.name"],+ + "Plans": [ + + { + + "Node Type": "Append", + + "Parent Relationship": "Outer", + + "Parallel Aware": false, + + "Async Capable": false, + + "Disabled": false, + + "Append RTIs": "1", + + "Subplans Removed": 0, + + "Plans": [ + + { + + "Node Type": "Seq Scan", + + "Parent Relationship": "Member", + + "Parallel Aware": false, + + "Async Capable": false, + + "Relation Name": "brassica", + + "Alias": "vegetables_1", + + "Disabled": false, + + "Scan RTI": 3 + + }, + + { + + "Node Type": "Seq Scan", + + "Parent Relationship": "Member", + + "Parallel Aware": false, + + "Async Capable": false, + + "Relation Name": "daucus", + + "Alias": "vegetables_2", + + "Disabled": false, + + "Scan RTI": 4 + + } + + ] + + } + + ] + + } + + ] + + }, + + "Range Table": [ + + { + + "RTI": 1, + + "Kind": "relation", + + "Inherited": true, + + "In From Clause": true, + + "Eref": "vegetables (id, name, genus)", + + "Relation": "vegetables", + + "Relation Kind": "partitioned_table", + + "Relation Lock Mode": "AccessShareLock", + + "Permission Info Index": 1, + + "Security Barrier": false, + + "Lateral": false + + }, + + { + + "RTI": 2, + + "Kind": "group", + + "Inherited": false, + + "In From Clause": false, + + "Eref": "\"*GROUP*\" (genus)", + + "Security Barrier": false, + + "Lateral": false + + }, + + { + + "RTI": 3, + + "Kind": "relation", + + "Inherited": false, + + "In From Clause": true, + + "Alias": "vegetables (id, name, genus)", + + "Eref": "vegetables (id, name, genus)", + + "Relation": "brassica", + + "Relation Kind": "relation", + + "Relation Lock Mode": "AccessShareLock", + + "Security Barrier": false, + + "Lateral": false + + }, + + { + + "RTI": 4, + + "Kind": "relation", + + "Inherited": false, + + "In From Clause": true, + + "Alias": "vegetables (id, name, genus)", + + "Eref": "vegetables (id, name, genus)", + + "Relation": "daucus", + + "Relation Kind": "relation", + + "Relation Lock Mode": "AccessShareLock", + + "Security Barrier": false, + + "Lateral": false + + } + + ], + + "Unprunable RTIs": "1 3 4", + + "Result RTIs": "none" + + } + + ] +(1 row) + -- Test just the DEBUG option. Verify that it shows information about -- disabled nodes, parallel safety, and the parallelModeNeeded flag. SET enable_seqscan = false; diff --git a/contrib/pg_overexplain/pg_overexplain.c b/contrib/pg_overexplain/pg_overexplain.c index de824566f8c..78f128562e6 100644 --- a/contrib/pg_overexplain/pg_overexplain.c +++ b/contrib/pg_overexplain/pg_overexplain.c @@ -658,7 +658,14 @@ overexplain_range_table(PlannedStmt *plannedstmt, ExplainState *es) ExplainCloseGroup("Range Table Entry", NULL, true, es); } - /* Print PlannedStmt fields that contain RTIs. */ + /* Close the Range Table array before emitting PlannedStmt-level fields. */ + ExplainCloseGroup("Range Table", "Range Table", false, es); + + /* + * Print PlannedStmt fields that contain RTIs. These are properties of + * the PlannedStmt, not of individual RTEs, so they belong outside the + * Range Table array. + */ if (es->format != EXPLAIN_FORMAT_TEXT || !bms_is_empty(plannedstmt->unprunableRelids)) overexplain_bitmapset("Unprunable RTIs", plannedstmt->unprunableRelids, @@ -666,9 +673,6 @@ overexplain_range_table(PlannedStmt *plannedstmt, ExplainState *es) if (es->format != EXPLAIN_FORMAT_TEXT || plannedstmt->resultRelations != NIL) overexplain_intlist("Result RTIs", plannedstmt->resultRelations, es); - - /* Close group, we're all done */ - ExplainCloseGroup("Range Table", "Range Table", false, es); } /* diff --git a/contrib/pg_overexplain/sql/pg_overexplain.sql b/contrib/pg_overexplain/sql/pg_overexplain.sql index 42e275ac2f9..4703ac3e3d7 100644 --- a/contrib/pg_overexplain/sql/pg_overexplain.sql +++ b/contrib/pg_overexplain/sql/pg_overexplain.sql @@ -66,6 +66,12 @@ EXPLAIN (DEBUG, RANGE_TABLE, FORMAT XML, COSTS OFF) SELECT genus, array_agg(name ORDER BY name) FROM vegetables GROUP BY genus $$); +-- Test JSON format with RANGE_TABLE to verify valid JSON structure. +SELECT explain_filter($$ +EXPLAIN (RANGE_TABLE, FORMAT JSON, COSTS OFF) +SELECT genus, array_agg(name ORDER BY name) FROM vegetables GROUP BY genus +$$); + -- Test just the DEBUG option. Verify that it shows information about -- disabled nodes, parallel safety, and the parallelModeNeeded flag. SET enable_seqscan = false;