// Copyright 2021 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 tables_test import ( "context" "testing" "github.com/pingcap/tidb/ddl" "github.com/pingcap/tidb/errno" "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/parser" "github.com/pingcap/tidb/parser/ast" "github.com/pingcap/tidb/parser/model" "github.com/pingcap/tidb/table" "github.com/pingcap/tidb/table/tables" "github.com/pingcap/tidb/tablecodec" "github.com/pingcap/tidb/testkit" "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/util/codec" "github.com/pingcap/tidb/util/mock" "github.com/pingcap/tidb/util/rowcodec" "github.com/stretchr/testify/require" ) func TestMultiColumnCommonHandle(t *testing.T) { tblInfo := buildTableInfo(t, "create table t (a int, b int, u varchar(64) unique, nu varchar(64), primary key (a, b), index nu (nu))") var idxUnique, idxNonUnique table.Index for _, idxInfo := range tblInfo.Indices { idx := tables.NewIndex(tblInfo.ID, tblInfo, idxInfo) if idxInfo.Name.L == "u" { idxUnique = idx } else if idxInfo.Name.L == "nu" { idxNonUnique = idx } } 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, clean := testkit.CreateMockStore(t) defer clean() txn, err := store.Begin() require.NoError(t, err) mockCtx := mock.NewContext() sc := mockCtx.GetSessionVars().StmtCtx // create index for "insert t values (3, 2, "abc", "abc") idxColVals := types.MakeDatums("abc") handleColVals := types.MakeDatums(3, 2) encodedHandle, err := codec.EncodeKey(sc, nil, handleColVals...) require.NoError(t, err) commonHandle, err := kv.NewCommonHandle(encodedHandle) require.NoError(t, err) _ = idxNonUnique for _, idx := range []table.Index{idxUnique, idxNonUnique} { 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", d.GetString()) _, d, err = codec.DecodeOne(colVals[1]) require.NoError(t, err) require.Equal(t, int64(3), 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()) } } func TestSingleColumnCommonHandle(t *testing.T) { tblInfo := buildTableInfo(t, "create table t (a varchar(255) primary key, u int unique, nu int, index nu (nu))") var idxUnique, idxNonUnique table.Index for _, idxInfo := range tblInfo.Indices { idx := tables.NewIndex(tblInfo.ID, tblInfo, idxInfo) if idxInfo.Name.L == "u" { idxUnique = idx } else if idxInfo.Name.L == "nu" { idxNonUnique = idx } } store, clean := testkit.CreateMockStore(t) defer clean() txn, err := store.Begin() require.NoError(t, err) mockCtx := mock.NewContext() sc := mockCtx.GetSessionVars().StmtCtx // create index for "insert t values ('abc', 1, 1)" idxColVals := types.MakeDatums(1) handleColVals := types.MakeDatums("abc") encodedHandle, err := codec.EncodeKey(sc, nil, handleColVals...) require.NoError(t, err) commonHandle, err := kv.NewCommonHandle(encodedHandle) require.NoError(t, err) for _, idx := range []table.Index{idxUnique, idxNonUnique} { 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) colVals, err := tablecodec.DecodeIndexKV(key, val, 1, tablecodec.HandleDefault, tables.BuildRowcodecColInfoForIndexColumns(idx.Meta(), tblInfo)) require.NoError(t, err) require.Len(t, colVals, 2) _, d, err := codec.DecodeOne(colVals[0]) require.NoError(t, err) require.Equal(t, int64(1), d.GetInt64()) _, d, err = codec.DecodeOne(colVals[1]) require.NoError(t, err) require.Equal(t, "abc", d.GetString()) handle, err := tablecodec.DecodeIndexHandle(key, val, 1) require.NoError(t, err) require.False(t, handle.IsInt()) require.Equal(t, commonHandle.Encoded(), handle.Encoded()) unTouchedVal := append([]byte{1}, val[1:]...) unTouchedVal = append(unTouchedVal, kv.UnCommitIndexKVFlag) _, err = tablecodec.DecodeIndexKV(key, unTouchedVal, 1, tablecodec.HandleDefault, tables.BuildRowcodecColInfoForIndexColumns(idx.Meta(), tblInfo)) require.NoError(t, err) } } func buildTableInfo(t *testing.T, sql string) *model.TableInfo { stmt, err := parser.New().ParseOneStmt(sql, "", "") require.NoError(t, err) tblInfo, err := ddl.BuildTableInfoFromAST(stmt.(*ast.CreateTableStmt)) require.NoError(t, err) return tblInfo } func TestIssue29520(t *testing.T) { store, clean := testkit.CreateMockStore(t) defer clean() tk := testkit.NewTestKit(t, store) tk.MustExec("set @@tidb_enable_mutation_checker=1") tk.MustExec("use test") tk.MustExec("drop table if exists t") tk.MustExec("create table t(c year, PRIMARY KEY (c) CLUSTERED, KEY i1(c))") tk.MustExec("insert into t values('2020')") } func TestAssertionWithLazyCheck(t *testing.T) { store, clean := testkit.CreateMockStore(t) defer clean() tk := testkit.NewTestKit(t, store) tk.MustExec("set @@tidb_txn_assertion_level = 'STRICT'") tk.MustExec("use test") tk.MustExec("drop table if exists t") tk.MustExec("create table t (id int primary key, v1 int, v2 int, index (v1), unique index (v2))") tk.MustExec("set @@tidb_constraint_check_in_place = true") tk.MustExec("insert into t values (1, 1, 1)") tk.MustGetErrCode("insert into t values (2, 1, 1)", errno.ErrDupEntry) tk.MustExec("set @@tidb_constraint_check_in_place = false") tk.MustExec("insert into t values (3, 3, 3)") // The constraint check (index key must not exist) will be done while prewriting. TiDB should avoid setting // assertion on the index key. Even it's set, TiKV will skip checking assertion for mutation types `Insert` and // `CheckNotExist`. Anyway there should never be assertion failure. tk.MustGetErrCode("insert into t values (4, 3, 3)", errno.ErrDupEntry) }