From 622ec0d9f0d7db643136599d687c3e30bb0a9ce9 Mon Sep 17 00:00:00 2001 From: HuaiyuXu <391585975@qq.com> Date: Tue, 28 Apr 2020 13:00:40 +0800 Subject: [PATCH] executor, types: copy enum/set instead of refer the underlying data (#16858) --- executor/aggfuncs/func_first_row.go | 5 +++-- executor/executor_test.go | 23 +++++++++++++++++++++++ types/enum.go | 9 +++++++++ types/set.go | 9 +++++++++ 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/executor/aggfuncs/func_first_row.go b/executor/aggfuncs/func_first_row.go index b37c41ec128a9..432fcd805bcc8 100644 --- a/executor/aggfuncs/func_first_row.go +++ b/executor/aggfuncs/func_first_row.go @@ -487,7 +487,7 @@ func (e *firstRow4Enum) UpdatePartialResult(sctx sessionctx.Context, rowsInGroup if err != nil { return err } - p.gotFirstRow, p.isNull, p.val = true, d.IsNull(), d.GetMysqlEnum() + p.gotFirstRow, p.isNull, p.val = true, d.IsNull(), d.GetMysqlEnum().Copy() break } return nil @@ -500,6 +500,7 @@ func (*firstRow4Enum) MergePartialResult(sctx sessionctx.Context, src PartialRes } return nil } + func (e *firstRow4Enum) AppendFinalResult2Chunk(sctx sessionctx.Context, pr PartialResult, chk *chunk.Chunk) error { p := (*partialResult4FirstRowEnum)(pr) if p.isNull || !p.gotFirstRow { @@ -533,7 +534,7 @@ func (e *firstRow4Set) UpdatePartialResult(sctx sessionctx.Context, rowsInGroup if err != nil { return err } - p.gotFirstRow, p.isNull, p.val = true, d.IsNull(), d.GetMysqlSet() + p.gotFirstRow, p.isNull, p.val = true, d.IsNull(), d.GetMysqlSet().Copy() break } return nil diff --git a/executor/executor_test.go b/executor/executor_test.go index 97652307d8387..df4a65c0ec35a 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -4449,3 +4449,26 @@ func (s *testSuite1) TestIssue16025(c *C) { tk.MustQuery("SELECT * FROM t0 WHERE c0;").Check(testkit.Rows()) } + +func (s *testSuite1) TestIssue16854(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` enum('WAITING','PRINTED','STOCKUP','CHECKED','OUTSTOCK','PICKEDUP','WILLBACK','BACKED') DEFAULT NULL)") + tk.MustExec("insert into t values(1),(2),(3),(4),(5),(6),(7);") + for i := 0; i < 7; i++ { + tk.MustExec("insert into t select * from t;") + } + tk.MustExec("set @@tidb_max_chunk_size=100;") + tk.MustQuery("select distinct a from t order by a").Check(testkit.Rows("WAITING", "PRINTED", "STOCKUP", "CHECKED", "OUTSTOCK", "PICKEDUP", "WILLBACK")) + tk.MustExec("drop table t") + + tk.MustExec("CREATE TABLE `t` ( `a` set('WAITING','PRINTED','STOCKUP','CHECKED','OUTSTOCK','PICKEDUP','WILLBACK','BACKED') DEFAULT NULL)") + tk.MustExec("insert into t values(1),(2),(3),(4),(5),(6),(7);") + for i := 0; i < 7; i++ { + tk.MustExec("insert into t select * from t;") + } + tk.MustExec("set @@tidb_max_chunk_size=100;") + tk.MustQuery("select distinct a from t order by a").Check(testkit.Rows("WAITING", "PRINTED", "WAITING,PRINTED", "STOCKUP", "WAITING,STOCKUP", "PRINTED,STOCKUP", "WAITING,PRINTED,STOCKUP")) + tk.MustExec("drop table t") +} diff --git a/types/enum.go b/types/enum.go index 45c15a3d2208f..1aafdc345bcf4 100644 --- a/types/enum.go +++ b/types/enum.go @@ -18,6 +18,7 @@ import ( "strings" "github.com/pingcap/errors" + "github.com/pingcap/tidb/util/stringutil" ) // Enum is for MySQL enum type. @@ -26,6 +27,14 @@ type Enum struct { Value uint64 } +// Copy deep copy an Enum. +func (e Enum) Copy() Enum { + return Enum{ + Name: stringutil.Copy(e.Name), + Value: e.Value, + } +} + // String implements fmt.Stringer interface. func (e Enum) String() string { return e.Name diff --git a/types/set.go b/types/set.go index 0a337780c452f..57145855c9c43 100644 --- a/types/set.go +++ b/types/set.go @@ -18,6 +18,7 @@ import ( "strings" "github.com/pingcap/errors" + "github.com/pingcap/tidb/util/stringutil" ) var zeroSet = Set{Name: "", Value: 0} @@ -38,6 +39,14 @@ func (e Set) ToNumber() float64 { return float64(e.Value) } +// Copy deep copy a Set. +func (e Set) Copy() Set { + return Set{ + Name: stringutil.Copy(e.Name), + Value: e.Value, + } +} + // ParseSetName creates a Set with name. func ParseSetName(elems []string, name string) (Set, error) { if len(name) == 0 {