Files
tidb/pkg/executor/update_test.go

194 lines
5.8 KiB
Go

// Copyright 2019 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 executor_test
import (
"fmt"
"strconv"
"testing"
"time"
"github.com/pingcap/tidb/pkg/kv"
sessiontypes "github.com/pingcap/tidb/pkg/session/types"
"github.com/pingcap/tidb/pkg/sessionctx/variable"
"github.com/pingcap/tidb/pkg/testkit"
"github.com/stretchr/testify/require"
)
func TestPessimisticUpdatePKLazyCheck(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
testUpdatePKLazyCheck(t, tk, variable.ClusteredIndexDefModeOn)
testUpdatePKLazyCheck(t, tk, variable.ClusteredIndexDefModeOff)
testUpdatePKLazyCheck(t, tk, variable.ClusteredIndexDefModeIntOnly)
}
func testUpdatePKLazyCheck(t *testing.T, tk *testkit.TestKit, clusteredIndex variable.ClusteredIndexDefMode) {
tk.Session().GetSessionVars().EnableClusteredIndex = clusteredIndex
tk.MustExec(`drop table if exists upk`)
tk.MustExec(`create table upk (a int, b int, c int, primary key (a, b))`)
tk.MustExec(`insert upk values (1, 1, 1), (2, 2, 2), (3, 3, 3)`)
tk.MustExec("begin pessimistic")
tk.MustExec("update upk set b = b + 1 where a between 1 and 2")
require.Equal(t, 2, getPresumeExistsCount(t, tk.Session()))
err := tk.ExecToErr("update upk set a = 3, b = 3 where a between 1 and 2")
require.True(t, kv.ErrKeyExists.Equal(err))
tk.MustExec("commit")
}
func getPresumeExistsCount(t *testing.T, se sessiontypes.Session) int {
txn, err := se.Txn(false)
require.NoError(t, err)
buf := txn.GetMemBuffer()
it, err := buf.Iter(nil, nil)
require.NoError(t, err)
presumeNotExistsCnt := 0
for it.Valid() {
flags, err1 := buf.GetFlags(it.Key())
require.Nil(t, err1)
err = it.Next()
require.NoError(t, err)
if flags.HasPresumeKeyNotExists() {
presumeNotExistsCnt++
}
}
return presumeNotExistsCnt
}
func TestLockUnchangedUniqueKeys(t *testing.T) {
store := testkit.CreateMockStore(t)
tk1 := testkit.NewTestKit(t, store)
tk2 := testkit.NewTestKit(t, store)
tk1.MustExec("use test")
tk2.MustExec("use test")
for _, shouldLock := range []bool{true, false} {
for _, tt := range []struct {
name string
create string
insert string
update string
isClusteredPK bool
}{
{
// ref https://github.com/pingcap/tidb/issues/36438
"Issue36438",
"create table t (i varchar(10), unique key(i))",
"insert into t values ('a')",
"update t set i = 'a'",
false,
},
{
"ClusteredAndRowUnchanged",
"create table t (k int, v int, primary key(k) clustered, key sk(k))",
"insert into t values (1, 10)",
"update t force index(sk) set v = 10 where k = 1",
true,
},
{
"ClusteredAndRowUnchangedAndParted",
"create table t (k int, v int, primary key(k) clustered, key sk(k)) partition by hash(k) partitions 4",
"insert into t values (1, 10)",
"update t force index(sk) set v = 10 where k = 1",
true,
},
{
"ClusteredAndRowChanged",
"create table t (k int, v int, primary key(k) clustered, key sk(k))",
"insert into t values (1, 10)",
"update t force index(sk) set v = 11 where k = 1",
true,
},
{
"NonClusteredAndRowUnchanged",
"create table t (k int, v int, primary key(k) nonclustered, key sk(k))",
"insert into t values (1, 10)",
"update t force index(sk) set v = 10 where k = 1",
false,
},
{
"NonClusteredAndRowUnchangedAndParted",
"create table t (k int, v int, primary key(k) nonclustered, key sk(k)) partition by hash(k) partitions 4",
"insert into t values (1, 10)",
"update t force index(sk) set v = 10 where k = 1",
false,
},
{
"NonClusteredAndRowChanged",
"create table t (k int, v int, primary key(k) nonclustered, key sk(k))",
"insert into t values (1, 10)",
"update t force index(sk) set v = 11 where k = 1",
false,
},
{
"UniqueAndRowUnchanged",
"create table t (k int, v int, unique key uk(k), key sk(k))",
"insert into t values (1, 10)",
"update t force index(sk) set v = 10 where k = 1",
false,
},
{
"UniqueAndRowUnchangedAndParted",
"create table t (k int, v int, unique key uk(k), key sk(k)) partition by hash(k) partitions 4",
"insert into t values (1, 10)",
"update t force index(sk) set v = 10 where k = 1",
false,
},
{
"UniqueAndRowChanged",
"create table t (k int, v int, unique key uk(k), key sk(k))",
"insert into t values (1, 10)",
"update t force index(sk) set v = 11 where k = 1",
false,
},
} {
t.Run(
tt.name+"-"+strconv.FormatBool(shouldLock), func(t *testing.T) {
tk1.MustExec(fmt.Sprintf("set @@tidb_lock_unchanged_keys = %v", shouldLock))
tk1.MustExec("drop table if exists t")
tk1.MustExec(tt.create)
tk1.MustExec(tt.insert)
tk1.MustExec("begin pessimistic")
tk1.MustExec(tt.update)
errCh := make(chan error, 1)
go func() {
_, err := tk2.Exec(tt.insert)
errCh <- err
}()
select {
case <-time.After(100 * time.Millisecond):
if !shouldLock && !tt.isClusteredPK {
require.Fail(t, "insert is blocked by update")
}
tk1.MustExec("rollback")
require.Error(t, <-errCh)
case err := <-errCh:
require.Error(t, err)
if shouldLock {
require.Fail(t, "insert is not blocked by update")
}
}
},
)
}
}
}