Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Fix pg_stat_statements for MERGE
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Tue, 27 Sep 2022 08:44:42 +0000 (10:44 +0200)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
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 <bt22nakamorit@oss.nttdata.com>
Reviewed-by: Julien Rouhaud <rjuju123@gmail.com>
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 8f7f93172a250657199e76f87eedfde8ba0960cf..49deebdfcacd6351f7c2b991a7695995c5af4fa0 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 dffd2c8c18707bbb317b40bc320fc4e4c8bb9470..8828e74e8942b3dbb1f5457b242334ecf3332b0a 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 3bac350bf50cd167ebfc57647f388a2ecfb655bd..6029da3ee1fbf98707e236bcf735548f77427cd9 100644 (file)
@@ -2266,10 +2266,10 @@ expression_tree_walker(Node *node,
            {
                MergeAction *action = (MergeAction *) node;
 
-               if (walker(action->targetList, context))
-                   return true;
                if (walker(action->qual, context))
                    return true;
+               if (walker(action->targetList, context))
+                   return true;
            }
            break;
        case T_PartitionPruneStepOp:
index a67487e5fe88730eb555d34ad4bb35e8f0f9853a..1224bb6c6600d40aeb2e2d633dea0717ca130fe5 100644 (file)
@@ -247,6 +247,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);
@@ -737,6 +738,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)
            {