Fix pg_stat_statements for MERGE

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
This commit is contained in:
Alvaro Herrera 2022-09-27 10:44:42 +02:00
parent d1f95fa247
commit 72abf03b64
No known key found for this signature in database
GPG Key ID: 1C20ACB9D5C564AE
5 changed files with 77 additions and 5 deletions

View File

@ -222,12 +222,51 @@ SELECT * FROM test WHERE a IN (1, 2, 3, 4, 5);
3 | c 3 | c
(8 rows) (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"; SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls | rows query | calls | rows
------------------------------------------------------------------------------+-------+------ ------------------------------------------------------------------------------+-------+------
DELETE FROM test WHERE a > $1 | 1 | 1 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 (a, b) VALUES ($1, $2), ($3, $4), ($5, $6) | 1 | 3
INSERT INTO test VALUES(generate_series($1, $2), $3) | 1 | 10 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 ORDER BY a | 1 | 12
SELECT * FROM test WHERE a > $1 ORDER BY a | 2 | 4 SELECT * FROM test WHERE a > $1 ORDER BY a | 2 | 4
SELECT * FROM test WHERE a IN ($1, $2, $3, $4, $5) | 1 | 8 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 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 | 6 | 6
UPDATE test SET b = $1 WHERE a > $2 | 1 | 3 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 -- INSERT, UPDATE, DELETE on test table to validate WAL generation metrics

View File

@ -100,6 +100,28 @@ SELECT * FROM test ORDER BY a;
-- SELECT with IN clause -- SELECT with IN clause
SELECT * FROM test WHERE a IN (1, 2, 3, 4, 5); 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"; SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- --

View File

@ -487,7 +487,7 @@
<para> <para>
Plannable queries (that is, <command>SELECT</command>, <command>INSERT</command>, 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 <structname>pg_stat_statements</structname> entry whenever they have identical query
structures according to an internal hash calculation. Typically, two structures according to an internal hash calculation. Typically, two
queries will be considered the same for this purpose if they are queries will be considered the same for this purpose if they are
@ -783,7 +783,7 @@
<varname>pg_stat_statements.track_utility</varname> controls whether <varname>pg_stat_statements.track_utility</varname> controls whether
utility commands are tracked by the module. Utility commands are utility commands are tracked by the module. Utility commands are
all those other than <command>SELECT</command>, <command>INSERT</command>, 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>. The default value is <literal>on</literal>.
Only superusers can change this setting. Only superusers can change this setting.
</para> </para>

View File

@ -2266,10 +2266,10 @@ expression_tree_walker(Node *node,
{ {
MergeAction *action = (MergeAction *) node; MergeAction *action = (MergeAction *) node;
if (walker(action->targetList, context))
return true;
if (walker(action->qual, context)) if (walker(action->qual, context))
return true; return true;
if (walker(action->targetList, context))
return true;
} }
break; break;
case T_PartitionPruneStepOp: case T_PartitionPruneStepOp:

View File

@ -247,6 +247,7 @@ JumbleQueryInternal(JumbleState *jstate, Query *query)
JumbleExpr(jstate, (Node *) query->cteList); JumbleExpr(jstate, (Node *) query->cteList);
JumbleRangeTable(jstate, query->rtable); JumbleRangeTable(jstate, query->rtable);
JumbleExpr(jstate, (Node *) query->jointree); JumbleExpr(jstate, (Node *) query->jointree);
JumbleExpr(jstate, (Node *) query->mergeActionList);
JumbleExpr(jstate, (Node *) query->targetList); JumbleExpr(jstate, (Node *) query->targetList);
JumbleExpr(jstate, (Node *) query->onConflict); JumbleExpr(jstate, (Node *) query->onConflict);
JumbleExpr(jstate, (Node *) query->returningList); JumbleExpr(jstate, (Node *) query->returningList);
@ -737,6 +738,16 @@ JumbleExpr(JumbleState *jstate, Node *node)
JumbleExpr(jstate, (Node *) conf->exclRelTlist); JumbleExpr(jstate, (Node *) conf->exclRelTlist);
} }
break; 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: case T_List:
foreach(temp, (List *) node) foreach(temp, (List *) node)
{ {