Files
tidb/table/tables/index_test.go

206 lines
7.3 KiB
Go

// 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)
}