Show parameters of CALL as constants in pg_stat_statements
authorMichael Paquier <michael@paquier.xyz>
Thu, 28 Sep 2023 06:17:55 +0000 (15:17 +0900)
committerMichael Paquier <michael@paquier.xyz>
Thu, 28 Sep 2023 06:17:55 +0000 (15:17 +0900)
This commit changes the query jumbling of CallStmt so as its IN/OUT
parameters are able to show up as constants with a parameter symbol in
pg_stat_statements, like:
CALL proc1($1, $2);
CALL proc2($1, $2, $3);

The transformed FuncExpr is used in the query ID computation instead of
the FuncCall generated by the parser, so as it is sensitive to the OID
of the procedure and its list of input arguments.  The output arguments
are handled in a separate list in CallStmt, which is also included in
the computation.

Tests are added to pg_stat_statements to show how this affects CALL with
IN/OUT parameters as well as overloaded functions.

Like 638d42a3c520 or 31de7e60da34, this improves the monitoring of
workloads with a lot of CALL statements, preventing unnecessary bloat
when these use different input (or event output) values.

Author: Sami Imseih
Discussion: https://postgr.es/m/B44FA29D-EBD0-4DD9-ABC2-16F1CB087074@amazon.com

contrib/pg_stat_statements/expected/utility.out
contrib/pg_stat_statements/sql/utility.sql
src/include/nodes/parsenodes.h

index f331044f3ea781deeb3f51d793b56683c2daf4fe..cc6e898cdf59907694015ff3f3716c62ea8b2afa 100644 (file)
@@ -300,6 +300,27 @@ DECLARE
 BEGIN
   SELECT (i + j)::int INTO r;
 END; $$ LANGUAGE plpgsql;
+-- Overloaded functions.
+CREATE OR REPLACE PROCEDURE overload(i int) AS $$
+DECLARE
+  r int;
+BEGIN
+  SELECT (i + i)::int INTO r;
+END; $$ LANGUAGE plpgsql;
+CREATE OR REPLACE PROCEDURE overload(i text) AS $$
+DECLARE
+  r text;
+BEGIN
+  SELECT i::text INTO r;
+END; $$ LANGUAGE plpgsql;
+-- Mix of IN/OUT parameters.
+CREATE OR REPLACE PROCEDURE in_out(i int, i2 OUT int, i3 INOUT int) AS $$
+DECLARE
+  r int;
+BEGIN
+  i2 := i;
+  i3 := i3 + i;
+END; $$ LANGUAGE plpgsql;
 SELECT pg_stat_statements_reset();
  pg_stat_statements_reset 
 --------------------------
@@ -310,15 +331,30 @@ CALL sum_one(3);
 CALL sum_one(199);
 CALL sum_two(1,1);
 CALL sum_two(1,2);
+CALL overload(1);
+CALL overload('A');
+CALL in_out(1, NULL, 1);
+ i2 | i3 
+----+----
+  1 |  2
+(1 row)
+
+CALL in_out(2, 1, 2);
+ i2 | i3 
+----+----
+  2 |  4
+(1 row)
+
 SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
  calls | rows |               query               
 -------+------+-----------------------------------
-     1 |    0 | CALL sum_one(199)
-     1 |    0 | CALL sum_one(3)
-     1 |    0 | CALL sum_two(1,1)
-     1 |    0 | CALL sum_two(1,2)
+     2 |    0 | CALL in_out($1, $2, $3)
+     1 |    0 | CALL overload($1)
+     1 |    0 | CALL overload($1)
+     2 |    0 | CALL sum_one($1)
+     2 |    0 | CALL sum_two($1,$2)
      1 |    1 | SELECT pg_stat_statements_reset()
-(5 rows)
+(6 rows)
 
 -- COPY
 CREATE TABLE copy_stats (a int, b int);
index 5f7d4a467f040aafb6c3e54ac2d6bc1aacd7ec61..04598e5ae4603ac4a478a465828409f6a06dddb2 100644 (file)
@@ -164,11 +164,36 @@ DECLARE
 BEGIN
   SELECT (i + j)::int INTO r;
 END; $$ LANGUAGE plpgsql;
+-- Overloaded functions.
+CREATE OR REPLACE PROCEDURE overload(i int) AS $$
+DECLARE
+  r int;
+BEGIN
+  SELECT (i + i)::int INTO r;
+END; $$ LANGUAGE plpgsql;
+CREATE OR REPLACE PROCEDURE overload(i text) AS $$
+DECLARE
+  r text;
+BEGIN
+  SELECT i::text INTO r;
+END; $$ LANGUAGE plpgsql;
+-- Mix of IN/OUT parameters.
+CREATE OR REPLACE PROCEDURE in_out(i int, i2 OUT int, i3 INOUT int) AS $$
+DECLARE
+  r int;
+BEGIN
+  i2 := i;
+  i3 := i3 + i;
+END; $$ LANGUAGE plpgsql;
 SELECT pg_stat_statements_reset();
 CALL sum_one(3);
 CALL sum_one(199);
 CALL sum_two(1,1);
 CALL sum_two(1,2);
+CALL overload(1);
+CALL overload('A');
+CALL in_out(1, NULL, 1);
+CALL in_out(2, 1, 2);
 SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
 
 -- COPY
index fef4c714b8e827cf0326c9e76d3b516deca4df02..f637937cd2052575814cf97b13f334bfea1a1d88 100644 (file)
@@ -3376,11 +3376,12 @@ typedef struct InlineCodeBlock
 typedef struct CallStmt
 {
    NodeTag     type;
-   FuncCall   *funccall;       /* from the parser */
+   /* from the parser */
+   FuncCall   *funccall pg_node_attr(query_jumble_ignore);
    /* transformed call, with only input args */
-   FuncExpr   *funcexpr pg_node_attr(query_jumble_ignore);
+   FuncExpr   *funcexpr;
    /* transformed output-argument expressions */
-   List       *outargs pg_node_attr(query_jumble_ignore);
+   List       *outargs;
 } CallStmt;
 
 typedef struct CallContext