From 0b22d7130d7f1315aa3d916520f2dd4aa0cfa91b Mon Sep 17 00:00:00 2001 From: Fujii Masao Date: Sat, 20 Jun 2026 23:11:41 +0900 Subject: [PATCH v1] pg_stat_statements: add room for squashed placeholders --- .../pg_stat_statements/expected/squashing.out | 24 +++++++++++++++++++ .../pg_stat_statements/pg_stat_statements.c | 17 ++++++++++++- contrib/pg_stat_statements/sql/squashing.sql | 7 ++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/contrib/pg_stat_statements/expected/squashing.out b/contrib/pg_stat_statements/expected/squashing.out index 8438235a2ce..057f56c4741 100644 --- a/contrib/pg_stat_statements/expected/squashing.out +++ b/contrib/pg_stat_statements/expected/squashing.out @@ -79,6 +79,30 @@ SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C"; SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 (3 rows) +-- small array literals still fit once squashed placeholders reach 2 digits +SELECT pg_stat_statements_reset() IS NOT NULL AS t; + t +--- + t +(1 row) + +SELECT ARRAY[1,2] AS a1, ARRAY[1,2] AS a2, ARRAY[1,2] AS a3, ARRAY[1,2] AS a4, + ARRAY[1,2] AS a5, ARRAY[1,2] AS a6, ARRAY[1,2] AS a7, ARRAY[1,2] AS a8, + ARRAY[1,2] AS a9, ARRAY[1,2] AS a10; + a1 | a2 | a3 | a4 | a5 | a6 | a7 | a8 | a9 | a10 +-------+-------+-------+-------+-------+-------+-------+-------+-------+------- + {1,2} | {1,2} | {1,2} | {1,2} | {1,2} | {1,2} | {1,2} | {1,2} | {1,2} | {1,2} +(1 row) + +SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C"; + query | calls +------------------------------------------------------------------------------------------------------------------------+------- + SELECT ARRAY[$1 /*, ... */] AS a1, ARRAY[$2 /*, ... */] AS a2, ARRAY[$3 /*, ... */] AS a3, ARRAY[$4 /*, ... */] AS a4,+| 1 + ARRAY[$5 /*, ... */] AS a5, ARRAY[$6 /*, ... */] AS a6, ARRAY[$7 /*, ... */] AS a7, ARRAY[$8 /*, ... */] AS a8,+| + ARRAY[$9 /*, ... */] AS a9, ARRAY[$10 /*, ... */] AS a10 | + SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 +(2 rows) + -- built-in functions will be squashed -- the IN and ARRAY forms of this statement will have the same queryId SELECT pg_stat_statements_reset() IS NOT NULL AS t; diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c index 92315627916..bd79a7d1470 100644 --- a/contrib/pg_stat_statements/pg_stat_statements.c +++ b/contrib/pg_stat_statements/pg_stat_statements.c @@ -2822,6 +2822,7 @@ generate_normalized_query(const JumbleState *jstate, const char *query, last_off = 0, /* Offset from start for previous tok */ last_tok_len = 0; /* Length (in bytes) of that tok */ int num_constants_replaced = 0; + int squashed_count = 0; LocationLen *locs = NULL; /* @@ -2837,8 +2838,22 @@ generate_normalized_query(const JumbleState *jstate, const char *query, * certainly isn't more than 11 bytes, even if n reaches INT_MAX. We * could refine that limit based on the max value of n for the current * query, but it hardly seems worth any extra effort to do so. + * + * Squashed lists need a little more room. The shortest squashed source + * text is "1,2" (three bytes), while the longest squashed placeholder is + * 22 bytes long (an INT_MAX placeholder plus the squash marker), so each + * squashed list can need up to 9 bytes more than the base allowance of 10 + * bytes per entry. */ - norm_query_buflen = query_len + jstate->clocations_count * 10; + for (int i = 0; i < jstate->clocations_count; i++) + { + /* Ignore duplicate locations that ComputeConstantLengths() disabled */ + if (locs[i].squashed && locs[i].length >= 0) + squashed_count++; + } + + norm_query_buflen = query_len + jstate->clocations_count * 10 + + squashed_count * 9; /* Allocate result buffer */ norm_query = palloc(norm_query_buflen + 1); diff --git a/contrib/pg_stat_statements/sql/squashing.sql b/contrib/pg_stat_statements/sql/squashing.sql index fc9e6573873..2bd26b24eec 100644 --- a/contrib/pg_stat_statements/sql/squashing.sql +++ b/contrib/pg_stat_statements/sql/squashing.sql @@ -24,6 +24,13 @@ SELECT ARRAY[1, 2, 3, 4]; SELECT ARRAY[1, 2, 3, 4, 5]; SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C"; +-- small array literals still fit once squashed placeholders reach 2 digits +SELECT pg_stat_statements_reset() IS NOT NULL AS t; +SELECT ARRAY[1,2] AS a1, ARRAY[1,2] AS a2, ARRAY[1,2] AS a3, ARRAY[1,2] AS a4, + ARRAY[1,2] AS a5, ARRAY[1,2] AS a6, ARRAY[1,2] AS a7, ARRAY[1,2] AS a8, + ARRAY[1,2] AS a9, ARRAY[1,2] AS a10; +SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C"; + -- built-in functions will be squashed -- the IN and ARRAY forms of this statement will have the same queryId SELECT pg_stat_statements_reset() IS NOT NULL AS t; -- 2.53.0