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

expressions: make time func now related unit tests stable(#11412)(#11342) #11394

Merged
merged 17 commits into from
Aug 5, 2019
Merged
Show file tree
Hide file tree
Changes from 10 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
12 changes: 12 additions & 0 deletions executor/write_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2454,3 +2454,15 @@ func (s *testSuite4) TestAutoIDInRetry(c *C) {
tk.MustExec("insert into t values ()")
tk.MustQuery(`select * from t`).Check(testkit.Rows("1", "2", "3", "4", "5"))
}

func (s *testSuite4) TestSetWithCurrentTimestampAndNow(c *C) {
tk := testkit.NewTestKitWithInit(c, s.store)
tk.MustExec("use test")
tk.MustExec(`drop table if exists tbl;`)
tk.MustExec(`create table t1(c1 timestamp default current_timestamp, c2 int, c3 timestamp default current_timestamp);`)
//c1 insert using now() function result, c3 using default value calculation, should be same
tk.MustExec(`insert into t1 set c1 = current_timestamp, c2 = sleep(2);`)
tk.MustQuery("select c1 = c3 from t1").Check(testkit.Rows("1"))
tk.MustExec(`insert into t1 set c1 = current_timestamp, c2 = sleep(1);`)
tk.MustQuery("select c1 = c3 from t1").Check(testkit.Rows("1", "1"))
}
62 changes: 29 additions & 33 deletions expression/builtin_time.go
Original file line number Diff line number Diff line change
Expand Up @@ -2029,9 +2029,9 @@ func (b *builtinCurrentDateSig) Clone() builtinFunc {
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_curdate
func (b *builtinCurrentDateSig) evalTime(row chunk.Row) (d types.Time, isNull bool, err error) {
tz := b.ctx.GetSessionVars().Location()
var nowTs = &b.ctx.GetSessionVars().StmtCtx.NowTs
if nowTs.Equal(time.Time{}) {
*nowTs = time.Now()
nowTs, err := getStmtTimestamp(b.ctx)
if err != nil {
return types.Time{}, true, err
}
year, month, day := nowTs.In(tz).Date()
result := types.Time{
Expand Down Expand Up @@ -2088,9 +2088,9 @@ func (b *builtinCurrentTime0ArgSig) Clone() builtinFunc {

func (b *builtinCurrentTime0ArgSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
tz := b.ctx.GetSessionVars().Location()
var nowTs = &b.ctx.GetSessionVars().StmtCtx.NowTs
if nowTs.Equal(time.Time{}) {
*nowTs = time.Now()
nowTs, err := getStmtTimestamp(b.ctx)
if err != nil {
return types.Duration{}, true, err
}
dur := nowTs.In(tz).Format(types.TimeFormat)
res, err := types.ParseDuration(b.ctx.GetSessionVars().StmtCtx, dur, types.MinFsp)
Expand All @@ -2116,9 +2116,9 @@ func (b *builtinCurrentTime1ArgSig) evalDuration(row chunk.Row) (types.Duration,
return types.Duration{}, true, err
}
tz := b.ctx.GetSessionVars().Location()
var nowTs = &b.ctx.GetSessionVars().StmtCtx.NowTs
if nowTs.Equal(time.Time{}) {
*nowTs = time.Now()
nowTs, err := getStmtTimestamp(b.ctx)
if err != nil {
return types.Duration{}, true, err
}
dur := nowTs.In(tz).Format(types.TimeFSPFormat)
res, err := types.ParseDuration(b.ctx.GetSessionVars().StmtCtx, dur, int(fsp))
Expand Down Expand Up @@ -2258,9 +2258,9 @@ func (b *builtinUTCDateSig) Clone() builtinFunc {
// evalTime evals UTC_DATE, UTC_DATE().
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_utc-date
func (b *builtinUTCDateSig) evalTime(row chunk.Row) (types.Time, bool, error) {
var nowTs = &b.ctx.GetSessionVars().StmtCtx.NowTs
if nowTs.Equal(time.Time{}) {
*nowTs = time.Now()
nowTs, err := getStmtTimestamp(b.ctx)
if err != nil {
return types.Time{}, true, err
}
year, month, day := nowTs.UTC().Date()
result := types.Time{
Expand Down Expand Up @@ -2319,9 +2319,9 @@ func (c *utcTimestampFunctionClass) getFunction(ctx sessionctx.Context, args []E
}

func evalUTCTimestampWithFsp(ctx sessionctx.Context, fsp int) (types.Time, bool, error) {
var nowTs = &ctx.GetSessionVars().StmtCtx.NowTs
if nowTs.Equal(time.Time{}) {
*nowTs = time.Now()
nowTs, err := getStmtTimestamp(ctx)
if err != nil {
return types.Time{}, true, err
}
result, err := convertTimeToMysqlTime(nowTs.UTC(), fsp, types.ModeHalfEven)
if err != nil {
Expand Down Expand Up @@ -2406,13 +2406,9 @@ func (c *nowFunctionClass) getFunction(ctx sessionctx.Context, args []Expression
}

func evalNowWithFsp(ctx sessionctx.Context, fsp int) (types.Time, bool, error) {
var sysTs = &ctx.GetSessionVars().StmtCtx.SysTs
if sysTs.Equal(time.Time{}) {
var err error
*sysTs, err = getSystemTimestamp(ctx)
if err != nil {
return types.Time{}, true, err
}
nowTs, err := getStmtTimestamp(ctx)
if err != nil {
return types.Time{}, true, err
}

// In MySQL's implementation, now() will truncate the result instead of rounding it.
Expand All @@ -2423,7 +2419,7 @@ func evalNowWithFsp(ctx sessionctx.Context, fsp int) (types.Time, bool, error) {
// +----------------------------+-------------------------+---------------------+
// | 2019-03-25 15:57:56.612966 | 2019-03-25 15:57:56.612 | 2019-03-25 15:57:56 |
// +----------------------------+-------------------------+---------------------+
result, err := convertTimeToMysqlTime(*sysTs, fsp, types.ModeTruncate)
result, err := convertTimeToMysqlTime(nowTs, fsp, types.ModeTruncate)
if err != nil {
return types.Time{}, true, err
}
Expand Down Expand Up @@ -4287,11 +4283,11 @@ func (b *builtinUnixTimestampCurrentSig) Clone() builtinFunc {
// evalInt evals a UNIX_TIMESTAMP().
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_unix-timestamp
func (b *builtinUnixTimestampCurrentSig) evalInt(row chunk.Row) (int64, bool, error) {
var nowTs = &b.ctx.GetSessionVars().StmtCtx.NowTs
if nowTs.Equal(time.Time{}) {
*nowTs = time.Now()
nowTs, err := getStmtTimestamp(b.ctx)
if err != nil {
return 0, true, err
}
dec, err := goTimeToMysqlUnixTimestamp(*nowTs, 1)
dec, err := goTimeToMysqlUnixTimestamp(nowTs, 1)
if err != nil {
return 0, true, err
}
Expand Down Expand Up @@ -6349,9 +6345,9 @@ func (b *builtinUTCTimeWithoutArgSig) Clone() builtinFunc {
// evalDuration evals a builtinUTCTimeWithoutArgSig.
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_utc-time
func (b *builtinUTCTimeWithoutArgSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
var nowTs = &b.ctx.GetSessionVars().StmtCtx.NowTs
if nowTs.Equal(time.Time{}) {
*nowTs = time.Now()
nowTs, err := getStmtTimestamp(b.ctx)
if err != nil {
return types.Duration{}, true, err
}
v, err := types.ParseDuration(b.ctx.GetSessionVars().StmtCtx, nowTs.UTC().Format(types.TimeFormat), 0)
return v, false, err
Expand Down Expand Up @@ -6380,9 +6376,9 @@ func (b *builtinUTCTimeWithArgSig) evalDuration(row chunk.Row) (types.Duration,
if fsp < int64(types.MinFsp) {
return types.Duration{}, true, errors.Errorf("Invalid negative %d specified, must in [0, 6].", fsp)
}
var nowTs = &b.ctx.GetSessionVars().StmtCtx.NowTs
if nowTs.Equal(time.Time{}) {
*nowTs = time.Now()
nowTs, err := getStmtTimestamp(b.ctx)
if err != nil {
return types.Duration{}, true, err
}
v, err := types.ParseDuration(b.ctx.GetSessionVars().StmtCtx, nowTs.UTC().Format(types.TimeFSPFormat), int(fsp))
return v, false, err
Expand Down
24 changes: 15 additions & 9 deletions expression/builtin_time_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -764,8 +764,7 @@ func (s *testEvaluatorSuite) TestTime(c *C) {
}

func resetStmtContext(ctx sessionctx.Context) {
ctx.GetSessionVars().StmtCtx.NowTs = time.Time{}
ctx.GetSessionVars().StmtCtx.SysTs = time.Time{}
ctx.GetSessionVars().StmtCtx.ResetNowTs()
}

func (s *testEvaluatorSuite) TestNowAndUTCTimestamp(c *C) {
Expand All @@ -784,9 +783,9 @@ func (s *testEvaluatorSuite) TestNowAndUTCTimestamp(c *C) {
{funcs[ast.Now], func() time.Time { return time.Now() }},
{funcs[ast.UTCTimestamp], func() time.Time { return time.Now().UTC() }},
} {
resetStmtContext(s.ctx)
f, err := x.fc.getFunction(s.ctx, s.datumsToConstants(nil))
c.Assert(err, IsNil)
resetStmtContext(s.ctx)
v, err := evalBuiltinFunc(f, chunk.Row{})
ts := x.now()
c.Assert(err, IsNil)
Expand All @@ -796,9 +795,9 @@ func (s *testEvaluatorSuite) TestNowAndUTCTimestamp(c *C) {
c.Assert(strings.Contains(t.String(), "."), IsFalse)
c.Assert(ts.Sub(gotime(t, ts.Location())), LessEqual, time.Second)

resetStmtContext(s.ctx)
f, err = x.fc.getFunction(s.ctx, s.datumsToConstants(types.MakeDatums(6)))
c.Assert(err, IsNil)
resetStmtContext(s.ctx)
v, err = evalBuiltinFunc(f, chunk.Row{})
ts = x.now()
c.Assert(err, IsNil)
Expand Down Expand Up @@ -1072,6 +1071,7 @@ func (s *testEvaluatorSuite) TestSysDate(c *C) {
variable.SetSessionSystemVar(ctx.GetSessionVars(), "timestamp", timezone)
f, err := fc.getFunction(ctx, s.datumsToConstants(nil))
c.Assert(err, IsNil)
resetStmtContext(s.ctx)
v, err := evalBuiltinFunc(f, chunk.Row{})
last := time.Now()
c.Assert(err, IsNil)
Expand All @@ -1082,6 +1082,7 @@ func (s *testEvaluatorSuite) TestSysDate(c *C) {
last := time.Now()
f, err := fc.getFunction(ctx, s.datumsToConstants(types.MakeDatums(6)))
c.Assert(err, IsNil)
resetStmtContext(s.ctx)
v, err := evalBuiltinFunc(f, chunk.Row{})
c.Assert(err, IsNil)
n := v.GetMysqlTime()
Expand Down Expand Up @@ -1214,6 +1215,7 @@ func (s *testEvaluatorSuite) TestCurrentDate(c *C) {
fc := funcs[ast.CurrentDate]
f, err := fc.getFunction(mock.NewContext(), s.datumsToConstants(nil))
c.Assert(err, IsNil)
resetStmtContext(s.ctx)
v, err := evalBuiltinFunc(f, chunk.Row{})
c.Assert(err, IsNil)
n := v.GetMysqlTime()
Expand All @@ -1228,6 +1230,7 @@ func (s *testEvaluatorSuite) TestCurrentTime(c *C) {
fc := funcs[ast.CurrentTime]
f, err := fc.getFunction(s.ctx, s.datumsToConstants(types.MakeDatums(nil)))
c.Assert(err, IsNil)
resetStmtContext(s.ctx)
v, err := evalBuiltinFunc(f, chunk.Row{})
c.Assert(err, IsNil)
n := v.GetMysqlDuration()
Expand All @@ -1236,6 +1239,7 @@ func (s *testEvaluatorSuite) TestCurrentTime(c *C) {

f, err = fc.getFunction(s.ctx, s.datumsToConstants(types.MakeDatums(3)))
c.Assert(err, IsNil)
resetStmtContext(s.ctx)
v, err = evalBuiltinFunc(f, chunk.Row{})
c.Assert(err, IsNil)
n = v.GetMysqlDuration()
Expand All @@ -1244,6 +1248,7 @@ func (s *testEvaluatorSuite) TestCurrentTime(c *C) {

f, err = fc.getFunction(s.ctx, s.datumsToConstants(types.MakeDatums(6)))
c.Assert(err, IsNil)
resetStmtContext(s.ctx)
v, err = evalBuiltinFunc(f, chunk.Row{})
c.Assert(err, IsNil)
n = v.GetMysqlDuration()
Expand All @@ -1270,9 +1275,9 @@ func (s *testEvaluatorSuite) TestUTCTime(c *C) {
}{{0, 8}, {3, 12}, {6, 15}, {-1, 0}, {7, 0}}

for _, test := range tests {
resetStmtContext(s.ctx)
f, err := fc.getFunction(s.ctx, s.datumsToConstants(types.MakeDatums(test.param)))
c.Assert(err, IsNil)
resetStmtContext(s.ctx)
v, err := evalBuiltinFunc(f, chunk.Row{})
if test.expect > 0 {
c.Assert(err, IsNil)
Expand All @@ -1286,6 +1291,7 @@ func (s *testEvaluatorSuite) TestUTCTime(c *C) {

f, err := fc.getFunction(s.ctx, make([]Expression, 0))
c.Assert(err, IsNil)
resetStmtContext(s.ctx)
v, err := evalBuiltinFunc(f, chunk.Row{})
c.Assert(err, IsNil)
n := v.GetMysqlDuration()
Expand All @@ -1297,9 +1303,9 @@ func (s *testEvaluatorSuite) TestUTCDate(c *C) {
defer testleak.AfterTest(c)()
last := time.Now().UTC()
fc := funcs[ast.UTCDate]
resetStmtContext(mock.NewContext())
f, err := fc.getFunction(mock.NewContext(), s.datumsToConstants(nil))
c.Assert(err, IsNil)
resetStmtContext(mock.NewContext())
v, err := evalBuiltinFunc(f, chunk.Row{})
c.Assert(err, IsNil)
n := v.GetMysqlTime()
Expand Down Expand Up @@ -1636,9 +1642,9 @@ func (s *testEvaluatorSuite) TestTimestampDiff(c *C) {
func (s *testEvaluatorSuite) TestUnixTimestamp(c *C) {
// Test UNIX_TIMESTAMP().
fc := funcs[ast.UnixTimestamp]
resetStmtContext(s.ctx)
f, err := fc.getFunction(s.ctx, nil)
c.Assert(err, IsNil)
resetStmtContext(s.ctx)
d, err := evalBuiltinFunc(f, chunk.Row{})
c.Assert(err, IsNil)
c.Assert(d.GetInt64()-time.Now().Unix(), GreaterEqual, int64(-1))
Expand All @@ -1653,9 +1659,9 @@ func (s *testEvaluatorSuite) TestUnixTimestamp(c *C) {
n := types.Datum{}
n.SetMysqlTime(now)
args := []types.Datum{n}
resetStmtContext(s.ctx)
f, err = fc.getFunction(s.ctx, s.datumsToConstants(args))
c.Assert(err, IsNil)
resetStmtContext(s.ctx)
d, err = evalBuiltinFunc(f, chunk.Row{})
c.Assert(err, IsNil)
val, _ := d.GetMysqlDecimal().ToInt()
Expand Down Expand Up @@ -2715,9 +2721,9 @@ func (s *testEvaluatorSuite) TestWithTimeZone(c *C) {

for _, t := range tests {
now := time.Now().In(sv.TimeZone)
resetStmtContext(s.ctx)
f, err := funcs[t.method].getFunction(s.ctx, s.datumsToConstants(t.Input))
c.Assert(err, IsNil)
resetStmtContext(s.ctx)
d, err := evalBuiltinFunc(f, chunk.Row{})
c.Assert(err, IsNil)
result := t.convertToTime(d, sv.TimeZone)
Expand Down
27 changes: 15 additions & 12 deletions expression/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func GetTimeValue(ctx sessionctx.Context, v interface{}, tp byte, fsp int) (d ty
case string:
upperX := strings.ToUpper(x)
if upperX == strings.ToUpper(ast.CurrentTimestamp) {
defaultTime, err := getSystemTimestamp(ctx)
defaultTime, err := getStmtTimestamp(ctx)
if err != nil {
return d, err
}
Expand Down Expand Up @@ -120,7 +120,9 @@ func GetTimeValue(ctx sessionctx.Context, v interface{}, tp byte, fsp int) (d ty
return d, nil
}

func getSystemTimestamp(ctx sessionctx.Context) (time.Time, error) {
// if timestamp session variable set, use session variable as current time, otherwise use cached time
// during one sql statement, the "current_time" should be the same
func getStmtTimestamp(ctx sessionctx.Context) (time.Time, error) {
now := time.Now()

if ctx == nil {
Expand All @@ -133,15 +135,16 @@ func getSystemTimestamp(ctx sessionctx.Context) (time.Time, error) {
return now, err
}

if timestampStr == "" {
return now, nil
}
timestamp, err := types.StrToInt(sessionVars.StmtCtx, timestampStr)
if err != nil {
return time.Time{}, err
}
if timestamp <= 0 {
return now, nil
if timestampStr != "" {
timestamp, err := types.StrToInt(sessionVars.StmtCtx, timestampStr)
if err != nil {
return time.Time{}, err
}
if timestamp <= 0 {
return now, nil
}
return time.Unix(timestamp, 0), nil
}
return time.Unix(timestamp, 0), nil
stmtCtx := ctx.GetSessionVars().StmtCtx
return stmtCtx.GetNowTsCached(), nil
}
19 changes: 17 additions & 2 deletions sessionctx/stmtctx/stmtctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ type StatementContext struct {
RuntimeStatsColl *execdetails.RuntimeStatsColl
TableIDs []int64
IndexIDs []int64
NowTs time.Time
SysTs time.Time
nowTs time.Time // use this variable for now/current_timestamp calculation/cache for one stmt
stmtTimeCached bool
StmtType string
OriginalSQL string
digestMemo struct {
Expand All @@ -136,6 +136,21 @@ type StatementContext struct {
Tables []TableEntry
}

// GetNowTsCached getter for nowTs, if not set get now time and cache it
func (sc *StatementContext) GetNowTsCached() time.Time {
if !sc.stmtTimeCached {
now := time.Now()
sc.nowTs = now
sc.stmtTimeCached = true
}
return sc.nowTs
}

// ResetNowTs resetter for nowTs, clear cached time flag
func (sc *StatementContext) ResetNowTs() {
sc.stmtTimeCached = false
}

// SQLDigest gets normalized and digest for provided sql.
// it will cache result after first calling.
func (sc *StatementContext) SQLDigest() (normalized, sqlDigest string) {
Expand Down