matrixorigin / matrixorigin/matrixone

[Bug]: DATA BRANCH cannot apply primary-key updates with ON UPDATE CASCADE

Open
#28,026 95 comments 0 reactions 1 assignee Claimed by @gouhongshen View on GitHub
ai-easy deferred kind/bug
Dominant language
Go
Stars
1.9k
Forks
311
Avg merge
1d 3h
Merged PRs (30d)
768

Description

## Is there an existing issue for the same bug?

- [x] I searched open and closed DATA BRANCH issues; none covers primary-key update provenance with foreign-key `ON UPDATE` actions.

## Branch Name

main

## Commit ID

`ad143902644719e65fa703df6397a011dc7d7dfa` (PR #27996 head reviewed on 2026-09-02)

## Other Environment Information

- Local embedded single-CN / single-TN / single-Log test cluster
- Parent table with an integer primary key and a referencing child foreign key using `ON UPDATE CASCADE`

## Actual Behavior

A branch-side primary-key change is emitted as an independent delete of the old key plus an insert of the new key. `DATA BRANCH MERGE` and `DATA BRANCH PICK` replay that as DELETE/INSERT, so the parent-row delete fails with the foreign-key constraint error. An ordinary `UPDATE` on the same parent key succeeds and cascades to the child.

## Expected Behavior

When a source update changes a referenced primary key, Data Branch apply should preserve it as one native destination `UPDATE`, allowing declared `ON UPDATE CASCADE` actions to run.

## Steps to Reproduce

```sql
create database branch_pk_cascade;
use branch_pk_cascade;
create table parent_t (id int primary key, payload varchar(32));
create table child_t (id int primary key, parent_id int,
constraint fk_child_parent foreign key (parent_id) references parent_t(id) on update cascade);
insert into parent_t values (2, 'two');
insert into child_t values (1, 2);

data branch create table merge_src from parent_t;
update merge_src set id = 3 where id = 2;
data branch merge merge_src into parent_t when conflict accept;

-- Control: update parent_t set id = 3 where id = 2; cascades child_t.parent_id.
```

The equivalent PICK setup (source and destination branches, `keys(2)`) fails for the same reason.

## Root Cause and Scope

`multi_update` deletes the old row while writing the replacement without a durable operation identity. `databranchutils.CollectChanges` therefore exposes the old row ID/key as a tombstone and the replacement row ID/key as data. `buildHashmapForTable` and `findDeleteAndUpdateBat` correlate the two only by primary-key value; once the key changes, there is no safe association.

Matching rows by commit timestamp or iteration order would conflate independent delete/insert operations with updates and could trigger unintended cascades, so it is not a safe fix. This needs an explicit old-to-new update provenance link through the change stream, followed by native UPDATE apply and MERGE/PICK regressions for cascade, non-cascade rejection, and atomicity.

## Related

- Related to #27992: #27996 fixes the documented stable-key non-key-update case.
- Related to PR #27996: this issue tracks the distinct primary-key-move protocol gap identified during review.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.