Fix pg_stat_statements for MERGE
authorAlvaro Herrera <[email protected]>
Tue, 27 Sep 2022 08:44:42 +0000 (10:44 +0200)
committerAlvaro Herrera <[email protected]>
Tue, 27 Sep 2022 08:44:42 +0000 (10:44 +0200)
We weren't jumbling the merge action list, so wildly different commands
would be considered to use the same query ID.  Add that, mention it in
the docs, and some test lines.

Backpatch to 15.

Author: Tatsu <[email protected]>
Reviewed-by: Julien Rouhaud <[email protected]>
Discussion: https://postgr.es/m/d87e391694db75a038abc3b2597828e8@oss.nttdata.com

contrib/pg_stat_statements/expected/pg_stat_statements.out
contrib/pg_stat_statements/sql/pg_stat_statements.sql
doc/src/sgml/pgstatstatements.sgml
src/backend/nodes/nodeFuncs.c
src/backend/utils/misc/queryjumble.c

index ff0166fb9d20821a3667d6d8be0e03faf7583959..9ac5c87c3a263639d3778d377f2d424e4d7103d5 100644 (file)
@@ -222,12 +222,51 @@ SELECT * FROM test WHERE a IN (1, 2, 3, 4, 5);
  3 | c                   
 (8 rows)
 
+-- MERGE
+MERGE INTO test USING test st ON (st.a = test.a AND st.a >= 4)
+ WHEN MATCHED THEN UPDATE SET b = st.b || st.a::text;
+MERGE INTO test USING test st ON (st.a = test.a AND st.a >= 4)
+ WHEN MATCHED THEN UPDATE SET b = test.b || st.a::text;
+MERGE INTO test USING test st ON (st.a = test.a AND st.a >= 4)
+ WHEN MATCHED AND length(st.b) > 1 THEN UPDATE SET b = test.b || st.a::text;
+MERGE INTO test USING test st ON (st.a = test.a)
+ WHEN NOT MATCHED THEN INSERT (a, b) VALUES (0, NULL);
+MERGE INTO test USING test st ON (st.a = test.a)
+ WHEN NOT MATCHED THEN INSERT VALUES (0, NULL);    -- same as above
+MERGE INTO test USING test st ON (st.a = test.a)
+ WHEN NOT MATCHED THEN INSERT (b, a) VALUES (NULL, 0);
+MERGE INTO test USING test st ON (st.a = test.a)
+ WHEN NOT MATCHED THEN INSERT (a) VALUES (0);
+MERGE INTO test USING test st ON (st.a = test.a AND st.a >= 4)
+ WHEN MATCHED THEN DELETE;
+MERGE INTO test USING test st ON (st.a = test.a AND st.a >= 4)
+ WHEN MATCHED THEN DO NOTHING;
+MERGE INTO test USING test st ON (st.a = test.a AND st.a >= 4)
+ WHEN NOT MATCHED THEN DO NOTHING;
 SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
                                     query                                     | calls | rows 
 ------------------------------------------------------------------------------+-------+------
  DELETE FROM test WHERE a > $1                                                |     1 |    1
  INSERT INTO test (a, b) VALUES ($1, $2), ($3, $4), ($5, $6)                  |     1 |    3
  INSERT INTO test VALUES(generate_series($1, $2), $3)                         |     1 |   10
+ MERGE INTO test USING test st ON (st.a = test.a AND st.a >= $1)             +|     1 |    6
+  WHEN MATCHED AND length(st.b) > $2 THEN UPDATE SET b = test.b || st.a::text |       | 
+ MERGE INTO test USING test st ON (st.a = test.a AND st.a >= $1)             +|     1 |    6
+  WHEN MATCHED THEN DELETE                                                    |       | 
+ MERGE INTO test USING test st ON (st.a = test.a AND st.a >= $1)             +|     1 |    0
+  WHEN MATCHED THEN DO NOTHING                                                |       | 
+ MERGE INTO test USING test st ON (st.a = test.a AND st.a >= $1)             +|     1 |    6
+  WHEN MATCHED THEN UPDATE SET b = st.b || st.a::text                         |       | 
+ MERGE INTO test USING test st ON (st.a = test.a AND st.a >= $1)             +|     1 |    6
+  WHEN MATCHED THEN UPDATE SET b = test.b || st.a::text                       |       | 
+ MERGE INTO test USING test st ON (st.a = test.a AND st.a >= $1)             +|     1 |    0
+  WHEN NOT MATCHED THEN DO NOTHING                                            |       | 
+ MERGE INTO test USING test st ON (st.a = test.a)                            +|     1 |    0
+  WHEN NOT MATCHED THEN INSERT (a) VALUES ($1)                                |       | 
+ MERGE INTO test USING test st ON (st.a = test.a)                            +|     2 |    0
+  WHEN NOT MATCHED THEN INSERT (a, b) VALUES ($1, $2)                         |       | 
+ MERGE INTO test USING test st ON (st.a = test.a)                            +|     1 |    0
+  WHEN NOT MATCHED THEN INSERT (b, a) VALUES ($1, $2)                         |       | 
  SELECT * FROM test ORDER BY a                                                |     1 |   12
  SELECT * FROM test WHERE a > $1 ORDER BY a                                   |     2 |    4
  SELECT * FROM test WHERE a IN ($1, $2, $3, $4, $5)                           |     1 |    8
