Skip to content

DM: Add foreign key causality for DM syncer. (#12414)#12552

Merged
ti-chi-bot[bot] merged 6 commits intopingcap:release-8.5from
ti-chi-bot:cherry-pick-12414-to-release-8.5
Mar 18, 2026
Merged

DM: Add foreign key causality for DM syncer. (#12414)#12552
ti-chi-bot[bot] merged 6 commits intopingcap:release-8.5from
ti-chi-bot:cherry-pick-12414-to-release-8.5

Conversation

@ti-chi-bot
Copy link
Member

This is an automated cherry-pick of #12414

What problem does this PR solve?

Issue Number: ref #12350

What is changed and how it works?

This PR introduces foreign key-causality support to the DM syncer.

  • In the schema tracker, DownstreamTableInfo now captures a set of ForeignKeyCausalityRelation entries that represent parent→child table dependencies via foreign key constraints.
  • The syncer’s RowChange objects now include these relations so that change processing can honour FK dependencies.
  • A new integration test suite foreign_key_multi_worker validates correct multi-worker behaviour when foreign key chains exist, helping ensure correctness under concurrent execution.

Check List

Tests

  • Unit test
  • Integration test
  • Manual test (add detailed scripts or steps below)
  • No code

Questions

Will it cause performance regression or break compatibility?
Do you need to update user documentation, design documentation or monitoring documentation?

Release note

Please refer to [Release Notes Language Style Guide](https://pingcap.github.io/tidb-dev-guide/contribute-to-tidb/release-notes-style-guide.html) to write a quality release note.

If you don't think this PR needs a release note then fill it with `None`.

Signed-off-by: ti-chi-bot <ti-community-prow-bot@tidb.io>
@ti-chi-bot ti-chi-bot added area/dm Issues or PRs related to DM. do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. lgtm release-note Denotes a PR that will be considered when it comes time to generate release notes. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. type/cherry-pick-for-release-8.5 This PR is cherry-picked to release-8.5 from a source PR. labels Mar 13, 2026
@ti-chi-bot
Copy link
Member Author

@OliverS929 This PR has conflicts, I have hold it.
Please resolve them or ask others to resolve them, then comment /unhold to remove the hold label.

@ti-chi-bot
Copy link
Contributor

ti-chi-bot bot commented Mar 13, 2026

@ti-chi-bot: ## If you want to know how to resolve it, please read the guide in TiDB Dev Guide.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the ti-community-infra/tichi repository.

@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the DM syncer's capability to handle foreign key constraints, particularly in complex replication scenarios. It introduces a robust mechanism to track and enforce foreign key causality across related tables, ensuring data integrity even when multiple workers are processing changes concurrently. The changes also include stricter validation for DDL operations involving foreign keys and improved handling of safe-mode DMLs to prevent violations of referential integrity. This work is crucial for supporting more intricate database schemas and replication topologies.

Highlights

  • Foreign Key Causality Tracking: The DownstreamTableInfo struct in the schema tracker now includes ForeignKeyRelations to capture parent-child table dependencies. New functions buildForeignKeyRelations and InitDownStreamForeignKeyRelations are introduced to recursively build these relations, allowing child rows to be linked to their root parent's causality key domain.
  • Enhanced DML Processing: The syncer's RowChange objects now incorporate these ForeignKeyRelations. This ensures that DML operations (INSERT, UPDATE, DELETE) respect foreign key dependencies during change processing, especially in multi-worker environments.
  • Foreign Key DDL Validation: The DDL worker now includes logic to reject DDL statements that create or alter foreign key constraints when foreign_key_checks=1 is enabled, preventing unsupported operations.
  • Safe Mode Handling for FKs: New logic in the DML worker validates safe-mode updates. Specifically, it prevents updates that change primary or unique keys when foreign_key_checks=1 is enabled in safe mode, as such operations could break foreign key constraints and cascade operations.
  • Pre-checks for Routing and Filtering: New pre-check functions (precheckForeignKeyCausality, precheckForeignKeyReferencedTables) are added to the syncer to validate foreign key causality in scenarios involving table routing or filtered parent/ancestor tables, ensuring data integrity and preventing unsupported configurations.
  • New Integration Tests: A comprehensive suite of integration tests (foreign_key_multi_worker, foreign_key_route_single_worker, foreign_key_schema_repair, safe_mode_foreign_key) has been added to validate the correctness of foreign key causality handling under various conditions, including multi-worker execution, table routing, schema misalignment, and safe mode.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • dm/pkg/schema/tracker.go
    • Added cdcmodel import.
    • Extended DownstreamTableInfo with ForeignKeyRelations, foreignKeyInitOnce, and foreignKeyInitErr.
    • Modified GetDownStreamTableInfo to initialize foreign key relations.
    • Introduced GetDownStreamTableInfoWithoutForeignKey to fetch table info without FK initialization.
    • Added InitDownStreamForeignKeyRelations to explicitly initialize FK relations for routed tables.
    • Implemented initForeignKeyRelations and buildForeignKeyRelations to recursively determine foreign key causality.
    • Added helper functions findMatchingForeignKey, sameColumns, sameTableIdentity, newForeignKeyRouteUnsupportedError, newForeignKeySchemaAlignmentError, buildColumnIndexMap, and getColumnsByNames.
  • dm/pkg/schema/tracker_test.go
    • Added cdcmodel import.
    • Added new test cases: TestForeignKeyRelationBuildsRootParents, TestForeignKeyRelationBuildsRootParentsWithHiddenColumns, TestForeignKeyRelationSchemaAlignmentErrorGuidesRepair, TestForeignKeyRelationFallsBackToDirectParentWhenUnmappable to validate foreign key causality logic.
  • dm/syncer/data_validator.go
    • Modified genValidateTableInfo to use the new getDownStreamTableInfo signature.
    • Set ForeignKeyRelations on rowChange objects during processRowsEvent.
  • dm/syncer/ddl.go
    • Added foreignKeyChecksEnabled field to DDLWorker.
    • Initialized foreignKeyChecksEnabled in NewDDLWorker.
    • Added rejectForeignKeyDDL method to prevent DDLs that add foreign keys when foreign_key_checks=1.
    • Called rejectForeignKeyDDL within genDDLInfo.
  • dm/syncer/ddl_test.go
    • Added TestRejectForeignKeyDDL to verify the DDL worker's foreign key rejection logic.
  • dm/syncer/dml.go
    • Removed unused import github.com/pingcap/tiflow/dm/pkg/utils.
    • Modified genAndFilterInsertDMLs, genAndFilterUpdateDMLs, and genAndFilterDeleteDMLs to use s.prepareDownStreamTableInfo and set ForeignKeyRelations on RowChange objects.
  • dm/syncer/dml_worker.go
    • Added isForeignKeyChecksEnabled to check the foreign_key_checks session variable.
    • Added shouldDisableForeignKeyChecksForJob to determine if FK checks should be temporarily disabled for a job.
    • Added validateSafeModeForeignKeyUpdate to prevent safe-mode updates that change PK/UK when FK checks are enabled.
    • Called validateSafeModeForeignKeyUpdate in executeBatchJobs.
  • dm/syncer/dml_worker_test.go
    • Added TestShouldDisableForeignKeyChecksForJob, TestIsForeignKeyChecksEnabled, TestShouldDisableForeignKeyChecks, TestValidateSafeModeForeignKeyUpdate, and TestExecuteBatchJobsWithForeignKey to test new FK-related DML worker logic.
  • dm/syncer/foreign_key_causality_route_test.go
    • Added new file with integration tests for foreign key causality and routing scenarios.
  • dm/syncer/syncer.go
    • Added needForeignKeyCausality to check if FK causality is required.
    • Added precheckForeignKeyCausality to validate routing configurations with FKs.
    • Added precheckForeignKeyReferencedTables to ensure all FK-related tables are included in the allow-list.
    • Added basicDownStreamTableInfo helper.
    • Introduced prepareDownStreamTableInfo to orchestrate the retrieval and initialization of downstream table info, including FK causality.
    • Modified getDownStreamTableInfo to call prepareDownStreamTableInfo.
  • dm/tests/_utils/run_dm_ctl_with_retry
    • Updated grep command to correctly count regex matches, fixing issues with sed-based escaping.
  • dm/tests/foreign_key_multi_worker/conf/diff_config.toml
    • Added new file for diff configuration for the foreign key multi-worker test.
  • dm/tests/foreign_key_multi_worker/conf/dm-master.toml
    • Added new file for DM master configuration for the foreign key multi-worker test.
  • dm/tests/foreign_key_multi_worker/conf/dm-task.yaml
    • Added new file for DM task configuration for the foreign key multi-worker test, enabling foreign_key_checks: 1 and worker-count: 4.
  • dm/tests/foreign_key_multi_worker/conf/dm-worker1.toml
    • Added new file for DM worker configuration for the foreign key multi-worker test.
  • dm/tests/foreign_key_multi_worker/conf/source1.yaml
    • Added new file for source configuration for the foreign key multi-worker test.
  • dm/tests/foreign_key_multi_worker/data/db1.increment.sql
    • Added new file with incremental SQL changes for the foreign key multi-worker test.
  • dm/tests/foreign_key_multi_worker/data/db1.prepare.sql
    • Added new file with prepare SQL for the foreign key multi-worker test, including grandparent, parent, and child tables with FKs.
  • dm/tests/foreign_key_multi_worker/run.sh
    • Added new test script to run the foreign key multi-worker integration test.
  • dm/tests/foreign_key_route_single_worker/conf/diff_config.toml
    • Added new file for diff configuration for the foreign key route single-worker test.
  • dm/tests/foreign_key_route_single_worker/conf/dm-master.toml
    • Added new file for DM master configuration for the foreign key route single-worker test.
  • dm/tests/foreign_key_route_single_worker/conf/dm-task.yaml
    • Added new file for DM task configuration for the foreign key route single-worker test, enabling foreign_key_checks: 1, worker-count: 1, and table routing.
  • dm/tests/foreign_key_route_single_worker/conf/dm-worker1.toml
    • Added new file for DM worker configuration for the foreign key route single-worker test.
  • dm/tests/foreign_key_route_single_worker/conf/source1.yaml
    • Added new file for source configuration for the foreign key route single-worker test.
  • dm/tests/foreign_key_route_single_worker/data/db1.increment.sql
    • Added new file with incremental SQL changes for the foreign key route single-worker test.
  • dm/tests/foreign_key_route_single_worker/data/db1.prepare.sql
    • Added new file with prepare SQL for the foreign key route single-worker test.
  • dm/tests/foreign_key_route_single_worker/data/tidb.prepare.sql
    • Added new file with TiDB prepare SQL for the foreign key route single-worker test, defining routed tables.
  • dm/tests/foreign_key_route_single_worker/run.sh
    • Added new test script to run the foreign key route single-worker integration test.
  • dm/tests/foreign_key_schema_repair/data/child_wrong_schema.sql
    • Added new file with a deliberately wrong child table schema for testing schema repair.
  • dm/tests/foreign_key_schema_repair/run.sh
    • Added new test script to validate foreign key schema repair functionality.
  • dm/tests/ha_cases_1/run.sh
    • Updated expected output for query-status commands from "stage": "Running" to "unit": "Sync" to reflect changes in status reporting.
  • dm/tests/run_group.sh
    • Updated test groups to include new foreign key related test cases: safe_mode_foreign_key, foreign_key_multi_worker, foreign_key_route_single_worker, foreign_key_schema_repair.
  • dm/tests/safe_mode_foreign_key/conf/dm-task.yaml
    • Added new file for DM task configuration for the safe-mode foreign key test, enabling foreign_key_checks: 1 and safe-mode: true.
  • dm/tests/safe_mode_foreign_key/conf/source1.yaml
    • Added new file for source configuration for the safe-mode foreign key test, disabling checker to avoid auto-resume.
  • dm/tests/safe_mode_foreign_key/data/db1.increment.sql
    • Added new file with incremental SQL changes for the safe-mode foreign key test.
  • dm/tests/safe_mode_foreign_key/data/db1.prepare.sql
    • Added new file with prepare SQL for the safe-mode foreign key test.
  • dm/tests/safe_mode_foreign_key/run.sh
    • Added new test script to run the safe-mode foreign key integration test, including scenarios that trigger the FK safe-mode guard.
  • pkg/sqlmodel/causality.go
    • Modified CausalityKeys to include foreign key causality strings.
    • Added getForeignKeyCausalityString to generate causality keys based on foreign key relations.
  • pkg/sqlmodel/causality_fk_test.go
    • Added new file with unit tests for foreign key causality key generation.
  • pkg/sqlmodel/foreign_key.go
    • Added new file defining the ForeignKeyCausalityRelation struct.
  • pkg/sqlmodel/row_change.go
    • Added foreignKeyRelations field to RowChange struct.
    • Added SetForeignKeyRelations method.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces foreign key causality support for the DM syncer, which is a significant and valuable feature. The implementation appears well-thought-out, covering recursive causality chain building, pre-checks for unsupported configurations like table routing with multiple workers, and handling of filtered parent tables. The addition of comprehensive unit and integration tests is commendable and greatly increases confidence in the changes. However, there are several merge conflicts present in the code that must be resolved before this PR can be merged. These conflicts are critical as they prevent the code from compiling.

Comment on lines +61 to +109
<<<<<<< HEAD
=======
func isForeignKeyChecksEnabled(session map[string]string) bool {
for key, value := range session {
if strings.EqualFold(key, "foreign_key_checks") {
trimmed := strings.Trim(value, " '\"")
return variable.TiDBOptOn(trimmed)
}
}
return false
}

func (w *DMLWorker) shouldDisableForeignKeyChecksForJob(j *job) bool {
if !w.foreignKeyChecksEnabled {
return false
}
if j == nil || j.tp != dml || !j.safeMode || j.dml == nil {
return false
}
switch j.dml.Type() {
case sqlmodel.RowChangeInsert, sqlmodel.RowChangeUpdate:
return true
default:
return false
}
}

func (w *DMLWorker) validateSafeModeForeignKeyUpdate(jobs []*job) error {
if !w.foreignKeyChecksEnabled {
return nil
}
for _, j := range jobs {
if j == nil || j.tp != dml || !j.safeMode || j.dml == nil {
continue
}
if j.dml.Type() != sqlmodel.RowChangeUpdate {
continue
}
if j.dml.IsPrimaryOrUniqueKeyUpdated() {
return terror.ErrSyncerUnitNotSupportedOperate.Generatef(
"safe-mode update with foreign_key_checks=1 and PK/UK changes is not supported; " +
"delete+replace may break foreign key constraints and cascade operations",
)
}
}
return nil
}

>>>>>>> e709452702 (DM: Add foreign key causality for DM syncer. (#12414))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This file contains merge conflict markers that need to be resolved. The code within the markers seems to introduce important logic for handling foreign key checks in safe mode, but the conflict prevents the code from compiling.

Comment on lines +131 to +361
<<<<<<< HEAD
=======

func TestShouldDisableForeignKeyChecksForJob(t *testing.T) {
t.Parallel()

worker := &DMLWorker{}

source := &cdcmodel.TableName{Schema: "db", Table: "tb"}
target := &cdcmodel.TableName{Schema: "target", Table: "tb"}
tableInfo := mockTableInfo(t, "create table db.tb(id int primary key, name varchar(10))")

insertChange := sqlmodel.NewRowChange(source, target, nil, []interface{}{1, "v"}, tableInfo, nil, nil)
insertJob := &job{tp: dml, safeMode: true, dml: insertChange}
require.False(t, worker.shouldDisableForeignKeyChecksForJob(insertJob))

worker.foreignKeyChecksEnabled = true
require.True(t, worker.shouldDisableForeignKeyChecksForJob(insertJob))

updateChange := sqlmodel.NewRowChange(source, target, []interface{}{1, "a"}, []interface{}{1, "b"}, tableInfo, nil, nil)
updateJob := &job{tp: dml, safeMode: true, dml: updateChange}
require.True(t, worker.shouldDisableForeignKeyChecksForJob(updateJob))

deleteChange := sqlmodel.NewRowChange(source, target, []interface{}{1, "a"}, nil, tableInfo, nil, nil)
deleteJob := &job{tp: dml, safeMode: true, dml: deleteChange}
require.False(t, worker.shouldDisableForeignKeyChecksForJob(deleteJob))

insertJob.safeMode = false
require.False(t, worker.shouldDisableForeignKeyChecksForJob(insertJob))
}

func TestIsForeignKeyChecksEnabled(t *testing.T) {
t.Parallel()

cases := []struct {
name string
session map[string]string
expected bool
}{
{name: "nil session", session: nil, expected: false},
{name: "disabled", session: map[string]string{"foreign_key_checks": "0"}, expected: false},
{name: "enabled numeric", session: map[string]string{"foreign_key_checks": "1"}, expected: true},
{name: "enabled literal", session: map[string]string{"FOREIGN_KEY_CHECKS": "ON"}, expected: true},
{name: "enabled quoted", session: map[string]string{"foreign_key_checks": "'1'"}, expected: true},
{name: "other value", session: map[string]string{"foreign_key_checks": "off"}, expected: false},
{name: "unrelated", session: map[string]string{"sql_mode": ""}, expected: false},
}

for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
t.Parallel()
require.Equal(t, c.expected, isForeignKeyChecksEnabled(c.session))
})
}
}

func TestShouldDisableForeignKeyChecks(t *testing.T) {
t.Parallel()

worker := &DMLWorker{foreignKeyChecksEnabled: true}
source := &cdcmodel.TableName{Schema: "db", Table: "tb"}
target := &cdcmodel.TableName{Schema: "target", Table: "tbl"}
tableInfo := mockTableInfo(t, "create table db.tb(id int primary key, col1 int unique not null, col2 int unique, name varchar(24))")

insertChange := sqlmodel.NewRowChange(source, target, nil, []interface{}{1, 2, 3, "v"}, tableInfo, nil, nil)
insertJob := newDMLJob(insertChange, ecWithSafeMode)
require.True(t, worker.shouldDisableForeignKeyChecksForJob(insertJob))

insertJob.safeMode = false
require.False(t, worker.shouldDisableForeignKeyChecksForJob(insertJob))

updateChange := sqlmodel.NewRowChange(source, target, []interface{}{1, 2, 3, "v"}, []interface{}{1, 2, 3, "v2"}, tableInfo, nil, nil)
updateJob := newDMLJob(updateChange, ecWithSafeMode)
require.True(t, worker.shouldDisableForeignKeyChecksForJob(updateJob))

deleteChange := sqlmodel.NewRowChange(source, target, []interface{}{1, 2, 3, "v"}, nil, tableInfo, nil, nil)
deleteJob := newDMLJob(deleteChange, ecWithSafeMode)
require.False(t, worker.shouldDisableForeignKeyChecksForJob(deleteJob))

worker.foreignKeyChecksEnabled = false
anotherJob := newDMLJob(insertChange, ecWithSafeMode)
require.False(t, worker.shouldDisableForeignKeyChecksForJob(anotherJob))
}

func TestValidateSafeModeForeignKeyUpdate(t *testing.T) {
t.Parallel()

worker := &DMLWorker{foreignKeyChecksEnabled: true}
source := &cdcmodel.TableName{Schema: "db", Table: "tb"}
target := &cdcmodel.TableName{Schema: "target", Table: "tb"}

// payload is UK, v is non-key
tableInfo := mockTableInfo(t,
"create table db.tb(id int primary key, payload varchar(10) unique, v varchar(10))",
)

// PK change
pkUpdate := sqlmodel.NewRowChange(
source, target,
[]interface{}{1, "a", "x"},
[]interface{}{2, "a", "x"},
tableInfo, nil, nil,
)

// UK change
ukUpdate := sqlmodel.NewRowChange(
source, target,
[]interface{}{1, "a", "x"},
[]interface{}{1, "b", "x"},
tableInfo, nil, nil,
)

// Non-key change
nonKeyUpdate := sqlmodel.NewRowChange(
source, target,
[]interface{}{1, "a", "x"},
[]interface{}{1, "a", "y"},
tableInfo, nil, nil,
)

t.Run("safe-mode pk update should error", func(t *testing.T) {
j := newDMLJob(pkUpdate, ecWithSafeMode)
err := worker.validateSafeModeForeignKeyUpdate([]*job{j})
require.Error(t, err)
require.Contains(t, err.Error(), "PK/UK changes")
})

t.Run("safe-mode uk update should error", func(t *testing.T) {
j := newDMLJob(ukUpdate, ecWithSafeMode)
err := worker.validateSafeModeForeignKeyUpdate([]*job{j})
require.Error(t, err)
require.Contains(t, err.Error(), "PK/UK changes")
})

t.Run("safe-mode non-key update should pass", func(t *testing.T) {
j := newDMLJob(nonKeyUpdate, ecWithSafeMode)
require.NoError(t, worker.validateSafeModeForeignKeyUpdate([]*job{j}))
})

t.Run("non-safe-mode pk update should pass", func(t *testing.T) {
j := newDMLJob(pkUpdate, ec)
require.NoError(t, worker.validateSafeModeForeignKeyUpdate([]*job{j}))
})
}

func TestExecuteBatchJobsWithForeignKey(t *testing.T) {
t.Parallel()

// helper: convert []interface{} -> []driver.Value
toDriverValues := func(args []interface{}) []driver.Value {
out := make([]driver.Value, len(args))
for i, v := range args {
out[i] = v
}
return out
}

db, mock, err := sqlmock.New()
require.NoError(t, err)
defer db.Close()

sqlConn, err := db.Conn(context.Background())
require.NoError(t, err)
defer sqlConn.Close()

baseConn := connpkg.NewBaseConnForTest(sqlConn, nil)
cfg := &config.SubTaskConfig{Name: "test"}
dbConn := dbconn.NewDBConn(cfg, baseConn)

worker := &DMLWorker{
toDBConns: []*dbconn.DBConn{dbConn},
syncCtx: tcontext.Background(),
successFunc: func(int, int, []*job) {},
fatalFunc: func(*job, error) {},
metricProxies: nil,
foreignKeyChecksEnabled: true,
logger: log.L(),
}

source := &cdcmodel.TableName{Schema: "db", Table: "tb"}
target := &cdcmodel.TableName{Schema: "targetSchema", Table: "targetTable"}
createSQL := "create table db.tb(id int primary key, col1 int unique not null, col2 int unique, name varchar(24))"
tableInfo := mockTableInfo(t, createSQL)

insertChange := sqlmodel.NewRowChange(source, target, nil, []interface{}{1, 2, 3, "normal"}, tableInfo, nil, nil)
replaceChange := sqlmodel.NewRowChange(source, target, nil, []interface{}{2, 3, 4, "safe"}, tableInfo, nil, nil)

insertJob := newDMLJob(insertChange, ec)
replaceJob := newDMLJob(replaceChange, ecWithSafeMode)

insertQueries, insertArgs := worker.genSQLs([]*job{insertJob})
replaceQueries, replaceArgs := worker.genSQLs([]*job{replaceJob})

require.Len(t, insertQueries, 1)
require.Len(t, replaceQueries, 1)

// Normal insert should execute directly
mock.ExpectBegin()
mock.ExpectExec(regexp.QuoteMeta(insertQueries[0])).
WithArgs(toDriverValues(insertArgs[0])...).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()

// Safe-mode REPLACE should trigger disable + REPLACE + restore
// disable FKC
mock.ExpectBegin()
mock.ExpectExec(regexp.QuoteMeta("SET SESSION foreign_key_checks=0")).
WillReturnResult(sqlmock.NewResult(0, 0))
mock.ExpectCommit()

// REPLACE job
mock.ExpectBegin()
mock.ExpectExec(regexp.QuoteMeta(replaceQueries[0])).
WithArgs(toDriverValues(replaceArgs[0])...).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()

// restore FKC
mock.ExpectBegin()
mock.ExpectExec(regexp.QuoteMeta("SET SESSION foreign_key_checks=1")).
WillReturnResult(sqlmock.NewResult(0, 0))
mock.ExpectCommit()

// Run two jobs separately: first insert, then REPLACE
worker.executeBatchJobs(0, []*job{insertJob}, false)
worker.executeBatchJobs(0, []*job{replaceJob}, true)

require.NoError(t, mock.ExpectationsWereMet())
}
>>>>>>> e709452702 (DM: Add foreign key causality for DM syncer. (#12414))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This test file has unresolved merge conflicts. It appears new tests for foreign key handling have been added, but the conflict markers must be removed for the tests to be valid.

Comment on lines +32 to +36
<<<<<<< HEAD
"relay_interrupt safe_mode sequence_safe_mode lightning_load_task lightning_mode metrics"
=======
"relay_interrupt safe_mode safe_mode_foreign_key foreign_key_multi_worker foreign_key_route_single_worker foreign_key_schema_repair sequence_safe_mode lightning_load_task lightning_mode metrics"
>>>>>>> e709452702 (DM: Add foreign key causality for DM syncer. (#12414))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

There is a merge conflict in this script. It seems new integration tests related to the foreign key feature have been added to the test group, but the changes from different branches need to be merged correctly.

@wuhuizuo
Copy link
Contributor

/test pull-dm-compatibility-test

@ti-chi-bot ti-chi-bot bot added cherry-pick-approved Cherry pick PR approved by release team. and removed do-not-merge/cherry-pick-not-approved labels Mar 16, 2026
Copy link
Contributor

@Benjamin2037 Benjamin2037 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@ti-chi-bot
Copy link
Contributor

ti-chi-bot bot commented Mar 16, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: Benjamin2037

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@ti-chi-bot ti-chi-bot bot added the approved label Mar 16, 2026
@codecov
Copy link

codecov bot commented Mar 16, 2026

Codecov Report

❌ Patch coverage is 63.85542% with 150 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (release-8.5@28fc694). Learn more about missing BASE report.

Additional details and impacted files
Components Coverage Δ
cdc 57.5813% <0.0000%> (?)
dm 48.9293% <0.0000%> (?)
engine 50.7388% <0.0000%> (?)
Flag Coverage Δ
cdc 57.5813% <71.4285%> (?)
unit 53.3975% <63.8554%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

@@               Coverage Diff                @@
##             release-8.5     #12552   +/-   ##
================================================
  Coverage               ?   53.3975%           
================================================
  Files                  ?       1026           
  Lines                  ?     137836           
  Branches               ?          0           
================================================
  Hits                   ?      73601           
  Misses                 ?      58731           
  Partials               ?       5504           
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@OliverS929
Copy link
Contributor

/retest

@OliverS929
Copy link
Contributor

/unhold

@ti-chi-bot ti-chi-bot bot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Mar 16, 2026
@EmmaDuDu
Copy link

/retest

@OliverS929
Copy link
Contributor

/retest

2 similar comments
@OliverS929
Copy link
Contributor

/retest

@OliverS929
Copy link
Contributor

/retest

@ti-chi-bot ti-chi-bot bot merged commit f0b61ee into pingcap:release-8.5 Mar 18, 2026
27 checks passed
@ti-chi-bot ti-chi-bot bot deleted the cherry-pick-12414-to-release-8.5 branch March 18, 2026 02:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved area/dm Issues or PRs related to DM. cherry-pick-approved Cherry pick PR approved by release team. lgtm release-note Denotes a PR that will be considered when it comes time to generate release notes. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. type/cherry-pick-for-release-8.5 This PR is cherry-picked to release-8.5 from a source PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants