From bd6fcc7a8eff7236c530da3d7790b8d73c8d9005 Mon Sep 17 00:00:00 2001 From: MyonKeminta Date: Mon, 9 Aug 2021 17:00:47 +0800 Subject: [PATCH 1/3] Fix lock view cannot show key info correctly for partition table --- util/keydecoder/keydecoder.go | 40 ++++++++++--- util/keydecoder/keydecoder_test.go | 91 ++++++++++++++++++++++++++---- 2 files changed, 111 insertions(+), 20 deletions(-) diff --git a/util/keydecoder/keydecoder.go b/util/keydecoder/keydecoder.go index 37986f63ef748..1b9d51403eeda 100644 --- a/util/keydecoder/keydecoder.go +++ b/util/keydecoder/keydecoder.go @@ -15,8 +15,8 @@ package keydecoder import ( "fmt" - "github.com/pingcap/errors" + "github.com/pingcap/parser/model" "github.com/pingcap/tidb/infoschema" "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/tablecodec" @@ -41,6 +41,8 @@ type DecodedKey struct { DbName string `json:"db_name,omitempty"` TableID int64 `json:"table_id"` TableName string `json:"table_name,omitempty"` + PartitionID int64 `json:"partition_id,omitempty"` + PartitionName string `json:"partition_name,omitempty"` HandleType HandleType `json:"handle_type,omitempty"` IsPartitionHandle bool `json:"partition_handle,omitempty"` HandleValue string `json:"handle_value,omitempty"` @@ -60,7 +62,6 @@ func handleType(handle kv.Handle) HandleType { return handleType(h.Handle) } else { logutil.BgLogger().Warn("Unexpected kv.Handle type", - zap.Any("handle", handle), zap.String("handle Type", fmt.Sprintf("%T", handle)), ) } @@ -73,13 +74,13 @@ func DecodeKey(key []byte, infoschema infoschema.InfoSchema) (DecodedKey, error) if !tablecodec.IsRecordKey(key) && !tablecodec.IsIndexKey(key) { return result, errors.Errorf("Unknown key type for key %v", key) } - tableID, indexID, isRecordKey, err := tablecodec.DecodeKeyHead(key) + tableOrPartitionID, indexID, isRecordKey, err := tablecodec.DecodeKeyHead(key) if err != nil { return result, err } - result.TableID = tableID + result.TableID = tableOrPartitionID - table, tableFound := infoschema.TableByID(tableID) + table, tableFound := infoschema.TableByID(tableOrPartitionID) // The schema may have changed since when the key is get. // Then we just omit the table name and show the table ID only. @@ -89,21 +90,42 @@ func DecodeKey(key []byte, infoschema infoschema.InfoSchema) (DecodedKey, error) schema, ok := infoschema.SchemaByTable(table.Meta()) if !ok { - logutil.BgLogger().Warn("no schema associated with table found in infoschema", zap.Int64("tableID", tableID), zap.Error(err)) + logutil.BgLogger().Warn("no schema associated with table found in infoschema", zap.Int64("tableOrPartitionID", tableOrPartitionID), zap.Error(err)) return result, nil } result.DbID = schema.ID result.DbName = schema.Name.O } else { - logutil.BgLogger().Warn("no table found in infoschema", zap.Int64("tableID", tableID), zap.Error(err)) + // If the table of this ID is not found, try to find it as a partition. + var schema *model.DBInfo + var partition *model.PartitionDefinition + table, schema, partition = infoschema.FindTableByPartitionID(tableOrPartitionID) + if table != nil { + tableFound = true + result.TableID = table.Meta().ID + result.TableName = table.Meta().Name.O + } + if schema != nil { + result.DbID = schema.ID + result.DbName = schema.Name.O + } + if partition != nil { + result.PartitionID = partition.ID + result.PartitionName = partition.Name.O + } + if !tableFound { + logutil.BgLogger().Warn("no table found in infoschema", zap.Int64("tableOrPartitionID", tableOrPartitionID), zap.Error(err)) + } } if isRecordKey { _, handle, err := tablecodec.DecodeRecordKey(key) if err != nil { - logutil.BgLogger().Warn("decode record key failed", zap.Int64("tableID", tableID), zap.Error(err)) - return result, errors.Errorf("cannot decode record key of table %d", tableID) + logutil.BgLogger().Warn("decode record key failed", zap.Int64("tableOrPartitionID", tableOrPartitionID), zap.Error(err)) + return result, errors.Errorf("cannot decode record key of table %d", tableOrPartitionID) } result.HandleType = handleType(handle) + // The PartitionHandle is used by the Global Index feature for partition tables, which is currently an + // unfinished feature. So we don't care about it much for now. _, result.IsPartitionHandle = handle.(kv.PartitionHandle) result.HandleValue = handle.String() } else { diff --git a/util/keydecoder/keydecoder_test.go b/util/keydecoder/keydecoder_test.go index b50ddf78be3c6..8fa64049a383c 100644 --- a/util/keydecoder/keydecoder_test.go +++ b/util/keydecoder/keydecoder_test.go @@ -38,7 +38,27 @@ func TestDecodeKey(t *testing.T) { }, } tableInfo2 := &model.TableInfo{ID: 2, Name: model.NewCIStr("table2")} - stubTableInfos := []*model.TableInfo{tableInfo1, tableInfo2} + tableInfo3 := &model.TableInfo{ + ID: 3, + Name: model.NewCIStr("table3"), + Columns: []*model.ColumnInfo{ + {ID: 10, Name: model.NewCIStr("col"), State: model.StatePublic}, + }, + Indices: []*model.IndexInfo{ + {ID: 4, Name: model.NewCIStr("index4"), State: model.StatePublic}, + }, + Partition: &model.PartitionInfo{ + Type: model.PartitionTypeRange, + Expr: "`col`", + Enable: true, + Definitions: []model.PartitionDefinition{ + {ID: 5, Name: model.NewCIStr("p0"), LessThan: []string{"10"}}, + {ID: 6, Name: model.NewCIStr("p1"), LessThan: []string{"MAXVALUE"}}, + }, + }, + } + + stubTableInfos := []*model.TableInfo{tableInfo1, tableInfo2, tableInfo3} stubInfoschema := infoschema.MockInfoSchema(stubTableInfos) decodedKey, err := DecodeKey([]byte{ @@ -55,10 +75,12 @@ func TestDecodeKey(t *testing.T) { assert.Equal(t, decodedKey.DbName, "test") assert.Equal(t, decodedKey.TableID, int64(1)) assert.Equal(t, decodedKey.TableName, "table1") + assert.Equal(t, decodedKey.PartitionID, int64(0)) + assert.Equal(t, decodedKey.PartitionName, "") assert.Equal(t, decodedKey.HandleType, IntHandle) assert.Equal(t, decodedKey.IsPartitionHandle, false) assert.Equal(t, decodedKey.HandleValue, "1") - // these are default values, ie. will be omitted when got marshaled into json + // These are default values, ie. will be omitted when got marshaled into json assert.Equal(t, decodedKey.IndexID, int64(0)) assert.Equal(t, decodedKey.IndexName, "") assert.Nil(t, decodedKey.IndexValues) @@ -80,10 +102,12 @@ func TestDecodeKey(t *testing.T) { assert.Equal(t, decodedKey.DbName, "test") assert.Equal(t, decodedKey.TableID, int64(2)) assert.Equal(t, decodedKey.TableName, "table2") + assert.Equal(t, decodedKey.PartitionID, int64(0)) + assert.Equal(t, decodedKey.PartitionName, "") assert.Equal(t, decodedKey.HandleType, CommonHandle) assert.Equal(t, decodedKey.IsPartitionHandle, false) assert.Equal(t, decodedKey.HandleValue, "{100, abc}") - // these are default values, ie. will be omitted when got marshaled into json + // These are default values, ie. will be omitted when got marshaled into json assert.Equal(t, decodedKey.IndexID, int64(0)) assert.Equal(t, decodedKey.IndexName, "") assert.Nil(t, decodedKey.IndexValues) @@ -109,15 +133,58 @@ func TestDecodeKey(t *testing.T) { assert.Equal(t, decodedKey.DbName, "test") assert.Equal(t, decodedKey.TableID, int64(1)) assert.Equal(t, decodedKey.TableName, "table1") + assert.Equal(t, decodedKey.PartitionID, int64(0)) + assert.Equal(t, decodedKey.PartitionName, "") assert.Equal(t, decodedKey.IndexID, int64(1)) assert.Equal(t, decodedKey.IndexName, "index1") assert.Equal(t, decodedKey.IndexValues, []string{"abc", "1"}) - // these are default values, ie. will be omitted when got marshaled into json + // These are default values, ie. will be omitted when got marshaled into json + assert.Equal(t, decodedKey.HandleType, HandleType("")) + assert.Equal(t, decodedKey.HandleValue, "") + assert.Equal(t, decodedKey.IsPartitionHandle, false) + + // Row key in a partitioned table. + key = []byte("t\x80\x00\x00\x00\x00\x00\x00\x05_r\x80\x00\x00\x00\x00\x00\x00\x0a") + decodedKey, err = DecodeKey(key, stubInfoschema) + assert.Nil(t, err) + assert.Equal(t, decodedKey.DbID, int64(0)) + assert.Equal(t, decodedKey.DbName, "test") + assert.Equal(t, decodedKey.TableID, int64(3)) + assert.Equal(t, decodedKey.TableName, "table3") + assert.Equal(t, decodedKey.PartitionID, int64(5)) + assert.Equal(t, decodedKey.PartitionName, "p0") + assert.Equal(t, decodedKey.HandleType, IntHandle) + assert.Equal(t, decodedKey.HandleValue, "10") + // These are default values, ie. will be omitted when got marshaled into json + assert.Equal(t, decodedKey.IndexID, int64(0)) + assert.Equal(t, decodedKey.IndexName, "") + assert.Nil(t, decodedKey.IndexValues) + assert.Equal(t, decodedKey.IsPartitionHandle, false) + + // Index key in a partitioned table. + values = types.MakeDatums("abcde", 2) + encodedValue, err = codec.EncodeKey(sc, nil, values...) + assert.Nil(t, err) + key = []byte("t\x80\x00\x00\x00\x00\x00\x00\x06_i\x80\x00\x00\x00\x00\x00\x00\x04") + key = append(key, encodedValue...) + + decodedKey, err = DecodeKey(key, stubInfoschema) + assert.Nil(t, err) + assert.Equal(t, decodedKey.DbID, int64(0)) + assert.Equal(t, decodedKey.DbName, "test") + assert.Equal(t, decodedKey.TableID, int64(3)) + assert.Equal(t, decodedKey.TableName, "table3") + assert.Equal(t, decodedKey.PartitionID, int64(6)) + assert.Equal(t, decodedKey.PartitionName, "p1") + assert.Equal(t, decodedKey.IndexID, int64(4)) + assert.Equal(t, decodedKey.IndexName, "index4") + assert.Equal(t, decodedKey.IndexValues, []string{"abcde", "2"}) + // These are default values, ie. will be omitted when got marshaled into json assert.Equal(t, decodedKey.HandleType, HandleType("")) assert.Equal(t, decodedKey.HandleValue, "") assert.Equal(t, decodedKey.IsPartitionHandle, false) - // totally invalid key + // Totally invalid key key = []byte("this-is-a-totally-invalidkey") decodedKey, err = DecodeKey(key, stubInfoschema) assert.NotNil(t, err) @@ -131,27 +198,29 @@ func TestDecodeKey(t *testing.T) { decodedKey, err = DecodeKey(key, stubInfoschema) assert.NotNil(t, err) - // table cannot be found in infoschema - // this is possible when the schema have changed since when the key is get. + // Table cannot be found in infoschema + // This is possible when the schema have changed since when the key is get. decodedKey, err = DecodeKey([]byte{ 't', // table id = 3 - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, '_', 'r', // int handle, value = 1 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, }, stubInfoschema) - // we should get as much information as we can + // We should get as much information as we can assert.Nil(t, err) - assert.Equal(t, decodedKey.TableID, int64(3)) + assert.Equal(t, decodedKey.TableID, int64(4)) assert.Equal(t, decodedKey.HandleType, IntHandle) assert.Equal(t, decodedKey.HandleValue, "1") - // rest information are all default value, ie. omitted when got marshaled into json + // Rest information are all default value, ie. omitted when got marshaled into json assert.Equal(t, decodedKey.DbID, int64(0)) assert.Equal(t, decodedKey.DbName, "") assert.Equal(t, decodedKey.TableName, "") + assert.Equal(t, decodedKey.PartitionID, int64(0)) + assert.Equal(t, decodedKey.PartitionName, "") assert.Equal(t, decodedKey.IndexID, int64(0)) assert.Equal(t, decodedKey.IndexName, "") assert.Equal(t, decodedKey.IsPartitionHandle, false) From 6fcdef871d42e630a9f0404eca5fefab31444832 Mon Sep 17 00:00:00 2001 From: MyonKeminta Date: Mon, 9 Aug 2021 17:33:57 +0800 Subject: [PATCH 2/3] Fix format --- util/keydecoder/keydecoder.go | 1 + 1 file changed, 1 insertion(+) diff --git a/util/keydecoder/keydecoder.go b/util/keydecoder/keydecoder.go index 1b9d51403eeda..b424034666f3a 100644 --- a/util/keydecoder/keydecoder.go +++ b/util/keydecoder/keydecoder.go @@ -15,6 +15,7 @@ package keydecoder import ( "fmt" + "github.com/pingcap/errors" "github.com/pingcap/parser/model" "github.com/pingcap/tidb/infoschema" From 7136c3a746a3b55cf8303b207a1c232783be8a9c Mon Sep 17 00:00:00 2001 From: MyonKeminta Date: Mon, 9 Aug 2021 18:58:58 +0800 Subject: [PATCH 3/3] Address comments --- util/keydecoder/keydecoder.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/keydecoder/keydecoder.go b/util/keydecoder/keydecoder.go index b424034666f3a..f5c84d9c6627c 100644 --- a/util/keydecoder/keydecoder.go +++ b/util/keydecoder/keydecoder.go @@ -91,7 +91,7 @@ func DecodeKey(key []byte, infoschema infoschema.InfoSchema) (DecodedKey, error) schema, ok := infoschema.SchemaByTable(table.Meta()) if !ok { - logutil.BgLogger().Warn("no schema associated with table found in infoschema", zap.Int64("tableOrPartitionID", tableOrPartitionID), zap.Error(err)) + logutil.BgLogger().Warn("no schema associated with table found in infoschema", zap.Int64("tableOrPartitionID", tableOrPartitionID)) return result, nil } result.DbID = schema.ID @@ -115,7 +115,7 @@ func DecodeKey(key []byte, infoschema infoschema.InfoSchema) (DecodedKey, error) result.PartitionName = partition.Name.O } if !tableFound { - logutil.BgLogger().Warn("no table found in infoschema", zap.Int64("tableOrPartitionID", tableOrPartitionID), zap.Error(err)) + logutil.BgLogger().Warn("no table found in infoschema", zap.Int64("tableOrPartitionID", tableOrPartitionID)) } } if isRecordKey {