Repository: pingcap/tidb (Go)

Issue:
Analyze worker panicked while auto analyzing
## Bug Report

Please answer these questions before submitting your issue. Thanks!

### 1. Minimal reproduce step (Required)

```sql
CREATE TABLE `t` ( `ldecimal` decimal(32,4) DEFAULT NULL, `rdecimal` decimal(32,4) DEFAULT NULL, `gen_col` decimal(36,4) GENERATED ALWAYS AS (`ldecimal` + `rdecimal`) VIRTUAL, `col_timestamp` timestamp(3) NULL DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
INSERT INTO `t` (`ldecimal`, `rdecimal`, `col_timestamp`) VALUES (2265.2200, 9843.4100, '1999-12-31 16:00:00') , (2449.2500, 8420.0900, '1999-12-31 16:16:40') , (9757.8100, 1015.0800, '1999-12-31 16:33:20') , (9970.5200, 9690.9200, '1999-12-31 16:50:00') , (-71221.0900, 8564.5300, '1999-12-31 17:06:40') , (8177.9800, 1066.2800, '1999-12-31 17:23:20') , (2517.3800, 31.7100, '1999-12-31 17:40:00') , (1191.2800, 9511.0600, '1999-12-31 17:56:40') , (4818.4100, 8510.2000, '1999-12-31 18:13:20') , (4381.3400, 661.4100, '1999-12-31 18:30:00');
INSERT INTO `t` (`ldecimal`, `rdecimal`, `col_timestamp`) SELECT `ldecimal`, `rdecimal`, `col_timestamp` FROM `t`;
INSERT INTO `t` (`ldecimal`, `rdecimal`, `col_timestamp`) SELECT `ldecimal`, `rdecimal`, `col_timestamp` FROM `t`;
INSERT INTO `t` (`ldecimal`, `rdecimal`, `col_timestamp`) SELECT `ldecimal`, `rdecimal`, `col_timestamp` FROM `t`;
INSERT INTO `t` (`ldecimal`, `rdecimal`, `col_timestamp`) SELECT `ldecimal`, `rdecimal`, `col_timestamp` FROM `t`;
INSERT INTO `t` (`ldecimal`, `rdecimal`, `col_timestamp`) SELECT `ldecimal`, `rdecimal`, `col_timestamp` FROM `t`;
INSERT INTO `t` (`ldecimal`, `rdecimal`, `col_timestamp`) SELECT `ldecimal`, `rdecimal`, `col_timestamp` FROM `t`;
INSERT INTO `t` (`ldecimal`, `rdecimal`, `col_timestamp`) SELECT `ldecimal`, `rdecimal`, `col_timestamp` FROM `t`;
```

<!-- a step by step guide for reproducing the bug. -->

### 2. What did you expect to see? (Required)

The table gets auto-analyzed after 1min without error reported.

### 3. What did you see instead (Required)

