diff --git a/executor/delete.go b/executor/delete.go index c859b41a83..c327236ee0 100644 --- a/executor/delete.go +++ b/executor/delete.go @@ -159,6 +159,9 @@ func (e *DeleteExec) doBatchDelete(ctx context.Context) error { func (e *DeleteExec) composeTblRowMap(tblRowMap tableRowMapType, colPosInfos []plannercore.TblColPosInfo, joinedRow []types.Datum) error { // iterate all the joined tables, and got the copresonding rows in joinedRow. for _, info := range colPosInfos { + if unmatchedOuterRow(info, joinedRow) { + continue + } if tblRowMap[info.TblID] == nil { tblRowMap[info.TblID] = kv.NewHandleMap() } diff --git a/executor/executor_test.go b/executor/executor_test.go index 2c61049d0e..f3d46e60d0 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -8521,3 +8521,33 @@ func (s *testSuite1) TestBitColumnIn(c *C) { tk.MustExec("insert into t values (65)") tk.MustQuery("select * from t where id not in (-1,2)").Check(testkit.Rows("\x00A")) } + +func (s *testSuite) TestDeleteWithMulTbl(c *C) { + tk := testkit.NewTestKit(c, s.store) + + // Delete multiple tables from left joined table. + // The result of left join is (3, null, null). + // Because rows in t2 are not matched, so no row will be deleted in t2. + // But row in t1 is matched, so it should be deleted. + tk.MustExec("use test;") + tk.MustExec("drop table if exists t1, t2;") + tk.MustExec("create table t1 (c1 int);") + tk.MustExec("create table t2 (c1 int primary key, c2 int);") + tk.MustExec("insert into t1 values(3);") + tk.MustExec("insert into t2 values(2, 2);") + tk.MustExec("insert into t2 values(0, 0);") + tk.MustExec("delete from t1, t2 using t1 left join t2 on t1.c1 = t2.c2;") + tk.MustQuery("select * from t1 order by c1;").Check(testkit.Rows()) + tk.MustQuery("select * from t2 order by c1;").Check(testkit.Rows("0 0", "2 2")) + + // Rows in both t1 and t2 are matched, so will be deleted even if it's null. + // NOTE: The null values are not generated by join. + tk.MustExec("drop table if exists t1, t2;") + tk.MustExec("create table t1 (c1 int);") + tk.MustExec("create table t2 (c2 int);") + tk.MustExec("insert into t1 values(null);") + tk.MustExec("insert into t2 values(null);") + tk.MustExec("delete from t1, t2 using t1 join t2 where t1.c1 is null;") + tk.MustQuery("select * from t1;").Check(testkit.Rows()) + tk.MustQuery("select * from t2;").Check(testkit.Rows()) +} diff --git a/executor/update.go b/executor/update.go index 7173897757..ed5c76f44c 100644 --- a/executor/update.go +++ b/executor/update.go @@ -96,7 +96,7 @@ func (e *UpdateExec) prepare(row []types.Datum) (err error) { break } } - if e.unmatchedOuterRow(content, row) { + if unmatchedOuterRow(content, row) { updatable = false } e.tableUpdatable = append(e.tableUpdatable, updatable) @@ -211,7 +211,7 @@ func (e *UpdateExec) exec(ctx context.Context, schema *expression.Schema, row, n // the inner handle field is filled with a NULL value. // // This fixes: https://github.com/pingcap/tidb/issues/7176. -func (e *UpdateExec) unmatchedOuterRow(tblPos plannercore.TblColPosInfo, waitUpdateRow []types.Datum) bool { +func unmatchedOuterRow(tblPos plannercore.TblColPosInfo, waitUpdateRow []types.Datum) bool { firstHandleIdx := tblPos.HandleCols.GetCol(0) return waitUpdateRow[firstHandleIdx.Index].IsNull() }