mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	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
This commit is contained in:
		@@ -1033,7 +1033,16 @@ CheckValidResultRelNew(ResultRelInfo *resultRelInfo, CmdType operation,
 | 
				
			|||||||
	{
 | 
						{
 | 
				
			||||||
		case RELKIND_RELATION:
 | 
							case RELKIND_RELATION:
 | 
				
			||||||
		case RELKIND_PARTITIONED_TABLE:
 | 
							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
 | 
								 * For INSERT ON CONFLICT DO UPDATE, additionally check that the
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1765,6 +1765,34 @@ INSERT INTO testpub_insert_onconfl_parted VALUES (1, 1) ON CONFLICT DO NOTHING;
 | 
				
			|||||||
DROP PUBLICATION pub1;
 | 
					DROP PUBLICATION pub1;
 | 
				
			||||||
DROP TABLE testpub_insert_onconfl_no_ri;
 | 
					DROP TABLE testpub_insert_onconfl_no_ri;
 | 
				
			||||||
DROP TABLE testpub_insert_onconfl_parted;
 | 
					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;
 | 
					RESET SESSION AUTHORIZATION;
 | 
				
			||||||
DROP ROLE regress_publication_user, regress_publication_user2;
 | 
					DROP ROLE regress_publication_user, regress_publication_user2;
 | 
				
			||||||
DROP ROLE regress_publication_user_dummy;
 | 
					DROP ROLE regress_publication_user_dummy;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1132,6 +1132,37 @@ DROP PUBLICATION pub1;
 | 
				
			|||||||
DROP TABLE testpub_insert_onconfl_no_ri;
 | 
					DROP TABLE testpub_insert_onconfl_no_ri;
 | 
				
			||||||
DROP TABLE testpub_insert_onconfl_parted;
 | 
					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;
 | 
					RESET SESSION AUTHORIZATION;
 | 
				
			||||||
DROP ROLE regress_publication_user, regress_publication_user2;
 | 
					DROP ROLE regress_publication_user, regress_publication_user2;
 | 
				
			||||||
DROP ROLE regress_publication_user_dummy;
 | 
					DROP ROLE regress_publication_user_dummy;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user