```
[2021/06/23 05:44:34.815 +00:00] [INFO] [update.go:983] ["[stats] auto analyze triggered"] [sql="analyze table `test`.`t`"] [reason="table unanalyzed, time since last updated 1m0.042886272s"]
[2021/06/23 05:44:34.828 +00:00] [ERROR] [analyze.go:256] ["analyze worker panicked"] [stack="goroutine 260410 [running]:\ngithub.com/pingcap/tidb/executor.(*AnalyzeExec).analyzeWorker.func1(0xc001076240, 0xc0005f5d78, 0xc00115e870, 0x1)\n\t/home/jenkins/agent/workspace/optimization-build-tidb-linux-amd/go/src/github.com/pingcap/tidb/executor/analyze.go:254 +0x105\npanic(0x325ec40, 0x3b69e40)\n\t/usr/local/go/src/runtime/panic.go:965 +0x1b9\ntime.Time.In(...)\n\t/usr/local/go/src/time/time.go:1099\ngithub.com/pingcap/tidb/types.(*Time).ConvertTimeZone(0xc0005f4da0, 0x5105060, 0x0, 0x0, 0xf)\n\t/home/jenkins/agent/workspace/optimization-build-tidb-linux-amd/go/src/github.com/pingcap/tidb/types/time.go:362 +0x186\ngithub.com/pingcap/tidb/util/codec.appendUintToChunk(0x1963ff0854000000, 0xc001a1caf0, 0x3, 0xc0014cbc80, 0x0, 0x0, 0x1963ff0854000000)\n\t/home/jenkins/agent/workspace/optimization-build-tidb-linux-amd/go/src/github.com/pingcap/tidb/util/codec/codec.go:1169 +0x12a\ngithub.com/pingcap/tidb/util/codec.(*Decoder).DecodeOne(0xc0005f5028, 0xc002c1cee2, 0x0, 0x0, 0x3, 0xc0014cbc80, 0xc000d53533, 0x0, 0x0, 0x0, ...)\n\t/home/jenkins/agent/workspace/optimization-build-tidb-linux-amd/go/src/github.com/pingcap/tidb/util/codec/codec.go:1071 +0x4bf\ngithub.com/pingcap/tidb/executor.AnalyzeColumnsExec.decodeSampleDataWithVirtualColumn(0x3c2a450, 0xc001511540, 0xc001baed80, 0xc002802bc0, 0x5, 0x8, 0x3c12b98, 0xc0018741f8, 0x1, 0xc001b11680, ...)\n\t/home/jenkins/agent/workspace/optimization-build-tidb-linux-amd/go/src/github.com/pingcap/tidb/executor/analyze.go:791 +0x2c5\ngithub.com/pingcap/tidb/executor.(*AnalyzeColumnsExec).buildSamplingStats(0xc0021ca200, 0xc001874228, 0x1, 0x1, 0x0, 0x57b5a40, 0x0, 0x0, 0xc0010762a0, 0x0, ...)\n\t/home/jenkins/agent/workspace/optimization-build-tidb-linux-amd/go/src/github.com/pingcap/tidb/executor/analyze.go:891 +0x1e08\ngithub.com/pingcap/tidb/executor.analyzeColumnsPushdown(0xc0021ca200, 0xc0020df580, 0xc002494f01, 0x35f3980)\n\t/home/jenkins/agent/workspace/optimization-build-tidb-linux-amd/go/src/github.com/pingcap/tidb/executor/analyze.go:607 +0x512\ngithub.com/pingcap/tidb/executor.(*AnalyzeExec).analyzeWorker(0xc00115e870, 0xc0010761e0, 0xc001076240, 0x1)\n\t/home/jenkins/agent/workspace/optimization-build-tidb-linux-amd/go/src/github.com/pingcap/tidb/executor/analyze.go:279 +0x1f2\ncreated by github.com/pingcap/tidb/executor.(*AnalyzeExec).Next\n\t/home/jenkins/agent/workspace/optimization-build-tidb-linux-amd/go/src/github.com/pingcap/tidb/executor/analyze.go:93 +0x158\n"]
```

### 4. What is your TiDB version? (Required)

<!-- Paste the output of SELECT tidb_version() -->

master (6d032c58acd43538e40eda2b71aa69b7dc58f90d)
release-5.1 (ffba3ee09ab05c15655b7d0cd48a1baf16bbab5a)


Discussion:
/assign
After analysis, this panic is caused by https://github.com/pingcap/tidb/blob/8a0be6181bf47d4f6ca17af856bcab93e3c62e02/executor/analyze.go#L777 . The e.ctx.GetSessionVaris().TimeZone is nil, therefore the decoder paniced because it needs a location information to decode the timestamp.

This session variable lacks initialization in the session pool https://github.com/pingcap/tidb/blob/8a0be6181bf47d4f6ca17af856bcab93e3c62e02/session/session.go#L878 , a simple fix is to add the time zone initialization here, but I double if there are systematical issues here (i.e. how to avoid such kind of problems in principle). @tiancaiamao @morgo do you have suggestions on this?

cc @winoros 
I've seen this bug before in other places. There is a helper function for TimeZone which can be used instead:
https://github.com/pingcap/tidb/blob/12cc1d038218ad5306d85ee804a860ddb8c81b9e/sessionctx/variable/session.go#L1330-L1337

Does this help?
> I've seen this bug before in other places. There is a helper function for TimeZone which can be used instead:
> https://github.com/pingcap/tidb/blob/12cc1d038218ad5306d85ee804a860ddb8c81b9e/sessionctx/variable/session.go#L1330-L1337
> 
> Does this help?

sure for this case!

but I believe we might need a more systematic way to avoid similar problems in the future.
## Please edit this comment or add a new comment to complete the following information

### Not a bug

1. Remove the 'type/bug' label
2. Add notes to indicate why it is not a bug

### Duplicate bug

1. Add the 'type/duplicate' label
2. Add the link to the original bug

### Bug

Note: Make Sure that 'component', and 'severity' labels are added
Example for how to fill out the template: https://github.com/pingcap/tidb/issues/20100

#### 1. Root Cause Analysis (RCA) (optional) 
<!-- Write down the reason why this bug occurs -->

#### 2. Symptom (optional)

<!-- What will the user see when this bug occurs. The error message may be in the terminal, log or monitoring -->

#### 3. All Trigger Conditions (optional)

