From 71d2a040a200668ac5ef25a15ec90f204275f960 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Mon, 28 Nov 2022 16:15:14 +0800 Subject: [PATCH 1/2] executor: fix issue of foreign key cascade don't work cause by untouch index key Signed-off-by: crazycs520 --- executor/fktest/foreign_key_test.go | 20 ++++++++++++++++++++ table/tables/tables.go | 4 +++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/executor/fktest/foreign_key_test.go b/executor/fktest/foreign_key_test.go index 17a6f7226e545..24c33c81e70b8 100644 --- a/executor/fktest/foreign_key_test.go +++ b/executor/fktest/foreign_key_test.go @@ -2081,3 +2081,23 @@ func TestForeignKeyOnInsertOnDuplicateUpdate(t *testing.T) { tk.MustQuery("select * from t2").Check(testkit.Rows("1")) tk.MustQuery("select * from t3").Check(testkit.Rows("1")) } + +func TestForeignKeyIssue39419(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + tk.MustExec("create table t1 (id int key);") + tk.MustExec("create table t2 (id int key, a int, b int, " + + "foreign key fk_1 (a) references t1(id) ON DELETE SET NULL ON UPDATE SET NULL, " + + "foreign key fk_2 (b) references t1(id) ON DELETE CASCADE ON UPDATE CASCADE);") + tk.MustExec("insert into t1 values (1), (2), (3);") + tk.MustExec("insert into t2 values (1, 1, 1), (2, 2, 2), (3, 3, 3);") + tk.MustExec("update t1 set id=id+10 where id in (1, 3);") + tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("2", "11", "13")) + tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("1 11", "2 2 2", "3 13")) + tk.MustExec("delete from t1 where id = 2;") + tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("11", "13")) + tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("1 11", "3 13")) +} diff --git a/table/tables/tables.go b/table/tables/tables.go index 631b26ef4296e..84fae3c35f5f5 100644 --- a/table/tables/tables.go +++ b/table/tables/tables.go @@ -526,7 +526,9 @@ func (t *TableCommon) rebuildIndices(ctx sessionctx.Context, txn kv.Transaction, break } // If txn is auto commit and index is untouched, no need to write index value. - if untouched && !ctx.GetSessionVars().InTxn() { + // If InHandleForeignKeyTrigger is true indicate handling foreign key cascade, then we still need to write + // index value, otherwise, the later foreign cascade executor may see data-index inconsistency in txn-mem-buffer. + if untouched && !ctx.GetSessionVars().InTxn() && !ctx.GetSessionVars().StmtCtx.InHandleForeignKeyTrigger { continue } newVs, err := idx.FetchValues(newData, nil) From ffd143aeff7e7c8efbdf7eca48399f0ec1efcfe8 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Mon, 28 Nov 2022 16:45:16 +0800 Subject: [PATCH 2/2] add test case and fix bug Signed-off-by: crazycs520 --- executor/fktest/foreign_key_test.go | 6 ++++++ table/tables/tables.go | 9 ++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/executor/fktest/foreign_key_test.go b/executor/fktest/foreign_key_test.go index 24c33c81e70b8..dbe02ccd52772 100644 --- a/executor/fktest/foreign_key_test.go +++ b/executor/fktest/foreign_key_test.go @@ -2100,4 +2100,10 @@ func TestForeignKeyIssue39419(t *testing.T) { tk.MustExec("delete from t1 where id = 2;") tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("11", "13")) tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("1 11", "3 13")) + + tk.MustExec("drop table t1,t2") + tk.MustExec("create table t1 (id int, b int, index(id), foreign key fk_2 (b) references t1(id) ON UPDATE CASCADE);") + tk.MustExec("insert into t1 values (1, 1), (2, 2), (3, 3);") + tk.MustExec("update t1 set id=id+10 where id > 1") + tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("1 1", "12 12", "13 13")) } diff --git a/table/tables/tables.go b/table/tables/tables.go index 84fae3c35f5f5..825f05d7ffa2e 100644 --- a/table/tables/tables.go +++ b/table/tables/tables.go @@ -526,9 +526,12 @@ func (t *TableCommon) rebuildIndices(ctx sessionctx.Context, txn kv.Transaction, break } // If txn is auto commit and index is untouched, no need to write index value. - // If InHandleForeignKeyTrigger is true indicate handling foreign key cascade, then we still need to write - // index value, otherwise, the later foreign cascade executor may see data-index inconsistency in txn-mem-buffer. - if untouched && !ctx.GetSessionVars().InTxn() && !ctx.GetSessionVars().StmtCtx.InHandleForeignKeyTrigger { + // If InHandleForeignKeyTrigger or ForeignKeyTriggerCtx.HasFKCascades is true indicate we may have + // foreign key cascade need to handle later, then we still need to write index value, + // otherwise, the later foreign cascade executor may see data-index inconsistency in txn-mem-buffer. + sessVars := ctx.GetSessionVars() + if untouched && !sessVars.InTxn() && + !sessVars.StmtCtx.InHandleForeignKeyTrigger && !sessVars.StmtCtx.ForeignKeyTriggerCtx.HasFKCascades { continue } newVs, err := idx.FetchValues(newData, nil)