Skip to content

Commit

Permalink
executor: convert json numeric value to float64 in hash (pingcap#38065)
Browse files Browse the repository at this point in the history
  • Loading branch information
YangKeao authored Sep 22, 2022
1 parent 85b8104 commit aef905c
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 5 deletions.
3 changes: 1 addition & 2 deletions executor/aggfuncs/aggfunc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,7 @@ func distinctUpdateMemDeltaGens(srcChk *chunk.Chunk, dataType *types.FieldType)
case mysql.TypeJSON:
jsonVal := row.GetJSON(0)
bytes := make([]byte, 0)
bytes = append(bytes, jsonVal.TypeCode)
bytes = append(bytes, jsonVal.Value...)
bytes = jsonVal.HashValue(bytes)
val = string(bytes)
memDelta = int64(len(val))
default:
Expand Down
2 changes: 1 addition & 1 deletion executor/aggfuncs/func_count_distinct.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ func evalAndEncode(
if err != nil || isNull {
break
}
encodedBytes = appendJSON(encodedBytes, buf, val)
encodedBytes = val.HashValue(encodedBytes)
case types.ETString:
var val string
val, isNull, err = arg.EvalString(sctx, row)
Expand Down
34 changes: 34 additions & 0 deletions executor/executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5981,6 +5981,40 @@ func TestIsFastPlan(t *testing.T) {
}
}

func TestCountDistinctJSON(t *testing.T) {
store := testkit.CreateMockStore(t)

tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")

tk.MustExec("drop table if exists t")
tk.MustExec("create table t(j JSON)")
tk.MustExec("insert into t values('2010')")
tk.MustExec("insert into t values('2011')")
tk.MustExec("insert into t values('2012')")
tk.MustExec("insert into t values('2010.000')")
tk.MustExec("insert into t values(cast(? as JSON))", uint64(math.MaxUint64))
tk.MustExec("insert into t values(cast(? as JSON))", float64(math.MaxUint64))

tk.MustQuery("select count(distinct j) from t").Check(testkit.Rows("5"))
}

func TestHashJoinJSON(t *testing.T) {
store := testkit.CreateMockStore(t)

tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")

tk.MustExec("drop table if exists t")
tk.MustExec("create table t(id int(11), j JSON, d DOUBLE)")
tk.MustExec("insert into t values(0, '2010', 2010)")
tk.MustExec("insert into t values(1, '2011', 2011)")
tk.MustExec("insert into t values(2, '2012', 2012)")
tk.MustExec("insert into t values(3, cast(? as JSON), ?)", uint64(math.MaxUint64), float64(math.MaxUint64))

tk.MustQuery("select /*+inl_hash_join(t2)*/ t1.id, t2.id from t t1 join t t2 on t1.j = t2.d;").Check(testkit.Rows("0 0", "1 1", "2 2"))
}

func TestBinaryStrNumericOperator(t *testing.T) {
store := testkit.CreateMockStore(t)

Expand Down
6 changes: 6 additions & 0 deletions types/json_binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,12 @@ func (bj BinaryJSON) HashValue(buf []byte) []byte {
} else {
buf = append(buf, bj.Value...)
}
case JSONTypeCodeUint64:
if bj.GetUint64() == uint64(float64(bj.GetUint64())) {
buf = appendBinaryFloat64(buf, float64(bj.GetUint64()))
} else {
buf = append(buf, bj.Value...)
}
case JSONTypeCodeArray:
elemCount := int(jsonEndian.Uint32(bj.Value))
for i := 0; i < elemCount; i++ {
Expand Down
7 changes: 5 additions & 2 deletions util/codec/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,8 @@ func encodeHashChunkRowIdx(sc *stmtctx.StatementContext, row chunk.Row, tp *type
b = (*[unsafe.Sizeof(v)]byte)(unsafe.Pointer(&v))[:]
case mysql.TypeJSON:
flag = jsonFlag
b = row.GetBytes(idx)
json := row.GetJSON(idx)
b = json.HashValue(b)
default:
return 0, nil, errors.Errorf("unsupport column type for encode %d", tp.GetType())
}
Expand Down Expand Up @@ -645,7 +646,9 @@ func HashChunkSelected(sc *stmtctx.StatementContext, h []hash.Hash64, chk *chunk
isNull[i] = !ignoreNull
} else {
buf[0] = jsonFlag
b = column.GetBytes(i)
json := column.GetJSON(i)
b = b[:0]
b = json.HashValue(b)
}

// As the golang doc described, `Hash.Write` never returns an error..
Expand Down
3 changes: 3 additions & 0 deletions util/codec/codec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1187,6 +1187,9 @@ func TestHashChunkRow(t *testing.T) {

testHashChunkRowEqual(t, "x", []byte("x"), true)
testHashChunkRowEqual(t, "x", []byte("y"), false)

testHashChunkRowEqual(t, types.CreateBinaryJSON(int64(1)), types.CreateBinaryJSON(float64(1.0)), true)
testHashChunkRowEqual(t, types.CreateBinaryJSON(uint64(math.MaxUint64)), types.CreateBinaryJSON(float64(math.MaxUint64)), false)
}

func TestValueSizeOfSignedInt(t *testing.T) {
Expand Down

0 comments on commit aef905c

Please sign in to comment.