Fix replica identity check for MERGE.

When executing a MERGE, check that the target relation supports all
actions mentioned in the MERGE command. Specifically, check that it
has a REPLICA IDENTITY if it publishes updates or deletes and the
MERGE command contains update or delete actions. Failing to do this
can silently break replication.

Author: Zhijie Hou <houzj.fnst@fujitsu.com>
Reviewed-by: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
Reviewed-by: Dean Rasheed <dean.a.rasheed@gmail.com>
Tested-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/OS3PR01MB57180C87E43A679A730482DF94B62@OS3PR01MB5718.jpnprd01.prod.outlook.com
Backpatch-through: 15
REL_18_STABLE
Dean Rasheed 7 days ago
parent 344662848a
commit 311340f178
  1. 11
      src/backend/executor/execMain.c
  2. 28
      src/test/regress/expected/publication.out
  3. 31
      src/test/regress/sql/publication.sql

@ -1061,7 +1061,16 @@ CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation,
{
case RELKIND_RELATION:
case RELKIND_PARTITIONED_TABLE:
CheckCmdReplicaIdentity(resultRel, operation);
/*
* For MERGE, check that the target relation supports each action.
* For other operations, just check the operation itself.
*/
if (operation == CMD_MERGE)
foreach_node(MergeAction, action, mergeActions)
CheckCmdReplicaIdentity(resultRel, action->commandType);
else
CheckCmdReplicaIdentity(resultRel, operation);
/*
* For INSERT ON CONFLICT DO UPDATE, additionally check that the

@ -1947,6 +1947,34 @@ INSERT INTO testpub_insert_onconfl_parted VALUES (1, 1) ON CONFLICT DO NOTHING;
DROP PUBLICATION pub1;
DROP TABLE testpub_insert_onconfl_no_ri;
DROP TABLE testpub_insert_onconfl_parted;
-- Test that the MERGE command correctly checks REPLICA IDENTITY when the
-- target table is published.
CREATE TABLE testpub_merge_no_ri (a int, b int);
CREATE TABLE testpub_merge_pk (a int primary key, b int);
SET client_min_messages = 'ERROR';
CREATE PUBLICATION pub1 FOR ALL TABLES;
RESET client_min_messages;
-- fail - missing REPLICA IDENTITY
MERGE INTO testpub_merge_no_ri USING testpub_merge_pk s ON s.a >= 1
WHEN MATCHED THEN UPDATE SET b = s.b;
ERROR: cannot update table "testpub_merge_no_ri" because it does not have a replica identity and publishes updates
HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE.
-- fail - missing REPLICA IDENTITY
MERGE INTO testpub_merge_no_ri USING testpub_merge_pk s ON s.a >= 1
WHEN MATCHED THEN DELETE;
ERROR: cannot delete from table "testpub_merge_no_ri" because it does not have a replica identity and publishes deletes
HINT: To enable deleting from the table, set REPLICA IDENTITY using ALTER TABLE.
-- ok - insert and do nothing are not restricted
MERGE INTO testpub_merge_no_ri USING testpub_merge_pk s ON s.a >= 1
WHEN MATCHED THEN DO NOTHING
WHEN NOT MATCHED THEN INSERT (a, b) VALUES (0, 0);
-- ok - REPLICA IDENTITY is DEFAULT and table has a PK
MERGE INTO testpub_merge_pk USING testpub_merge_no_ri s ON s.a >= 1
WHEN MATCHED AND s.a > 0 THEN UPDATE SET b = s.b
WHEN MATCHED THEN DELETE;
DROP PUBLICATION pub1;
DROP TABLE testpub_merge_no_ri;
DROP TABLE testpub_merge_pk;
RESET SESSION AUTHORIZATION;
DROP ROLE regress_publication_user, regress_publication_user2;
DROP ROLE regress_publication_user_dummy;

@ -1250,6 +1250,37 @@ DROP PUBLICATION pub1;
DROP TABLE testpub_insert_onconfl_no_ri;
DROP TABLE testpub_insert_onconfl_parted;
-- Test that the MERGE command correctly checks REPLICA IDENTITY when the
-- target table is published.
CREATE TABLE testpub_merge_no_ri (a int, b int);
CREATE TABLE testpub_merge_pk (a int primary key, b int);
SET client_min_messages = 'ERROR';
CREATE PUBLICATION pub1 FOR ALL TABLES;
RESET client_min_messages;
-- fail - missing REPLICA IDENTITY
MERGE INTO testpub_merge_no_ri USING testpub_merge_pk s ON s.a >= 1
WHEN MATCHED THEN UPDATE SET b = s.b;
-- fail - missing REPLICA IDENTITY
MERGE INTO testpub_merge_no_ri USING testpub_merge_pk s ON s.a >= 1
WHEN MATCHED THEN DELETE;
-- ok - insert and do nothing are not restricted
MERGE INTO testpub_merge_no_ri USING testpub_merge_pk s ON s.a >= 1
WHEN MATCHED THEN DO NOTHING
WHEN NOT MATCHED THEN INSERT (a, b) VALUES (0, 0);
-- ok - REPLICA IDENTITY is DEFAULT and table has a PK
MERGE INTO testpub_merge_pk USING testpub_merge_no_ri s ON s.a >= 1
WHEN MATCHED AND s.a > 0 THEN UPDATE SET b = s.b
WHEN MATCHED THEN DELETE;
DROP PUBLICATION pub1;
DROP TABLE testpub_merge_no_ri;
DROP TABLE testpub_merge_pk;
RESET SESSION AUTHORIZATION;
DROP ROLE regress_publication_user, regress_publication_user2;
DROP ROLE regress_publication_user_dummy;

Loading…
Cancel
Save