248 lines
9.7 KiB
Go
248 lines
9.7 KiB
Go
// Copyright 2023-2023 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 ddl_test
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/pingcap/failpoint"
|
|
"github.com/pingcap/tidb/pkg/meta/model"
|
|
"github.com/pingcap/tidb/pkg/table"
|
|
"github.com/pingcap/tidb/pkg/table/tables"
|
|
"github.com/pingcap/tidb/pkg/testkit"
|
|
"github.com/pingcap/tidb/pkg/testkit/external"
|
|
"github.com/pingcap/tidb/pkg/testkit/testfailpoint"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestAlterConstraintAddDrop(t *testing.T) {
|
|
store := testkit.CreateMockStore(t)
|
|
tk := testkit.NewTestKit(t, store)
|
|
tk.MustExec("use test")
|
|
tk.MustExec("drop table if exists t")
|
|
tk.MustExec("set @@global.tidb_enable_check_constraint = 1")
|
|
tk.MustExec("create table t (a int check(a>1), b int, constraint a_b check(a<b))")
|
|
|
|
tk1 := testkit.NewTestKit(t, store)
|
|
tk1.MustExec("use test")
|
|
_, err := tk1.Exec("insert into t (a, b) values(2, 3)")
|
|
require.NoError(t, err)
|
|
_, err = tk1.Exec("insert into t (a, b) values(3, 4)")
|
|
require.NoError(t, err)
|
|
_, err = tk1.Exec("insert into t (a, b) values(4, 3)")
|
|
require.Error(t, err)
|
|
|
|
var checkErr error
|
|
testfailpoint.EnableCall(t, "github.com/pingcap/tidb/pkg/ddl/onJobUpdated", func(job *model.Job) {
|
|
if checkErr != nil {
|
|
return
|
|
}
|
|
if job.SchemaState == model.StateWriteOnly {
|
|
_, checkErr = tk1.Exec("insert into t (a, b) values(5,6) ")
|
|
}
|
|
})
|
|
tk.MustExec("alter table t add constraint cc check ( b < 5 )")
|
|
require.Errorf(t, err, "[table:3819]Check constraint 'cc' is violated.")
|
|
|
|
tk.MustExec("alter table t drop constraint cc")
|
|
require.Errorf(t, err, "[table:3819]Check constraint 'cc' is violated.")
|
|
tk.MustExec("drop table if exists t")
|
|
}
|
|
|
|
func TestAlterAddConstraintStateChange(t *testing.T) {
|
|
store := testkit.CreateMockStore(t)
|
|
tk := testkit.NewTestKit(t, store)
|
|
tk.MustExec("use test")
|
|
tk.MustExec("drop table if exists t")
|
|
tk.MustExec("set @@global.tidb_enable_check_constraint = 1")
|
|
tk.MustExec("create table t (a int)")
|
|
|
|
tk1 := testkit.NewTestKit(t, store)
|
|
tk1.MustExec("use test")
|
|
tk1.MustExec("insert into t values(12)")
|
|
|
|
var checkErr error
|
|
testfailpoint.EnableCall(t, "github.com/pingcap/tidb/pkg/ddl/onJobUpdated", func(job *model.Job) {
|
|
if checkErr != nil {
|
|
return
|
|
}
|
|
if job.SchemaState == model.StateWriteReorganization {
|
|
tk1.MustQuery(fmt.Sprintf("select count(1) from `%s`.`%s` where not %s limit 1", "test", "t", "a > 10")).Check(testkit.Rows("0"))
|
|
// set constraint state
|
|
constraintTable := external.GetTableByName(t, tk1, "test", "t")
|
|
tableCommon, ok := constraintTable.(*tables.TableCommon)
|
|
require.True(t, ok)
|
|
originCons := tableCommon.Constraints
|
|
tableCommon.Constraints = []*table.Constraint{}
|
|
tableCommon.ResetColumnsCache()
|
|
// insert data
|
|
tk1.MustExec("insert into t values(1)")
|
|
// recover
|
|
tableCommon.Constraints = originCons
|
|
tableCommon.WritableConstraint()
|
|
}
|
|
})
|
|
|
|
//StatNone StateWriteReorganization
|
|
require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockVerifyRemainDataSuccess", "return(true)"))
|
|
tk.MustExec("alter table t add constraint c0 check ( a > 10)")
|
|
tk.MustQuery("select * from t").Check(testkit.Rows("12", "1"))
|
|
tk.MustQuery("show create table t").Check(testkit.Rows("t CREATE TABLE `t` (\n `a` int(11) DEFAULT NULL,\n CONSTRAINT `c0` CHECK ((`a` > 10))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin"))
|
|
tk.MustExec("alter table t drop constraint c0")
|
|
tk.MustExec("delete from t where a = 1")
|
|
require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockVerifyRemainDataSuccess"))
|
|
}
|
|
|
|
func TestAlterAddConstraintStateChange1(t *testing.T) {
|
|
store := testkit.CreateMockStore(t)
|
|
tk := testkit.NewTestKit(t, store)
|
|
tk.MustExec("use test")
|
|
tk.MustExec("drop table if exists t")
|
|
tk.MustExec("set @@global.tidb_enable_check_constraint = 1")
|
|
tk.MustExec("create table t (a int)")
|
|
tk1 := testkit.NewTestKit(t, store)
|
|
tk1.MustExec("use test")
|
|
tk1.MustExec("insert into t values(12)")
|
|
|
|
// StatNone -> StateWriteOnly
|
|
testfailpoint.EnableCall(t, "github.com/pingcap/tidb/pkg/ddl/onJobUpdated", func(job *model.Job) {
|
|
if job.SchemaState == model.StateWriteOnly {
|
|
// set constraint state
|
|
constraintTable := external.GetTableByName(t, tk1, "test", "t")
|
|
tableCommon, ok := constraintTable.(*tables.TableCommon)
|
|
require.True(t, ok)
|
|
originCons := tableCommon.Constraints
|
|
tableCommon.Constraints = []*table.Constraint{}
|
|
tableCommon.ResetColumnsCache()
|
|
// insert data
|
|
tk1.MustExec("insert into t values(1)")
|
|
// recover
|
|
tableCommon.Constraints = originCons
|
|
tableCommon.WritableConstraint()
|
|
}
|
|
})
|
|
tk.MustGetErrMsg("alter table t add constraint c1 check ( a > 10)", "[ddl:3819]Check constraint 'c1' is violated.")
|
|
tk.MustQuery("select * from t").Check(testkit.Rows("12", "1"))
|
|
tk.MustQuery("show create table t").Check(testkit.Rows("t CREATE TABLE `t` (\n `a` int(11) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin"))
|
|
tk.MustExec("delete from t where a = 1")
|
|
}
|
|
|
|
func TestAlterAddConstraintStateChange2(t *testing.T) {
|
|
store := testkit.CreateMockStore(t)
|
|
tk := testkit.NewTestKit(t, store)
|
|
tk.MustExec("use test")
|
|
tk.MustExec("drop table if exists t")
|
|
tk.MustExec("set @@global.tidb_enable_check_constraint = 1")
|
|
tk.MustExec("create table t (a int)")
|
|
tk1 := testkit.NewTestKit(t, store)
|
|
tk1.MustExec("use test")
|
|
tk1.MustExec("insert into t values(12)")
|
|
|
|
// StateWriteOnly -> StateWriteReorganization
|
|
testfailpoint.EnableCall(t, "github.com/pingcap/tidb/pkg/ddl/onJobUpdated", func(job *model.Job) {
|
|
if job.SchemaState == model.StateWriteReorganization {
|
|
// set constraint state
|
|
constraintTable := external.GetTableByName(t, tk1, "test", "t")
|
|
tableCommon, ok := constraintTable.(*tables.TableCommon)
|
|
require.True(t, ok)
|
|
tableCommon.Constraints[0].State = model.StateWriteOnly
|
|
// insert data
|
|
tk1.MustGetErrMsg("insert into t values(1)", "[table:3819]Check constraint 'c2' is violated.")
|
|
// recover
|
|
tableCommon.Constraints[0].State = model.StateWriteReorganization
|
|
tableCommon.WritableConstraint()
|
|
}
|
|
})
|
|
tk.MustExec("alter table t add constraint c2 check ( a > 10)")
|
|
tk.MustQuery("select * from t").Check(testkit.Rows("12"))
|
|
tk.MustQuery("show create table t").Check(testkit.Rows("t CREATE TABLE `t` (\n `a` int(11) DEFAULT NULL,\n CONSTRAINT `c2` CHECK ((`a` > 10))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin"))
|
|
tk.MustExec("alter table t drop constraint c2")
|
|
}
|
|
|
|
func TestAlterAddConstraintStateChange3(t *testing.T) {
|
|
store := testkit.CreateMockStore(t)
|
|
tk := testkit.NewTestKit(t, store)
|
|
tk.MustExec("use test")
|
|
tk.MustExec("drop table if exists t")
|
|
tk.MustExec("set @@global.tidb_enable_check_constraint = 1")
|
|
tk.MustExec("create table t (a int)")
|
|
tk1 := testkit.NewTestKit(t, store)
|
|
tk1.MustExec("use test")
|
|
tk1.MustExec("insert into t values(12)")
|
|
|
|
addCheckDone := false
|
|
// StateWriteReorganization -> StatePublic
|
|
testfailpoint.EnableCall(t, "github.com/pingcap/tidb/pkg/ddl/onJobUpdated", func(job *model.Job) {
|
|
if job.Type != model.ActionAddCheckConstraint || job.TableName != "t" {
|
|
return
|
|
}
|
|
if job.SchemaState == model.StatePublic && job.IsDone() {
|
|
// set constraint state
|
|
constraintTable := external.GetTableByName(t, tk1, "test", "t")
|
|
tableCommon, ok := constraintTable.(*tables.TableCommon)
|
|
require.True(t, ok)
|
|
tableCommon.Constraints[0].State = model.StateWriteReorganization
|
|
// insert data
|
|
tk1.MustGetErrMsg("insert into t values(1)", "[table:3819]Check constraint 'c3' is violated.")
|
|
// recover
|
|
tableCommon.Constraints[0].State = model.StatePublic
|
|
tableCommon.WritableConstraint()
|
|
addCheckDone = true
|
|
}
|
|
})
|
|
tk.MustExec("alter table t add constraint c3 check ( a > 10)")
|
|
// Issue TiDB#48123.
|
|
for i := 0; i <= 100; i++ {
|
|
if addCheckDone {
|
|
break
|
|
}
|
|
time.Sleep(10 * time.Millisecond)
|
|
}
|
|
tk.MustQuery("select * from t").Check(testkit.Rows("12"))
|
|
tk.MustQuery("show create table t").Check(testkit.Rows("t CREATE TABLE `t` (\n `a` int(11) DEFAULT NULL,\n CONSTRAINT `c3` CHECK ((`a` > 10))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin"))
|
|
}
|
|
|
|
func TestAlterEnforcedConstraintStateChange(t *testing.T) {
|
|
store := testkit.CreateMockStore(t)
|
|
tk := testkit.NewTestKit(t, store)
|
|
tk.MustExec("use test")
|
|
tk.MustExec("drop table if exists t")
|
|
tk.MustExec("set @@global.tidb_enable_check_constraint = 1")
|
|
tk.MustExec("create table t (a int, constraint c1 check (a > 10) not enforced)")
|
|
tk1 := testkit.NewTestKit(t, store)
|
|
tk1.MustExec("use test")
|
|
tk1.MustExec("insert into t values(12)")
|
|
|
|
// StateWriteReorganization -> StatePublic
|
|
testfailpoint.EnableCall(t, "github.com/pingcap/tidb/pkg/ddl/onJobUpdated", func(job *model.Job) {
|
|
if job.SchemaState == model.StateWriteReorganization {
|
|
// set constraint state
|
|
constraintTable := external.GetTableByName(t, tk1, "test", "t")
|
|
tableCommon, ok := constraintTable.(*tables.TableCommon)
|
|
require.True(t, ok)
|
|
tableCommon.Constraints[0].State = model.StateWriteOnly
|
|
// insert data
|
|
tk1.MustGetErrMsg("insert into t values(1)", "[table:3819]Check constraint 'c1' is violated.")
|
|
// recover
|
|
tableCommon.Constraints[0].State = model.StateWriteReorganization
|
|
tableCommon.WritableConstraint()
|
|
}
|
|
})
|
|
tk.MustExec("alter table t alter constraint c1 enforced")
|
|
tk.MustQuery("select * from t").Check(testkit.Rows("12"))
|
|
}
|