diff --git a/table/tables/index_test.go b/table/tables/index_test.go index efcae3b337c3c..2e1a028099235 100644 --- a/table/tables/index_test.go +++ b/table/tables/index_test.go @@ -16,6 +16,7 @@ package tables_test import ( "context" + "strings" "testing" "github.com/pingcap/tidb/ddl" @@ -199,3 +200,72 @@ func TestAssertionWithLazyCheck(t *testing.T) { // `CheckNotExist`. Anyway there should never be assertion failure. tk.MustGetErrCode("insert into t values (4, 3, 3)", errno.ErrDupEntry) } + +func TestGenIndexValueWithLargePaddingSize(t *testing.T) { + // ref https://github.com/pingcap/tidb/issues/47115 + tblInfo := buildTableInfo(t, "create table t (a int, b int, k varchar(255), primary key (a, b), key (k))") + var idx table.Index + for _, idxInfo := range tblInfo.Indices { + if !idxInfo.Primary { + idx = tables.NewIndex(tblInfo.ID, tblInfo, idxInfo) + break + } + } + var a, b *model.ColumnInfo + for _, col := range tblInfo.Columns { + if col.Name.String() == "a" { + a = col + } else if col.Name.String() == "b" { + b = col + } + } + require.NotNil(t, a) + require.NotNil(t, b) + + store := testkit.CreateMockStore(t) + txn, err := store.Begin() + require.NoError(t, err) + mockCtx := mock.NewContext() + sc := mockCtx.GetSessionVars().StmtCtx + padding := strings.Repeat(" ", 128) + idxColVals := types.MakeDatums("abc" + padding) + handleColVals := types.MakeDatums(1, 2) + encodedHandle, err := codec.EncodeKey(sc, nil, handleColVals...) + require.NoError(t, err) + commonHandle, err := kv.NewCommonHandle(encodedHandle) + require.NoError(t, err) + + key, _, err := idx.GenIndexKey(sc, idxColVals, commonHandle, nil) + require.NoError(t, err) + _, err = idx.Create(mockCtx, txn, idxColVals, commonHandle, nil) + require.NoError(t, err) + val, err := txn.Get(context.Background(), key) + require.NoError(t, err) + colInfo := tables.BuildRowcodecColInfoForIndexColumns(idx.Meta(), tblInfo) + colInfo = append(colInfo, rowcodec.ColInfo{ + ID: a.ID, + IsPKHandle: false, + Ft: rowcodec.FieldTypeFromModelColumn(a), + }) + colInfo = append(colInfo, rowcodec.ColInfo{ + ID: b.ID, + IsPKHandle: false, + Ft: rowcodec.FieldTypeFromModelColumn(b), + }) + colVals, err := tablecodec.DecodeIndexKV(key, val, 1, tablecodec.HandleDefault, colInfo) + require.NoError(t, err) + require.Len(t, colVals, 3) + _, d, err := codec.DecodeOne(colVals[0]) + require.NoError(t, err) + require.Equal(t, "abc"+padding, d.GetString()) + _, d, err = codec.DecodeOne(colVals[1]) + require.NoError(t, err) + require.Equal(t, int64(1), d.GetInt64()) + _, d, err = codec.DecodeOne(colVals[2]) + require.NoError(t, err) + require.Equal(t, int64(2), d.GetInt64()) + handle, err := tablecodec.DecodeIndexHandle(key, val, 1) + require.NoError(t, err) + require.False(t, handle.IsInt()) + require.Equal(t, commonHandle.Encoded(), handle.Encoded()) +} diff --git a/tablecodec/tablecodec.go b/tablecodec/tablecodec.go index 8766b188d9164..859115ff880fd 100644 --- a/tablecodec/tablecodec.go +++ b/tablecodec/tablecodec.go @@ -843,7 +843,11 @@ func buildRestoredColumn(allCols []rowcodec.ColInfo) []rowcodec.ColInfo { } if collate.IsBinCollation(col.Ft.GetCollate()) { // Change the fieldType from string to uint since we store the number of the truncated spaces. + // NOTE: the corresponding datum is generated as `types.NewUintDatum(paddingSize)`, and the raw data is + // encoded via `encodeUint`. Thus we should mark the field type as unsigened here so that the BytesDecoder + // can decode it correctly later. Otherwise there might be issues like #47115. copyColInfo.Ft = types.NewFieldType(mysql.TypeLonglong) + copyColInfo.Ft.AddFlag(mysql.UnsignedFlag) } else { copyColInfo.Ft = allCols[i].Ft }