From d50a8e34eafcfc59503cd95ed4b39ae91542bef2 Mon Sep 17 00:00:00 2001 From: Ilya Staroverov Date: Mon, 26 Oct 2026 12:55:35 +0300 Subject: [PATCH v1 05/11] Include SEARCH and CYCLE clauses in query jumbling CommonTableExpr.search_clause and CommonTableExpr.cycle_clause have not been part of the query ID since these clauses were added in 14. They are only expanded by the rewriter, after query jumbling, so queries that differ in them, like SEARCH DEPTH FIRST and SEARCH BREADTH FIRST, or CYCLE on different columns, were merged into a single pg_stat_statements entry. Include both fields in query jumbling. The names of the columns that these clauses add, given by SET and USING, are left out, as other column names are. This changes the query IDs of the queries with a WITH clause, so it is for master only. Author: Ilya Staroverov --- contrib/pg_stat_statements/expected/select.out | 18 +++++++++++++++--- src/include/nodes/parsenodes.h | 10 +++++----- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/contrib/pg_stat_statements/expected/select.out b/contrib/pg_stat_statements/expected/select.out index bdc740d1c5..1ea8c622ce 100644 --- a/contrib/pg_stat_statements/expected/select.out +++ b/contrib/pg_stat_statements/expected/select.out @@ -1059,15 +1059,27 @@ SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C"; -------+----------------------------------------------------------------------------------------- 0 | SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C" 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t - 6 | WITH RECURSIVE t AS (SELECT a, b FROM pgss_empty_tab UNION ALL SELECT a + $1, b FROM t)+ + 1 | WITH RECURSIVE t AS (SELECT a, b FROM pgss_empty_tab UNION ALL SELECT a + $1, b FROM t)+ + | CYCLE a SET is_cycle TO $2 DEFAULT $3 USING path SELECT * FROM t + 3 | WITH RECURSIVE t AS (SELECT a, b FROM pgss_empty_tab UNION ALL SELECT a + $1, b FROM t)+ | CYCLE a SET is_cycle USING path SELECT * FROM t + 1 | WITH RECURSIVE t AS (SELECT a, b FROM pgss_empty_tab UNION ALL SELECT a + $1, b FROM t)+ + | CYCLE a, b SET is_cycle USING path SELECT * FROM t + 1 | WITH RECURSIVE t AS (SELECT a, b FROM pgss_empty_tab UNION ALL SELECT a + $1, b FROM t)+ + | CYCLE b SET is_cycle USING path SELECT * FROM t + 1 | WITH RECURSIVE t AS (SELECT a, b FROM pgss_empty_tab UNION ALL SELECT a + $1, b FROM t)+ + | SEARCH BREADTH FIRST BY a SET seq SELECT * FROM t 1 | WITH RECURSIVE t AS (SELECT a, b FROM pgss_empty_tab UNION ALL SELECT a + $1, b FROM t)+ | SEARCH DEPTH FIRST BY a SET seq CYCLE a SET is_cycle USING path SELECT * FROM t - 5 | WITH RECURSIVE t AS (SELECT a, b FROM pgss_empty_tab UNION ALL SELECT a + $1, b FROM t)+ + 2 | WITH RECURSIVE t AS (SELECT a, b FROM pgss_empty_tab UNION ALL SELECT a + $1, b FROM t)+ | SEARCH DEPTH FIRST BY a SET seq SELECT * FROM t + 1 | WITH RECURSIVE t AS (SELECT a, b FROM pgss_empty_tab UNION ALL SELECT a + $1, b FROM t)+ + | SEARCH DEPTH FIRST BY a, b SET seq SELECT * FROM t + 1 | WITH RECURSIVE t AS (SELECT a, b FROM pgss_empty_tab UNION ALL SELECT a + $1, b FROM t)+ + | SEARCH DEPTH FIRST BY b SET seq SELECT * FROM t 1 | WITH RECURSIVE t AS (SELECT a, b FROM pgss_empty_tab UNION ALL SELECT a + $1, b FROM t)+ | SELECT * FROM t -(6 rows) +(12 rows) SELECT pg_stat_statements_reset() IS NOT NULL AS t; t diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 6ac314f64c..26208eddb3 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1676,7 +1676,7 @@ typedef struct CTESearchClause NodeTag type; List *search_col_list; bool search_breadth_first; - char *search_seq_column; + char *search_seq_column pg_node_attr(query_jumble_ignore); ParseLoc location; } CTESearchClause; @@ -1684,10 +1684,10 @@ typedef struct CTECycleClause { NodeTag type; List *cycle_col_list; - char *cycle_mark_column; + char *cycle_mark_column pg_node_attr(query_jumble_ignore); Node *cycle_mark_value; Node *cycle_mark_default; - char *cycle_path_column; + char *cycle_path_column pg_node_attr(query_jumble_ignore); ParseLoc location; /* These fields are set during parse analysis: */ Oid cycle_mark_type; /* common type of _value and _default */ @@ -1710,8 +1710,8 @@ typedef struct CommonTableExpr CTEMaterialize ctematerialized; /* is this an optimization fence? */ /* SelectStmt/InsertStmt/etc before parse analysis, Query afterwards: */ Node *ctequery; /* the CTE's subquery */ - CTESearchClause *search_clause pg_node_attr(query_jumble_ignore); - CTECycleClause *cycle_clause pg_node_attr(query_jumble_ignore); + CTESearchClause *search_clause; + CTECycleClause *cycle_clause; ParseLoc location; /* token location, or -1 if unknown */ /* These fields are set during parse analysis: */ /* is this CTE actually recursive? */ -- 2.43.0