<!-- All the user scenarios that may trigger this bug -->

#### 4. Workaround (optional)

#### 5. Affected versions

<!--
In the format of [start_version:end_version], multiple version ranges are
accepted. If the bug only affects the unreleased version, please input:
"unreleased". For example:

Notes:
  1. Do not use any white spaces in '[]'.
  2. The range in '[]' is a closed interval
  3. The version format is `v$Major.$Minor.$Patch`, the $Majoy and $Minor
     number in a version range should be the same. [v3.0.1:v3.1.2] is
     invalid because the $Minor number of the version range is different.

Example 1: [v3.0.1:v3.0.5], [v4.0.1:v4.0.5]
Example 2: unreleased
-->

#### 6. Fixed versions

<!--
The first released version that contains this fix in each minor version. If the bug's affected version has been released, the fixed version should be a detailed version number; If the bug doesn't affect any released version, the fixed version can be "master";  

Example 1: v3.0.13, v4.0.5
Example 2: master
-->



Patch:
diff --git a/executor/analyze.go b/executor/analyze.go
index e9a6b2268b33d..4b59913efe4ac 100644
--- a/executor/analyze.go
+++ b/executor/analyze.go
@@ -778,7 +778,7 @@ func (e AnalyzeColumnsExec) decodeSampleDataWithVirtualColumn(
 		totFts = append(totFts, col.RetType)
 	}
 	chk := chunk.NewChunkWithCapacity(totFts, len(collector.Samples))
-	decoder := codec.NewDecoder(chk, e.ctx.GetSessionVars().TimeZone)
+	decoder := codec.NewDecoder(chk, e.ctx.GetSessionVars().Location())
 	for _, sample := range collector.Samples {
 		for i := range sample.Columns {
 			if schema.Columns[i].VirtualExpr != nil {


Test patch:
diff --git a/statistics/handle/update_test.go b/statistics/handle/update_test.go
index d23bb51cca050..badb7eb95b860 100644
--- a/statistics/handle/update_test.go
+++ b/statistics/handle/update_test.go
@@ -583,6 +583,30 @@ func (s *testSerialStatsSuite) TestAutoAnalyzeOnEmptyTable(c *C) {
 	c.Assert(s.do.StatsHandle().HandleAutoAnalyze(s.do.InfoSchema()), IsTrue)
 }
 
+func (s *testSerialStatsSuite) TestIssue25700(c *C) {
+	defer cleanEnv(c, s.store, s.do)
+	tk := testkit.NewTestKit(c, s.store)
+	oriStart := tk.MustQuery("select @@tidb_auto_analyze_start_time").Rows()[0][0].(string)
+	oriEnd := tk.MustQuery("select @@tidb_auto_analyze_end_time").Rows()[0][0].(string)
+	defer func() {
+		tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_start_time='%v'", oriStart))
+		tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_end_time='%v'", oriEnd))
+	}()
+	tk.MustExec("set global tidb_auto_analyze_start_time='00:00 +0000'")
+	tk.MustExec("set global tidb_auto_analyze_end_time='23:59 +0000'")
+
+	tk.MustExec("use test")
+	tk.MustExec("drop table if exists t")
+	tk.MustExec("CREATE TABLE `t` ( `ldecimal` decimal(32,4) DEFAULT NULL, `rdecimal` decimal(32,4) DEFAULT NULL, `gen_col` decimal(36,4) GENERATED ALWAYS AS (`ldecimal` + `rdecimal`) VIRTUAL, `col_timestamp` timestamp(3) NULL DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;")
+	tk.MustExec("analyze table t")
+	tk.MustExec("INSERT INTO `t` (`ldecimal`, `rdecimal`, `col_timestamp`) VALUES (2265.2200, 9843.4100, '1999-12-31 16:00:00')" + strings.Repeat(", (2265.2200, 9843.4100, '1999-12-31 16:00:00')", int(handle.AutoAnalyzeMinCnt)))
+	c.Assert(s.do.StatsHandle().DumpStatsDeltaToKV(handle.DumpAll), IsNil)
+	c.Assert(s.do.StatsHandle().Update(s.do.InfoSchema()), IsNil)
+
+	c.Assert(s.do.StatsHandle().HandleAutoAnalyze(s.do.InfoSchema()), IsTrue)
+	c.Assert(tk.MustQuery("show analyze status").Rows()[1][7], Equals, "finished")
+}
+
 func (s *testSerialStatsSuite) TestAutoAnalyzeOnChangeAnalyzeVer(c *C) {
 	defer cleanEnv(c, s.store, s.do)
 	tk := testkit.NewTestKit(c, s.store)