Skip to content

Commit

Permalink
planner: disallow multi-updates on primary key (#20603) (#21113)
Browse files Browse the repository at this point in the history
Signed-off-by: ti-srebot <ti-srebot@pingcap.com>
  • Loading branch information
ti-srebot authored Nov 26, 2020
1 parent 9cc0ea7 commit 295e702
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 8 deletions.
5 changes: 5 additions & 0 deletions errors.toml
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,11 @@ error = '''
`%-.192s`.`%-.192s` contains view recursion
'''

["planner:1706"]
error = '''
Primary key/partition key update is not allowed since the table is updated both as '%-.192s' and '%-.192s'.
'''

["planner:1747"]
error = '''
PARTITION () clause on non partitioned table
Expand Down
8 changes: 4 additions & 4 deletions executor/executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1881,16 +1881,16 @@ func (s *testSuiteP1) TestGeneratedColumnRead(c *C) {
result.Check(testkit.Rows(`0 <nil> <nil> <nil>`, `1 3 2 6`, `3 7 12 14`, `8 16 64 32`))

// Test multi-update on generated columns.
tk.MustExec(`UPDATE test_gc_read m, test_gc_read n SET m.a = m.a + 10, n.a = n.a + 10`)
tk.MustExec(`UPDATE test_gc_read m, test_gc_read n SET m.b = m.b + 10, n.b = n.b + 10`)
result = tk.MustQuery(`SELECT * FROM test_gc_read ORDER BY a`)
result.Check(testkit.Rows(`10 <nil> <nil> <nil> <nil>`, `11 2 13 22 26`, `13 4 17 52 34`, `18 8 26 144 52`))
result.Check(testkit.Rows(`0 <nil> <nil> <nil> <nil>`, `1 12 13 12 26`, `3 14 17 42 34`, `8 18 26 144 52`))

tk.MustExec("drop table if exists t")
tk.MustExec("create table t(a int)")
tk.MustExec("insert into t values(18)")
tk.MustExec("insert into t values(8)")
tk.MustExec("update test_gc_read set a = a+1 where a in (select a from t)")
result = tk.MustQuery("select * from test_gc_read order by a")
result.Check(testkit.Rows(`10 <nil> <nil> <nil> <nil>`, `11 2 13 22 26`, `13 4 17 52 34`, `19 8 27 152 54`))
result.Check(testkit.Rows(`0 <nil> <nil> <nil> <nil>`, `1 12 13 12 26`, `3 14 17 42 34`, `9 18 27 162 54`))

// Test different types between generation expression and generated column.
tk.MustExec(`CREATE TABLE test_gc_read_cast(a VARCHAR(255), b VARCHAR(255), c INT AS (JSON_EXTRACT(a, b)), d INT AS (JSON_EXTRACT(a, b)) STORED)`)
Expand Down
1 change: 1 addition & 0 deletions planner/core/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ var (
ErrWrongGroupField = dbterror.ClassOptimizer.NewStd(mysql.ErrWrongGroupField)
ErrDupFieldName = dbterror.ClassOptimizer.NewStd(mysql.ErrDupFieldName)
ErrNonUpdatableTable = dbterror.ClassOptimizer.NewStd(mysql.ErrNonUpdatableTable)
ErrMultiUpdateKeyConflict = dbterror.ClassOptimizer.NewStd(mysql.ErrMultiUpdateKeyConflict)
ErrInternal = dbterror.ClassOptimizer.NewStd(mysql.ErrInternal)
ErrNonUniqTable = dbterror.ClassOptimizer.NewStd(mysql.ErrNonuniqTable)
ErrWindowInvalidWindowFuncUse = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowInvalidWindowFuncUse)
Expand Down
35 changes: 35 additions & 0 deletions planner/core/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1523,3 +1523,38 @@ func (s *testIntegrationSuite) TestIssue10448(c *C) {
tk.MustExec("insert into t values(1),(2),(3)")
tk.MustQuery("select a from (select pk as a from t) t1 where a = 18446744073709551615").Check(testkit.Rows())
}

func (s *testIntegrationSuite) TestUpdateMultiUpdatePK(c *C) {
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("use test")

tk.MustExec("drop table if exists t")
tk.MustExec("create table t (a int not null primary key)")
tk.MustExec("insert into t values (1)")
tk.MustGetErrMsg(`UPDATE t m, t n SET m.a = m.a + 10, n.a = n.a + 10`,
`[planner:1706]Primary key/partition key update is not allowed since the table is updated both as 'm' and 'n'.`)

tk.MustExec("drop table if exists t")
tk.MustExec("create table t (a varchar(10) not null primary key)")
tk.MustExec("insert into t values ('abc')")
tk.MustGetErrMsg(`UPDATE t m, t n SET m.a = 'def', n.a = 'xyz'`,
`[planner:1706]Primary key/partition key update is not allowed since the table is updated both as 'm' and 'n'.`)

tk.MustExec("drop table if exists t")
tk.MustExec("create table t (a int, b int, primary key (a, b))")
tk.MustExec("insert into t values (1, 2)")
tk.MustGetErrMsg(`UPDATE t m, t n SET m.a = m.a + 10, n.b = n.b + 10`,
`[planner:1706]Primary key/partition key update is not allowed since the table is updated both as 'm' and 'n'.`)

tk.MustExec("drop table if exists t")
tk.MustExec("create table t (a int primary key, b int)")
tk.MustExec("insert into t values (1, 2)")
tk.MustGetErrMsg(`UPDATE t m, t n SET m.a = m.a + 10, n.a = n.a + 10`,
`[planner:1706]Primary key/partition key update is not allowed since the table is updated both as 'm' and 'n'.`)

tk.MustExec(`UPDATE t m, t n SET m.b = m.b + 10, n.b = n.b + 10`)
tk.MustQuery("SELECT * FROM t").Check(testkit.Rows("1 12"))

tk.MustExec(`UPDATE t m, t n SET m.a = m.a + 1, n.b = n.b + 10`)
tk.MustQuery("SELECT * FROM t").Check(testkit.Rows("2 12"))
}
16 changes: 16 additions & 0 deletions planner/core/logical_plan_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -3641,13 +3641,29 @@ func checkUpdateList(ctx sessionctx.Context, tblID2table map[int64]table.Table,
if err != nil {
return err
}
isPKUpdated := make(map[int64]model.CIStr)
for _, content := range updt.TblColPosInfos {
tbl := tblID2table[content.TblID]
flags := assignFlags[content.Start:content.End]
var updatePK bool
for i, col := range tbl.WritableCols() {
if flags[i] && col.State != model.StatePublic {
return ErrUnknownColumn.GenWithStackByArgs(col.Name, clauseMsg[fieldList])
}
// Check for multi-updates on primary key,
// see https://dev.mysql.com/doc/mysql-errors/5.7/en/server-error-reference.html#error_er_multi_update_key_conflict
if !flags[i] {
continue
}
if mysql.HasPriKeyFlag(col.Flag) {
updatePK = true
}
}
if updatePK {
if otherTblName, ok := isPKUpdated[tbl.Meta().ID]; ok {
return ErrMultiUpdateKeyConflict.GenWithStackByArgs(otherTblName.O, updt.names[content.Start].TblName.O)
}
isPKUpdated[tbl.Meta().ID] = updt.names[content.Start].TblName
}
}
return nil
Expand Down
4 changes: 2 additions & 2 deletions planner/core/testdata/plan_suite_in.json
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,8 @@
// Test join hint for delete and update
"delete /*+ TIDB_INLJ(t1, t2) */ t1 from t t1, t t2 where t1.c=t2.c",
"delete /*+ TIDB_SMJ(t1, t2) */ from t1 using t t1, t t2 where t1.c=t2.c",
"update /*+ TIDB_SMJ(t1, t2) */ t t1, t t2 set t1.a=1, t2.a=1 where t1.a=t2.a",
"update /*+ TIDB_HJ(t1, t2) */ t t1, t t2 set t1.a=1, t2.a=1 where t1.a=t2.a",
"update /*+ TIDB_SMJ(t1, t2) */ t t1, t t2 set t1.c=1, t2.a=1 where t1.a=t2.a",
"update /*+ TIDB_HJ(t1, t2) */ t t1, t t2 set t1.c=1, t2.a=1 where t1.a=t2.a",
// Test complex delete.
"delete from t where b < 1 order by d limit 1",
// Test simple delete.
Expand Down
4 changes: 2 additions & 2 deletions planner/core/testdata/plan_suite_out.json
Original file line number Diff line number Diff line change
Expand Up @@ -677,12 +677,12 @@
"Hints": "use_index(@`del_1` `test`.`t1` `c_d_e`), use_index(@`del_1` `test`.`t2` `c_d_e`), merge_join(@`del_1` `test`.`t1`)"
},
{
"SQL": "update /*+ TIDB_SMJ(t1, t2) */ t t1, t t2 set t1.a=1, t2.a=1 where t1.a=t2.a",
"SQL": "update /*+ TIDB_SMJ(t1, t2) */ t t1, t t2 set t1.c=1, t2.a=1 where t1.a=t2.a",
"Best": "MergeInnerJoin{TableReader(Table(t))->TableReader(Table(t))}(test.t.a,test.t.a)->Update",
"Hints": "use_index(@`upd_1` `test`.`t1` ), use_index(@`upd_1` `test`.`t2` ), merge_join(@`upd_1` `test`.`t1`)"
},
{
"SQL": "update /*+ TIDB_HJ(t1, t2) */ t t1, t t2 set t1.a=1, t2.a=1 where t1.a=t2.a",
"SQL": "update /*+ TIDB_HJ(t1, t2) */ t t1, t t2 set t1.c=1, t2.a=1 where t1.a=t2.a",
"Best": "LeftHashJoin{TableReader(Table(t))->TableReader(Table(t))}(test.t.a,test.t.a)->Update",
"Hints": "use_index(@`upd_1` `test`.`t1` ), use_index(@`upd_1` `test`.`t2` ), hash_join(@`upd_1` `test`.`t1`)"
},
Expand Down

0 comments on commit 295e702

Please sign in to comment.