matrixorigin / matrixorigin/matrixone
[Bug]: DATA BRANCH cannot apply primary-key updates with ON UPDATE CASCADE
- 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
Assessment
This issue has not been assessed yet.