diff --git a/statistics/handle/bootstrap.go b/statistics/handle/bootstrap.go index 05e971488b..783e27800d 100644 --- a/statistics/handle/bootstrap.go +++ b/statistics/handle/bootstrap.go @@ -17,6 +17,7 @@ package handle import ( "context" "strconv" + "time" "github.com/pingcap/errors" "github.com/pingcap/tidb/config" @@ -25,6 +26,7 @@ import ( "github.com/pingcap/tidb/parser/model" "github.com/pingcap/tidb/parser/mysql" "github.com/pingcap/tidb/parser/terror" + "github.com/pingcap/tidb/sessionctx/stmtctx" "github.com/pingcap/tidb/statistics" "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/util/chunk" @@ -310,15 +312,18 @@ func (h *Handle) initStatsBuckets4Chunk(cache *statsCache, iter *chunk.Iterator4 } hist = &column.Histogram d := types.NewBytesDatum(row.GetBytes(5)) + // Setting TimeZone to time.UTC aligns with HistogramFromStorage and can fix #41938. However, #41985 still exist. + // TODO: do the correct time zone conversion for timestamp-type columns' upper/lower bounds. + sc := &stmtctx.StatementContext{TimeZone: time.UTC, AllowInvalidDate: true, IgnoreZeroInDate: true} var err error - lower, err = d.ConvertTo(h.mu.ctx.GetSessionVars().StmtCtx, &column.Info.FieldType) + lower, err = d.ConvertTo(sc, &column.Info.FieldType) if err != nil { logutil.BgLogger().Debug("decode bucket lower bound failed", zap.Error(err)) delete(table.Columns, histID) continue } d = types.NewBytesDatum(row.GetBytes(6)) - upper, err = d.ConvertTo(h.mu.ctx.GetSessionVars().StmtCtx, &column.Info.FieldType) + upper, err = d.ConvertTo(sc, &column.Info.FieldType) if err != nil { logutil.BgLogger().Debug("decode bucket upper bound failed", zap.Error(err)) delete(table.Columns, histID) @@ -335,7 +340,7 @@ func (h *Handle) initTopNCountSum(tableID, colID int64) (int64, error) { // So we need to add TopN total count here. ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) selSQL := "select sum(count) from mysql.stats_top_n where table_id = %? and is_index = 0 and hist_id = %?" - rs, err := h.mu.ctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, selSQL, tableID, colID) + rs, err := h.initStatsCtx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, selSQL, tableID, colID) if rs != nil { defer terror.Call(rs.Close) } diff --git a/statistics/handle/handletest/handle_test.go b/statistics/handle/handletest/handle_test.go index ae498c355c..28d4accc12 100644 --- a/statistics/handle/handletest/handle_test.go +++ b/statistics/handle/handletest/handle_test.go @@ -488,6 +488,23 @@ func TestInitStatsVer2(t *testing.T) { h.SetLease(0) } +func TestInitStatsIssue41938(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@global.tidb_analyze_version=1") + tk.MustExec("set @@session.tidb_analyze_version=1") + tk.MustExec("create table t1 (a timestamp primary key)") + tk.MustExec("insert into t1 values ('2023-03-07 14:24:30'), ('2023-03-07 14:24:31'), ('2023-03-07 14:24:32'), ('2023-03-07 14:24:33')") + tk.MustExec("analyze table t1 with 0 topn") + h := dom.StatsHandle() + // `InitStats` is only called when `Lease` is not 0, so here we just change it. + h.SetLease(time.Millisecond) + h.Clear() + require.NoError(t, h.InitStats(dom.InfoSchema())) + h.SetLease(0) +} + func TestReloadExtStatsLockRelease(t *testing.T) { store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store)