executor: convert json numeric value to float64 in hash (#38065)

close pingcap/tidb#38049
This commit is contained in:
YangKeao
2022-09-21 23:39:02 -04:00
committed by GitHub
parent 85b8104e21
commit aef905cb16
6 changed files with 50 additions and 5 deletions

View File

@ -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:

View File

@ -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)

View File

@ -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)

View File

@ -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++ {

View File

@ -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())
}
@ -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..

View File

@ -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) {