Skip to content

Commit 5374ff8

Browse files
dbjoangaut
authored andcommitted
expression,planner: support non-deterministic functions (e.g., now) in the plan cache (#8105)
1 parent b7f431e commit 5374ff8

File tree

8 files changed

+236
-55
lines changed

8 files changed

+236
-55
lines changed

executor/executor.go

+2
Original file line numberDiff line numberDiff line change
@@ -1173,6 +1173,8 @@ func ResetContextOfStmt(ctx sessionctx.Context, s ast.StmtNode) (err error) {
11731173
sc := new(stmtctx.StatementContext)
11741174
sc.TimeZone = vars.Location()
11751175
sc.MemTracker = memory.NewTracker(s.Text(), vars.MemQuotaQuery)
1176+
sc.NowTs = time.Time{}
1177+
sc.SysTs = time.Time{}
11761178
switch config.GetGlobalConfig().OOMAction {
11771179
case config.OOMActionCancel:
11781180
sc.MemTracker.SetActionOnExceed(&memory.PanicOnExceed{})

expression/builtin_time.go

+51-15
Original file line numberDiff line numberDiff line change
@@ -1971,7 +1971,11 @@ func (b *builtinCurrentDateSig) Clone() builtinFunc {
19711971
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_curdate
19721972
func (b *builtinCurrentDateSig) evalTime(row chunk.Row) (d types.Time, isNull bool, err error) {
19731973
tz := b.ctx.GetSessionVars().Location()
1974-
year, month, day := time.Now().In(tz).Date()
1974+
var nowTs = &b.ctx.GetSessionVars().StmtCtx.NowTs
1975+
if nowTs.Equal(time.Time{}) {
1976+
*nowTs = time.Now()
1977+
}
1978+
year, month, day := nowTs.In(tz).Date()
19751979
result := types.Time{
19761980
Time: types.FromDate(year, int(month), day, 0, 0, 0, 0),
19771981
Type: mysql.TypeDate,
@@ -2026,7 +2030,11 @@ func (b *builtinCurrentTime0ArgSig) Clone() builtinFunc {
20262030

20272031
func (b *builtinCurrentTime0ArgSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
20282032
tz := b.ctx.GetSessionVars().Location()
2029-
dur := time.Now().In(tz).Format(types.TimeFormat)
2033+
var nowTs = &b.ctx.GetSessionVars().StmtCtx.NowTs
2034+
if nowTs.Equal(time.Time{}) {
2035+
*nowTs = time.Now()
2036+
}
2037+
dur := nowTs.In(tz).Format(types.TimeFormat)
20302038
res, err := types.ParseDuration(b.ctx.GetSessionVars().StmtCtx, dur, types.MinFsp)
20312039
if err != nil {
20322040
return types.Duration{}, true, errors.Trace(err)
@@ -2050,7 +2058,11 @@ func (b *builtinCurrentTime1ArgSig) evalDuration(row chunk.Row) (types.Duration,
20502058
return types.Duration{}, true, errors.Trace(err)
20512059
}
20522060
tz := b.ctx.GetSessionVars().Location()
2053-
dur := time.Now().In(tz).Format(types.TimeFSPFormat)
2061+
var nowTs = &b.ctx.GetSessionVars().StmtCtx.NowTs
2062+
if nowTs.Equal(time.Time{}) {
2063+
*nowTs = time.Now()
2064+
}
2065+
dur := nowTs.In(tz).Format(types.TimeFSPFormat)
20542066
res, err := types.ParseDuration(b.ctx.GetSessionVars().StmtCtx, dur, int(fsp))
20552067
if err != nil {
20562068
return types.Duration{}, true, errors.Trace(err)
@@ -2188,7 +2200,11 @@ func (b *builtinUTCDateSig) Clone() builtinFunc {
21882200
// evalTime evals UTC_DATE, UTC_DATE().
21892201
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_utc-date
21902202
func (b *builtinUTCDateSig) evalTime(row chunk.Row) (types.Time, bool, error) {
2191-
year, month, day := time.Now().UTC().Date()
2203+
var nowTs = &b.ctx.GetSessionVars().StmtCtx.NowTs
2204+
if nowTs.Equal(time.Time{}) {
2205+
*nowTs = time.Now()
2206+
}
2207+
year, month, day := nowTs.UTC().Date()
21922208
result := types.Time{
21932209
Time: types.FromGoTime(time.Date(year, month, day, 0, 0, 0, 0, time.UTC)),
21942210
Type: mysql.TypeDate,
@@ -2244,8 +2260,12 @@ func (c *utcTimestampFunctionClass) getFunction(ctx sessionctx.Context, args []E
22442260
return sig, nil
22452261
}
22462262

2247-
func evalUTCTimestampWithFsp(fsp int) (types.Time, bool, error) {
2248-
result, err := convertTimeToMysqlTime(time.Now().UTC(), fsp)
2263+
func evalUTCTimestampWithFsp(ctx sessionctx.Context, fsp int) (types.Time, bool, error) {
2264+
var nowTs = &ctx.GetSessionVars().StmtCtx.NowTs
2265+
if nowTs.Equal(time.Time{}) {
2266+
*nowTs = time.Now()
2267+
}
2268+
result, err := convertTimeToMysqlTime(nowTs.UTC(), fsp)
22492269
if err != nil {
22502270
return types.Time{}, true, errors.Trace(err)
22512271
}
@@ -2277,7 +2297,7 @@ func (b *builtinUTCTimestampWithArgSig) evalTime(row chunk.Row) (types.Time, boo
22772297
return types.Time{}, true, errors.Errorf("Invalid negative %d specified, must in [0, 6].", num)
22782298
}
22792299

2280-
result, isNull, err := evalUTCTimestampWithFsp(int(num))
2300+
result, isNull, err := evalUTCTimestampWithFsp(b.ctx, int(num))
22812301
return result, isNull, errors.Trace(err)
22822302
}
22832303

@@ -2294,7 +2314,7 @@ func (b *builtinUTCTimestampWithoutArgSig) Clone() builtinFunc {
22942314
// evalTime evals UTC_TIMESTAMP().
22952315
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_utc-timestamp
22962316
func (b *builtinUTCTimestampWithoutArgSig) evalTime(row chunk.Row) (types.Time, bool, error) {
2297-
result, isNull, err := evalUTCTimestampWithFsp(0)
2317+
result, isNull, err := evalUTCTimestampWithFsp(b.ctx, 0)
22982318
return result, isNull, errors.Trace(err)
22992319
}
23002320

@@ -2328,12 +2348,16 @@ func (c *nowFunctionClass) getFunction(ctx sessionctx.Context, args []Expression
23282348
}
23292349

23302350
func evalNowWithFsp(ctx sessionctx.Context, fsp int) (types.Time, bool, error) {
2331-
sysTs, err := getSystemTimestamp(ctx)
2332-
if err != nil {
2333-
return types.Time{}, true, errors.Trace(err)
2351+
var sysTs = &ctx.GetSessionVars().StmtCtx.SysTs
2352+
if sysTs.Equal(time.Time{}) {
2353+
var err error
2354+
*sysTs, err = getSystemTimestamp(ctx)
2355+
if err != nil {
2356+
return types.Time{}, true, errors.Trace(err)
2357+
}
23342358
}
23352359

2336-
result, err := convertTimeToMysqlTime(sysTs, fsp)
2360+
result, err := convertTimeToMysqlTime(*sysTs, fsp)
23372361
if err != nil {
23382362
return types.Time{}, true, errors.Trace(err)
23392363
}
@@ -3557,7 +3581,11 @@ func (b *builtinUnixTimestampCurrentSig) Clone() builtinFunc {
35573581
// evalInt evals a UNIX_TIMESTAMP().
35583582
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_unix-timestamp
35593583
func (b *builtinUnixTimestampCurrentSig) evalInt(row chunk.Row) (int64, bool, error) {
3560-
dec, err := goTimeToMysqlUnixTimestamp(time.Now(), 1)
3584+
var nowTs = &b.ctx.GetSessionVars().StmtCtx.NowTs
3585+
if nowTs.Equal(time.Time{}) {
3586+
*nowTs = time.Now()
3587+
}
3588+
dec, err := goTimeToMysqlUnixTimestamp(*nowTs, 1)
35613589
if err != nil {
35623590
return 0, true, errors.Trace(err)
35633591
}
@@ -5497,7 +5525,11 @@ func (b *builtinUTCTimeWithoutArgSig) Clone() builtinFunc {
54975525
// evalDuration evals a builtinUTCTimeWithoutArgSig.
54985526
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_utc-time
54995527
func (b *builtinUTCTimeWithoutArgSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
5500-
v, err := types.ParseDuration(b.ctx.GetSessionVars().StmtCtx, time.Now().UTC().Format(types.TimeFormat), 0)
5528+
var nowTs = &b.ctx.GetSessionVars().StmtCtx.NowTs
5529+
if nowTs.Equal(time.Time{}) {
5530+
*nowTs = time.Now()
5531+
}
5532+
v, err := types.ParseDuration(b.ctx.GetSessionVars().StmtCtx, nowTs.UTC().Format(types.TimeFormat), 0)
55015533
return v, false, err
55025534
}
55035535

@@ -5524,7 +5556,11 @@ func (b *builtinUTCTimeWithArgSig) evalDuration(row chunk.Row) (types.Duration,
55245556
if fsp < int64(types.MinFsp) {
55255557
return types.Duration{}, true, errors.Errorf("Invalid negative %d specified, must in [0, 6].", fsp)
55265558
}
5527-
v, err := types.ParseDuration(b.ctx.GetSessionVars().StmtCtx, time.Now().UTC().Format(types.TimeFSPFormat), int(fsp))
5559+
var nowTs = &b.ctx.GetSessionVars().StmtCtx.NowTs
5560+
if nowTs.Equal(time.Time{}) {
5561+
*nowTs = time.Now()
5562+
}
5563+
v, err := types.ParseDuration(b.ctx.GetSessionVars().StmtCtx, nowTs.UTC().Format(types.TimeFSPFormat), int(fsp))
55285564
return v, false, err
55295565
}
55305566

expression/builtin_time_test.go

+24
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,11 @@ func (s *testEvaluatorSuite) TestTime(c *C) {
762762
c.Assert(err, IsNil)
763763
}
764764

765+
func resetStmtContext(ctx sessionctx.Context) {
766+
ctx.GetSessionVars().StmtCtx.NowTs = time.Time{}
767+
ctx.GetSessionVars().StmtCtx.SysTs = time.Time{}
768+
}
769+
765770
func (s *testEvaluatorSuite) TestNowAndUTCTimestamp(c *C) {
766771
defer testleak.AfterTest(c)()
767772

@@ -778,6 +783,7 @@ func (s *testEvaluatorSuite) TestNowAndUTCTimestamp(c *C) {
778783
{funcs[ast.Now], func() time.Time { return time.Now() }},
779784
{funcs[ast.UTCTimestamp], func() time.Time { return time.Now().UTC() }},
780785
} {
786+
resetStmtContext(s.ctx)
781787
f, err := x.fc.getFunction(s.ctx, s.datumsToConstants(nil))
782788
c.Assert(err, IsNil)
783789
v, err := evalBuiltinFunc(f, chunk.Row{})
@@ -789,6 +795,7 @@ func (s *testEvaluatorSuite) TestNowAndUTCTimestamp(c *C) {
789795
c.Assert(strings.Contains(t.String(), "."), IsFalse)
790796
c.Assert(ts.Sub(gotime(t, ts.Location())), LessEqual, time.Second)
791797

798+
resetStmtContext(s.ctx)
792799
f, err = x.fc.getFunction(s.ctx, s.datumsToConstants(types.MakeDatums(6)))
793800
c.Assert(err, IsNil)
794801
v, err = evalBuiltinFunc(f, chunk.Row{})
@@ -798,11 +805,13 @@ func (s *testEvaluatorSuite) TestNowAndUTCTimestamp(c *C) {
798805
c.Assert(strings.Contains(t.String(), "."), IsTrue)
799806
c.Assert(ts.Sub(gotime(t, ts.Location())), LessEqual, time.Millisecond)
800807

808+
resetStmtContext(s.ctx)
801809
f, err = x.fc.getFunction(s.ctx, s.datumsToConstants(types.MakeDatums(8)))
802810
c.Assert(err, IsNil)
803811
_, err = evalBuiltinFunc(f, chunk.Row{})
804812
c.Assert(err, NotNil)
805813

814+
resetStmtContext(s.ctx)
806815
f, err = x.fc.getFunction(s.ctx, s.datumsToConstants(types.MakeDatums(-2)))
807816
c.Assert(err, IsNil)
808817
_, err = evalBuiltinFunc(f, chunk.Row{})
@@ -813,6 +822,7 @@ func (s *testEvaluatorSuite) TestNowAndUTCTimestamp(c *C) {
813822
variable.SetSessionSystemVar(s.ctx.GetSessionVars(), "time_zone", types.NewDatum("+00:00"))
814823
variable.SetSessionSystemVar(s.ctx.GetSessionVars(), "timestamp", types.NewDatum(1234))
815824
fc := funcs[ast.Now]
825+
resetStmtContext(s.ctx)
816826
f, err := fc.getFunction(s.ctx, s.datumsToConstants(nil))
817827
c.Assert(err, IsNil)
818828
v, err := evalBuiltinFunc(f, chunk.Row{})
@@ -877,6 +887,7 @@ func (s *testEvaluatorSuite) TestAddTimeSig(c *C) {
877887

878888
// This is a test for issue 7334
879889
du := newDateArighmeticalUtil()
890+
resetStmtContext(s.ctx)
880891
now, _, err := evalNowWithFsp(s.ctx, 0)
881892
c.Assert(err, IsNil)
882893
res, _, err := du.add(s.ctx, now, "1", "MICROSECOND")
@@ -1203,6 +1214,7 @@ func (s *testEvaluatorSuite) TestUTCTime(c *C) {
12031214
}{{0, 8}, {3, 12}, {6, 15}, {-1, 0}, {7, 0}}
12041215

12051216
for _, test := range tests {
1217+
resetStmtContext(s.ctx)
12061218
f, err := fc.getFunction(s.ctx, s.datumsToConstants(types.MakeDatums(test.param)))
12071219
c.Assert(err, IsNil)
12081220
v, err := evalBuiltinFunc(f, chunk.Row{})
@@ -1229,6 +1241,7 @@ func (s *testEvaluatorSuite) TestUTCDate(c *C) {
12291241
defer testleak.AfterTest(c)()
12301242
last := time.Now().UTC()
12311243
fc := funcs[ast.UTCDate]
1244+
resetStmtContext(mock.NewContext())
12321245
f, err := fc.getFunction(mock.NewContext(), s.datumsToConstants(nil))
12331246
c.Assert(err, IsNil)
12341247
v, err := evalBuiltinFunc(f, chunk.Row{})
@@ -1500,6 +1513,7 @@ func (s *testEvaluatorSuite) TestTimestampDiff(c *C) {
15001513
types.NewStringDatum(test.t1),
15011514
types.NewStringDatum(test.t2),
15021515
}
1516+
resetStmtContext(s.ctx)
15031517
f, err := fc.getFunction(s.ctx, s.datumsToConstants(args))
15041518
c.Assert(err, IsNil)
15051519
d, err := evalBuiltinFunc(f, chunk.Row{})
@@ -1509,6 +1523,7 @@ func (s *testEvaluatorSuite) TestTimestampDiff(c *C) {
15091523
sc := s.ctx.GetSessionVars().StmtCtx
15101524
sc.IgnoreTruncate = true
15111525
sc.IgnoreZeroInDate = true
1526+
resetStmtContext(s.ctx)
15121527
f, err := fc.getFunction(s.ctx, s.datumsToConstants([]types.Datum{types.NewStringDatum("DAY"),
15131528
types.NewStringDatum("2017-01-00"),
15141529
types.NewStringDatum("2017-01-01")}))
@@ -1517,6 +1532,7 @@ func (s *testEvaluatorSuite) TestTimestampDiff(c *C) {
15171532
c.Assert(err, IsNil)
15181533
c.Assert(d.Kind(), Equals, types.KindNull)
15191534

1535+
resetStmtContext(s.ctx)
15201536
f, err = fc.getFunction(s.ctx, s.datumsToConstants([]types.Datum{types.NewStringDatum("DAY"),
15211537
{}, types.NewStringDatum("2017-01-01")}))
15221538
c.Assert(err, IsNil)
@@ -1528,6 +1544,7 @@ func (s *testEvaluatorSuite) TestTimestampDiff(c *C) {
15281544
func (s *testEvaluatorSuite) TestUnixTimestamp(c *C) {
15291545
// Test UNIX_TIMESTAMP().
15301546
fc := funcs[ast.UnixTimestamp]
1547+
resetStmtContext(s.ctx)
15311548
f, err := fc.getFunction(s.ctx, nil)
15321549
c.Assert(err, IsNil)
15331550
d, err := evalBuiltinFunc(f, chunk.Row{})
@@ -1537,12 +1554,14 @@ func (s *testEvaluatorSuite) TestUnixTimestamp(c *C) {
15371554

15381555
// https://github.com/pingcap/tidb/issues/2496
15391556
// Test UNIX_TIMESTAMP(NOW()).
1557+
resetStmtContext(s.ctx)
15401558
now, isNull, err := evalNowWithFsp(s.ctx, 0)
15411559
c.Assert(err, IsNil)
15421560
c.Assert(isNull, IsFalse)
15431561
n := types.Datum{}
15441562
n.SetMysqlTime(now)
15451563
args := []types.Datum{n}
1564+
resetStmtContext(s.ctx)
15461565
f, err = fc.getFunction(s.ctx, s.datumsToConstants(args))
15471566
c.Assert(err, IsNil)
15481567
d, err = evalBuiltinFunc(f, chunk.Row{})
@@ -1554,6 +1573,7 @@ func (s *testEvaluatorSuite) TestUnixTimestamp(c *C) {
15541573
// https://github.com/pingcap/tidb/issues/2852
15551574
// Test UNIX_TIMESTAMP(NULL).
15561575
args = []types.Datum{types.NewDatum(nil)}
1576+
resetStmtContext(s.ctx)
15571577
f, err = fc.getFunction(s.ctx, s.datumsToConstants(args))
15581578
c.Assert(err, IsNil)
15591579
d, err = evalBuiltinFunc(f, chunk.Row{})
@@ -1598,6 +1618,7 @@ func (s *testEvaluatorSuite) TestUnixTimestamp(c *C) {
15981618
fmt.Printf("Begin Test %v\n", test)
15991619
expr := s.datumsToConstants([]types.Datum{test.input})
16001620
expr[0].GetType().Decimal = test.inputDecimal
1621+
resetStmtContext(s.ctx)
16011622
f, err := fc.getFunction(s.ctx, expr)
16021623
c.Assert(err, IsNil, Commentf("%+v", test))
16031624
d, err := evalBuiltinFunc(f, chunk.Row{})
@@ -1681,6 +1702,7 @@ func (s *testEvaluatorSuite) TestTimestamp(c *C) {
16811702
}
16821703
fc := funcs[ast.Timestamp]
16831704
for _, test := range tests {
1705+
resetStmtContext(s.ctx)
16841706
f, err := fc.getFunction(s.ctx, s.datumsToConstants(test.t))
16851707
c.Assert(err, IsNil)
16861708
d, err := evalBuiltinFunc(f, chunk.Row{})
@@ -1690,6 +1712,7 @@ func (s *testEvaluatorSuite) TestTimestamp(c *C) {
16901712
}
16911713

16921714
nilDatum := types.NewDatum(nil)
1715+
resetStmtContext(s.ctx)
16931716
f, err := fc.getFunction(s.ctx, s.datumsToConstants([]types.Datum{nilDatum}))
16941717
c.Assert(err, IsNil)
16951718
d, err := evalBuiltinFunc(f, chunk.Row{})
@@ -2357,6 +2380,7 @@ func (s *testEvaluatorSuite) TestWithTimeZone(c *C) {
23572380

23582381
for _, t := range tests {
23592382
now := time.Now().In(sv.TimeZone)
2383+
resetStmtContext(s.ctx)
23602384
f, err := funcs[t.method].getFunction(s.ctx, s.datumsToConstants(t.Input))
23612385
c.Assert(err, IsNil)
23622386
d, err := evalBuiltinFunc(f, chunk.Row{})

expression/function_traits.go

+23-17
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,12 @@ import (
1919

2020
// UnCacheableFunctions stores functions which can not be cached to plan cache.
2121
var UnCacheableFunctions = map[string]struct{}{
22-
ast.Now: {},
23-
ast.CurrentTimestamp: {},
24-
ast.UTCTime: {},
25-
ast.Curtime: {},
26-
ast.CurrentTime: {},
27-
ast.UTCTimestamp: {},
28-
ast.UnixTimestamp: {},
29-
ast.Sysdate: {},
30-
ast.Curdate: {},
31-
ast.CurrentDate: {},
32-
ast.UTCDate: {},
33-
ast.Database: {},
34-
ast.CurrentUser: {},
35-
ast.User: {},
36-
ast.ConnectionID: {},
37-
ast.LastInsertId: {},
38-
ast.Version: {},
22+
ast.Database: {},
23+
ast.CurrentUser: {},
24+
ast.User: {},
25+
ast.ConnectionID: {},
26+
ast.LastInsertId: {},
27+
ast.Version: {},
3928
}
4029

4130
// unFoldableFunctions stores functions which can not be folded duration constant folding stage.
@@ -52,6 +41,23 @@ var unFoldableFunctions = map[string]struct{}{
5241
ast.GetParam: {},
5342
}
5443

44+
// DeferredFunctions stores non-deterministic functions, which can be deferred only when the plan cache is enabled.
45+
var DeferredFunctions = map[string]struct{}{
46+
ast.Now: {},
47+
ast.CurrentTimestamp: {},
48+
ast.UTCTime: {},
49+
ast.Curtime: {},
50+
ast.CurrentTime: {},
51+
ast.UTCTimestamp: {},
52+
ast.UnixTimestamp: {},
53+
ast.Sysdate: {},
54+
ast.Curdate: {},
55+
ast.CurrentDate: {},
56+
ast.UTCDate: {},
57+
ast.Rand: {},
58+
ast.UUID: {},
59+
}
60+
5561
// inequalFunctions stores functions which cannot be propagated from column equal condition.
5662
var inequalFunctions = map[string]struct{}{
5763
ast.IsNull: {},

0 commit comments

Comments
 (0)