Skip to content

Commit ad75261

Browse files
qw4990zz-jason
authored andcommitted
expression: fix issue that function dayname is incompatible with Mysql when doing arithmetic (#9975)
1 parent 488200e commit ad75261

File tree

4 files changed

+100
-6
lines changed

4 files changed

+100
-6
lines changed

expression/builtin_cast.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1687,6 +1687,23 @@ func (i inCastContext) String() string {
16871687
// @see BuildCastFunction4Union
16881688
const inUnionCastContext inCastContext = 0
16891689

1690+
// hasSpecialCast checks if this expr has its own special cast function.
1691+
// for example(#9713): when doing arithmetic using results of function DayName,
1692+
// "Monday" should be regarded as 0, "Tuesday" should be regarded as 1 and so on.
1693+
func hasSpecialCast(ctx sessionctx.Context, expr Expression, tp *types.FieldType) bool {
1694+
switch f := expr.(type) {
1695+
case *ScalarFunction:
1696+
switch f.FuncName.L {
1697+
case ast.DayName:
1698+
switch tp.EvalType() {
1699+
case types.ETInt, types.ETReal:
1700+
return true
1701+
}
1702+
}
1703+
}
1704+
return false
1705+
}
1706+
16901707
// BuildCastFunction4Union build a implicitly CAST ScalarFunction from the Union
16911708
// Expression.
16921709
func BuildCastFunction4Union(ctx sessionctx.Context, expr Expression, tp *types.FieldType) (res Expression) {
@@ -1699,6 +1716,10 @@ func BuildCastFunction4Union(ctx sessionctx.Context, expr Expression, tp *types.
16991716

17001717
// BuildCastFunction builds a CAST ScalarFunction from the Expression.
17011718
func BuildCastFunction(ctx sessionctx.Context, expr Expression, tp *types.FieldType) (res Expression) {
1719+
if hasSpecialCast(ctx, expr, tp) {
1720+
return expr
1721+
}
1722+
17021723
var fc functionClass
17031724
switch tp.EvalType() {
17041725
case types.ETInt:

expression/builtin_time.go

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,21 +1079,45 @@ func (b *builtinDayNameSig) Clone() builtinFunc {
10791079
return newSig
10801080
}
10811081

1082-
// evalString evals a builtinDayNameSig.
1083-
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_dayname
1084-
func (b *builtinDayNameSig) evalString(row chunk.Row) (string, bool, error) {
1082+
func (b *builtinDayNameSig) evalIndex(row chunk.Row) (int64, bool, error) {
10851083
arg, isNull, err := b.args[0].EvalTime(b.ctx, row)
10861084
if isNull || err != nil {
1087-
return "", isNull, err
1085+
return 0, isNull, err
10881086
}
10891087
if arg.InvalidZero() {
1090-
return "", true, handleInvalidTimeError(b.ctx, types.ErrIncorrectDatetimeValue.GenWithStackByArgs(arg.String()))
1088+
return 0, true, handleInvalidTimeError(b.ctx, types.ErrIncorrectDatetimeValue.GenWithStackByArgs(arg.String()))
10911089
}
10921090
// Monday is 0, ... Sunday = 6 in MySQL
10931091
// but in go, Sunday is 0, ... Saturday is 6
10941092
// w will do a conversion.
10951093
res := (int64(arg.Time.Weekday()) + 6) % 7
1096-
return types.WeekdayNames[res], false, nil
1094+
return res, false, nil
1095+
}
1096+
1097+
// evalString evals a builtinDayNameSig.
1098+
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_dayname
1099+
func (b *builtinDayNameSig) evalString(row chunk.Row) (string, bool, error) {
1100+
idx, isNull, err := b.evalIndex(row)
1101+
if isNull || err != nil {
1102+
return "", isNull, err
1103+
}
1104+
return types.WeekdayNames[idx], false, nil
1105+
}
1106+
1107+
func (b *builtinDayNameSig) evalReal(row chunk.Row) (float64, bool, error) {
1108+
idx, isNull, err := b.evalIndex(row)
1109+
if isNull || err != nil {
1110+
return 0, isNull, err
1111+
}
1112+
return float64(idx), false, nil
1113+
}
1114+
1115+
func (b *builtinDayNameSig) evalInt(row chunk.Row) (int64, bool, error) {
1116+
idx, isNull, err := b.evalIndex(row)
1117+
if isNull || err != nil {
1118+
return 0, isNull, err
1119+
}
1120+
return idx, false, nil
10971121
}
10981122

10991123
type dayOfMonthFunctionClass struct {

expression/function_traits.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ var unFoldableFunctions = map[string]struct{}{
4242
ast.GetVar: {},
4343
ast.GetParam: {},
4444
ast.Benchmark: {},
45+
ast.DayName: {},
4546
}
4647

4748
// DisableFoldFunctions stores functions which prevent child scope functions from being constant folded.

expression/integration_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4134,3 +4134,51 @@ func (s *testIntegrationSuite) TestDecimalConvertToTime(c *C) {
41344134
tk.MustExec("insert t values (20010101100000.123456, 20110707101112.123456)")
41354135
tk.MustQuery("select * from t").Check(testkit.Rows("2001-01-01 10:00:00.123456 2011-07-07 10:11:12"))
41364136
}
4137+
4138+
func (s *testIntegrationSuite) TestDaynameArithmetic(c *C) {
4139+
tk := testkit.NewTestKit(c, s.store)
4140+
defer s.cleanEnv(c)
4141+
4142+
cases := []struct {
4143+
sql string
4144+
result string
4145+
}{
4146+
{`select dayname("1962-03-01")+0;`, "3"},
4147+
{`select dayname("1962-03-02")+0;`, "4"},
4148+
{`select dayname("1962-03-03")+0;`, "5"},
4149+
{`select dayname("1962-03-04")+0;`, "6"},
4150+
{`select dayname("1962-03-05")+0;`, "0"},
4151+
{`select dayname("1962-03-06")+0;`, "1"},
4152+
{`select dayname("1962-03-07")+0;`, "2"},
4153+
{`select dayname("1962-03-08")+0;`, "3"},
4154+
{`select dayname("1962-03-01")+1;`, "4"},
4155+
{`select dayname("1962-03-01")+2;`, "5"},
4156+
{`select dayname("1962-03-01")+3;`, "6"},
4157+
{`select dayname("1962-03-01")+4;`, "7"},
4158+
{`select dayname("1962-03-01")+5;`, "8"},
4159+
{`select dayname("1962-03-01")+6;`, "9"},
4160+
{`select dayname("1962-03-01")+7;`, "10"},
4161+
{`select dayname("1962-03-01")+2333;`, "2336"},
4162+
{`select dayname("1962-03-01")+2.333;`, "5.333"},
4163+
{`select dayname("1962-03-01")>2;`, "1"},
4164+
{`select dayname("1962-03-01")<2;`, "0"},
4165+
{`select dayname("1962-03-01")=3;`, "1"},
4166+
{`select dayname("1962-03-01")!=3;`, "0"},
4167+
{`select dayname("1962-03-01")<4;`, "1"},
4168+
{`select dayname("1962-03-01")>4;`, "0"},
4169+
{`select !dayname("1962-03-01");`, "0"},
4170+
{`select dayname("1962-03-01")&1;`, "1"},
4171+
{`select dayname("1962-03-01")&3;`, "3"},
4172+
{`select dayname("1962-03-01")&7;`, "3"},
4173+
{`select dayname("1962-03-01")|1;`, "3"},
4174+
{`select dayname("1962-03-01")|3;`, "3"},
4175+
{`select dayname("1962-03-01")|7;`, "7"},
4176+
{`select dayname("1962-03-01")^1;`, "2"},
4177+
{`select dayname("1962-03-01")^3;`, "0"},
4178+
{`select dayname("1962-03-01")^7;`, "4"},
4179+
}
4180+
4181+
for _, c := range cases {
4182+
tk.MustQuery(c.sql).Check(testkit.Rows(c.result))
4183+
}
4184+
}

0 commit comments

Comments
 (0)