From 2cbb5a9c55e9316a7f26fdbcd0d056feff15c86f Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Fri, 18 Mar 2022 12:04:20 +0100 Subject: [PATCH 2/2] fixup! MERGE SQL Command following SQL:2016 --- doc/src/sgml/mvcc.sgml | 2 +- doc/src/sgml/ref/insert.sgml | 2 +- doc/src/sgml/ref/merge.sgml | 36 ++++++++++++-------------- src/backend/catalog/sql_features.txt | 2 +- src/backend/executor/README | 2 +- src/backend/executor/nodeModifyTable.c | 6 ++--- src/backend/parser/gram.y | 24 ++++++++--------- src/backend/parser/parse_merge.c | 2 +- src/bin/psql/tab-complete.c | 19 +++++--------- 9 files changed, 42 insertions(+), 53 deletions(-) diff --git a/doc/src/sgml/mvcc.sgml b/doc/src/sgml/mvcc.sgml index 9b3061c03d..db86326aef 100644 --- a/doc/src/sgml/mvcc.sgml +++ b/doc/src/sgml/mvcc.sgml @@ -450,7 +450,7 @@ Read Committed Isolation Level and a unique index is present and a duplicate row is concurrently inserted, then a uniqueness violation is raised. MERGE does not attempt to avoid the - ERROR by executing an UPDATE. + error by executing an UPDATE. diff --git a/doc/src/sgml/ref/insert.sgml b/doc/src/sgml/ref/insert.sgml index 41cafc8455..a9af9959c0 100644 --- a/doc/src/sgml/ref/insert.sgml +++ b/doc/src/sgml/ref/insert.sgml @@ -766,7 +766,7 @@ Compatibility Also, the case in which a column name list is omitted, but not all the columns are filled from the VALUES clause or query, - is disallowed by the standard. If you prefer a more SQL Standard + is disallowed by the standard. If you prefer a more SQL standard conforming statement than ON CONFLICT, see . diff --git a/doc/src/sgml/ref/merge.sgml b/doc/src/sgml/ref/merge.sgml index 3a1823ceed..8d1d160e02 100644 --- a/doc/src/sgml/ref/merge.sgml +++ b/doc/src/sgml/ref/merge.sgml @@ -13,7 +13,7 @@ MERGE - insert, update, or delete rows of a table based upon source data + conditionally insert, update, or delete rows of a table @@ -24,32 +24,32 @@ ON join_condition when_clause [...] -where data_source is +where data_source is { source_table_name | - ( source_query ) + ( source_query ) } [ [ AS ] source_alias ] -and when_clause is +and when_clause is { WHEN MATCHED [ AND condition ] THEN { merge_update | merge_delete | DO NOTHING } | WHEN NOT MATCHED [ AND condition ] THEN { merge_insert | DO NOTHING } } -and merge_insert is +and merge_insert is INSERT [( column_name [, ...] )] [ OVERRIDING { SYSTEM | USER } VALUE ] { VALUES ( { expression | DEFAULT } [, ...] ) | DEFAULT VALUES } -and merge_update is +and merge_update is UPDATE SET { column_name = { expression | DEFAULT } | ( column_name [, ...] ) = ( { expression | DEFAULT } [, ...] ) } [, ...] -and merge_delete is +and merge_delete is DELETE @@ -414,18 +414,18 @@ Outputs On successful completion, a MERGE command returns a command tag of the form -MERGE total-count +MERGE total_count - The total-count is the total + The total_count is the total number of rows changed (whether inserted, updated, or deleted). - If total-count is 0, no rows + If total_count is 0, no rows were changed in any way. - Execution + Notes The following steps take place during the execution of @@ -458,7 +458,7 @@ Execution - When a condition returns true, perform the following actions + When a condition returns true, perform the following actions: @@ -531,10 +531,6 @@ Execution possible that no action will be taken for a candidate change row. - - - Notes - The order in which rows are generated from the data source is indeterminate by default. @@ -550,7 +546,6 @@ Notes RETURNING or WITH clauses. - You may also wish to consider using INSERT ... ON CONFLICT as an alternative statement which offers the ability to run an @@ -558,7 +553,6 @@ Notes occurs. There are a variety of differences and restrictions between the two statement types and they are not interchangeable. - @@ -578,10 +572,12 @@ Examples INSERT (CustomerId, Balance) VALUES (T.CustomerId, T.TransactionValue); + - notice that this would be exactly equivalent to the following + + Notice that this would be exactly equivalent to the following statement because the MATCHED result does not change - during execution + during execution. MERGE INTO CustomerAccount CA diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt index d4cb8128ff..4c3e29111d 100644 --- a/src/backend/catalog/sql_features.txt +++ b/src/backend/catalog/sql_features.txt @@ -240,7 +240,7 @@ F311 Schema definition statement 02 CREATE TABLE for persistent base tables YES F311 Schema definition statement 03 CREATE VIEW YES F311 Schema definition statement 04 CREATE VIEW: WITH CHECK OPTION YES F311 Schema definition statement 05 GRANT statement YES -F312 MERGE statement YES also consider INSERT ... ON CONFLICT DO UPDATE +F312 MERGE statement YES F313 Enhanced MERGE statement YES F314 MERGE statement with DELETE branch YES F321 User authorization YES diff --git a/src/backend/executor/README b/src/backend/executor/README index ea7469508e..0b5183fc4a 100644 --- a/src/backend/executor/README +++ b/src/backend/executor/README @@ -226,7 +226,7 @@ are simple Vars using only one step instead of two. MERGE ----- -MERGE is a multiple-table, multiple-action command: it specifies a target +MERGE is a multiple-table, multiple-action command: It specifies a target table and a source relation, and can contain multiple WHEN MATCHED and WHEN NOT MATCHED clauses, each of which specifies one UPDATE, INSERT, UPDATE, or DO NOTHING actions. The target table is modified by MERGE, diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 6fc318583a..02b6513423 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -2290,7 +2290,7 @@ ExecOnConflictUpdate(ModifyTableContext *context, * to break. * * It is the user's responsibility to prevent this situation from - * occurring. These problems are why SQL Standard similarly + * occurring. These problems are why the SQL standard similarly * specifies that for SQL MERGE, an exception must be raised in * the event of an attempt to update the same row twice. */ @@ -2303,7 +2303,7 @@ ExecOnConflictUpdate(ModifyTableContext *context, if (TransactionIdIsCurrentTransactionId(xmin)) ereport(ERROR, (errcode(ERRCODE_CARDINALITY_VIOLATION), - /*- translator: %s is a SQL command name */ + /* translator: %s is a SQL command name */ errmsg("%s command cannot affect row a second time", "ON CONFLICT DO UPDATE"), errhint("Ensure that no rows proposed for insertion within the same command have duplicate constrained values."))); @@ -2723,7 +2723,7 @@ lmerge_matched:; if (TransactionIdIsCurrentTransactionId(context->tmfd.xmax)) ereport(ERROR, (errcode(ERRCODE_CARDINALITY_VIOLATION), - /*- translator: %s is a SQL command name */ + /* translator: %s is a SQL command name */ errmsg("%s command cannot affect row a second time", "MERGE"), errhint("Ensure that not more than one source row matches any one target row."))); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 34371d3869..6ad50fa11d 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11475,7 +11475,7 @@ set_target_list: /***************************************************************************** * * QUERY: - * MERGE STATEMENTS + * MERGE * *****************************************************************************/ @@ -11495,13 +11495,13 @@ MergeStmt: $$ = (Node *)m; } - ; + ; merge_when_list: merge_when_clause { $$ = list_make1($1); } | merge_when_list merge_when_clause { $$ = lappend($1,$2); } - ; + ; merge_when_clause: WHEN MATCHED opt_merge_when_and_condition THEN merge_update @@ -11550,16 +11550,16 @@ merge_when_clause: $$ = (Node *)m; } - ; + ; opt_merge_when_and_condition: - AND a_expr { $$ = $2; } - | { $$ = NULL; } - ; + AND a_expr { $$ = $2; } + | { $$ = NULL; } + ; merge_delete: - DELETE_P { $$ = NULL; } - ; + DELETE_P { $$ = NULL; } + ; merge_update: UPDATE SET set_clause_list @@ -11569,7 +11569,7 @@ merge_update: $$ = n; } - ; + ; merge_insert: INSERT merge_values_clause @@ -11609,14 +11609,14 @@ merge_insert: n->values = NIL; $$ = n; } - ; + ; merge_values_clause: VALUES '(' expr_list ')' { $$ = $3; } - ; + ; /***************************************************************************** * diff --git a/src/backend/parser/parse_merge.c b/src/backend/parser/parse_merge.c index 1f0f9058b3..0f54e9e8e1 100644 --- a/src/backend/parser/parse_merge.c +++ b/src/backend/parser/parse_merge.c @@ -383,7 +383,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) qry->mergeActionList = mergeActionList; - /* RETURNING could potentially be added in the future, but not in SQL Std */ + /* RETURNING could potentially be added in the future, but not in SQL std */ qry->returningList = NULL; qry->hasTargetSRFs = false; diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 85ed254d85..09054b22e8 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -804,24 +804,17 @@ static const SchemaQuery Query_for_list_of_updatables = { /* Relations supporting MERGE */ static const SchemaQuery Query_for_list_of_mergetargets = { - /* min_server_version */ - 150000, - /* catname */ - "pg_catalog.pg_class c", - /* selcondition */ + .min_server_version = 150000, + .catname = "pg_catalog.pg_class c", + .selcondition = "c.relkind IN (" CppAsString2(RELKIND_RELATION) ", " CppAsString2(RELKIND_PARTITIONED_TABLE) ") AND " "c.relhasrules = false AND " "(c.relhassubclass = false OR " " c.relkind = " CppAsString2(RELKIND_PARTITIONED_TABLE) ")", - /* viscondition */ - "pg_catalog.pg_table_is_visible(c.oid)", - /* namespace */ - "c.relnamespace", - /* result */ - "pg_catalog.quote_ident(c.relname)", - /* qualresult */ - NULL + .viscondition = "pg_catalog.pg_table_is_visible(c.oid)", + .namespace = "c.relnamespace", + .result = "pg_catalog.quote_ident(c.relname)", }; /* Relations supporting SELECT */ -- 2.35.1