Skip to content

Commit

Permalink
executor: implement json time/duration (#37579)
Browse files Browse the repository at this point in the history
close #9988, close #31104
  • Loading branch information
YangKeao authored Sep 8, 2022
1 parent 330ed6d commit cdcbfc8
Show file tree
Hide file tree
Showing 17 changed files with 375 additions and 119 deletions.
2 changes: 1 addition & 1 deletion ddl/column_type_change_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1141,7 +1141,7 @@ func TestColumnTypeChangeFromDateTimeTypeToOthers(t *testing.T) {
tk.MustExec("alter table t modify dt json")
tk.MustExec("alter table t modify tmp json")
tk.MustExec("alter table t modify y json")
tk.MustQuery("select * from t").Check(testkit.Rows("\"2020-10-30\" \"19:38:25.001\" \"2020-10-30 08:21:33.455555\" \"2020-10-30 08:21:33.455555\" 2020"))
tk.MustQuery("select * from t").Check(testkit.Rows("\"2020-10-30\" \"19:38:25.001000\" \"2020-10-30 08:21:33.455555\" \"2020-10-30 08:21:33.455555\" 2020"))
}

func TestColumnTypeChangeFromJsonToOthers(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion executor/aggfuncs/func_json_arrayagg.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func (e *jsonArrayagg) UpdatePartialResult(sctx sessionctx.Context, rowsInGroup
}

switch x := realItem.(type) {
case nil, bool, int64, uint64, float64, string, types.BinaryJSON, types.Opaque:
case nil, bool, int64, uint64, float64, string, types.BinaryJSON, types.Opaque, types.Time, types.Duration:
p.entries = append(p.entries, realItem)
memDelta += getValMemDelta(realItem)
default:
Expand Down
12 changes: 5 additions & 7 deletions executor/aggfuncs/func_json_arrayagg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
)

func TestMergePartialResult4JsonArrayagg(t *testing.T) {
typeList := []byte{mysql.TypeLonglong, mysql.TypeDouble, mysql.TypeFloat, mysql.TypeString, mysql.TypeJSON}
typeList := []byte{mysql.TypeLonglong, mysql.TypeDouble, mysql.TypeFloat, mysql.TypeString, mysql.TypeJSON, mysql.TypeDate, mysql.TypeDuration}

tests := make([]aggTest, 0, len(typeList))
numRows := 5
Expand Down Expand Up @@ -64,7 +64,7 @@ func TestMergePartialResult4JsonArrayagg(t *testing.T) {
}

func TestJsonArrayagg(t *testing.T) {
typeList := []byte{mysql.TypeLonglong, mysql.TypeDouble, mysql.TypeFloat, mysql.TypeString, mysql.TypeJSON}
typeList := []byte{mysql.TypeLonglong, mysql.TypeDouble, mysql.TypeFloat, mysql.TypeString, mysql.TypeJSON, mysql.TypeDate, mysql.TypeDuration}

tests := make([]aggTest, 0, len(typeList))
numRows := 5
Expand Down Expand Up @@ -116,11 +116,9 @@ func jsonArrayaggMemDeltaGens(srcChk *chunk.Chunk, dataType *types.FieldType) (m
// +1 for the memory usage of the JSONTypeCode of json
memDelta += int64(len(val.Value) + 1)
case mysql.TypeDuration:
val := row.GetDuration(0, dataType.GetDecimal())
memDelta += int64(len(val.String()))
case mysql.TypeDate:
val := row.GetTime(0)
memDelta += int64(len(val.String()))
memDelta += aggfuncs.DefDurationSize
case mysql.TypeDate, mysql.TypeDatetime:
memDelta += aggfuncs.DefTimeSize
case mysql.TypeNewDecimal:
memDelta += aggfuncs.DefFloat64Size
default:
Expand Down
4 changes: 2 additions & 2 deletions executor/aggfuncs/func_json_objectagg.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func (e *jsonObjectAgg) UpdatePartialResult(sctx sessionctx.Context, rowsInGroup
}

switch x := realVal.(type) {
case nil, bool, int64, uint64, float64, string, types.BinaryJSON, types.Opaque:
case nil, bool, int64, uint64, float64, string, types.BinaryJSON, types.Opaque, types.Time, types.Duration:
if _, ok := p.entries[key]; !ok {
memDelta += int64(len(key)) + getValMemDelta(realVal)
if len(p.entries)+1 > (1<<p.bInMap)*hack.LoadFactorNum/hack.LoadFactorDen {
Expand Down Expand Up @@ -145,7 +145,7 @@ func getRealJSONValue(value types.Datum, ft *types.FieldType) (interface{}, erro
return nil, errors.Trace(err)
}
realVal = float64Val
case []uint8, types.Time, types.Duration:
case []uint8:
strVal, err := types.ToString(x)
if err != nil {
return nil, errors.Trace(err)
Expand Down
10 changes: 6 additions & 4 deletions executor/aggfuncs/func_json_objectagg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ func TestMergePartialResult4JsonObjectagg(t *testing.T) {
types.NewFieldType(mysql.TypeString),
types.NewFieldType(mysql.TypeJSON),
types.NewFieldTypeBuilder().SetType(mysql.TypeString).SetFlen(10).SetCharset(charset.CharsetBin).BuildP(),
types.NewFieldType(mysql.TypeDate),
types.NewFieldType(mysql.TypeDuration),
}
var argCombines [][]*types.FieldType
for i := 0; i < len(typeList); i++ {
Expand Down Expand Up @@ -112,6 +114,8 @@ func TestJsonObjectagg(t *testing.T) {
types.NewFieldType(mysql.TypeString),
types.NewFieldType(mysql.TypeJSON),
types.NewFieldTypeBuilder().SetType(mysql.TypeString).SetFlen(10).SetCharset(charset.CharsetBin).BuildP(),
types.NewFieldType(mysql.TypeDate),
types.NewFieldType(mysql.TypeDuration),
}
var argCombines [][]*types.FieldType
for i := 0; i < len(typeList); i++ {
Expand Down Expand Up @@ -244,11 +248,9 @@ func jsonMultiArgsMemDeltaGens(srcChk *chunk.Chunk, dataTypes []*types.FieldType
// +1 for the memory usage of the JSONTypeCode of json
memDelta += int64(len(val.Value) + 1)
case mysql.TypeDuration:
val := row.GetDuration(1, dataTypes[1].GetDecimal())
memDelta += int64(len(val.String()))
memDelta += aggfuncs.DefDurationSize
case mysql.TypeDate:
val := row.GetTime(1)
memDelta += int64(len(val.String()))
memDelta += aggfuncs.DefTimeSize
case mysql.TypeNewDecimal:
memDelta += aggfuncs.DefFloat64Size
default:
Expand Down
2 changes: 1 addition & 1 deletion expression/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ func newJSONTimeGener() *jsonTimeGener {

func (g *jsonTimeGener) gen() interface{} {
tm := types.NewTime(getRandomTime(g.randGen.Rand), mysql.TypeDatetime, types.DefaultFsp)
return types.CreateBinaryJSON(tm.String())
return types.CreateBinaryJSON(tm)
}

type rangeDurationGener struct {
Expand Down
97 changes: 73 additions & 24 deletions expression/builtin_cast.go
Original file line number Diff line number Diff line change
Expand Up @@ -744,7 +744,7 @@ func (b *builtinCastDurationAsJSONSig) evalJSON(row chunk.Row) (res types.Binary
return res, isNull, err
}
val.Fsp = types.MaxFsp
return types.CreateBinaryJSON(val.String()), false, nil
return types.CreateBinaryJSON(val), false, nil
}

type builtinCastTimeAsJSONSig struct {
Expand All @@ -765,7 +765,7 @@ func (b *builtinCastTimeAsJSONSig) evalJSON(row chunk.Row) (res types.BinaryJSON
if val.Type() == mysql.TypeDatetime || val.Type() == mysql.TypeTimestamp {
val.SetFsp(types.MaxFsp)
}
return types.CreateBinaryJSON(val.String()), false, nil
return types.CreateBinaryJSON(val), false, nil
}

type builtinCastRealAsRealSig struct {
Expand Down Expand Up @@ -1740,20 +1740,49 @@ func (b *builtinCastJSONAsTimeSig) evalTime(row chunk.Row) (res types.Time, isNu
if isNull || err != nil {
return res, isNull, err
}
s, err := val.Unquote()
if err != nil {
return res, false, err
}
sc := b.ctx.GetSessionVars().StmtCtx
res, err = types.ParseTime(sc, s, b.tp.GetType(), b.tp.GetDecimal())
if err != nil {
return types.ZeroTime, true, handleInvalidTimeError(b.ctx, err)
}
if b.tp.GetType() == mysql.TypeDate {
// Truncate hh:mm:ss part if the type is Date.
res.SetCoreTime(types.FromDate(res.Year(), res.Month(), res.Day(), 0, 0, 0, 0))

switch val.TypeCode {
case types.JSONTypeCodeDate, types.JSONTypeCodeDatetime, types.JSONTypeCodeTimestamp:
res = val.GetTime()
res.SetType(b.tp.GetType())
if b.tp.GetType() == mysql.TypeDate {
// Truncate hh:mm:ss part if the type is Date.
res.SetCoreTime(types.FromDate(res.Year(), res.Month(), res.Day(), 0, 0, 0, 0))
}
return res, isNull, err
case types.JSONTypeCodeDuration:
duration := val.GetDuration()

sc := b.ctx.GetSessionVars().StmtCtx
ts, err := getStmtTimestamp(b.ctx)
if err != nil {
ts = gotime.Now()
}
res, err = duration.ConvertToTimeWithTimestamp(sc, b.tp.GetType(), ts)
if err != nil {
return types.ZeroTime, true, handleInvalidTimeError(b.ctx, err)
}
res, err = res.RoundFrac(sc, b.tp.GetDecimal())
return res, isNull, err
case types.JSONTypeCodeString:
s, err := val.Unquote()
if err != nil {
return res, false, err
}
sc := b.ctx.GetSessionVars().StmtCtx
res, err = types.ParseTime(sc, s, b.tp.GetType(), b.tp.GetDecimal())
if err != nil {
return types.ZeroTime, true, handleInvalidTimeError(b.ctx, err)
}
if b.tp.GetType() == mysql.TypeDate {
// Truncate hh:mm:ss part if the type is Date.
res.SetCoreTime(types.FromDate(res.Year(), res.Month(), res.Day(), 0, 0, 0, 0))
}
return res, isNull, err
default:
err = types.ErrTruncatedWrongVal.GenWithStackByArgs(types.TypeStr(b.tp.GetType()), val.String())
return res, true, b.ctx.GetSessionVars().StmtCtx.HandleTruncate(err)
}
return
}

type builtinCastJSONAsDurationSig struct {
Expand All @@ -1771,16 +1800,36 @@ func (b *builtinCastJSONAsDurationSig) evalDuration(row chunk.Row) (res types.Du
if isNull || err != nil {
return res, isNull, err
}
s, err := val.Unquote()
if err != nil {
return res, false, err
}
res, _, err = types.ParseDuration(b.ctx.GetSessionVars().StmtCtx, s, b.tp.GetDecimal())
if types.ErrTruncatedWrongVal.Equal(err) {
sc := b.ctx.GetSessionVars().StmtCtx
err = sc.HandleTruncate(err)

stmtCtx := b.ctx.GetSessionVars().StmtCtx

switch val.TypeCode {
case types.JSONTypeCodeDate, types.JSONTypeCodeDatetime, types.JSONTypeCodeTimestamp:
time := val.GetTime()
res, err = time.ConvertToDuration()
if err != nil {
return res, false, err
}
res, err = res.RoundFrac(b.tp.GetDecimal(), b.ctx.GetSessionVars().Location())
return res, isNull, err
case types.JSONTypeCodeDuration:
res = val.GetDuration()
return res, isNull, err
case types.JSONTypeCodeString:
s, err := val.Unquote()
if err != nil {
return res, false, err
}
res, _, err = types.ParseDuration(stmtCtx, s, b.tp.GetDecimal())
if types.ErrTruncatedWrongVal.Equal(err) {
sc := b.ctx.GetSessionVars().StmtCtx
err = sc.HandleTruncate(err)
}
return res, isNull, err
default:
err = types.ErrTruncatedWrongVal.GenWithStackByArgs("TIME", val.String())
return res, true, stmtCtx.HandleTruncate(err)
}
return
}

// inCastContext is session key type that indicates whether executing
Expand Down
4 changes: 2 additions & 2 deletions expression/builtin_cast_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,10 +286,10 @@ var (
jsonInt = types.NewDatum(types.CreateBinaryJSON(int64(3)))

// jsonTime indicates "CURRENT_DAY 12:59:59"
jsonTime = types.NewDatum(types.CreateBinaryJSON(tm.String()))
jsonTime = types.NewDatum(types.CreateBinaryJSON(tm))

// jsonDuration indicates
jsonDuration = types.NewDatum(types.CreateBinaryJSON(duration.String()))
jsonDuration = types.NewDatum(types.CreateBinaryJSON(duration))
)

func TestCastFuncSig(t *testing.T) {
Expand Down
Loading

0 comments on commit cdcbfc8

Please sign in to comment.