Skip to content

Commit

Permalink
types/datum.go: casting zero to enum should not change value (#29759)
Browse files Browse the repository at this point in the history
  • Loading branch information
tangenta committed Nov 22, 2021
1 parent 02a8f04 commit d711f18
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 9 deletions.
10 changes: 10 additions & 0 deletions executor/executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3314,6 +3314,16 @@ func (s *testSuite) TestEmptyEnum(c *C) {
tk.MustQuery("select * from t").Check(testkit.Rows("", ""))
tk.MustExec("insert into t values (null)")
tk.MustQuery("select * from t").Check(testkit.Rows("", "", "<nil>"))

// Test https://github.com/pingcap/tidb/issues/29525.
tk.MustExec("drop table if exists t;")
tk.MustExec("create table t (id int auto_increment primary key, c1 enum('a', '', 'c'));")
tk.MustExec("insert into t(c1) values (0);")
tk.MustQuery("select id, c1+0, c1 from t;").Check(testkit.Rows("1 0 "))
tk.MustExec("alter table t change c1 c1 enum('a', '') not null;")
tk.MustQuery("select id, c1+0, c1 from t;").Check(testkit.Rows("1 0 "))
tk.MustExec("insert into t(c1) values (0);")
tk.MustQuery("select id, c1+0, c1 from t;").Check(testkit.Rows("1 0 ", "2 0 "))
}

// TestIssue4024 This tests https://github.com/pingcap/tidb/issues/4024
Expand Down
5 changes: 5 additions & 0 deletions expression/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9536,6 +9536,11 @@ func (s *testIntegrationSuite) TestEnumPushDown(c *C) {
tk.MustExec("insert into tdm values (1, 'a');")
tk.MustExec("update tdm set c12 = 2 where id = 1;")
tk.MustQuery("select * from tdm").Check(testkit.Rows("1 b"))
tk.MustExec("set @@sql_mode = '';")
tk.MustExec("update tdm set c12 = 0 where id = 1;")
tk.MustQuery("select c12+0 from tdm").Check(testkit.Rows("0"))
tk.MustExec("update tdm set c12 = '0' where id = 1;")
tk.MustQuery("select c12+0 from tdm").Check(testkit.Rows("0"))
}

func (s *testIntegrationSuite) TestJiraSetInnoDBDefaultRowFormat(c *C) {
Expand Down
8 changes: 8 additions & 0 deletions expression/scalar_function.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,14 @@ func (sf *ScalarFunction) Eval(row chunk.Row) (d types.Datum, err error) {
str, isNull, err = sf.EvalString(sf.GetCtx(), row)
if !isNull && err == nil && tp.Tp == mysql.TypeEnum {
res, err = types.ParseEnum(tp.Elems, str, tp.Collate)
if ctx := sf.GetCtx(); ctx != nil {
if sc := ctx.GetSessionVars().StmtCtx; sc != nil {
if sc.TruncateAsWarning {
ctx.GetSessionVars().StmtCtx.AppendWarning(err)
err = nil
}
}
}
} else {
res = str
}
Expand Down
13 changes: 9 additions & 4 deletions types/datum.go
Original file line number Diff line number Diff line change
Expand Up @@ -1499,19 +1499,24 @@ func (d *Datum) convertToMysqlEnum(sc *stmtctx.StatementContext, target *FieldTy
case KindString, KindBytes, KindBinaryLiteral:
e, err = ParseEnum(target.Elems, d.GetString(), target.Collate)
case KindMysqlEnum:
e, err = ParseEnum(target.Elems, d.GetMysqlEnum().Name, target.Collate)
if d.i == 0 {
// MySQL enum zero value has an empty string name(Enum{Name: '', Value: 0}). It is
// different from the normal enum string value(Enum{Name: '', Value: n}, n > 0).
e = Enum{}
} else {
e, err = ParseEnum(target.Elems, d.GetMysqlEnum().Name, target.Collate)
}
case KindMysqlSet:
e, err = ParseEnum(target.Elems, d.GetMysqlSet().Name, target.Collate)
default:
var uintDatum Datum
uintDatum, err = d.convertToUint(sc, target)
if err == nil {
e, err = ParseEnumValue(target.Elems, uintDatum.GetUint64())
} else {
err = errors.Wrap(ErrTruncated, "convert to MySQL enum failed: "+err.Error())
}
}
if err != nil {
err = errors.Wrap(ErrTruncated, "convert to MySQL enum failed: "+err.Error())
}
ret.SetMysqlEnum(e, target.Collate)
return ret, err
}
Expand Down
12 changes: 7 additions & 5 deletions types/enum.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package types

import (
"fmt"
"strconv"

"github.com/pingcap/errors"
Expand Down Expand Up @@ -55,8 +56,8 @@ func ParseEnum(elems []string, name string, collation string) (Enum, error) {
if num, err := strconv.ParseUint(name, 0, 64); err == nil {
return ParseEnumValue(elems, num)
}

return Enum{}, errors.Errorf("item %s is not in enum %v", name, elems)
errMsg := fmt.Sprintf("convert to MySQL enum failed: item %s is not in enum %v", name, elems)
return Enum{}, errors.Wrap(ErrTruncated, errMsg)
}

// ParseEnumName creates a Enum with item name.
Expand All @@ -67,14 +68,15 @@ func ParseEnumName(elems []string, name string, collation string) (Enum, error)
return Enum{Name: n, Value: uint64(i) + 1}, nil
}
}

return Enum{}, errors.Errorf("item %s is not in enum %v", name, elems)
errMsg := fmt.Sprintf("convert to MySQL enum failed: item %s is not in enum %v", name, elems)
return Enum{}, errors.Wrap(ErrTruncated, errMsg)
}

// ParseEnumValue creates a Enum with special number.
func ParseEnumValue(elems []string, number uint64) (Enum, error) {
if number == 0 || number > uint64(len(elems)) {
return Enum{}, errors.Errorf("number %d overflow enum boundary [1, %d]", number, len(elems))
errMsg := fmt.Sprintf("convert to MySQL enum failed: number %d overflow enum boundary [1, %d]", number, len(elems))
return Enum{}, errors.Wrap(ErrTruncated, errMsg)
}

return Enum{Name: elems[number-1], Value: number}, nil
Expand Down

0 comments on commit d711f18

Please sign in to comment.