Skip to content

Commit

Permalink
executor, expression: fix current_timestamp/now not consistent… (#11394)
Browse files Browse the repository at this point in the history
  • Loading branch information
cfzjywxk authored and winkyao committed Aug 5, 2019
1 parent abf8cea commit 94498e7
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 56 deletions.
12 changes: 12 additions & 0 deletions executor/write_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2435,3 +2435,15 @@ func (s *testSuite4) TestDefEnumInsert(c *C) {
tk.MustExec("insert into test (id) values (1)")
tk.MustQuery("select prescription_type from test").Check(testkit.Rows("a"))
}

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 @@ -4288,11 +4284,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 @@ -6350,9 +6346,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 @@ -6381,9 +6377,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 @@ -66,7 +66,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 @@ -132,7 +132,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 @@ -145,15 +147,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

0 comments on commit 94498e7

Please sign in to comment.