Files
tidb/util/rowcodec/rowcodec_test.go
2022-09-05 19:42:55 +08:00

918 lines
23 KiB
Go

// Copyright 2019 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rowcodec_test
import (
"math"
"strings"
"testing"
"time"
"github.com/pingcap/tidb/kv"
"github.com/pingcap/tidb/parser/mysql"
"github.com/pingcap/tidb/sessionctx/stmtctx"
"github.com/pingcap/tidb/tablecodec"
"github.com/pingcap/tidb/types"
"github.com/pingcap/tidb/util/chunk"
"github.com/pingcap/tidb/util/codec"
"github.com/pingcap/tidb/util/collate"
"github.com/pingcap/tidb/util/rowcodec"
"github.com/stretchr/testify/require"
)
type testData struct {
id int64
ft *types.FieldType
input types.Datum
output types.Datum
def *types.Datum
handle bool
}
func TestEncodeLargeSmallReuseBug(t *testing.T) {
// reuse one rowcodec.Encoder.
var encoder rowcodec.Encoder
colFt := types.NewFieldType(mysql.TypeString)
largeColID := int64(300)
b, err := encoder.Encode(&stmtctx.StatementContext{}, []int64{largeColID}, []types.Datum{types.NewBytesDatum([]byte(""))}, nil)
require.NoError(t, err)
bDecoder := rowcodec.NewDatumMapDecoder([]rowcodec.ColInfo{
{
ID: largeColID,
Ft: colFt,
IsPKHandle: false,
},
}, nil)
_, err = bDecoder.DecodeToDatumMap(b, nil)
require.NoError(t, err)
colFt = types.NewFieldType(mysql.TypeLonglong)
smallColID := int64(1)
b, err = encoder.Encode(&stmtctx.StatementContext{}, []int64{smallColID}, []types.Datum{types.NewIntDatum(2)}, nil)
require.NoError(t, err)
bDecoder = rowcodec.NewDatumMapDecoder([]rowcodec.ColInfo{
{
ID: smallColID,
Ft: colFt,
IsPKHandle: false,
},
}, nil)
m, err := bDecoder.DecodeToDatumMap(b, nil)
require.NoError(t, err)
v := m[smallColID]
require.Equal(t, int64(2), v.GetInt64())
}
func TestDecodeRowWithHandle(t *testing.T) {
handleID := int64(-1)
handleValue := int64(10000)
tests := []struct {
name string
testData []testData
}{
{
"signed int",
[]testData{
{
handleID,
types.NewFieldType(mysql.TypeLonglong),
types.NewIntDatum(handleValue),
types.NewIntDatum(handleValue),
nil,
true,
},
{
10,
types.NewFieldType(mysql.TypeLonglong),
types.NewIntDatum(1),
types.NewIntDatum(1),
nil,
false,
},
},
},
{
"unsigned int",
[]testData{
{
handleID,
withUnsigned(types.NewFieldType(mysql.TypeLonglong)),
types.NewUintDatum(uint64(handleValue)),
types.NewUintDatum(uint64(handleValue)), // decode as bytes will uint if unsigned.
nil,
true,
},
{
10,
types.NewFieldType(mysql.TypeLonglong),
types.NewIntDatum(1),
types.NewIntDatum(1),
nil,
false,
},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
td := test.testData
// transform test data into input.
colIDs := make([]int64, 0, len(td))
dts := make([]types.Datum, 0, len(td))
fts := make([]*types.FieldType, 0, len(td))
cols := make([]rowcodec.ColInfo, 0, len(td))
handleColFtMap := make(map[int64]*types.FieldType)
for _, d := range td {
if d.handle {
handleColFtMap[handleID] = d.ft
} else {
colIDs = append(colIDs, d.id)
dts = append(dts, d.input)
}
fts = append(fts, d.ft)
cols = append(cols, rowcodec.ColInfo{
ID: d.id,
IsPKHandle: d.handle,
Ft: d.ft,
})
}
// test encode input.
var encoder rowcodec.Encoder
sc := new(stmtctx.StatementContext)
sc.TimeZone = time.UTC
newRow, err := encoder.Encode(sc, colIDs, dts, nil)
require.NoError(t, err)
// decode to datum map.
mDecoder := rowcodec.NewDatumMapDecoder(cols, sc.TimeZone)
dm, err := mDecoder.DecodeToDatumMap(newRow, nil)
require.NoError(t, err)
dm, err = tablecodec.DecodeHandleToDatumMap(kv.IntHandle(handleValue), []int64{handleID}, handleColFtMap, sc.TimeZone, dm)
require.NoError(t, err)
for _, d := range td {
dat, exists := dm[d.id]
require.True(t, exists)
require.Equal(t, d.input, dat)
}
// decode to chunk.
cDecoder := rowcodec.NewChunkDecoder(cols, []int64{-1}, nil, sc.TimeZone)
chk := chunk.New(fts, 1, 1)
err = cDecoder.DecodeToChunk(newRow, kv.IntHandle(handleValue), chk)
require.NoError(t, err)
chkRow := chk.GetRow(0)
cdt := chkRow.GetDatumRow(fts)
for i, d := range td {
dat := cdt[i]
if dat.Kind() == types.KindMysqlDecimal {
require.Equal(t, d.output.GetMysqlDecimal(), dat.GetMysqlDecimal())
} else {
require.Equal(t, d.output, dat)
}
}
// decode to old row bytes.
colOffset := make(map[int64]int)
for i, t := range td {
colOffset[t.id] = i
}
bDecoder := rowcodec.NewByteDecoder(cols, []int64{-1}, nil, nil)
oldRow, err := bDecoder.DecodeToBytes(colOffset, kv.IntHandle(handleValue), newRow, nil)
require.NoError(t, err)
for i, d := range td {
remain, dat, err := codec.DecodeOne(oldRow[i])
require.NoError(t, err)
require.Len(t, remain, 0)
if dat.Kind() == types.KindMysqlDecimal {
require.Equal(t, d.output.GetMysqlDecimal(), dat.GetMysqlDecimal())
} else {
require.Equal(t, d.output, dat)
}
}
})
}
}
func TestEncodeKindNullDatum(t *testing.T) {
var encoder rowcodec.Encoder
sc := new(stmtctx.StatementContext)
sc.TimeZone = time.UTC
colIDs := []int64{1, 2}
var nilDt types.Datum
nilDt.SetNull()
dts := []types.Datum{nilDt, types.NewIntDatum(2)}
ft := types.NewFieldType(mysql.TypeLonglong)
fts := []*types.FieldType{ft, ft}
newRow, err := encoder.Encode(sc, colIDs, dts, nil)
require.NoError(t, err)
cols := []rowcodec.ColInfo{{ID: 1, Ft: ft}, {ID: 2, Ft: ft}}
cDecoder := rowcodec.NewChunkDecoder(cols, []int64{-1}, nil, sc.TimeZone)
chk := chunk.New(fts, 1, 1)
err = cDecoder.DecodeToChunk(newRow, kv.IntHandle(-1), chk)
require.NoError(t, err)
chkRow := chk.GetRow(0)
cdt := chkRow.GetDatumRow(fts)
require.True(t, cdt[0].IsNull())
require.Equal(t, int64(2), cdt[1].GetInt64())
}
func TestDecodeDecimalFspNotMatch(t *testing.T) {
var encoder rowcodec.Encoder
sc := new(stmtctx.StatementContext)
sc.TimeZone = time.UTC
colIDs := []int64{
1,
}
dec := withFrac(4)(withLen(6)(types.NewDecimalDatum(types.NewDecFromStringForTest("11.9900"))))
dts := []types.Datum{dec}
ft := types.NewFieldType(mysql.TypeNewDecimal)
ft.SetDecimal(4)
fts := []*types.FieldType{ft}
newRow, err := encoder.Encode(sc, colIDs, dts, nil)
require.NoError(t, err)
// decode to chunk.
ft = types.NewFieldType(mysql.TypeNewDecimal)
ft.SetDecimal(3)
cols := make([]rowcodec.ColInfo, 0)
cols = append(cols, rowcodec.ColInfo{
ID: 1,
Ft: ft,
})
cDecoder := rowcodec.NewChunkDecoder(cols, []int64{-1}, nil, sc.TimeZone)
chk := chunk.New(fts, 1, 1)
err = cDecoder.DecodeToChunk(newRow, kv.IntHandle(-1), chk)
require.NoError(t, err)
chkRow := chk.GetRow(0)
cdt := chkRow.GetDatumRow(fts)
dec = withFrac(3)(withLen(6)(types.NewDecimalDatum(types.NewDecFromStringForTest("11.990"))))
require.Equal(t, dec.GetMysqlDecimal().String(), cdt[0].GetMysqlDecimal().String())
}
func TestTypesNewRowCodec(t *testing.T) {
getJSONDatum := func(value string) types.Datum {
j, err := types.ParseBinaryJSONFromString(value)
require.NoError(t, err)
var d types.Datum
d.SetMysqlJSON(j)
return d
}
getSetDatum := func(name string, value uint64) types.Datum {
var d types.Datum
d.SetMysqlSet(types.Set{Name: name, Value: value}, mysql.DefaultCollationName)
return d
}
getTime := func(value string) types.Time {
d, err := types.ParseTime(&stmtctx.StatementContext{TimeZone: time.UTC}, value, mysql.TypeTimestamp, 6)
require.NoError(t, err)
return d
}
blobTp := types.NewFieldType(mysql.TypeBlob)
blobTp.SetCollate(mysql.DefaultCollationName)
blobTp.SetFlen(types.UnspecifiedLength)
strTp := types.NewFieldType(mysql.TypeString)
strTp.SetCollate(mysql.DefaultCollationName)
enumTp := types.NewFieldType(mysql.TypeEnum)
enumTp.SetCollate(mysql.DefaultCollationName)
enumTp.SetFlen(collate.DefaultLen)
setTp := types.NewFieldType(mysql.TypeSet)
setTp.SetCollate(mysql.DefaultCollationName)
setTp.SetFlen(collate.DefaultLen)
varStrTp := types.NewFieldType(mysql.TypeVarString)
varStrTp.SetCollate(mysql.DefaultCollationName)
smallTestDataList := []testData{
{
1,
types.NewFieldType(mysql.TypeLonglong),
types.NewIntDatum(1),
types.NewIntDatum(1),
nil,
false,
},
{
22,
withUnsigned(types.NewFieldType(mysql.TypeShort)),
types.NewUintDatum(1),
types.NewUintDatum(1),
nil,
false,
},
{
3,
types.NewFieldType(mysql.TypeDouble),
types.NewFloat64Datum(2),
types.NewFloat64Datum(2),
nil,
false,
},
{
24,
blobTp,
types.NewStringDatum("abc"),
types.NewStringDatum("abc"),
nil,
false,
},
{
25,
strTp,
types.NewStringDatum("ab"),
types.NewBytesDatum([]byte("ab")),
nil,
false,
},
{
5,
withFsp(6)(types.NewFieldType(mysql.TypeTimestamp)),
types.NewTimeDatum(getTime("2011-11-10 11:11:11.999999")),
types.NewUintDatum(1840446893366133311),
nil,
false,
},
{
16,
withFsp(0)(types.NewFieldType(mysql.TypeDuration)),
types.NewDurationDatum(getDuration("4:00:00")),
types.NewIntDatum(14400000000000),
nil,
false,
},
{
8,
types.NewFieldType(mysql.TypeNewDecimal),
withFrac(4)(withLen(6)(types.NewDecimalDatum(types.NewDecFromStringForTest("11.9900")))),
withFrac(4)(withLen(6)(types.NewDecimalDatum(types.NewDecFromStringForTest("11.9900")))),
nil,
false,
},
{
12,
types.NewFieldType(mysql.TypeYear),
types.NewIntDatum(1999),
types.NewIntDatum(1999),
nil,
false,
},
{
9,
withEnumElems("y", "n")(enumTp),
types.NewMysqlEnumDatum(types.Enum{Name: "n", Value: 2}),
types.NewUintDatum(2),
nil,
false,
},
{
14,
types.NewFieldType(mysql.TypeJSON),
getJSONDatum(`{"a":2}`),
getJSONDatum(`{"a":2}`),
nil,
false,
},
{
11,
types.NewFieldType(mysql.TypeNull),
types.NewDatum(nil),
types.NewDatum(nil),
nil,
false,
},
{
2,
types.NewFieldType(mysql.TypeNull),
types.NewDatum(nil),
types.NewDatum(nil),
nil,
false,
},
{
100,
types.NewFieldType(mysql.TypeNull),
types.NewDatum(nil),
types.NewDatum(nil),
nil,
false,
},
{
116,
types.NewFieldType(mysql.TypeFloat),
types.NewFloat32Datum(6),
types.NewFloat64Datum(6),
nil,
false,
},
{
117,
withEnumElems("n1", "n2")(setTp),
getSetDatum("n1", 1),
types.NewUintDatum(1),
nil,
false,
},
{
118,
withFlen(24)(types.NewFieldType(mysql.TypeBit)), // 3 bit
types.NewMysqlBitDatum(types.NewBinaryLiteralFromUint(3223600, 3)),
types.NewUintDatum(3223600),
nil,
false,
},
{
119,
varStrTp,
types.NewStringDatum(""),
types.NewBytesDatum([]byte("")),
nil,
false,
},
}
largeColIDTestDataList := make([]testData, len(smallTestDataList))
copy(largeColIDTestDataList, smallTestDataList)
largeColIDTestDataList[0].id = 300
largeTestDataList := make([]testData, len(smallTestDataList))
copy(largeTestDataList, smallTestDataList)
largeTestDataList[3].input = types.NewStringDatum(strings.Repeat("a", math.MaxUint16+1))
largeTestDataList[3].output = types.NewStringDatum(strings.Repeat("a", math.MaxUint16+1))
var encoder rowcodec.Encoder
tests := []struct {
name string
testData []testData
}{
{
"small",
smallTestDataList,
},
{
"largeColID",
largeColIDTestDataList,
},
{
"largeData",
largeTestDataList,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
td := test.testData
// transform test data into input.
colIDs := make([]int64, 0, len(td))
dts := make([]types.Datum, 0, len(td))
fts := make([]*types.FieldType, 0, len(td))
cols := make([]rowcodec.ColInfo, 0, len(td))
for _, d := range td {
colIDs = append(colIDs, d.id)
dts = append(dts, d.input)
fts = append(fts, d.ft)
cols = append(cols, rowcodec.ColInfo{
ID: d.id,
IsPKHandle: d.handle,
Ft: d.ft,
})
}
// test encode input.
sc := new(stmtctx.StatementContext)
sc.TimeZone = time.UTC
newRow, err := encoder.Encode(sc, colIDs, dts, nil)
require.NoError(t, err)
// decode to datum map.
mDecoder := rowcodec.NewDatumMapDecoder(cols, sc.TimeZone)
dm, err := mDecoder.DecodeToDatumMap(newRow, nil)
require.NoError(t, err)
for _, d := range td {
dat, exists := dm[d.id]
require.True(t, exists)
require.Equal(t, d.input, dat)
}
// decode to chunk.
cDecoder := rowcodec.NewChunkDecoder(cols, []int64{-1}, nil, sc.TimeZone)
chk := chunk.New(fts, 1, 1)
err = cDecoder.DecodeToChunk(newRow, kv.IntHandle(-1), chk)
require.NoError(t, err)
chkRow := chk.GetRow(0)
cdt := chkRow.GetDatumRow(fts)
for i, d := range td {
dat := cdt[i]
if dat.Kind() == types.KindMysqlDecimal {
require.Equal(t, d.output.GetMysqlDecimal(), dat.GetMysqlDecimal())
} else {
require.Equal(t, d.input, dat)
}
}
// decode to old row bytes.
colOffset := make(map[int64]int)
for i, t := range td {
colOffset[t.id] = i
}
bDecoder := rowcodec.NewByteDecoder(cols, []int64{-1}, nil, nil)
oldRow, err := bDecoder.DecodeToBytes(colOffset, kv.IntHandle(-1), newRow, nil)
require.NoError(t, err)
for i, d := range td {
remain, dat, err := codec.DecodeOne(oldRow[i])
require.NoError(t, err)
require.Len(t, remain, 0)
if dat.Kind() == types.KindMysqlDecimal {
require.Equal(t, d.output.GetMysqlDecimal(), dat.GetMysqlDecimal())
} else if dat.Kind() == types.KindBytes {
require.Equal(t, d.output.GetBytes(), dat.GetBytes())
} else {
require.Equal(t, d.output, dat)
}
}
})
}
}
func TestNilAndDefault(t *testing.T) {
td := []testData{
{
1,
types.NewFieldType(mysql.TypeLonglong),
types.NewIntDatum(1),
types.NewIntDatum(1),
nil,
false,
},
{
2,
withUnsigned(types.NewFieldType(mysql.TypeLonglong)),
types.NewUintDatum(1),
types.NewUintDatum(9),
getDatumPoint(types.NewUintDatum(9)),
false,
},
}
// transform test data into input.
colIDs := make([]int64, 0, len(td))
dts := make([]types.Datum, 0, len(td))
cols := make([]rowcodec.ColInfo, 0, len(td))
fts := make([]*types.FieldType, 0, len(td))
for i := range td {
d := td[i]
if d.def == nil {
colIDs = append(colIDs, d.id)
dts = append(dts, d.input)
}
fts = append(fts, d.ft)
cols = append(cols, rowcodec.ColInfo{
ID: d.id,
IsPKHandle: d.handle,
Ft: d.ft,
})
}
ddf := func(i int, chk *chunk.Chunk) error {
d := td[i]
if d.def == nil {
chk.AppendNull(i)
return nil
}
chk.AppendDatum(i, d.def)
return nil
}
bdf := func(i int) ([]byte, error) {
d := td[i]
if d.def == nil {
return nil, nil
}
return getOldDatumByte(*d.def), nil
}
// test encode input.
var encoder rowcodec.Encoder
sc := new(stmtctx.StatementContext)
sc.TimeZone = time.UTC
newRow, err := encoder.Encode(sc, colIDs, dts, nil)
require.NoError(t, err)
// decode to datum map.
mDecoder := rowcodec.NewDatumMapDecoder(cols, sc.TimeZone)
dm, err := mDecoder.DecodeToDatumMap(newRow, nil)
require.NoError(t, err)
for _, d := range td {
dat, exists := dm[d.id]
if d.def != nil {
// for datum should not fill default value.
require.False(t, exists)
} else {
require.True(t, exists)
require.Equal(t, d.output, dat)
}
}
// decode to chunk.
chk := chunk.New(fts, 1, 1)
cDecoder := rowcodec.NewChunkDecoder(cols, []int64{-1}, ddf, sc.TimeZone)
err = cDecoder.DecodeToChunk(newRow, kv.IntHandle(-1), chk)
require.NoError(t, err)
chkRow := chk.GetRow(0)
cdt := chkRow.GetDatumRow(fts)
for i, d := range td {
dat := cdt[i]
if dat.Kind() == types.KindMysqlDecimal {
require.Equal(t, d.output.GetMysqlDecimal(), dat.GetMysqlDecimal())
} else {
require.Equal(t, d.output, dat)
}
}
chk = chunk.New(fts, 1, 1)
cDecoder = rowcodec.NewChunkDecoder(cols, []int64{-1}, nil, sc.TimeZone)
err = cDecoder.DecodeToChunk(newRow, kv.IntHandle(-1), chk)
require.NoError(t, err)
chkRow = chk.GetRow(0)
cdt = chkRow.GetDatumRow(fts)
for i := range td {
if i == 0 {
continue
}
require.True(t, cdt[i].IsNull())
}
// decode to old row bytes.
colOffset := make(map[int64]int)
for i, t := range td {
colOffset[t.id] = i
}
bDecoder := rowcodec.NewByteDecoder(cols, []int64{-1}, bdf, sc.TimeZone)
oldRow, err := bDecoder.DecodeToBytes(colOffset, kv.IntHandle(-1), newRow, nil)
require.NoError(t, err)
for i, d := range td {
remain, dat, err := codec.DecodeOne(oldRow[i])
require.NoError(t, err)
require.Len(t, remain, 0)
if dat.Kind() == types.KindMysqlDecimal {
require.Equal(t, d.output.GetMysqlDecimal(), dat.GetMysqlDecimal())
} else {
require.Equal(t, d.output, dat)
}
}
}
func TestVarintCompatibility(t *testing.T) {
td := []testData{
{
1,
types.NewFieldType(mysql.TypeLonglong),
types.NewIntDatum(1),
types.NewIntDatum(1),
nil,
false,
},
{
2,
withUnsigned(types.NewFieldType(mysql.TypeLonglong)),
types.NewUintDatum(1),
types.NewUintDatum(1),
nil,
false,
},
}
// transform test data into input.
colIDs := make([]int64, 0, len(td))
dts := make([]types.Datum, 0, len(td))
cols := make([]rowcodec.ColInfo, 0, len(td))
for _, d := range td {
colIDs = append(colIDs, d.id)
dts = append(dts, d.input)
cols = append(cols, rowcodec.ColInfo{
ID: d.id,
IsPKHandle: d.handle,
Ft: d.ft,
})
}
// test encode input.
var encoder rowcodec.Encoder
sc := new(stmtctx.StatementContext)
sc.TimeZone = time.UTC
newRow, err := encoder.Encode(sc, colIDs, dts, nil)
require.NoError(t, err)
decoder := rowcodec.NewByteDecoder(cols, []int64{-1}, nil, sc.TimeZone)
// decode to old row bytes.
colOffset := make(map[int64]int)
for i, t := range td {
colOffset[t.id] = i
}
oldRow, err := decoder.DecodeToBytes(colOffset, kv.IntHandle(1), newRow, nil)
require.NoError(t, err)
for i, d := range td {
oldVarint, err := tablecodec.EncodeValue(nil, nil, d.output) // tablecodec will encode as varint/varuint
require.NoError(t, err)
require.Equal(t, oldRow[i], oldVarint)
}
}
func TestCodecUtil(t *testing.T) {
colIDs := []int64{1, 2, 3, 4}
tps := make([]*types.FieldType, 4)
for i := 0; i < 3; i++ {
tps[i] = types.NewFieldType(mysql.TypeLonglong)
}
tps[3] = types.NewFieldType(mysql.TypeNull)
sc := new(stmtctx.StatementContext)
oldRow, err := tablecodec.EncodeOldRow(sc, types.MakeDatums(1, 2, 3, nil), colIDs, nil, nil)
require.NoError(t, err)
var (
rb rowcodec.Encoder
newRow []byte
)
newRow, err = rowcodec.EncodeFromOldRow(&rb, nil, oldRow, nil)
require.NoError(t, err)
require.True(t, rowcodec.IsNewFormat(newRow))
require.False(t, rowcodec.IsNewFormat(oldRow))
// test stringer for decoder.
var cols = make([]rowcodec.ColInfo, 0, len(tps))
for i, ft := range tps {
cols = append(cols, rowcodec.ColInfo{
ID: colIDs[i],
IsPKHandle: false,
Ft: ft,
})
}
d := rowcodec.NewDecoder(cols, []int64{-1}, nil)
// test ColumnIsNull
isNull, err := d.ColumnIsNull(newRow, 4, nil)
require.NoError(t, err)
require.True(t, isNull)
isNull, err = d.ColumnIsNull(newRow, 1, nil)
require.NoError(t, err)
require.False(t, isNull)
isNull, err = d.ColumnIsNull(newRow, 5, nil)
require.NoError(t, err)
require.True(t, isNull)
isNull, err = d.ColumnIsNull(newRow, 5, []byte{1})
require.NoError(t, err)
require.False(t, isNull)
// test isRowKey
require.False(t, rowcodec.IsRowKey([]byte{'b', 't'}))
require.False(t, rowcodec.IsRowKey([]byte{'t', 'r'}))
}
func TestOldRowCodec(t *testing.T) {
colIDs := []int64{1, 2, 3, 4}
tps := make([]*types.FieldType, 4)
for i := 0; i < 3; i++ {
tps[i] = types.NewFieldType(mysql.TypeLonglong)
}
tps[3] = types.NewFieldType(mysql.TypeNull)
sc := new(stmtctx.StatementContext)
oldRow, err := tablecodec.EncodeOldRow(sc, types.MakeDatums(1, 2, 3, nil), colIDs, nil, nil)
require.NoError(t, err)
var (
rb rowcodec.Encoder
newRow []byte
)
newRow, err = rowcodec.EncodeFromOldRow(&rb, nil, oldRow, nil)
require.NoError(t, err)
cols := make([]rowcodec.ColInfo, len(tps))
for i, tp := range tps {
cols[i] = rowcodec.ColInfo{
ID: colIDs[i],
Ft: tp,
}
}
rd := rowcodec.NewChunkDecoder(cols, []int64{-1}, nil, time.Local)
chk := chunk.NewChunkWithCapacity(tps, 1)
err = rd.DecodeToChunk(newRow, kv.IntHandle(-1), chk)
require.NoError(t, err)
row := chk.GetRow(0)
for i := 0; i < 3; i++ {
require.Equal(t, int64(i+1), row.GetInt64(i))
}
}
func Test65535Bug(t *testing.T) {
colIds := []int64{1}
tps := make([]*types.FieldType, 1)
tps[0] = types.NewFieldType(mysql.TypeString)
sc := new(stmtctx.StatementContext)
text65535 := strings.Repeat("a", 65535)
encode := rowcodec.Encoder{}
bd, err := encode.Encode(sc, colIds, []types.Datum{types.NewStringDatum(text65535)}, nil)
require.NoError(t, err)
cols := make([]rowcodec.ColInfo, 1)
cols[0] = rowcodec.ColInfo{
ID: 1,
Ft: tps[0],
}
dc := rowcodec.NewDatumMapDecoder(cols, nil)
result, err := dc.DecodeToDatumMap(bd, nil)
require.NoError(t, err)
rs := result[1]
require.Equal(t, text65535, rs.GetString())
}
var (
withUnsigned = func(ft *types.FieldType) *types.FieldType {
ft.AddFlag(mysql.UnsignedFlag)
return ft
}
withEnumElems = func(elem ...string) func(ft *types.FieldType) *types.FieldType {
return func(ft *types.FieldType) *types.FieldType {
ft.SetElems(elem)
return ft
}
}
withFsp = func(fsp int) func(ft *types.FieldType) *types.FieldType {
return func(ft *types.FieldType) *types.FieldType {
ft.SetDecimal(fsp)
return ft
}
}
withFlen = func(flen int) func(ft *types.FieldType) *types.FieldType {
return func(ft *types.FieldType) *types.FieldType {
ft.SetFlen(flen)
return ft
}
}
getDuration = func(value string) types.Duration {
dur, _, _ := types.ParseDuration(nil, value, 0)
return dur
}
getOldDatumByte = func(d types.Datum) []byte {
b, err := tablecodec.EncodeValue(nil, nil, d)
if err != nil {
panic(err)
}
return b
}
getDatumPoint = func(d types.Datum) *types.Datum {
return &d
}
withFrac = func(f int) func(d types.Datum) types.Datum {
return func(d types.Datum) types.Datum {
d.SetFrac(f)
return d
}
}
withLen = func(l int) func(d types.Datum) types.Datum {
return func(d types.Datum) types.Datum {
d.SetLength(l)
return d
}
}
)