-- These tests display internal details that would not be stable under -- debug_parallel_query, so make sure that option is disabled. SET debug_parallel_query = off; -- Make sure that we don't print any JIT-related information, as that -- would also make results unstable. SET jit = off; -- These options do not exist, so these queries should all fail. EXPLAIN (DEBUFF) SELECT 1; ERROR: unrecognized EXPLAIN option "debuff" LINE 1: EXPLAIN (DEBUFF) SELECT 1; ^ EXPLAIN (DEBUG) SELECT 1; ERROR: unrecognized EXPLAIN option "debug" LINE 1: EXPLAIN (DEBUG) SELECT 1; ^ EXPLAIN (RANGE_TABLE) SELECT 1; ERROR: unrecognized EXPLAIN option "range_table" LINE 1: EXPLAIN (RANGE_TABLE) SELECT 1; ^ -- Load the module that creates the options. LOAD 'pg_overexplain'; -- The first option still does not exist, but the others do. EXPLAIN (DEBUFF) SELECT 1; ERROR: unrecognized EXPLAIN option "debuff" LINE 1: EXPLAIN (DEBUFF) SELECT 1; ^ EXPLAIN (DEBUG) SELECT 1; QUERY PLAN ------------------------------------------ Result (cost=0.00..0.01 rows=1 width=4) Disabled Nodes: 0 Parallel Safe: false Plan Node ID: 0 PlannedStmt: Command Type: select Flags: canSetTag Subplans Needing Rewind: none Relation OIDs: none Executor Parameter Types: none Parse Location: 16 for 8 bytes (11 rows) EXPLAIN (RANGE_TABLE) SELECT 1; QUERY PLAN ------------------------------------------ Result (cost=0.00..0.01 rows=1 width=4) RTI 1 (result): Eref: "*RESULT*" () (3 rows) -- Create a partitioned table. CREATE TABLE vegetables (id serial, name text, genus text) PARTITION BY LIST (genus); CREATE TABLE daucus PARTITION OF vegetables FOR VALUES IN ('daucus'); CREATE TABLE brassica PARTITION OF vegetables FOR VALUES IN ('brassica'); INSERT INTO vegetables (name, genus) VALUES ('carrot', 'daucus'), ('bok choy', 'brassica'), ('brocooli', 'brassica'), ('cauliflower', 'brassica'), ('cabbage', 'brassica'), ('kohlrabi', 'brassica'), ('rutabaga', 'brassica'), ('turnip', 'brassica'); VACUUM ANALYZE vegetables; -- We filter relation OIDs out of the test output in order to avoid -- test instability. This is currently only needed for EXPLAIN (DEBUG), not -- EXPLAIN (RANGE_TABLE). Also suppress actual row counts, which are not -- stable (e.g. 1/8 is 0.12 on some buildfarm machines and 0.13 on others). CREATE FUNCTION explain_filter(text) RETURNS SETOF text LANGUAGE plpgsql AS $$ DECLARE ln text; BEGIN FOR ln IN EXECUTE $1 LOOP ln := regexp_replace(ln, 'Relation OIDs:( \m\d+\M)+', 'Relation OIDs: NNN...', 'g'); ln := regexp_replace(ln, '( ?\m\d+\M)+', 'NNN...', 'g'); ln := regexp_replace(ln, 'actual rows=\d+\.\d+', 'actual rows=N.NN', 'g'); RETURN NEXT ln; END LOOP; END; $$; -- Test with both options together and an aggregate. SELECT explain_filter($$ EXPLAIN (DEBUG, RANGE_TABLE, COSTS OFF) SELECT genus, array_agg(name ORDER BY name) FROM vegetables GROUP BY genus $$); explain_filter ----------------------------------------------------- GroupAggregate Group Key: vegetables.genus Disabled Nodes: 0 Parallel Safe: true Plan Node ID: 0 -> Sort Sort Key: vegetables.genus, vegetables.name Disabled Nodes: 0 Parallel Safe: true Plan Node ID: 1 -> Append Disabled Nodes: 0 Parallel Safe: true Plan Node ID: 2 Append RTIs: 1 -> Seq Scan on brassica vegetables_1 Disabled Nodes: 0 Parallel Safe: true Plan Node ID: 3 Scan RTI: 3 -> Seq Scan on daucus vegetables_2 Disabled Nodes: 0 Parallel Safe: true Plan Node ID: 4 Scan RTI: 4 PlannedStmt: Command Type: select Flags: canSetTag Subplans Needing Rewind: none Relation OIDs: NNN... Executor Parameter Types: none Parse Location: 41 to end RTI 1 (relation, inherited, in-from-clause): Eref: vegetables (id, name, genus) Relation: vegetables Relation Kind: partitioned_table Relation Lock Mode: AccessShareLock Permission Info Index: 1 RTI 2 (group): Eref: "*GROUP*" (genus) RTI 3 (relation, in-from-clause): Alias: vegetables (id, name, genus) Eref: vegetables (id, name, genus) Relation: brassica Relation Kind: relation Relation Lock Mode: AccessShareLock RTI 4 (relation, in-from-clause): Alias: vegetables (id, name, genus) Eref: vegetables (id, name, genus) Relation: daucus Relation Kind: relation Relation Lock Mode: AccessShareLock Unprunable RTIs: 1 3 4 (53 rows) -- Test a different output format. SELECT explain_filter($$ EXPLAIN (DEBUG, RANGE_TABLE, FORMAT XML, COSTS OFF) SELECT genus, array_agg(name ORDER BY name) FROM vegetables GROUP BY genus $$); explain_filter --------------------------------------------------------------------- + + + Aggregate + Sorted + Simple + false + false + false + + vegetables.genus + + 0 + true + 0 + none + none + + + Sort + Outer + false + false + false + + vegetables.genus + vegetables.name + + 0 + true + 1 + none + none + + + Append + Outer + false + false + false + 0 + true + 2 + none + none + 1 + 0 + + + Seq Scan + Member+ false + false + brassica + vegetables_1 + false + 0 + true + 3 + none + none + 3 + + + Seq Scan + Member+ false + false + daucus + vegetables_2 + false + 0 + true + 4 + none + none + 4 + + + + + + + + + select + canSetTag + none + NNN... + none + 53 to end + + + + 1 + relation + true + true + vegetables (id, name, genus) + vegetables + partitioned_table + AccessShareLock + 1 + false + false + + + 2 + group + false + false + "*GROUP*" (genus) + false + false + + + 3 + relation + false + true + vegetables (id, name, genus) + vegetables (id, name, genus) + brassica + relation + AccessShareLock + false + false + + + 4 + relation + false + true + vegetables (id, name, genus) + vegetables (id, name, genus) + daucus + relation + AccessShareLock + false + false + + 1 3 4 + 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; SET debug_parallel_query = true; SELECT explain_filter($$ EXPLAIN (DEBUG, COSTS OFF) SELECT genus, array_agg(name ORDER BY name) FROM vegetables GROUP BY genus $$); explain_filter ----------------------------------------------------------- Gather Workers Planned: 1 Single Copy: true Disabled Nodes: 0 Parallel Safe: false Plan Node ID: 0 -> GroupAggregate Group Key: vegetables.genus Disabled Nodes: 2 Parallel Safe: true Plan Node ID: 1 -> Sort Sort Key: vegetables.genus, vegetables.name Disabled Nodes: 2 Parallel Safe: true Plan Node ID: 2 -> Append Disabled Nodes: 2 Parallel Safe: true Plan Node ID: 3 -> Seq Scan on brassica vegetables_1 Disabled: true Disabled Nodes: 1 Parallel Safe: true Plan Node ID: 4 -> Seq Scan on daucus vegetables_2 Disabled: true Disabled Nodes: 1 Parallel Safe: true Plan Node ID: 5 PlannedStmt: Command Type: select Flags: canSetTag, parallelModeNeeded Subplans Needing Rewind: none Relation OIDs: NNN... Executor Parameter Types: none Parse Location: 28 to end (37 rows) SET debug_parallel_query = false; RESET enable_seqscan; -- Test the DEBUG option with a non-SELECT query, and also verify that the -- hasReturning flag is shown. SELECT explain_filter($$ EXPLAIN (DEBUG, COSTS OFF) INSERT INTO vegetables (name, genus) VALUES ('Brotero''s carrot', 'brassica') RETURNING id $$); explain_filter ---------------------------------- Insert on vegetables Disabled Nodes: 0 Parallel Safe: false Plan Node ID: 0 -> Result Disabled Nodes: 0 Parallel Safe: false Plan Node ID: 1 PlannedStmt: Command Type: insert Flags: hasReturning, canSetTag Subplans Needing Rewind: none Relation OIDs: NNN... Executor Parameter Types: 0 Parse Location: 28 to end (15 rows) -- Create an index, and then attempt to force a nested loop with inner index -- scan so that we can see parameter-related information. Also, let's try -- actually running the query, but try to suppress potentially variable output. CREATE INDEX ON vegetables (id); ANALYZE vegetables; SET enable_hashjoin = false; SET enable_material = false; SET enable_mergejoin = false; SET enable_seqscan = false; SELECT explain_filter($$ EXPLAIN (BUFFERS OFF, COSTS OFF, SUMMARY OFF, TIMING OFF, ANALYZE, DEBUG) SELECT * FROM vegetables v1, vegetables v2 WHERE v1.id = v2.id; $$); explain_filter ------------------------------------------------------------------------------------------ Nested Loop (actual rows=N.NN loops=1) Disabled Nodes: 0 Parallel Safe: true Plan Node ID: 0 -> Append (actual rows=N.NN loops=1) Disabled Nodes: 0 Parallel Safe: true Plan Node ID: 1 -> Index Scan using brassica_id_idx on brassica v1_1 (actual rows=N.NN loops=1) Index Searches: 1 Disabled Nodes: 0 Parallel Safe: true Plan Node ID: 2 -> Index Scan using daucus_id_idx on daucus v1_2 (actual rows=N.NN loops=1) Index Searches: 1 Disabled Nodes: 0 Parallel Safe: true Plan Node ID: 3 -> Append (actual rows=N.NN loops=8) Disabled Nodes: 0 Parallel Safe: true Plan Node ID: 4 extParam: 0 allParam: 0 -> Index Scan using brassica_id_idx on brassica v2_1 (actual rows=N.NN loops=8) Index Cond: (id = v1.id) Index Searches: 8 Disabled Nodes: 0 Parallel Safe: true Plan Node ID: 5 extParam: 0 allParam: 0 -> Index Scan using daucus_id_idx on daucus v2_2 (actual rows=N.NN loops=8) Index Cond: (id = v1.id) Index Searches: 8 Disabled Nodes: 0 Parallel Safe: true Plan Node ID: 6 extParam: 0 allParam: 0 PlannedStmt: Command Type: select Flags: canSetTag Subplans Needing Rewind: none Relation OIDs: NNN... Executor Parameter Types: 23 Parse Location: 75 for 62 bytes (47 rows) RESET enable_hashjoin; RESET enable_material; RESET enable_mergejoin; RESET enable_seqscan; -- Test the RANGE_TABLE option with a case that allows partition pruning. EXPLAIN (RANGE_TABLE, COSTS OFF) SELECT * FROM vegetables WHERE genus = 'daucus'; QUERY PLAN ---------------------------------------------- Seq Scan on daucus vegetables Filter: (genus = 'daucus'::text) Scan RTI: 2 RTI 1 (relation, inherited, in-from-clause): Eref: vegetables (id, name, genus) Relation: vegetables Relation Kind: partitioned_table Relation Lock Mode: AccessShareLock Permission Info Index: 1 RTI 2 (relation, in-from-clause): Alias: vegetables (id, name, genus) Eref: vegetables (id, name, genus) Relation: daucus Relation Kind: relation Relation Lock Mode: AccessShareLock Unprunable RTIs: 1 2 (16 rows) -- Also test a case that involves a write. EXPLAIN (RANGE_TABLE, COSTS OFF) INSERT INTO vegetables (name, genus) VALUES ('broccoflower', 'brassica'); QUERY PLAN ---------------------------------------- Insert on vegetables Nominal RTI: 1 Exclude Relation RTI: 0 -> Result RTI 1 (relation): Eref: vegetables (id, name, genus) Relation: vegetables Relation Kind: partitioned_table Relation Lock Mode: RowExclusiveLock Permission Info Index: 1 RTI 2 (result): Eref: "*RESULT*" () Unprunable RTIs: 1 Result RTIs: 1 (14 rows)