Files
tidb/ddl/column_test.go
2023-01-29 14:29:54 +08:00

1024 lines
33 KiB
Go

// Copyright 2015 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 (
"context"
"fmt"
"reflect"
"strconv"
"sync"
"testing"
"github.com/pingcap/errors"
"github.com/pingcap/tidb/ddl/internal/callback"
"github.com/pingcap/tidb/domain"
"github.com/pingcap/tidb/kv"
"github.com/pingcap/tidb/parser/model"
"github.com/pingcap/tidb/parser/terror"
"github.com/pingcap/tidb/sessionctx"
"github.com/pingcap/tidb/sessiontxn"
"github.com/pingcap/tidb/store/mockstore"
"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/stretchr/testify/require"
)
func testCreateColumn(tk *testkit.TestKit, t *testing.T, ctx sessionctx.Context, tblID int64,
colName string, pos string, defaultValue interface{}, dom *domain.Domain) int64 {
sql := fmt.Sprintf("alter table t1 add column %s int ", colName)
if defaultValue != nil {
sql += fmt.Sprintf("default %v ", defaultValue)
}
sql += pos
tk.MustExec(sql)
idi, _ := strconv.Atoi(tk.MustQuery("admin show ddl jobs 1;").Rows()[0][0].(string))
id := int64(idi)
v := getSchemaVer(t, ctx)
require.NoError(t, dom.Reload())
tblInfo, exist := dom.InfoSchema().TableByID(tblID)
require.True(t, exist)
checkHistoryJobArgs(t, ctx, id, &historyJobArgs{ver: v, tbl: tblInfo.Meta()})
return id
}
func testCreateColumns(tk *testkit.TestKit, t *testing.T, ctx sessionctx.Context, tblID int64,
colNames []string, positions []string, defaultValue interface{}, dom *domain.Domain) int64 {
sql := "alter table t1 add column "
for i, colName := range colNames {
if i != 0 {
sql += ", add column "
}
sql += fmt.Sprintf("%s int %s", colName, positions[i])
if defaultValue != nil {
sql += fmt.Sprintf(" default %v", defaultValue)
}
}
tk.MustExec(sql)
idi, _ := strconv.Atoi(tk.MustQuery("admin show ddl jobs 1;").Rows()[0][0].(string))
id := int64(idi)
v := getSchemaVer(t, ctx)
require.NoError(t, dom.Reload())
tblInfo, exist := dom.InfoSchema().TableByID(tblID)
require.True(t, exist)
checkHistoryJobArgs(t, ctx, id, &historyJobArgs{ver: v, tbl: tblInfo.Meta()})
return id
}
func testDropColumnInternal(tk *testkit.TestKit, t *testing.T, ctx sessionctx.Context, tblID int64, colName string, isError bool, dom *domain.Domain) int64 {
sql := fmt.Sprintf("alter table t1 drop column %s ", colName)
_, err := tk.Exec(sql)
if isError {
require.Error(t, err)
} else {
require.NoError(t, err)
}
idi, _ := strconv.Atoi(tk.MustQuery("admin show ddl jobs 1;").Rows()[0][0].(string))
id := int64(idi)
v := getSchemaVer(t, ctx)
require.NoError(t, dom.Reload())
tblInfo, exist := dom.InfoSchema().TableByID(tblID)
require.True(t, exist)
checkHistoryJobArgs(t, ctx, id, &historyJobArgs{ver: v, tbl: tblInfo.Meta()})
return id
}
func testDropTable(tk *testkit.TestKit, t *testing.T, dbName, tblName string, dom *domain.Domain) int64 {
sql := fmt.Sprintf("drop table %s ", tblName)
tk.MustExec("use " + dbName)
tk.MustExec(sql)
idi, _ := strconv.Atoi(tk.MustQuery("admin show ddl jobs 1;").Rows()[0][0].(string))
id := int64(idi)
require.NoError(t, dom.Reload())
_, err := dom.InfoSchema().TableByName(model.NewCIStr(dbName), model.NewCIStr(tblName))
require.Error(t, err)
return id
}
func testCreateIndex(tk *testkit.TestKit, t *testing.T, ctx sessionctx.Context, tblID int64, unique bool, indexName string, colName string, dom *domain.Domain) int64 {
un := ""
if unique {
un = "unique"
}
sql := fmt.Sprintf("alter table t1 add %s index %s(%s)", un, indexName, colName)
tk.MustExec(sql)
idi, _ := strconv.Atoi(tk.MustQuery("admin show ddl jobs 1;").Rows()[0][0].(string))
id := int64(idi)
v := getSchemaVer(t, ctx)
require.NoError(t, dom.Reload())
tblInfo, exist := dom.InfoSchema().TableByID(tblID)
require.True(t, exist)
checkHistoryJobArgs(t, ctx, id, &historyJobArgs{ver: v, tbl: tblInfo.Meta()})
return id
}
func testDropColumns(tk *testkit.TestKit, t *testing.T, ctx sessionctx.Context, tblID int64, colName []string, isError bool, dom *domain.Domain) int64 {
sql := "alter table t1 drop column "
for i, name := range colName {
if i != 0 {
sql += ", drop column "
}
sql += name
}
_, err := tk.Exec(sql)
if isError {
require.Error(t, err)
} else {
require.NoError(t, err)
}
idi, _ := strconv.Atoi(tk.MustQuery("admin show ddl jobs 1;").Rows()[0][0].(string))
id := int64(idi)
v := getSchemaVer(t, ctx)
require.NoError(t, dom.Reload())
tblInfo, exist := dom.InfoSchema().TableByID(tblID)
require.True(t, exist)
checkHistoryJobArgs(t, ctx, id, &historyJobArgs{ver: v, tbl: tblInfo.Meta()})
return id
}
func TestColumnBasic(t *testing.T) {
store, dom := testkit.CreateMockStoreAndDomain(t, mockstore.WithDDLChecker())
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("create table t1 (c1 int, c2 int, c3 int);")
num := 10
for i := 0; i < num; i++ {
tk.MustExec(fmt.Sprintf("insert into t1 values(%d, %d, %d)", i, 10*i, 100*i))
}
ctx := testNewContext(store)
err := sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
var tableID int64
rs := tk.MustQuery("select TIDB_TABLE_ID from information_schema.tables where table_name='t1' and table_schema='test';")
tableIDi, _ := strconv.Atoi(rs.Rows()[0][0].(string))
tableID = int64(tableIDi)
tbl := testGetTable(t, dom, tableID)
i := 0
err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) {
require.Len(t, data, 3)
require.Equal(t, data[0].GetInt64(), int64(i))
require.Equal(t, data[1].GetInt64(), int64(10*i))
require.Equal(t, data[2].GetInt64(), int64(100*i))
i++
return true, nil
})
require.NoError(t, err)
require.Equal(t, i, num)
require.Nil(t, table.FindCol(tbl.Cols(), "c4"))
jobID := testCreateColumn(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c4", "after c3", 100, dom)
testCheckJobDone(t, store, jobID, true)
tbl = testGetTable(t, dom, tableID)
require.NotNil(t, table.FindCol(tbl.Cols(), "c4"))
i = 0
err = tables.IterRecords(tbl, ctx, tbl.Cols(),
func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) {
require.Len(t, data, 4)
require.Equal(t, data[0].GetInt64(), int64(i))
require.Equal(t, data[1].GetInt64(), int64(10*i))
require.Equal(t, data[2].GetInt64(), int64(100*i))
require.Equal(t, data[3].GetInt64(), int64(100))
i++
return true, nil
})
require.NoError(t, err)
require.Equal(t, i, num)
h, err := tbl.AddRecord(ctx, types.MakeDatums(11, 12, 13, 14))
require.NoError(t, err)
err = sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
values, err := tables.RowWithCols(tbl, ctx, h, tbl.Cols())
require.NoError(t, err)
require.Len(t, values, 4)
require.Equal(t, values[3].GetInt64(), int64(14))
jobID = testDropColumnInternal(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c4", false, dom)
testCheckJobDone(t, store, jobID, false)
tbl = testGetTable(t, dom, tableID)
values, err = tables.RowWithCols(tbl, ctx, h, tbl.Cols())
require.NoError(t, err)
require.Len(t, values, 3)
require.Equal(t, values[2].GetInt64(), int64(13))
jobID = testCreateColumn(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c4", "", 111, dom)
testCheckJobDone(t, store, jobID, true)
tbl = testGetTable(t, dom, tableID)
values, err = tables.RowWithCols(tbl, ctx, h, tbl.Cols())
require.NoError(t, err)
require.Len(t, values, 4)
require.Equal(t, values[3].GetInt64(), int64(111))
jobID = testCreateColumn(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c5", "", 101, dom)
testCheckJobDone(t, store, jobID, true)
tbl = testGetTable(t, dom, tableID)
values, err = tables.RowWithCols(tbl, ctx, h, tbl.Cols())
require.NoError(t, err)
require.Len(t, values, 5)
require.Equal(t, values[4].GetInt64(), int64(101))
jobID = testCreateColumn(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c6", "first", 202, dom)
testCheckJobDone(t, store, jobID, true)
tbl = testGetTable(t, dom, tableID)
cols := tbl.Cols()
require.Len(t, cols, 6)
require.Equal(t, cols[0].Offset, 0)
require.Equal(t, cols[0].Name.L, "c6")
require.Equal(t, cols[1].Offset, 1)
require.Equal(t, cols[1].Name.L, "c1")
require.Equal(t, cols[2].Offset, 2)
require.Equal(t, cols[2].Name.L, "c2")
require.Equal(t, cols[3].Offset, 3)
require.Equal(t, cols[3].Name.L, "c3")
require.Equal(t, cols[4].Offset, 4)
require.Equal(t, cols[4].Name.L, "c4")
require.Equal(t, cols[5].Offset, 5)
require.Equal(t, cols[5].Name.L, "c5")
values, err = tables.RowWithCols(tbl, ctx, h, cols)
require.NoError(t, err)
require.Len(t, values, 6)
require.Equal(t, values[0].GetInt64(), int64(202))
require.Equal(t, values[5].GetInt64(), int64(101))
jobID = testDropColumnInternal(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c2", false, dom)
testCheckJobDone(t, store, jobID, false)
tbl = testGetTable(t, dom, tableID)
values, err = tables.RowWithCols(tbl, ctx, h, tbl.Cols())
require.NoError(t, err)
require.Len(t, values, 5)
require.Equal(t, values[0].GetInt64(), int64(202))
require.Equal(t, values[4].GetInt64(), int64(101))
jobID = testDropColumnInternal(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c1", false, dom)
testCheckJobDone(t, store, jobID, false)
jobID = testDropColumnInternal(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c3", false, dom)
testCheckJobDone(t, store, jobID, false)
jobID = testDropColumnInternal(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c4", false, dom)
testCheckJobDone(t, store, jobID, false)
jobID = testCreateIndex(tk, t, testkit.NewTestKit(t, store).Session(), tableID, false, "c5_idx", "c5", dom)
testCheckJobDone(t, store, jobID, true)
jobID = testDropColumnInternal(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c5", false, dom)
testCheckJobDone(t, store, jobID, false)
jobID = testDropColumnInternal(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c6", true, dom)
testCheckJobDone(t, store, jobID, false)
testDropTable(tk, t, "test", "t1", dom)
}
func checkColumnKVExist(ctx sessionctx.Context, t table.Table, handle kv.Handle, col *table.Column, columnValue interface{}, isExist bool) error {
err := sessiontxn.NewTxn(context.Background(), ctx)
if err != nil {
return errors.Trace(err)
}
defer func() {
if txn, err1 := ctx.Txn(true); err1 == nil {
err = txn.Commit(context.Background())
if err != nil {
panic(err)
}
}
}()
key := tablecodec.EncodeRecordKey(t.RecordPrefix(), handle)
txn, err := ctx.Txn(true)
if err != nil {
return errors.Trace(err)
}
data, err := txn.Get(context.TODO(), key)
if !isExist {
if terror.ErrorEqual(err, kv.ErrNotExist) {
return nil
}
}
if err != nil {
return errors.Trace(err)
}
colMap := make(map[int64]*types.FieldType)
colMap[col.ID] = &col.FieldType
rowMap, err := tablecodec.DecodeRowToDatumMap(data, colMap, ctx.GetSessionVars().Location())
if err != nil {
return errors.Trace(err)
}
val, ok := rowMap[col.ID]
if isExist {
if !ok || val.GetValue() != columnValue {
return errors.Errorf("%v is not equal to %v", val.GetValue(), columnValue)
}
} else {
if ok {
return errors.Errorf("column value should not exists")
}
}
return nil
}
func checkNoneColumn(t *testing.T, ctx sessionctx.Context, tableID int64, handle kv.Handle, col *table.Column, columnValue interface{}, dom *domain.Domain) {
tbl := testGetTable(t, dom, tableID)
err := checkColumnKVExist(ctx, tbl, handle, col, columnValue, false)
require.NoError(t, err)
err = testGetColumn(tbl, col.Name.L, false)
require.NoError(t, err)
}
func checkDeleteOnlyColumn(t *testing.T, ctx sessionctx.Context, tableID int64, handle kv.Handle, col *table.Column, row []types.Datum, columnValue interface{}, dom *domain.Domain) {
tbl := testGetTable(t, dom, tableID)
err := sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
i := 0
err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) {
require.Truef(t, reflect.DeepEqual(data, row), "%v not equal to %v", data, row)
i++
return true, nil
})
require.NoError(t, err)
require.Equalf(t, 1, i, "expect 1, got %v", i)
err = checkColumnKVExist(ctx, tbl, handle, col, columnValue, false)
require.NoError(t, err)
// Test add a new row.
err = sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
newRow := types.MakeDatums(int64(11), int64(22), int64(33))
newHandle, err := tbl.AddRecord(ctx, newRow)
require.NoError(t, err)
err = sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
rows := [][]types.Datum{row, newRow}
i = 0
err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) {
require.Truef(t, reflect.DeepEqual(data, rows[i]), "%v not equal to %v", data, rows[i])
i++
return true, nil
})
require.NoError(t, err)
require.Equalf(t, 2, i, "expect 2, got %v", i)
err = checkColumnKVExist(ctx, tbl, handle, col, columnValue, false)
require.NoError(t, err)
// Test remove a row.
err = sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
err = tbl.RemoveRecord(ctx, newHandle, newRow)
require.NoError(t, err)
err = sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
i = 0
err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) {
i++
return true, nil
})
require.NoError(t, err)
require.Equalf(t, 1, i, "expect 1, got %v", i)
err = checkColumnKVExist(ctx, tbl, newHandle, col, columnValue, false)
require.NoError(t, err)
err = testGetColumn(tbl, col.Name.L, false)
require.NoError(t, err)
}
func checkWriteOnlyColumn(t *testing.T, ctx sessionctx.Context, tableID int64, handle kv.Handle, col *table.Column, row []types.Datum, columnValue interface{}, dom *domain.Domain) {
tbl := testGetTable(t, dom, tableID)
err := sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
i := 0
err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) {
require.Truef(t, reflect.DeepEqual(data, row), "%v not equal to %v", data, row)
i++
return true, nil
})
require.NoError(t, err)
require.Equalf(t, 1, i, "expect 1, got %v", i)
err = checkColumnKVExist(ctx, tbl, handle, col, columnValue, false)
require.NoError(t, err)
// Test add a new row.
err = sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
newRow := types.MakeDatums(int64(11), int64(22), int64(33))
newHandle, err := tbl.AddRecord(ctx, newRow)
require.NoError(t, err)
err = sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
rows := [][]types.Datum{row, newRow}
i = 0
err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) {
require.Truef(t, reflect.DeepEqual(data, rows[i]), "%v not equal to %v", data, rows[i])
i++
return true, nil
})
require.NoError(t, err)
require.Equalf(t, 2, i, "expect 2, got %v", i)
err = checkColumnKVExist(ctx, tbl, newHandle, col, columnValue, true)
require.NoError(t, err)
// Test remove a row.
err = sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
err = tbl.RemoveRecord(ctx, newHandle, newRow)
require.NoError(t, err)
err = sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
i = 0
err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) {
i++
return true, nil
})
require.NoError(t, err)
require.Equalf(t, 1, i, "expect 1, got %v", i)
err = checkColumnKVExist(ctx, tbl, newHandle, col, columnValue, false)
require.NoError(t, err)
err = testGetColumn(tbl, col.Name.L, false)
require.NoError(t, err)
}
func checkReorganizationColumn(t *testing.T, ctx sessionctx.Context, tableID int64, col *table.Column, row []types.Datum, columnValue interface{}, dom *domain.Domain) {
tbl := testGetTable(t, dom, tableID)
err := sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
i := 0
err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) {
require.Truef(t, reflect.DeepEqual(data, row), "%v not equal to %v", data, row)
i++
return true, nil
})
require.NoError(t, err)
require.Equalf(t, 1, i, "expect 1, got %v", i)
// Test add a new row.
err = sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
newRow := types.MakeDatums(int64(11), int64(22), int64(33))
newHandle, err := tbl.AddRecord(ctx, newRow)
require.NoError(t, err)
err = sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
rows := [][]types.Datum{row, newRow}
i = 0
err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) {
require.Truef(t, reflect.DeepEqual(data, rows[i]), "%v not equal to %v", data, rows[i])
i++
return true, nil
})
require.NoError(t, err)
require.Equalf(t, 2, i, "expect 2, got %v", i)
err = checkColumnKVExist(ctx, tbl, newHandle, col, columnValue, true)
require.NoError(t, err)
// Test remove a row.
err = sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
err = tbl.RemoveRecord(ctx, newHandle, newRow)
require.NoError(t, err)
err = sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
i = 0
err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) {
i++
return true, nil
})
require.NoError(t, err)
require.Equalf(t, 1, i, "expect 1, got %v", i)
err = testGetColumn(tbl, col.Name.L, false)
require.NoError(t, err)
}
func checkPublicColumn(t *testing.T, ctx sessionctx.Context, tableID int64, newCol *table.Column, oldRow []types.Datum, columnValue interface{}, dom *domain.Domain, columnCnt int) {
tbl := testGetTable(t, dom, tableID)
err := sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
i := 0
var updatedRow []types.Datum
updatedRow = append(updatedRow, oldRow...)
for j := 0; j < columnCnt; j++ {
updatedRow = append(updatedRow, types.NewDatum(columnValue))
}
err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) {
require.Truef(t, reflect.DeepEqual(data, updatedRow), "%v not equal to %v", data, updatedRow)
i++
return true, nil
})
require.NoError(t, err)
require.Equalf(t, 1, i, "expect 1, got %v", i)
// Test add a new row.
err = sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
newRow := types.MakeDatums(int64(11), int64(22), int64(33), int64(44))
for j := 1; j < columnCnt; j++ {
newRow = append(newRow, types.NewDatum(int64(44)))
}
handle, err := tbl.AddRecord(ctx, newRow)
require.NoError(t, err)
err = sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
rows := [][]types.Datum{updatedRow, newRow}
i = 0
err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) {
require.Truef(t, reflect.DeepEqual(data, rows[i]), "%v not equal to %v", data, rows[i])
i++
return true, nil
})
require.NoError(t, err)
require.Equalf(t, 2, i, "expect 2, got %v", i)
// Test remove a row.
err = sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
err = tbl.RemoveRecord(ctx, handle, newRow)
require.NoError(t, err)
err = sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
i = 0
err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) {
require.Truef(t, reflect.DeepEqual(data, rows[i]), "%v not equal to %v", data, rows[i])
i++
return true, nil
})
require.NoError(t, err)
require.Equalf(t, 1, i, "expect 1, got %v", i)
err = testGetColumn(tbl, newCol.Name.L, true)
require.NoError(t, err)
}
func checkAddColumn(t *testing.T, state model.SchemaState, tableID int64, handle kv.Handle, newCol *table.Column, oldRow []types.Datum, columnValue interface{}, dom *domain.Domain, store kv.Storage, columnCnt int) {
ctx := testNewContext(store)
switch state {
case model.StateNone:
checkNoneColumn(t, ctx, tableID, handle, newCol, columnValue, dom)
case model.StateDeleteOnly:
checkDeleteOnlyColumn(t, ctx, tableID, handle, newCol, oldRow, columnValue, dom)
case model.StateWriteOnly:
checkWriteOnlyColumn(t, ctx, tableID, handle, newCol, oldRow, columnValue, dom)
case model.StateWriteReorganization, model.StateDeleteReorganization:
checkReorganizationColumn(t, ctx, tableID, newCol, oldRow, columnValue, dom)
case model.StatePublic:
checkPublicColumn(t, ctx, tableID, newCol, oldRow, columnValue, dom, columnCnt)
}
}
func testGetColumn(t table.Table, name string, isExist bool) error {
col := table.FindCol(t.Cols(), name)
if isExist {
if col == nil {
return errors.Errorf("column should not be nil")
}
} else {
if col != nil {
return errors.Errorf("column should be nil")
}
}
return nil
}
func TestAddColumn(t *testing.T) {
store, dom := testkit.CreateMockStoreAndDomain(t, mockstore.WithDDLChecker())
d := dom.DDL()
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("create table t1 (c1 int, c2 int, c3 int);")
var tableID int64
rs := tk.MustQuery("select TIDB_TABLE_ID from information_schema.tables where table_name='t1' and table_schema='test';")
tableIDi, _ := strconv.Atoi(rs.Rows()[0][0].(string))
tableID = int64(tableIDi)
tbl := testGetTable(t, dom, tableID)
ctx := testNewContext(store)
err := sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
oldRow := types.MakeDatums(int64(1), int64(2), int64(3))
handle, err := tbl.AddRecord(ctx, oldRow)
require.NoError(t, err)
txn, err := ctx.Txn(true)
require.NoError(t, err)
err = txn.Commit(context.Background())
require.NoError(t, err)
newColName := "c4"
defaultColValue := int64(4)
checkOK := false
tc := &callback.TestDDLCallback{Do: dom}
onJobUpdatedExportedFunc := func(job *model.Job) {
if checkOK {
return
}
tbl := testGetTable(t, dom, tableID)
newCol := table.FindCol(tbl.(*tables.TableCommon).Columns, newColName)
if newCol == nil {
return
}
checkAddColumn(t, newCol.State, tableID, handle, newCol, oldRow, defaultColValue, dom, store, 1)
if newCol.State == model.StatePublic {
checkOK = true
}
}
tc.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc)
d.SetHook(tc)
jobID := testCreateColumn(tk, t, testkit.NewTestKit(t, store).Session(), tableID, newColName, "", defaultColValue, dom)
testCheckJobDone(t, store, jobID, true)
require.True(t, checkOK)
jobID = testDropTable(tk, t, "test", "t1", dom)
testCheckJobDone(t, store, jobID, false)
}
func TestAddColumns(t *testing.T) {
store, dom := testkit.CreateMockStoreAndDomain(t, mockstore.WithDDLChecker())
d := dom.DDL()
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("create table t1 (c1 int, c2 int, c3 int);")
newColNames := []string{"c4", "c5", "c6"}
positions := make([]string, 3)
for i := range positions {
positions[i] = ""
}
defaultColValue := int64(4)
var mu sync.Mutex
var hookErr error
checkOK := false
var tableID int64
rs := tk.MustQuery("select TIDB_TABLE_ID from information_schema.tables where table_name='t1' and table_schema='test';")
tableIDi, _ := strconv.Atoi(rs.Rows()[0][0].(string))
tableID = int64(tableIDi)
tbl := testGetTable(t, dom, tableID)
ctx := testNewContext(store)
err := sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
oldRow := types.MakeDatums(int64(1), int64(2), int64(3))
handle, err := tbl.AddRecord(ctx, oldRow)
require.NoError(t, err)
txn, err := ctx.Txn(true)
require.NoError(t, err)
err = txn.Commit(context.Background())
require.NoError(t, err)
tc := &callback.TestDDLCallback{Do: dom}
onJobUpdatedExportedFunc := func(job *model.Job) {
mu.Lock()
defer mu.Unlock()
if checkOK {
return
}
tbl := testGetTable(t, dom, tableID)
for _, newColName := range newColNames {
newCol := table.FindCol(tbl.(*tables.TableCommon).Columns, newColName)
if newCol == nil {
return
}
checkAddColumn(t, newCol.State, tableID, handle, newCol, oldRow, defaultColValue, dom, store, 3)
if newCol.State == model.StatePublic {
checkOK = true
}
}
}
tc.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc)
d.SetHook(tc)
jobID := testCreateColumns(tk, t, testkit.NewTestKit(t, store).Session(), tableID, newColNames, positions, defaultColValue, dom)
testCheckJobDone(t, store, jobID, true)
mu.Lock()
hErr := hookErr
ok := checkOK
mu.Unlock()
require.NoError(t, hErr)
require.True(t, ok)
jobID = testDropTable(tk, t, "test", "t1", dom)
testCheckJobDone(t, store, jobID, false)
}
func TestDropColumnInColumnTest(t *testing.T) {
store, dom := testkit.CreateMockStoreAndDomain(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("create table t1 (c1 int, c2 int, c3 int, c4 int);")
var tableID int64
rs := tk.MustQuery("select TIDB_TABLE_ID from information_schema.tables where table_name='t1' and table_schema='test';")
tableIDi, _ := strconv.Atoi(rs.Rows()[0][0].(string))
tableID = int64(tableIDi)
tbl := testGetTable(t, dom, tableID)
ctx := testNewContext(store)
colName := "c4"
defaultColValue := int64(4)
row := types.MakeDatums(int64(1), int64(2), int64(3))
err := sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
_, err = tbl.AddRecord(ctx, append(row, types.NewDatum(defaultColValue)))
require.NoError(t, err)
txn, err := ctx.Txn(true)
require.NoError(t, err)
err = txn.Commit(context.Background())
require.NoError(t, err)
checkOK := false
var hookErr error
var mu sync.Mutex
d := dom.DDL()
tc := &callback.TestDDLCallback{Do: dom}
onJobUpdatedExportedFunc := func(job *model.Job) {
mu.Lock()
defer mu.Unlock()
if checkOK {
return
}
tbl := testGetTable(t, dom, tableID)
col := table.FindCol(tbl.(*tables.TableCommon).Columns, colName)
if col == nil {
checkOK = true
return
}
}
tc.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc)
d.SetHook(tc)
jobID := testDropColumnInternal(tk, t, testkit.NewTestKit(t, store).Session(), tableID, colName, false, dom)
testCheckJobDone(t, store, jobID, false)
mu.Lock()
hErr := hookErr
ok := checkOK
mu.Unlock()
require.NoError(t, hErr)
require.True(t, ok)
jobID = testDropTable(tk, t, "test", "t1", dom)
testCheckJobDone(t, store, jobID, false)
}
func TestDropColumns(t *testing.T) {
store, dom := testkit.CreateMockStoreAndDomain(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("create table t1 (c1 int, c2 int, c3 int, c4 int);")
var tableID int64
rs := tk.MustQuery("select TIDB_TABLE_ID from information_schema.tables where table_name='t1' and table_schema='test';")
tableIDi, _ := strconv.Atoi(rs.Rows()[0][0].(string))
tableID = int64(tableIDi)
tbl := testGetTable(t, dom, tableID)
ctx := testNewContext(store)
err := sessiontxn.NewTxn(context.Background(), ctx)
require.NoError(t, err)
colNames := []string{"c3", "c4"}
defaultColValue := int64(4)
row := types.MakeDatums(int64(1), int64(2), int64(3))
_, err = tbl.AddRecord(ctx, append(row, types.NewDatum(defaultColValue)))
require.NoError(t, err)
txn, err := ctx.Txn(true)
require.NoError(t, err)
err = txn.Commit(context.Background())
require.NoError(t, err)
checkOK := false
var hookErr error
var mu sync.Mutex
d := dom.DDL()
tc := &callback.TestDDLCallback{Do: dom}
onJobUpdatedExportedFunc := func(job *model.Job) {
mu.Lock()
defer mu.Unlock()
if checkOK {
return
}
tbl := testGetTable(t, dom, tableID)
for _, colName := range colNames {
col := table.FindCol(tbl.(*tables.TableCommon).Columns, colName)
if col == nil {
checkOK = true
return
}
}
}
tc.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc)
d.SetHook(tc)
jobID := testDropColumns(tk, t, testkit.NewTestKit(t, store).Session(), tableID, colNames, false, dom)
testCheckJobDone(t, store, jobID, false)
mu.Lock()
hErr := hookErr
ok := checkOK
mu.Unlock()
require.NoError(t, hErr)
require.True(t, ok)
jobID = testDropTable(tk, t, "test", "t1", dom)
testCheckJobDone(t, store, jobID, false)
}
func testGetTable(t *testing.T, dom *domain.Domain, tableID int64) table.Table {
require.NoError(t, dom.Reload())
tbl, exist := dom.InfoSchema().TableByID(tableID)
require.True(t, exist)
return tbl
}
func TestGetDefaultValueOfColumn(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("create table t1 (da date default '1962-03-03 23:33:34', dt datetime default '1962-03-03'," +
" ti time default '2020-10-11 12:23:23', ts timestamp default '2020-10-13 12:23:23')")
tk.MustQuery("show create table t1").Check(testkit.RowsWithSep("|", ""+
"t1 CREATE TABLE `t1` (\n"+
" `da` date DEFAULT '1962-03-03',\n"+
" `dt` datetime DEFAULT '1962-03-03 00:00:00',\n"+
" `ti` time DEFAULT '12:23:23',\n"+
" `ts` timestamp DEFAULT '2020-10-13 12:23:23'\n"+
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin"))
tk.MustExec("insert into t1 values()")
tk.MustQuery("select * from t1").Check(testkit.RowsWithSep("|", ""+
"1962-03-03 1962-03-03 00:00:00 12:23:23 2020-10-13 12:23:23"))
tk.MustExec("alter table t1 add column da1 date default '2020-03-27 20:20:20 123456'")
tk.MustQuery("show create table t1").Check(testkit.RowsWithSep("|", ""+
"t1 CREATE TABLE `t1` (\n"+
" `da` date DEFAULT '1962-03-03',\n"+
" `dt` datetime DEFAULT '1962-03-03 00:00:00',\n"+
" `ti` time DEFAULT '12:23:23',\n"+
" `ts` timestamp DEFAULT '2020-10-13 12:23:23',\n"+
" `da1` date DEFAULT '2020-03-27'\n"+
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin"))
tk.MustQuery("select * from t1").Check(testkit.RowsWithSep("|", ""+
"1962-03-03 1962-03-03 00:00:00 12:23:23 2020-10-13 12:23:23 2020-03-27"))
tk.MustExec("alter table t1 change ts da2 date default '2020-10-10 20:20:20'")
tk.MustQuery("show create table t1").Check(testkit.RowsWithSep("|", ""+
"t1 CREATE TABLE `t1` (\n"+
" `da` date DEFAULT '1962-03-03',\n"+
" `dt` datetime DEFAULT '1962-03-03 00:00:00',\n"+
" `ti` time DEFAULT '12:23:23',\n"+
" `da2` date DEFAULT '2020-10-10',\n"+
" `da1` date DEFAULT '2020-03-27'\n"+
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin"))
tk.MustQuery("select * from t1").Check(testkit.RowsWithSep("|", ""+
"1962-03-03 1962-03-03 00:00:00 12:23:23 2020-10-13 2020-03-27"))
}
func TestIssue39080(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("CREATE TABLE t1(id INTEGER PRIMARY KEY, authorId INTEGER AUTO_INCREMENT UNIQUE)")
tk.MustQuery("show create table t1").Check(testkit.RowsWithSep("|", ""+
"t1 CREATE TABLE `t1` (\n"+
" `id` int(11) NOT NULL,\n"+
" `authorId` int(11) NOT NULL AUTO_INCREMENT,\n"+
" PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n"+
" UNIQUE KEY `authorId` (`authorId`)\n"+
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin"))
//Do not affect the specified name
tk.MustExec("CREATE TABLE `t2`( `id` INTEGER PRIMARY KEY, `authorId` int(11) AUTO_INCREMENT, UNIQUE KEY `authorIdx` (`authorId`))")
tk.MustQuery("show create table t2").Check(testkit.RowsWithSep("|", ""+
"t2 CREATE TABLE `t2` (\n"+
" `id` int(11) NOT NULL,\n"+
" `authorId` int(11) NOT NULL AUTO_INCREMENT,\n"+
" PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n"+
" UNIQUE KEY `authorIdx` (`authorId`)\n"+
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin"))
}
func TestWriteDataWriteOnlyMode(t *testing.T) {
store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, dbTestLease)
tk := testkit.NewTestKit(t, store)
tk2 := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk2.MustExec("use test")
tk.MustExec("CREATE TABLE t (`col1` bigint(20) DEFAULT 1,`col2` float,UNIQUE KEY `key1` (`col1`))")
originalCallback := dom.DDL().GetHook()
defer dom.DDL().SetHook(originalCallback)
hook := &callback.TestDDLCallback{Do: dom}
hook.OnJobRunBeforeExported = func(job *model.Job) {
if job.SchemaState != model.StateWriteOnly {
return
}
tk2.MustExec("insert ignore into t values (1, 2)")
tk2.MustExec("insert ignore into t values (2, 2)")
}
dom.DDL().SetHook(hook)
tk.MustExec("alter table t change column `col1` `col1` varchar(20)")
hook = &callback.TestDDLCallback{Do: dom}
hook.OnJobRunBeforeExported = func(job *model.Job) {
if job.SchemaState != model.StateWriteOnly {
return
}
tk2.MustExec("insert ignore into t values (1)")
tk2.MustExec("insert ignore into t values (2)")
}
dom.DDL().SetHook(hook)
tk.MustExec("alter table t drop column `col1`")
dom.DDL().SetHook(originalCallback)
}