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: refine date_add/sub return type and precision (#35009) #35448

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,154 changes: 3,154 additions & 0 deletions DEPS.bzl

Large diffs are not rendered by default.

221 changes: 213 additions & 8 deletions expression/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package expression
import (
"flag"
"fmt"
"math"
"math/rand"
"net"
"reflect"
Expand Down Expand Up @@ -732,6 +733,20 @@ func (g *randHexStrGener) gen() interface{} {
return string(buf)
}

// dateGener is used to generate a date
type dateGener struct {
randGen *defaultRandGen
}

func (g dateGener) gen() interface{} {
year := 1970 + g.randGen.Intn(100)
month := g.randGen.Intn(10) + 1
day := g.randGen.Intn(20) + 1
gt := types.FromDate(year, month, day, 0, 0, 0, 0)
d := types.NewTime(gt, mysql.TypeDate, types.DefaultFsp)
return d
}

// dateTimeGener is used to generate a dataTime
type dateTimeGener struct {
Fsp int
Expand Down Expand Up @@ -761,7 +776,9 @@ func (g *dateTimeGener) gen() interface{} {
return t
}

// dateTimeStrGener is used to generate strings which are dataTime format
// dateTimeStrGener is used to generate strings which are dateTime format.
// Fsp must be -1 to 9 otherwise will be ignored. -1 will generate a 0 to 9 random length fsp part, otherwise the fsp part will be of fixed length.
// Fsp more than 6 is to test robustness of fsp part parsing.
type dateTimeStrGener struct {
Fsp int
Year int
Expand All @@ -780,14 +797,17 @@ func (g *dateTimeStrGener) gen() interface{} {
if g.Day == 0 {
g.Day = g.randGen.Intn(20) + 1
}
if g.Fsp == -1 {
g.Fsp = g.randGen.Intn(10)
}
hour := g.randGen.Intn(12)
minute := g.randGen.Intn(60)
second := g.randGen.Intn(60)
dataTimeStr := fmt.Sprintf("%d-%d-%d %d:%d:%d",
g.Year, g.Month, g.Day, hour, minute, second)
if g.Fsp > 0 && g.Fsp <= 6 {
if g.Fsp > 0 && g.Fsp <= 9 {
microFmt := fmt.Sprintf(".%%0%dd", g.Fsp)
return dataTimeStr + fmt.Sprintf(microFmt, g.randGen.Int()%(10^g.Fsp))
return dataTimeStr + fmt.Sprintf(microFmt, g.randGen.Int()%int(math.Pow10(g.Fsp)))
}

return dataTimeStr
Expand Down Expand Up @@ -820,6 +840,21 @@ func (g *dateStrGener) gen() interface{} {
return fmt.Sprintf("%d-%d-%d", g.Year, g.Month, g.Day)
}

// dateOrDatetimeStrGener is used to generate strings which are date or datetime format.
type dateOrDatetimeStrGener struct {
dateRatio float64
dateStrGener
dateTimeStrGener
}

func (g dateOrDatetimeStrGener) gen() interface{} {
if g.dateRatio > 1e-6 && g.dateStrGener.randGen.Float64() < g.dateRatio {
return g.dateStrGener.gen()
}

return g.dateTimeStrGener.gen()
}

// timeStrGener is used to generate strings which are time format
type timeStrGener struct {
nullRation float64
Expand All @@ -837,24 +872,194 @@ func (g *timeStrGener) gen() interface{} {
return fmt.Sprintf("%d:%d:%d", hour, minute, second)
}

// dateIntGener is used to generate int values which are date format.
type dateIntGener struct {
dateGener
}

func (g dateIntGener) gen() interface{} {
t := g.dateGener.gen().(types.Time)
num, err := t.ToNumber().ToInt()
if err != nil {
panic(err)
}
return num
}

// dateTimeIntGener is used to generate int values which are dateTime format.
type dateTimeIntGener struct {
dateTimeGener
nullRation float64
}

func (g *dateTimeIntGener) gen() interface{} {
if g.randGen.Float64() < g.nullRation {
return nil
func (g dateTimeIntGener) gen() interface{} {
t := g.dateTimeGener.gen().(types.Time)
num, err := t.ToNumber().ToInt()
if err != nil {
panic(err)
}
return num
}

// dateOrDatetimeIntGener is used to generate int values which are date or datetime format.
type dateOrDatetimeIntGener struct {
dateRatio float64
dateIntGener
dateTimeIntGener
}

func (g dateOrDatetimeIntGener) gen() interface{} {
if g.dateRatio > 1e-6 && g.dateGener.randGen.Float64() < g.dateRatio {
return g.dateIntGener.gen()
}

return g.dateTimeIntGener.gen()
}

// dateRealGener is used to generate floating point values which are date format.
// `fspRatio` is used to control the ratio of values with fractional part. I.e., 20010203.000456789 is a valid representation of a date.
type dateRealGener struct {
fspRatio float64
dateGener
}

func (g dateRealGener) gen() interface{} {
t := g.dateGener.gen().(types.Time)
num, err := t.ToNumber().ToFloat64()
if err != nil {
panic(err)
}

if g.randGen.Float64() >= g.fspRatio {
return num
}

num += g.randGen.Float64()
return num
}

// dateTimeRealGener is used to generate floating point values which are dateTime format.
// `fspRatio` is used to control the ratio of values with fractional part.
type dateTimeRealGener struct {
fspRatio float64
dateTimeGener
}

func (g dateTimeRealGener) gen() interface{} {
t := g.dateTimeGener.gen().(types.Time)
num, err := t.ToNumber().ToInt()
tmp, err := t.ToNumber().ToInt()
if err != nil {
panic(err)
}
num := float64(tmp)

if g.randGen.Float64() >= g.fspRatio {
return num
}

// Not using `t`'s us part since it's too regular.
// Instead, generating a more arbitrary fractional part, e.g. with more than 6 digits.
// We want the parsing logic to be strong enough to deal with this arbitrary fractional number.
num += g.randGen.Float64()
return num
}

// dateOrDatetimeRealGener is used to generate floating point values which are date or datetime format.
type dateOrDatetimeRealGener struct {
dateRatio float64
dateRealGener
dateTimeRealGener
}

func (g dateOrDatetimeRealGener) gen() interface{} {
if g.dateRatio > 1e-6 && g.dateGener.randGen.Float64() < g.dateRatio {
return g.dateRealGener.gen()
}

return g.dateTimeRealGener.gen()
}

// dateDecimalGener is used to generate decimals which are date format.
// `fspRatio` is used to control the ratio of values with fractional part. I.e., 20010203.000456789 is a valid representation of a date.
type dateDecimalGener struct {
fspRatio float64
dateGener
}

func (g dateDecimalGener) gen() interface{} {
t := g.dateGener.gen().(types.Time)
intPart := t.ToNumber()

if g.randGen.Float64() >= g.fspRatio {
return intPart
}

// Generate a fractional part that is at most 9 digits.
fracDigits := g.randGen.Intn(1000000000)
fracPart := new(types.MyDecimal).FromInt(int64(fracDigits))
if err := fracPart.Shift(-9); err != nil {
panic(err)
}

res := new(types.MyDecimal)
err := types.DecimalAdd(intPart, fracPart, res)
if err != nil {
panic(err)
}
return res
}

// dateTimeDecimalGener is used to generate decimals which are dateTime format.
type dateTimeDecimalGener struct {
fspRatio float64
dateTimeGener
}

func (g dateTimeDecimalGener) gen() interface{} {
t := g.dateTimeGener.gen().(types.Time)
num := t.ToNumber()
// Not using `num`'s fractional part so that we can:
// 1. Return early for non-fsp values.
// 2. Generate a more arbitrary fractional part if needed.
i, err := num.ToInt()
if err != nil {
panic(err)
}
intPart := new(types.MyDecimal).FromInt(i)

if g.randGen.Float64() >= g.fspRatio {
return intPart
}

// Generate a fractional part that is at most 9 digits.
fracDigits := g.randGen.Intn(1000000000)
fracPart := new(types.MyDecimal).FromInt(int64(fracDigits))
if err := fracPart.Shift(-9); err != nil {
panic(err)
}

res := new(types.MyDecimal)
err = types.DecimalAdd(intPart, fracPart, res)
if err != nil {
panic(err)
}
return res
}

// dateOrDatetimeDecimalGener is used to generate decimals which are date or datetime format.
type dateOrDatetimeDecimalGener struct {
dateRatio float64
dateDecimalGener
dateTimeDecimalGener
}

func (g dateOrDatetimeDecimalGener) gen() interface{} {
if g.dateRatio > 1e-6 && g.dateGener.randGen.Float64() < g.dateRatio {
return g.dateDecimalGener.gen()
}

return g.dateTimeDecimalGener.gen()
}

// constStrGener always returns the given string
type constStrGener struct {
s string
Expand Down
8 changes: 4 additions & 4 deletions expression/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -610,10 +610,10 @@ var funcs = map[string]functionClass{
ast.Truncate: &truncateFunctionClass{baseFunctionClass{ast.Truncate, 2, 2}},

// time functions
ast.AddDate: &addDateFunctionClass{baseFunctionClass{ast.AddDate, 3, 3}},
ast.DateAdd: &addDateFunctionClass{baseFunctionClass{ast.DateAdd, 3, 3}},
ast.SubDate: &subDateFunctionClass{baseFunctionClass{ast.SubDate, 3, 3}},
ast.DateSub: &subDateFunctionClass{baseFunctionClass{ast.DateSub, 3, 3}},
ast.AddDate: &addSubDateFunctionClass{baseFunctionClass{ast.AddDate, 3, 3}, addTime, addDuration, setAdd},
ast.DateAdd: &addSubDateFunctionClass{baseFunctionClass{ast.DateAdd, 3, 3}, addTime, addDuration, setAdd},
ast.SubDate: &addSubDateFunctionClass{baseFunctionClass{ast.SubDate, 3, 3}, subTime, subDuration, setSub},
ast.DateSub: &addSubDateFunctionClass{baseFunctionClass{ast.DateSub, 3, 3}, subTime, subDuration, setSub},
ast.AddTime: &addTimeFunctionClass{baseFunctionClass{ast.AddTime, 2, 2}},
ast.ConvertTz: &convertTzFunctionClass{baseFunctionClass{ast.ConvertTz, 3, 3}},
ast.Curdate: &currentDateFunctionClass{baseFunctionClass{ast.Curdate, 0, 0}},
Expand Down
Loading