Skip to content

Commit 3b838a6

Browse files
eurekakaerjiaqing
authored andcommitted
expression: check timezone when encoding timestamp datum (pingcap#10303)
1 parent 9557708 commit 3b838a6

File tree

4 files changed

+43
-19
lines changed

4 files changed

+43
-19
lines changed

expression/distsql_builtin.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,7 @@ func convertTime(data []byte, ftPB *tipb.FieldType, tz *time.Location) (*Constan
557557
if err != nil {
558558
return nil, errors.Trace(err)
559559
}
560-
if ft.Tp == mysql.TypeTimestamp && !t.IsZero() {
560+
if ft.Tp == mysql.TypeTimestamp && tz != time.UTC {
561561
err = t.ConvertTimeZone(time.UTC, tz)
562562
if err != nil {
563563
return nil, errors.Trace(err)

expression/expr_to_pb.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ func (pc PbConverter) conOrCorColToPBExpr(expr Expression) *tipb.Expr {
106106
logutil.Logger(context.Background()).Error("eval constant or correlated column", zap.String("expression", expr.ExplainInfo()), zap.Error(err))
107107
return nil
108108
}
109-
tp, val, ok := pc.encodeDatum(d)
109+
tp, val, ok := pc.encodeDatum(ft, d)
110110
if !ok {
111111
return nil
112112
}
@@ -117,7 +117,7 @@ func (pc PbConverter) conOrCorColToPBExpr(expr Expression) *tipb.Expr {
117117
return &tipb.Expr{Tp: tp, Val: val, FieldType: ToPBFieldType(ft)}
118118
}
119119

120-
func (pc *PbConverter) encodeDatum(d types.Datum) (tipb.ExprType, []byte, bool) {
120+
func (pc *PbConverter) encodeDatum(ft *types.FieldType, d types.Datum) (tipb.ExprType, []byte, bool) {
121121
var (
122122
tp tipb.ExprType
123123
val []byte
@@ -157,13 +157,11 @@ func (pc *PbConverter) encodeDatum(d types.Datum) (tipb.ExprType, []byte, bool)
157157
case types.KindMysqlTime:
158158
if pc.client.IsRequestTypeSupported(kv.ReqTypeDAG, int64(tipb.ExprType_MysqlTime)) {
159159
tp = tipb.ExprType_MysqlTime
160-
t := d.GetMysqlTime()
161-
v, err := t.ToPackedUint()
160+
val, err := codec.EncodeMySQLTime(pc.sc, d, ft.Tp, nil)
162161
if err != nil {
163162
logutil.Logger(context.Background()).Error("encode mysql time", zap.Error(err))
164163
return tp, nil, false
165164
}
166-
val = codec.EncodeUint(nil, v)
167165
return tp, val, true
168166
}
169167
return tp, nil, false

expression/integration_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3897,3 +3897,17 @@ where
38973897
datediff(b.date8, date(from_unixtime(a.starttime))) >= 0`
38983898
tk.MustQuery(q)
38993899
}
3900+
3901+
func (s *testIntegrationSuite) TestTimestampDatumEncode(c *C) {
3902+
tk := testkit.NewTestKit(c, s.store)
3903+
tk.MustExec("use test")
3904+
tk.MustExec(`drop table if exists t;`)
3905+
tk.MustExec(`create table t (a bigint primary key, b timestamp)`)
3906+
tk.MustExec(`insert into t values (1, "2019-04-29 11:56:12")`)
3907+
tk.MustQuery(`explain select * from t where b = (select max(b) from t)`).Check(testkit.Rows(
3908+
"TableReader_43 10.00 root data:Selection_42",
3909+
"└─Selection_42 10.00 cop eq(test.t.b, 2019-04-29 11:56:12)",
3910+
" └─TableScan_41 10000.00 cop table:t, range:[-inf,+inf], keep order:false, stats:pseudo",
3911+
))
3912+
tk.MustQuery(`select * from t where b = (select max(b) from t)`).Check(testkit.Rows(`1 2019-04-29 11:56:12`))
3913+
}

util/codec/codec.go

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,21 +67,10 @@ func encode(sc *stmtctx.StatementContext, b []byte, vals []types.Datum, comparab
6767
b = encodeBytes(b, vals[i].GetBytes(), comparable)
6868
case types.KindMysqlTime:
6969
b = append(b, uintFlag)
70-
t := vals[i].GetMysqlTime()
71-
// Encoding timestamp need to consider timezone.
72-
// If it's not in UTC, transform to UTC first.
73-
if t.Type == mysql.TypeTimestamp && sc.TimeZone != time.UTC {
74-
err = t.ConvertTimeZone(sc.TimeZone, time.UTC)
75-
if err != nil {
76-
return nil, errors.Trace(err)
77-
}
78-
}
79-
var v uint64
80-
v, err = t.ToPackedUint()
70+
b, err = EncodeMySQLTime(sc, vals[i], mysql.TypeUnspecified, b)
8171
if err != nil {
82-
return nil, errors.Trace(err)
72+
return nil, err
8373
}
84-
b = EncodeUint(b, v)
8574
case types.KindMysqlDuration:
8675
// duration may have negative value, so we cannot use String to encode directly.
8776
b = append(b, durationFlag)
@@ -134,6 +123,29 @@ func encode(sc *stmtctx.StatementContext, b []byte, vals []types.Datum, comparab
134123
return b, errors.Trace(err)
135124
}
136125

126+
// EncodeMySQLTime encodes datum of `KindMysqlTime` to []byte.
127+
func EncodeMySQLTime(sc *stmtctx.StatementContext, d types.Datum, tp byte, b []byte) (_ []byte, err error) {
128+
t := d.GetMysqlTime()
129+
// Encoding timestamp need to consider timezone. If it's not in UTC, transform to UTC first.
130+
// This is compatible with `PBToExpr > convertTime`, and coprocessor assumes the passed timestamp is in UTC as well.
131+
if tp == mysql.TypeUnspecified {
132+
tp = t.Type
133+
}
134+
if tp == mysql.TypeTimestamp && sc.TimeZone != time.UTC {
135+
err = t.ConvertTimeZone(sc.TimeZone, time.UTC)
136+
if err != nil {
137+
return nil, err
138+
}
139+
}
140+
var v uint64
141+
v, err = t.ToPackedUint()
142+
if err != nil {
143+
return nil, err
144+
}
145+
b = EncodeUint(b, v)
146+
return b, nil
147+
}
148+
137149
func encodeBytes(b []byte, v []byte, comparable bool) []byte {
138150
if comparable {
139151
b = append(b, bytesFlag)

0 commit comments

Comments
 (0)