1
0
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:
Dean Rasheed
2025-09-04 11:50:59 +01:00
parent 451b22efd9
commit 5481cc332b
6 changed files with 94 additions and 7 deletions

View File

@@ -984,12 +984,16 @@ InitPlan(QueryDesc *queryDesc, int eflags)
* For INSERT ON CONFLICT, the result relation is required to support the
* onConflictAction, regardless of whether a conflict actually occurs.
*
* For MERGE, mergeActions is the list of actions that may be performed. The
* result relation is required to support every action, regardless of whether
* or not they are all executed.
*
* Note: when changing this function, you probably also need to look at
* CheckValidRowMarkRel.
*/
void
CheckValidResultRelNew(ResultRelInfo *resultRelInfo, CmdType operation,
OnConflictAction onConflictAction)
OnConflictAction onConflictAction, List *mergeActions)
{
Relation resultRel = resultRelInfo->ri_RelationDesc;
TriggerDesc *trigDesc = resultRel->trigdesc;
@@ -1003,7 +1007,24 @@ CheckValidResultRelNew(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)
{
ListCell *lc;
foreach(lc, mergeActions)
{
MergeAction *action = (MergeAction *) lfirst(lc);
CheckCmdReplicaIdentity(resultRel, action->commandType);
}
}
else
CheckCmdReplicaIdentity(resultRel, operation);
/*
* For INSERT ON CONFLICT DO UPDATE, additionally check that the
@@ -1136,7 +1157,7 @@ CheckValidResultRelNew(ResultRelInfo *resultRelInfo, CmdType operation,
void
CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation)
{
return CheckValidResultRelNew(resultRelInfo, operation, ONCONFLICT_NONE);
return CheckValidResultRelNew(resultRelInfo, operation, ONCONFLICT_NONE, NIL);
}
/*

View File

@@ -364,7 +364,8 @@ ExecFindPartition(ModifyTableState *mtstate,
/* Verify this ResultRelInfo allows INSERTs */
CheckValidResultRelNew(rri, CMD_INSERT,
node ? node->onConflictAction : ONCONFLICT_NONE);
node ? node->onConflictAction : ONCONFLICT_NONE,
NIL);
/*
* Initialize information needed to insert this and
@@ -531,7 +532,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
* required when the operation is CMD_UPDATE.
*/
CheckValidResultRelNew(leaf_part_rri, CMD_INSERT,
node ? node->onConflictAction : ONCONFLICT_NONE);
node ? node->onConflictAction : ONCONFLICT_NONE,
NIL);
/*
* Open partition indices. The user may have asked to check for conflicts

View File

@@ -4220,6 +4220,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
foreach(l, node->resultRelations)
{
Index resultRelation = lfirst_int(l);
List *mergeActions = NIL;
if (node->mergeActionLists)
mergeActions = list_nth(node->mergeActionLists, i);
if (resultRelInfo != mtstate->rootResultRelInfo)
{
@@ -4242,7 +4246,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
* Verify result relation is a valid target for the current operation
*/
CheckValidResultRelNew(resultRelInfo, operation,
node->onConflictAction);
node->onConflictAction, mergeActions);
resultRelInfo++;
i++;