@@ -235,7 +274,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
  SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" |     0 |    0
  UPDATE test SET b = $1 WHERE a = $2                                          |     6 |    6
  UPDATE test SET b = $1 WHERE a > $2                                          |     1 |    3
-(10 rows)
+(19 rows)
 
 --
 -- INSERT, UPDATE, DELETE on test table to validate WAL generation metrics
index a01f183727763f02851593ede6dadc20c596d286..8f5c866225bd558a0d3616ba6c8b8428cc6f7785 100644 (file)
@@ -100,6 +100,28 @@ SELECT * FROM test ORDER BY a;
 -- SELECT with IN clause
 SELECT * FROM test WHERE a IN (1, 2, 3, 4, 5);
 
+-- MERGE
+MERGE INTO test USING test st ON (st.a = test.a AND st.a >= 4)
+ WHEN MATCHED THEN UPDATE SET b = st.b || st.a::text;
+MERGE INTO test USING test st ON (st.a = test.a AND st.a >= 4)
+ WHEN MATCHED THEN UPDATE SET b = test.b || st.a::text;
+MERGE INTO test USING test st ON (st.a = test.a AND st.a >= 4)
+ WHEN MATCHED AND length(st.b) > 1 THEN UPDATE SET b = test.b || st.a::text;
+MERGE INTO test USING test st ON (st.a = test.a)
+ WHEN NOT MATCHED THEN INSERT (a, b) VALUES (0, NULL);
+MERGE INTO test USING test st ON (st.a = test.a)
+ WHEN NOT MATCHED THEN INSERT VALUES (0, NULL);    -- same as above
+MERGE INTO test USING test st ON (st.a = test.a)
+ WHEN NOT MATCHED THEN INSERT (b, a) VALUES (NULL, 0);
+MERGE INTO test USING test st ON (st.a = test.a)
+ WHEN NOT MATCHED THEN INSERT (a) VALUES (0);
+MERGE INTO test USING test st ON (st.a = test.a AND st.a >= 4)
+ WHEN MATCHED THEN DELETE;
+MERGE INTO test USING test st ON (st.a = test.a AND st.a >= 4)
+ WHEN MATCHED THEN DO NOTHING;
+MERGE INTO test USING test st ON (st.a = test.a AND st.a >= 4)
+ WHEN NOT MATCHED THEN DO NOTHING;
+
 SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
 
 --
index ecf6cd6bf3ec0dcdf5ddeeb0a93cd304202e585d..ea90365c7f2accad4f8748265afe2a97b93a5cd0 100644 (file)
 
   <para>
    Plannable queries (that is, <command>SELECT</command>, <command>INSERT</command>,
-   <command>UPDATE</command>, and <command>DELETE</command>) are combined into a single
+   <command>UPDATE</command>, <command>DELETE</command>, and <command>MERGE</command>) are combined into a single
    <structname>pg_stat_statements</structname> entry whenever they have identical query
    structures according to an internal hash calculation.  Typically, two
    queries will be considered the same for this purpose if they are
       <varname>pg_stat_statements.track_utility</varname> controls whether
       utility commands are tracked by the module.  Utility commands are
       all those other than <command>SELECT</command>, <command>INSERT</command>,
-      <command>UPDATE</command> and <command>DELETE</command>.
+      <command>UPDATE</command>, <command>DELETE</command>, and <command>MERGE</command>.
       The default value is <literal>on</literal>.
       Only superusers can change this setting.
      </para>
index 724d076674e5452c4e11bd5d6e592eee1b4eb3d2..0a7b22f97e7bb94565e6c1a93aadbd32c8d292e9 100644 (file)
@@ -2259,10 +2259,10 @@ expression_tree_walker_impl(Node *node,
            {
                MergeAction *action = (MergeAction *) node;
 
-               if (WALK(action->targetList))
-                   return true;
                if (WALK(action->qual))
                    return true;
+               if (WALK(action->targetList))
+                   return true;
            }
            break;
        case T_PartitionPruneStepOp:
index 6e75acda27408f049a92c5e5c83a69c063095b16..a8508463e77f2253c1b10c8da64330277689397e 100644 (file)
@@ -248,6 +248,7 @@ JumbleQueryInternal(JumbleState *jstate, Query *query)
    JumbleExpr(jstate, (Node *) query->cteList);
    JumbleRangeTable(jstate, query->rtable);
    JumbleExpr(jstate, (Node *) query->jointree);
+   JumbleExpr(jstate, (Node *) query->mergeActionList);
    JumbleExpr(jstate, (Node *) query->targetList);
    JumbleExpr(jstate, (Node *) query->onConflict);
    JumbleExpr(jstate, (Node *) query->returningList);
@@ -738,6 +739,16 @@ JumbleExpr(JumbleState *jstate, Node *node)
                JumbleExpr(jstate, (Node *) conf->exclRelTlist);
            }
            break;
+       case T_MergeAction:
+           {
+               MergeAction *mergeaction = (MergeAction *) node;
+
+               APP_JUMB(mergeaction->matched);
+               APP_JUMB(mergeaction->commandType);
+               JumbleExpr(jstate, mergeaction->qual);
+               JumbleExpr(jstate, (Node *) mergeaction->targetList);
+           }
+           break;
        case T_List:
            foreach(temp, (List *) node)
            {