Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

expression: check timezone when encoding timestamp datum (#10303) #10345

Merged
merged 2 commits into from
May 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion expression/distsql_builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ func convertTime(data []byte, ftPB *tipb.FieldType, tz *time.Location) (*Constan
if err != nil {
return nil, errors.Trace(err)
}
if ft.Tp == mysql.TypeTimestamp && !t.IsZero() {
if ft.Tp == mysql.TypeTimestamp && tz != time.UTC {
err = t.ConvertTimeZone(time.UTC, tz)
if err != nil {
return nil, errors.Trace(err)
Expand Down
8 changes: 3 additions & 5 deletions expression/expr_to_pb.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func (pc PbConverter) conOrCorColToPBExpr(expr Expression) *tipb.Expr {
logutil.Logger(context.Background()).Error("eval constant or correlated column", zap.String("expression", expr.ExplainInfo()), zap.Error(err))
return nil
}
tp, val, ok := pc.encodeDatum(d)
tp, val, ok := pc.encodeDatum(ft, d)
if !ok {
return nil
}
Expand All @@ -117,7 +117,7 @@ func (pc PbConverter) conOrCorColToPBExpr(expr Expression) *tipb.Expr {
return &tipb.Expr{Tp: tp, Val: val, FieldType: ToPBFieldType(ft)}
}

func (pc *PbConverter) encodeDatum(d types.Datum) (tipb.ExprType, []byte, bool) {
func (pc *PbConverter) encodeDatum(ft *types.FieldType, d types.Datum) (tipb.ExprType, []byte, bool) {
var (
tp tipb.ExprType
val []byte
Expand Down Expand Up @@ -157,13 +157,11 @@ func (pc *PbConverter) encodeDatum(d types.Datum) (tipb.ExprType, []byte, bool)
case types.KindMysqlTime:
if pc.client.IsRequestTypeSupported(kv.ReqTypeDAG, int64(tipb.ExprType_MysqlTime)) {
tp = tipb.ExprType_MysqlTime
t := d.GetMysqlTime()
v, err := t.ToPackedUint()
val, err := codec.EncodeMySQLTime(pc.sc, d, ft.Tp, nil)
if err != nil {
logutil.Logger(context.Background()).Error("encode mysql time", zap.Error(err))
return tp, nil, false
}
val = codec.EncodeUint(nil, v)
return tp, val, true
}
return tp, nil, false
Expand Down
14 changes: 14 additions & 0 deletions expression/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3897,3 +3897,17 @@ where
datediff(b.date8, date(from_unixtime(a.starttime))) >= 0`
tk.MustQuery(q)
}

func (s *testIntegrationSuite) TestTimestampDatumEncode(c *C) {
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("use test")
tk.MustExec(`drop table if exists t;`)
tk.MustExec(`create table t (a bigint primary key, b timestamp)`)
tk.MustExec(`insert into t values (1, "2019-04-29 11:56:12")`)
tk.MustQuery(`explain select * from t where b = (select max(b) from t)`).Check(testkit.Rows(
"TableReader_45 10.00 root data:Selection_44",
"└─Selection_44 10.00 cop eq(test.t.b, 2019-04-29 11:56:12)",
" └─TableScan_43 10000.00 cop table:t, range:[-inf,+inf], keep order:false, stats:pseudo",
))
tk.MustQuery(`select * from t where b = (select max(b) from t)`).Check(testkit.Rows(`1 2019-04-29 11:56:12`))
}
38 changes: 25 additions & 13 deletions util/codec/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,10 @@ func encode(sc *stmtctx.StatementContext, b []byte, vals []types.Datum, comparab
b = encodeBytes(b, vals[i].GetBytes(), comparable)
case types.KindMysqlTime:
b = append(b, uintFlag)
t := vals[i].GetMysqlTime()
// Encoding timestamp need to consider timezone.
// If it's not in UTC, transform to UTC first.
if t.Type == mysql.TypeTimestamp && sc.TimeZone != time.UTC {
err = t.ConvertTimeZone(sc.TimeZone, time.UTC)
if err != nil {
return nil, errors.Trace(err)
}
}
var v uint64
v, err = t.ToPackedUint()
b, err = EncodeMySQLTime(sc, vals[i], mysql.TypeUnspecified, b)
if err != nil {
return nil, errors.Trace(err)
return nil, err
}
b = EncodeUint(b, v)
case types.KindMysqlDuration:
// duration may have negative value, so we cannot use String to encode directly.
b = append(b, durationFlag)
Expand Down Expand Up @@ -134,6 +123,29 @@ func encode(sc *stmtctx.StatementContext, b []byte, vals []types.Datum, comparab
return b, errors.Trace(err)
}

// EncodeMySQLTime encodes datum of `KindMysqlTime` to []byte.
func EncodeMySQLTime(sc *stmtctx.StatementContext, d types.Datum, tp byte, b []byte) (_ []byte, err error) {
t := d.GetMysqlTime()
// Encoding timestamp need to consider timezone. If it's not in UTC, transform to UTC first.
// This is compatible with `PBToExpr > convertTime`, and coprocessor assumes the passed timestamp is in UTC as well.
if tp == mysql.TypeUnspecified {
tp = t.Type
}
if tp == mysql.TypeTimestamp && sc.TimeZone != time.UTC {
err = t.ConvertTimeZone(sc.TimeZone, time.UTC)
if err != nil {
return nil, err
}
}
var v uint64
v, err = t.ToPackedUint()
if err != nil {
return nil, err
}
b = EncodeUint(b, v)
return b, nil
}

func encodeBytes(b []byte, v []byte, comparable bool) []byte {
if comparable {
b = append(b, bytesFlag)
Expand Down