206 lines
7.3 KiB
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)
|
|
}
|