Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

expression: add implicit eval int and real for function dayname (#21806) #21850

Merged
merged 5 commits into from Jan 27, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
45 changes: 33 additions & 12 deletions expression/builtin_cast.go
Expand Up @@ -1134,6 +1134,12 @@ func (b *builtinCastStringAsIntSig) evalInt(row chunk.Row) (res int64, isNull bo
if b.args[0].GetType().Hybrid() || IsBinaryLiteral(b.args[0]) {
return b.args[0].EvalInt(b.ctx, row)
}

// Take the implicit evalInt path if possible.
if CanImplicitEvalInt(b.args[0]) {
return b.args[0].EvalInt(b.ctx, row)
}

val, isNull, err := b.args[0].EvalString(b.ctx, row)
if isNull || err != nil {
return res, isNull, err
Expand Down Expand Up @@ -1182,6 +1188,12 @@ func (b *builtinCastStringAsRealSig) evalReal(row chunk.Row) (res float64, isNul
if IsBinaryLiteral(b.args[0]) {
return b.args[0].EvalReal(b.ctx, row)
}

// Take the implicit evalReal path if possible.
if CanImplicitEvalReal(b.args[0]) {
return b.args[0].EvalReal(b.ctx, row)
}

val, isNull, err := b.args[0].EvalString(b.ctx, row)
if isNull || err != nil {
return res, isNull, err
Expand Down Expand Up @@ -1746,18 +1758,31 @@ func (i inCastContext) String() string {
// @see BuildCastFunction4Union
const inUnionCastContext inCastContext = 0

// hasSpecialCast checks if this expr has its own special cast function.
// for example(#9713): when doing arithmetic using results of function DayName,
// "Monday" should be regarded as 0, "Tuesday" should be regarded as 1 and so on.
func hasSpecialCast(ctx sessionctx.Context, expr Expression, tp *types.FieldType) bool {
// CanImplicitEvalInt represents the builtin functions that have an implicit path to evaluate as integer,
// regardless of the type that type inference decides it to be.
// This is a nasty way to match the weird behavior of MySQL functions like `dayname()` being implicitly evaluated as integer.
// See https://github.com/mysql/mysql-server/blob/ee4455a33b10f1b1886044322e4893f587b319ed/sql/item_timefunc.h#L423 for details.
func CanImplicitEvalInt(expr Expression) bool {
switch f := expr.(type) {
case *ScalarFunction:
switch f.FuncName.L {
case ast.DayName:
switch tp.EvalType() {
case types.ETInt, types.ETReal:
return true
}
return true
}
}
return false
}

// CanImplicitEvalReal represents the builtin functions that have an implicit path to evaluate as real,
// regardless of the type that type inference decides it to be.
// This is a nasty way to match the weird behavior of MySQL functions like `dayname()` being implicitly evaluated as real.
// See https://github.com/mysql/mysql-server/blob/ee4455a33b10f1b1886044322e4893f587b319ed/sql/item_timefunc.h#L423 for details.
func CanImplicitEvalReal(expr Expression) bool {
switch f := expr.(type) {
case *ScalarFunction:
switch f.FuncName.L {
case ast.DayName:
return true
}
}
return false
Expand All @@ -1775,10 +1800,6 @@ func BuildCastFunction4Union(ctx sessionctx.Context, expr Expression, tp *types.

// BuildCastFunction builds a CAST ScalarFunction from the Expression.
func BuildCastFunction(ctx sessionctx.Context, expr Expression, tp *types.FieldType) (res Expression) {
if hasSpecialCast(ctx, expr, tp) {
return expr
}

var fc functionClass
switch tp.EvalType() {
case types.ETInt:
Expand Down
12 changes: 12 additions & 0 deletions expression/builtin_cast_vec.go
Expand Up @@ -847,6 +847,12 @@ func (b *builtinCastStringAsIntSig) vecEvalInt(input *chunk.Chunk, result *chunk
if b.args[0].GetType().Hybrid() || IsBinaryLiteral(b.args[0]) {
return b.args[0].VecEvalInt(b.ctx, input, result)
}

// Take the implicit evalInt path if possible.
if CanImplicitEvalInt(b.args[0]) {
return b.args[0].VecEvalInt(b.ctx, input, result)
}

result.ResizeInt64(n, false)
buf, err := b.bufAllocator.get(types.ETString, n)
if err != nil {
Expand Down Expand Up @@ -1552,6 +1558,12 @@ func (b *builtinCastStringAsRealSig) vecEvalReal(input *chunk.Chunk, result *chu
if IsBinaryLiteral(b.args[0]) {
return b.args[0].VecEvalReal(b.ctx, input, result)
}

// Take the implicit evalReal path if possible.
if CanImplicitEvalReal(b.args[0]) {
return b.args[0].VecEvalReal(b.ctx, input, result)
}

n := input.NumRows()
buf, err := b.bufAllocator.get(types.ETString, n)
if err != nil {
Expand Down
41 changes: 37 additions & 4 deletions expression/expression.go
Expand Up @@ -331,20 +331,29 @@ func VecEvalBool(ctx sessionctx.Context, exprList CNFExprs, input *chunk.Chunk,
isZero := allocZeroSlice(n)
defer deallocateZeroSlice(isZero)
for _, expr := range exprList {
eType := expr.GetType().EvalType()
tp := expr.GetType()
eType := tp.EvalType()
if expr.GetType().Hybrid() {
eType = types.ETInt
}
if CanImplicitEvalReal(expr) {
eType = types.ETReal
}
buf, err := globalColumnAllocator.get(eType, n)
if err != nil {
return nil, nil, err
}

if err := EvalExpr(ctx, expr, eType, input, buf); err != nil {
// Take the implicit evalReal path if possible.
if CanImplicitEvalReal(expr) {
if err := implicitEvalReal(ctx, expr, input, buf); err != nil {
return nil, nil, err
}
} else if err := EvalExpr(ctx, expr, eType, input, buf); err != nil {
return nil, nil, err
}

err = toBool(ctx.GetSessionVars().StmtCtx, eType, buf, sel, isZero)
err = toBool(ctx.GetSessionVars().StmtCtx, tp, eType, buf, sel, isZero)
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -384,7 +393,7 @@ func VecEvalBool(ctx sessionctx.Context, exprList CNFExprs, input *chunk.Chunk,
return selected, nulls, nil
}

func toBool(sc *stmtctx.StatementContext, eType types.EvalType, buf *chunk.Column, sel []int, isZero []int8) error {
func toBool(sc *stmtctx.StatementContext, tp *types.FieldType, eType types.EvalType, buf *chunk.Column, sel []int, isZero []int8) error {
switch eType {
case types.ETInt:
i64s := buf.Int64s()
Expand Down Expand Up @@ -473,6 +482,30 @@ func toBool(sc *stmtctx.StatementContext, eType types.EvalType, buf *chunk.Colum
return nil
}

func implicitEvalReal(ctx sessionctx.Context, expr Expression, input *chunk.Chunk, result *chunk.Column) (err error) {
if expr.Vectorized() && ctx.GetSessionVars().EnableVectorizedExpression {
err = expr.VecEvalReal(ctx, input, result)
} else {
ind, n := 0, input.NumRows()
iter := chunk.NewIterator4Chunk(input)
result.ResizeFloat64(n, false)
f64s := result.Float64s()
for it := iter.Begin(); it != iter.End(); it = iter.Next() {
value, isNull, err := expr.EvalReal(ctx, it)
if err != nil {
return err
}
if isNull {
result.SetNull(ind, isNull)
} else {
f64s[ind] = value
}
ind++
}
}
return
}

// EvalExpr evaluates this expr according to its type.
// And it selects the method for evaluating expression based on
// the environment variables and whether the expression can be vectorized.
Expand Down
15 changes: 15 additions & 0 deletions expression/integration_test.go
Expand Up @@ -1907,6 +1907,21 @@ func (s *testIntegrationSuite2) TestTimeBuiltin(c *C) {
"Warning|1292|Incorrect datetime value: '0000-00-00 00:00:00.000000'",
"Warning|1292|Incorrect datetime value: '0000-01-00 00:00:00.000000'",
"Warning|1292|Incorrect datetime value: '0000-01-00 00:00:00.000000'"))
// for dayname implicit cast to boolean and real
result = tk.MustQuery(`select 1 from dual where dayname('2016-03-07')`)
result.Check(testkit.Rows())
result = tk.MustQuery(`select 1 from dual where dayname('2016-03-07') is true`)
result.Check(testkit.Rows())
result = tk.MustQuery(`select 1 from dual where dayname('2016-03-07') is false`)
result.Check(testkit.Rows("1"))
result = tk.MustQuery(`select 1 from dual where dayname('2016-03-08')`)
result.Check(testkit.Rows("1"))
result = tk.MustQuery(`select 1 from dual where dayname('2016-03-08') is true`)
result.Check(testkit.Rows("1"))
result = tk.MustQuery(`select 1 from dual where dayname('2016-03-08') is false`)
result.Check(testkit.Rows())
result = tk.MustQuery(`select cast(dayname("2016-03-07") as double), cast(dayname("2016-03-08") as double)`)
result.Check(testkit.Rows("0 1"))

// for sec_to_time
result = tk.MustQuery("select sec_to_time(NULL)")
Expand Down