executor: always decode the value first and then the handle (#22073)

This commit is contained in:
tangenta
2021-01-04 16:37:15 +08:00
committed by GitHub
parent 0ddf74b19d
commit 536dfdfe2f
4 changed files with 36 additions and 5 deletions

View File

@ -963,7 +963,7 @@ func (b *executorBuilder) buildUnionScanFromReader(reader Executor, v *plannerco
}
return x
}
// If reader is union, it means a partitiont table and we should transfer as above.
// If reader is union, it means a partition table and we should transfer as above.
if x, ok := reader.(*UnionExec); ok {
for i, child := range x.children {
x.children[i] = b.buildUnionScanFromReader(child, v)

View File

@ -17,10 +17,12 @@ import (
. "github.com/pingcap/check"
"github.com/pingcap/tidb/errno"
"github.com/pingcap/tidb/store/tikv"
"github.com/pingcap/tidb/util/collate"
"github.com/pingcap/tidb/util/testkit"
)
type testClusteredSuite struct{ *baseTestSuite }
type testClusteredSerialSuite struct{ *testClusteredSuite }
func (s *testClusteredSuite) SetUpTest(c *C) {
}
@ -184,6 +186,28 @@ func (s *testClusteredSuite) TestClusteredPrefixingPrimaryKey(c *C) {
tk.MustExec("admin check table t;")
}
// Test for union scan in prefixed clustered index table.
// See https://github.com/pingcap/tidb/issues/22069.
func (s *testClusteredSerialSuite) TestClusteredUnionScanOnPrefixingPrimaryKey(c *C) {
originCollate := collate.NewCollationEnabled()
collate.SetNewCollationEnabledForTest(false)
defer collate.SetNewCollationEnabledForTest(originCollate)
tk := s.newTK(c)
tk.MustExec("drop table if exists t;")
tk.MustExec("create table t (col_1 varchar(255), col_2 tinyint, primary key idx_1 (col_1(1)));")
tk.MustExec("insert into t values ('aaaaa', -38);")
tk.MustExec("insert into t values ('bbbbb', -48);")
tk.MustExec("begin PESSIMISTIC;")
tk.MustExec("update t set col_2 = 47 where col_1 in ('aaaaa') order by col_1,col_2;")
tk.MustQuery("select * from t;").Check(testkit.Rows("aaaaa 47", "bbbbb -48"))
tk.MustGetErrCode("insert into t values ('bb', 0);", errno.ErrDupEntry)
tk.MustGetErrCode("insert into t values ('aa', 0);", errno.ErrDupEntry)
tk.MustExec("commit;")
tk.MustQuery("select * from t;").Check(testkit.Rows("aaaaa 47", "bbbbb -48"))
tk.MustExec("admin check table t;")
}
func (s *testClusteredSuite) TestClusteredWithOldRowFormat(c *C) {
tk := s.newTK(c)
tk.Se.GetSessionVars().RowEncoder.Enable = false

View File

@ -119,6 +119,7 @@ var _ = Suite(&testSuite6{&baseTestSuite{}})
var _ = Suite(&testSuite7{&baseTestSuite{}})
var _ = Suite(&testSuite8{&baseTestSuite{}})
var _ = Suite(&testClusteredSuite{&baseTestSuite{}})
var _ = SerialSuites(&testClusteredSerialSuite{&testClusteredSuite{&baseTestSuite{}}})
var _ = SerialSuites(&testShowStatsSuite{&baseTestSuite{}})
var _ = Suite(&testBypassSuite{})
var _ = Suite(&testUpdateSuite{})

View File

@ -222,6 +222,9 @@ func (decoder *ChunkDecoder) DecodeToChunk(rowData []byte, handle kv.Handle, chk
continue
}
// Only try to decode handle when there is no corresponding column in the value.
// This is because the information in handle may be incomplete in some cases.
// For example, prefixed clustered index like 'primary key(col1(1))' only store the leftmost 1 char in the handle.
if decoder.tryAppendHandleColumn(colIdx, col, handle, chk) {
continue
}
@ -385,10 +388,6 @@ func (decoder *BytesDecoder) decodeToBytesInternal(outputOffset map[int64]int, h
tp := fieldType2Flag(col.Ft.Tp, col.Ft.Flag&mysql.UnsignedFlag == 0)
colID := col.ID
offset := outputOffset[colID]
if decoder.tryDecodeHandle(values, offset, col, handle, cacheBytes) {
continue
}
idx, isNil, notFound := r.findColID(colID)
if !notFound && !isNil {
val := r.getData(idx)
@ -396,6 +395,13 @@ func (decoder *BytesDecoder) decodeToBytesInternal(outputOffset map[int64]int, h
continue
}
// Only try to decode handle when there is no corresponding column in the value.
// This is because the information in handle may be incomplete in some cases.
// For example, prefixed clustered index like 'primary key(col1(1))' only store the leftmost 1 char in the handle.
if decoder.tryDecodeHandle(values, offset, col, handle, cacheBytes) {
continue
}
if isNil {
values[offset] = []byte{NilFlag}
continue