808 lines
23 KiB
Go
808 lines
23 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,
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package model
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/pingcap/tidb/parser/charset"
|
|
"github.com/pingcap/tidb/parser/mysql"
|
|
"github.com/pingcap/tidb/parser/types"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestT(t *testing.T) {
|
|
abc := NewCIStr("aBC")
|
|
require.Equal(t, "aBC", abc.O)
|
|
require.Equal(t, "abc", abc.L)
|
|
require.Equal(t, "aBC", abc.String())
|
|
}
|
|
|
|
func newColumnForTest(id int64, offset int) *ColumnInfo {
|
|
return &ColumnInfo{
|
|
ID: id,
|
|
Name: NewCIStr(fmt.Sprintf("c_%d", id)),
|
|
Offset: offset,
|
|
}
|
|
}
|
|
|
|
func newIndexForTest(id int64, cols ...*ColumnInfo) *IndexInfo {
|
|
idxCols := make([]*IndexColumn, 0, len(cols))
|
|
for _, c := range cols {
|
|
idxCols = append(idxCols, &IndexColumn{Offset: c.Offset, Name: c.Name})
|
|
}
|
|
return &IndexInfo{
|
|
ID: id,
|
|
Name: NewCIStr(fmt.Sprintf("i_%d", id)),
|
|
Columns: idxCols,
|
|
}
|
|
}
|
|
|
|
func checkOffsets(t *testing.T, tbl *TableInfo, ids ...int) {
|
|
require.Equal(t, len(ids), len(tbl.Columns))
|
|
for i := 0; i < len(ids); i++ {
|
|
expected := fmt.Sprintf("c_%d", ids[i])
|
|
require.Equal(t, expected, tbl.Columns[i].Name.L)
|
|
require.Equal(t, i, tbl.Columns[i].Offset)
|
|
}
|
|
for _, col := range tbl.Columns {
|
|
for _, idx := range tbl.Indices {
|
|
for _, idxCol := range idx.Columns {
|
|
if col.Name.L != idxCol.Name.L {
|
|
continue
|
|
}
|
|
// Columns with the same name should have a same offset.
|
|
require.Equal(t, col.Offset, idxCol.Offset)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMoveColumnInfo(t *testing.T) {
|
|
c0 := newColumnForTest(0, 0)
|
|
c1 := newColumnForTest(1, 1)
|
|
c2 := newColumnForTest(2, 2)
|
|
c3 := newColumnForTest(3, 3)
|
|
c4 := newColumnForTest(4, 4)
|
|
|
|
i0 := newIndexForTest(0, c0, c1, c2, c3, c4)
|
|
i1 := newIndexForTest(1, c4, c2)
|
|
i2 := newIndexForTest(2, c0, c4)
|
|
i3 := newIndexForTest(3, c1, c2, c3)
|
|
i4 := newIndexForTest(4, c3, c2, c1)
|
|
|
|
tbl := &TableInfo{
|
|
ID: 1,
|
|
Name: NewCIStr("t"),
|
|
Columns: []*ColumnInfo{c0, c1, c2, c3, c4},
|
|
Indices: []*IndexInfo{i0, i1, i2, i3, i4},
|
|
}
|
|
|
|
// Original offsets: [0, 1, 2, 3, 4]
|
|
tbl.MoveColumnInfo(4, 0)
|
|
checkOffsets(t, tbl, 4, 0, 1, 2, 3)
|
|
tbl.MoveColumnInfo(2, 3)
|
|
checkOffsets(t, tbl, 4, 0, 2, 1, 3)
|
|
tbl.MoveColumnInfo(3, 2)
|
|
checkOffsets(t, tbl, 4, 0, 1, 2, 3)
|
|
tbl.MoveColumnInfo(0, 4)
|
|
checkOffsets(t, tbl, 0, 1, 2, 3, 4)
|
|
tbl.MoveColumnInfo(2, 2)
|
|
checkOffsets(t, tbl, 0, 1, 2, 3, 4)
|
|
tbl.MoveColumnInfo(0, 0)
|
|
checkOffsets(t, tbl, 0, 1, 2, 3, 4)
|
|
tbl.MoveColumnInfo(1, 4)
|
|
checkOffsets(t, tbl, 0, 2, 3, 4, 1)
|
|
tbl.MoveColumnInfo(3, 0)
|
|
checkOffsets(t, tbl, 4, 0, 2, 3, 1)
|
|
}
|
|
|
|
func TestModelBasic(t *testing.T) {
|
|
column := &ColumnInfo{
|
|
ID: 1,
|
|
Name: NewCIStr("c"),
|
|
Offset: 0,
|
|
DefaultValue: 0,
|
|
FieldType: *types.NewFieldType(0),
|
|
Hidden: true,
|
|
}
|
|
column.AddFlag(mysql.PriKeyFlag)
|
|
|
|
index := &IndexInfo{
|
|
Name: NewCIStr("key"),
|
|
Table: NewCIStr("t"),
|
|
Columns: []*IndexColumn{
|
|
{
|
|
Name: NewCIStr("c"),
|
|
Offset: 0,
|
|
Length: 10,
|
|
}},
|
|
Unique: true,
|
|
Primary: true,
|
|
}
|
|
|
|
fk := &FKInfo{
|
|
RefCols: []CIStr{NewCIStr("a")},
|
|
Cols: []CIStr{NewCIStr("a")},
|
|
}
|
|
|
|
seq := &SequenceInfo{
|
|
Increment: 1,
|
|
MinValue: 1,
|
|
MaxValue: 100,
|
|
}
|
|
|
|
table := &TableInfo{
|
|
ID: 1,
|
|
Name: NewCIStr("t"),
|
|
Charset: "utf8",
|
|
Collate: "utf8_bin",
|
|
Columns: []*ColumnInfo{column},
|
|
Indices: []*IndexInfo{index},
|
|
ForeignKeys: []*FKInfo{fk},
|
|
PKIsHandle: true,
|
|
}
|
|
|
|
table2 := &TableInfo{
|
|
ID: 2,
|
|
Name: NewCIStr("s"),
|
|
Sequence: seq,
|
|
}
|
|
|
|
dbInfo := &DBInfo{
|
|
ID: 1,
|
|
Name: NewCIStr("test"),
|
|
Charset: "utf8",
|
|
Collate: "utf8_bin",
|
|
Tables: []*TableInfo{table},
|
|
}
|
|
|
|
n := dbInfo.Clone()
|
|
require.Equal(t, dbInfo, n)
|
|
|
|
pkName := table.GetPkName()
|
|
require.Equal(t, NewCIStr("c"), pkName)
|
|
newColumn := table.GetPkColInfo()
|
|
require.Equal(t, true, newColumn.Hidden)
|
|
require.Equal(t, column, newColumn)
|
|
inIdx := table.ColumnIsInIndex(column)
|
|
require.Equal(t, true, inIdx)
|
|
tp := IndexTypeBtree
|
|
require.Equal(t, "BTREE", tp.String())
|
|
tp = IndexTypeHash
|
|
require.Equal(t, "HASH", tp.String())
|
|
tp = 1e5
|
|
require.Equal(t, "", tp.String())
|
|
has := index.HasPrefixIndex()
|
|
require.Equal(t, true, has)
|
|
require.Equal(t, TSConvert2Time(table.UpdateTS), table.GetUpdateTime())
|
|
require.True(t, table2.IsSequence())
|
|
require.False(t, table2.IsBaseTable())
|
|
|
|
// Corner cases
|
|
column.ToggleFlag(mysql.PriKeyFlag)
|
|
pkName = table.GetPkName()
|
|
require.Equal(t, NewCIStr(""), pkName)
|
|
newColumn = table.GetPkColInfo()
|
|
require.Nil(t, newColumn)
|
|
anCol := &ColumnInfo{
|
|
Name: NewCIStr("d"),
|
|
}
|
|
exIdx := table.ColumnIsInIndex(anCol)
|
|
require.Equal(t, false, exIdx)
|
|
anIndex := &IndexInfo{
|
|
Columns: []*IndexColumn{},
|
|
}
|
|
no := anIndex.HasPrefixIndex()
|
|
require.Equal(t, false, no)
|
|
|
|
extraPK := NewExtraHandleColInfo()
|
|
require.Equal(t, mysql.NotNullFlag|mysql.PriKeyFlag, extraPK.GetFlag())
|
|
require.Equal(t, charset.CharsetBin, extraPK.GetCharset())
|
|
require.Equal(t, charset.CollationBin, extraPK.GetCollate())
|
|
}
|
|
|
|
func TestJobStartTime(t *testing.T) {
|
|
job := &Job{
|
|
ID: 123,
|
|
BinlogInfo: &HistoryInfo{},
|
|
}
|
|
require.Equal(t, TSConvert2Time(job.StartTS), time.Unix(0, 0))
|
|
require.Equal(t, fmt.Sprintf("ID:123, Type:none, State:none, SchemaState:none, SchemaID:0, TableID:0, RowCount:0, ArgLen:0, start time: %s, Err:<nil>, ErrCount:0, SnapshotVersion:0", time.Unix(0, 0)), job.String())
|
|
}
|
|
|
|
func TestJobCodec(t *testing.T) {
|
|
type A struct {
|
|
Name string
|
|
}
|
|
tzName, tzOffset := time.Now().In(time.UTC).Zone()
|
|
job := &Job{
|
|
ID: 1,
|
|
TableID: 2,
|
|
SchemaID: 1,
|
|
BinlogInfo: &HistoryInfo{},
|
|
Args: []interface{}{NewCIStr("a"), A{Name: "abc"}},
|
|
ReorgMeta: &DDLReorgMeta{
|
|
Location: &TimeZoneLocation{Name: tzName, Offset: tzOffset},
|
|
},
|
|
}
|
|
job.BinlogInfo.AddDBInfo(123, &DBInfo{ID: 1, Name: NewCIStr("test_history_db")})
|
|
job.BinlogInfo.AddTableInfo(123, &TableInfo{ID: 1, Name: NewCIStr("test_history_tbl")})
|
|
|
|
// Test IsDependentOn.
|
|
// job: table ID is 2
|
|
// job1: table ID is 2
|
|
var err error
|
|
job1 := &Job{
|
|
ID: 2,
|
|
TableID: 2,
|
|
SchemaID: 1,
|
|
Type: ActionRenameTable,
|
|
BinlogInfo: &HistoryInfo{},
|
|
Args: []interface{}{int64(3), NewCIStr("new_table_name")},
|
|
}
|
|
job1.RawArgs, err = json.Marshal(job1.Args)
|
|
require.NoError(t, err)
|
|
isDependent, err := job.IsDependentOn(job1)
|
|
require.NoError(t, err)
|
|
require.True(t, isDependent)
|
|
// job1: rename table, old schema ID is 3
|
|
// job2: create schema, schema ID is 3
|
|
job2 := &Job{
|
|
ID: 3,
|
|
TableID: 3,
|
|
SchemaID: 3,
|
|
Type: ActionCreateSchema,
|
|
BinlogInfo: &HistoryInfo{},
|
|
}
|
|
isDependent, err = job2.IsDependentOn(job1)
|
|
require.NoError(t, err)
|
|
require.True(t, isDependent)
|
|
|
|
// Test IsDependentOn for exchange partition with table.
|
|
// test ActionCreateSchema and ActionExchangeTablePartition is dependent.
|
|
job3 := &Job{
|
|
ID: 4,
|
|
TableID: 4,
|
|
SchemaID: 4,
|
|
Type: ActionExchangeTablePartition,
|
|
BinlogInfo: &HistoryInfo{},
|
|
Args: []interface{}{int64(6), int64(3), int64(5), "pt", true},
|
|
}
|
|
job3.RawArgs, err = json.Marshal(job3.Args)
|
|
require.NoError(t, err)
|
|
isDependent, err = job3.IsDependentOn(job2)
|
|
require.NoError(t, err)
|
|
require.True(t, isDependent)
|
|
|
|
// test random and ActionExchangeTablePartition is dependent because TableID is same.
|
|
job4 := &Job{
|
|
ID: 5,
|
|
TableID: 5,
|
|
SchemaID: 3,
|
|
Type: ActionExchangeTablePartition,
|
|
BinlogInfo: &HistoryInfo{},
|
|
Args: []interface{}{6, 4, 2, "pt", true},
|
|
}
|
|
job4.RawArgs, err = json.Marshal(job4.Args)
|
|
require.NoError(t, err)
|
|
isDependent, err = job4.IsDependentOn(job)
|
|
require.NoError(t, err)
|
|
require.True(t, isDependent)
|
|
|
|
// test ActionExchangeTablePartition and ActionExchangeTablePartition is dependent.
|
|
job5 := &Job{
|
|
ID: 6,
|
|
TableID: 6,
|
|
SchemaID: 6,
|
|
Type: ActionExchangeTablePartition,
|
|
BinlogInfo: &HistoryInfo{},
|
|
Args: []interface{}{2, 6, 5, "pt", true},
|
|
}
|
|
job5.RawArgs, err = json.Marshal(job5.Args)
|
|
require.NoError(t, err)
|
|
isDependent, err = job5.IsDependentOn(job4)
|
|
require.NoError(t, err)
|
|
require.True(t, isDependent)
|
|
|
|
job6 := &Job{
|
|
ID: 7,
|
|
TableID: 7,
|
|
SchemaID: 7,
|
|
Type: ActionExchangeTablePartition,
|
|
BinlogInfo: &HistoryInfo{},
|
|
Args: []interface{}{6, 4, 2, "pt", true},
|
|
}
|
|
job6.RawArgs, err = json.Marshal(job6.Args)
|
|
require.NoError(t, err)
|
|
isDependent, err = job6.IsDependentOn(job5)
|
|
require.NoError(t, err)
|
|
require.True(t, isDependent)
|
|
|
|
job7 := &Job{
|
|
ID: 8,
|
|
TableID: 8,
|
|
SchemaID: 8,
|
|
Type: ActionExchangeTablePartition,
|
|
BinlogInfo: &HistoryInfo{},
|
|
Args: []interface{}{8, 4, 6, "pt", true},
|
|
}
|
|
job7.RawArgs, err = json.Marshal(job7.Args)
|
|
require.NoError(t, err)
|
|
isDependent, err = job7.IsDependentOn(job6)
|
|
require.NoError(t, err)
|
|
require.True(t, isDependent)
|
|
|
|
job8 := &Job{
|
|
ID: 9,
|
|
TableID: 9,
|
|
SchemaID: 9,
|
|
Type: ActionExchangeTablePartition,
|
|
BinlogInfo: &HistoryInfo{},
|
|
Args: []interface{}{8, 9, 9, "pt", true},
|
|
}
|
|
job8.RawArgs, err = json.Marshal(job8.Args)
|
|
require.NoError(t, err)
|
|
isDependent, err = job8.IsDependentOn(job7)
|
|
require.NoError(t, err)
|
|
require.True(t, isDependent)
|
|
|
|
job9 := &Job{
|
|
ID: 10,
|
|
TableID: 10,
|
|
SchemaID: 10,
|
|
Type: ActionExchangeTablePartition,
|
|
BinlogInfo: &HistoryInfo{},
|
|
Args: []interface{}{10, 10, 8, "pt", true},
|
|
}
|
|
job9.RawArgs, err = json.Marshal(job9.Args)
|
|
require.NoError(t, err)
|
|
isDependent, err = job9.IsDependentOn(job8)
|
|
require.NoError(t, err)
|
|
require.True(t, isDependent)
|
|
|
|
// test ActionDropSchema and ActionExchangeTablePartition is dependent.
|
|
job10 := &Job{
|
|
ID: 11,
|
|
TableID: 11,
|
|
SchemaID: 11,
|
|
Type: ActionDropSchema,
|
|
BinlogInfo: &HistoryInfo{},
|
|
}
|
|
job10.RawArgs, err = json.Marshal(job10.Args)
|
|
require.NoError(t, err)
|
|
|
|
job11 := &Job{
|
|
ID: 12,
|
|
TableID: 12,
|
|
SchemaID: 11,
|
|
Type: ActionExchangeTablePartition,
|
|
BinlogInfo: &HistoryInfo{},
|
|
Args: []interface{}{10, 10, 8, "pt", true},
|
|
}
|
|
job11.RawArgs, err = json.Marshal(job11.Args)
|
|
require.NoError(t, err)
|
|
isDependent, err = job11.IsDependentOn(job10)
|
|
require.NoError(t, err)
|
|
require.True(t, isDependent)
|
|
|
|
// test ActionDropTable and ActionExchangeTablePartition is dependent.
|
|
job12 := &Job{
|
|
ID: 13,
|
|
TableID: 13,
|
|
SchemaID: 11,
|
|
Type: ActionDropTable,
|
|
BinlogInfo: &HistoryInfo{},
|
|
}
|
|
job12.RawArgs, err = json.Marshal(job12.Args)
|
|
require.NoError(t, err)
|
|
isDependent, err = job11.IsDependentOn(job12)
|
|
require.NoError(t, err)
|
|
require.False(t, isDependent)
|
|
|
|
job13 := &Job{
|
|
ID: 14,
|
|
TableID: 12,
|
|
SchemaID: 14,
|
|
Type: ActionDropTable,
|
|
BinlogInfo: &HistoryInfo{},
|
|
}
|
|
job13.RawArgs, err = json.Marshal(job13.Args)
|
|
require.NoError(t, err)
|
|
isDependent, err = job11.IsDependentOn(job13)
|
|
require.NoError(t, err)
|
|
require.True(t, isDependent)
|
|
|
|
// test ActionDropTable and ActionExchangeTablePartition is dependent.
|
|
job14 := &Job{
|
|
ID: 15,
|
|
TableID: 15,
|
|
SchemaID: 15,
|
|
Type: ActionExchangeTablePartition,
|
|
BinlogInfo: &HistoryInfo{},
|
|
Args: []interface{}{16, 17, 12, "pt", true},
|
|
}
|
|
job14.RawArgs, err = json.Marshal(job14.Args)
|
|
require.NoError(t, err)
|
|
isDependent, err = job13.IsDependentOn(job14)
|
|
require.NoError(t, err)
|
|
require.True(t, isDependent)
|
|
|
|
// test ActionFlashbackCluster with other ddl jobs are dependent.
|
|
job15 := &Job{
|
|
ID: 16,
|
|
Type: ActionFlashbackCluster,
|
|
BinlogInfo: &HistoryInfo{},
|
|
Args: []interface{}{0, map[string]interface{}{}, "ON", true},
|
|
}
|
|
job15.RawArgs, err = json.Marshal(job15.Args)
|
|
require.NoError(t, err)
|
|
isDependent, err = job.IsDependentOn(job15)
|
|
require.NoError(t, err)
|
|
require.True(t, isDependent)
|
|
|
|
require.Equal(t, false, job.IsCancelled())
|
|
b, err := job.Encode(false)
|
|
require.NoError(t, err)
|
|
newJob := &Job{}
|
|
err = newJob.Decode(b)
|
|
require.NoError(t, err)
|
|
require.Equal(t, job.BinlogInfo, newJob.BinlogInfo)
|
|
name := CIStr{}
|
|
a := A{}
|
|
err = newJob.DecodeArgs(&name, &a)
|
|
require.NoError(t, err)
|
|
require.Equal(t, NewCIStr(""), name)
|
|
require.Equal(t, A{Name: ""}, a)
|
|
require.Greater(t, len(newJob.String()), 0)
|
|
require.Equal(t, newJob.ReorgMeta.Location.Name, tzName)
|
|
require.Equal(t, newJob.ReorgMeta.Location.Offset, tzOffset)
|
|
|
|
job.BinlogInfo.Clean()
|
|
b1, err := job.Encode(true)
|
|
require.NoError(t, err)
|
|
newJob = &Job{}
|
|
err = newJob.Decode(b1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, &HistoryInfo{}, newJob.BinlogInfo)
|
|
name = CIStr{}
|
|
a = A{}
|
|
err = newJob.DecodeArgs(&name, &a)
|
|
require.NoError(t, err)
|
|
require.Equal(t, NewCIStr("a"), name)
|
|
require.Equal(t, A{Name: "abc"}, a)
|
|
require.Greater(t, len(newJob.String()), 0)
|
|
|
|
b2, err := job.Encode(true)
|
|
require.NoError(t, err)
|
|
newJob = &Job{}
|
|
err = newJob.Decode(b2)
|
|
require.NoError(t, err)
|
|
name = CIStr{}
|
|
// Don't decode to a here.
|
|
err = newJob.DecodeArgs(&name)
|
|
require.NoError(t, err)
|
|
require.Equal(t, NewCIStr("a"), name)
|
|
require.Greater(t, len(newJob.String()), 0)
|
|
|
|
job.State = JobStateDone
|
|
require.True(t, job.IsDone())
|
|
require.True(t, job.IsFinished())
|
|
require.False(t, job.IsRunning())
|
|
require.False(t, job.IsSynced())
|
|
require.False(t, job.IsRollbackDone())
|
|
job.SetRowCount(3)
|
|
require.Equal(t, int64(3), job.GetRowCount())
|
|
}
|
|
|
|
func TestState(t *testing.T) {
|
|
schemaTbl := []SchemaState{
|
|
StateDeleteOnly,
|
|
StateWriteOnly,
|
|
StateWriteReorganization,
|
|
StateDeleteReorganization,
|
|
StatePublic,
|
|
StateGlobalTxnOnly,
|
|
}
|
|
|
|
for _, state := range schemaTbl {
|
|
require.Greater(t, len(state.String()), 0)
|
|
}
|
|
|
|
jobTbl := []JobState{
|
|
JobStateRunning,
|
|
JobStateDone,
|
|
JobStateCancelled,
|
|
JobStateRollingback,
|
|
JobStateRollbackDone,
|
|
JobStateSynced,
|
|
}
|
|
|
|
for _, state := range jobTbl {
|
|
require.Greater(t, len(state.String()), 0)
|
|
}
|
|
}
|
|
|
|
func TestString(t *testing.T) {
|
|
acts := []struct {
|
|
act ActionType
|
|
result string
|
|
}{
|
|
{ActionNone, "none"},
|
|
{ActionAddForeignKey, "add foreign key"},
|
|
{ActionDropForeignKey, "drop foreign key"},
|
|
{ActionTruncateTable, "truncate table"},
|
|
{ActionModifyColumn, "modify column"},
|
|
{ActionRenameTable, "rename table"},
|
|
{ActionRenameTables, "rename tables"},
|
|
{ActionSetDefaultValue, "set default value"},
|
|
{ActionCreateSchema, "create schema"},
|
|
{ActionDropSchema, "drop schema"},
|
|
{ActionCreateTable, "create table"},
|
|
{ActionDropTable, "drop table"},
|
|
{ActionAddIndex, "add index"},
|
|
{ActionDropIndex, "drop index"},
|
|
{ActionAddColumn, "add column"},
|
|
{ActionDropColumn, "drop column"},
|
|
{ActionModifySchemaCharsetAndCollate, "modify schema charset and collate"},
|
|
{ActionAlterTablePlacement, "alter table placement"},
|
|
{ActionAlterTablePartitionPlacement, "alter table partition placement"},
|
|
{ActionAlterNoCacheTable, "alter table nocache"},
|
|
}
|
|
|
|
for _, v := range acts {
|
|
str := v.act.String()
|
|
require.Equal(t, v.result, str)
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalCIStr(t *testing.T) {
|
|
var ci CIStr
|
|
|
|
// Test unmarshal CIStr from a single string.
|
|
str := "aaBB"
|
|
buf, err := json.Marshal(str)
|
|
require.NoError(t, err)
|
|
require.NoError(t, ci.UnmarshalJSON(buf))
|
|
require.Equal(t, str, ci.O)
|
|
require.Equal(t, "aabb", ci.L)
|
|
|
|
buf, err = json.Marshal(ci)
|
|
require.NoError(t, err)
|
|
require.Equal(t, `{"O":"aaBB","L":"aabb"}`, string(buf))
|
|
require.NoError(t, ci.UnmarshalJSON(buf))
|
|
require.Equal(t, str, ci.O)
|
|
require.Equal(t, "aabb", ci.L)
|
|
}
|
|
|
|
func TestDefaultValue(t *testing.T) {
|
|
srcCol := &ColumnInfo{
|
|
ID: 1,
|
|
}
|
|
randPlainStr := "random_plain_string"
|
|
|
|
oldPlainCol := srcCol.Clone()
|
|
oldPlainCol.Name = NewCIStr("oldPlainCol")
|
|
oldPlainCol.FieldType = *types.NewFieldType(mysql.TypeLong)
|
|
oldPlainCol.DefaultValue = randPlainStr
|
|
oldPlainCol.OriginDefaultValue = randPlainStr
|
|
|
|
newPlainCol := srcCol.Clone()
|
|
newPlainCol.Name = NewCIStr("newPlainCol")
|
|
newPlainCol.FieldType = *types.NewFieldType(mysql.TypeLong)
|
|
err := newPlainCol.SetDefaultValue(1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, newPlainCol.GetDefaultValue())
|
|
err = newPlainCol.SetDefaultValue(randPlainStr)
|
|
require.NoError(t, err)
|
|
require.Equal(t, randPlainStr, newPlainCol.GetDefaultValue())
|
|
|
|
randBitStr := string([]byte{25, 185})
|
|
|
|
oldBitCol := srcCol.Clone()
|
|
oldBitCol.Name = NewCIStr("oldBitCol")
|
|
oldBitCol.FieldType = *types.NewFieldType(mysql.TypeBit)
|
|
oldBitCol.DefaultValue = randBitStr
|
|
oldBitCol.OriginDefaultValue = randBitStr
|
|
|
|
newBitCol := srcCol.Clone()
|
|
newBitCol.Name = NewCIStr("newBitCol")
|
|
newBitCol.FieldType = *types.NewFieldType(mysql.TypeBit)
|
|
err = newBitCol.SetDefaultValue(1)
|
|
// Only string type is allowed in BIT column.
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "Invalid default value")
|
|
require.Equal(t, 1, newBitCol.GetDefaultValue())
|
|
err = newBitCol.SetDefaultValue(randBitStr)
|
|
require.NoError(t, err)
|
|
require.Equal(t, randBitStr, newBitCol.GetDefaultValue())
|
|
|
|
nullBitCol := srcCol.Clone()
|
|
nullBitCol.Name = NewCIStr("nullBitCol")
|
|
nullBitCol.FieldType = *types.NewFieldType(mysql.TypeBit)
|
|
err = nullBitCol.SetOriginDefaultValue(nil)
|
|
require.NoError(t, err)
|
|
require.Nil(t, nullBitCol.GetOriginDefaultValue())
|
|
|
|
testCases := []struct {
|
|
col *ColumnInfo
|
|
isConsistent bool
|
|
}{
|
|
{oldPlainCol, true},
|
|
{oldBitCol, false},
|
|
{newPlainCol, true},
|
|
{newBitCol, true},
|
|
{nullBitCol, true},
|
|
}
|
|
for _, tc := range testCases {
|
|
col, isConsistent := tc.col, tc.isConsistent
|
|
comment := fmt.Sprintf("%s assertion failed", col.Name.O)
|
|
bytes, err := json.Marshal(col)
|
|
require.NoError(t, err, comment)
|
|
var newCol ColumnInfo
|
|
err = json.Unmarshal(bytes, &newCol)
|
|
require.NoError(t, err, comment)
|
|
if isConsistent {
|
|
require.Equal(t, col.GetDefaultValue(), newCol.GetDefaultValue(), comment)
|
|
require.Equal(t, col.GetOriginDefaultValue(), newCol.GetOriginDefaultValue(), comment)
|
|
} else {
|
|
require.NotEqual(t, col.GetDefaultValue(), newCol.GetDefaultValue(), comment)
|
|
require.NotEqual(t, col.GetOriginDefaultValue(), newCol.GetOriginDefaultValue(), comment)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPlacementSettingsString(t *testing.T) {
|
|
settings := &PlacementSettings{
|
|
PrimaryRegion: "us-east-1",
|
|
Regions: "us-east-1,us-east-2",
|
|
Schedule: "EVEN",
|
|
}
|
|
require.Equal(t, "PRIMARY_REGION=\"us-east-1\" REGIONS=\"us-east-1,us-east-2\" SCHEDULE=\"EVEN\"", settings.String())
|
|
|
|
settings = &PlacementSettings{
|
|
LeaderConstraints: "[+region=bj]",
|
|
}
|
|
require.Equal(t, "LEADER_CONSTRAINTS=\"[+region=bj]\"", settings.String())
|
|
|
|
settings = &PlacementSettings{
|
|
Voters: 1,
|
|
VoterConstraints: "[+region=us-east-1]",
|
|
Followers: 2,
|
|
FollowerConstraints: "[+disk=ssd]",
|
|
Learners: 3,
|
|
LearnerConstraints: "[+region=us-east-2]",
|
|
}
|
|
require.Equal(t, "VOTERS=1 VOTER_CONSTRAINTS=\"[+region=us-east-1]\" FOLLOWERS=2 FOLLOWER_CONSTRAINTS=\"[+disk=ssd]\" LEARNERS=3 LEARNER_CONSTRAINTS=\"[+region=us-east-2]\"", settings.String())
|
|
|
|
settings = &PlacementSettings{
|
|
Voters: 3,
|
|
Followers: 2,
|
|
Learners: 1,
|
|
Constraints: "{\"+us-east-1\":1,+us-east-2:1}",
|
|
}
|
|
require.Equal(t, "CONSTRAINTS=\"{\\\"+us-east-1\\\":1,+us-east-2:1}\" VOTERS=3 FOLLOWERS=2 LEARNERS=1", settings.String())
|
|
}
|
|
|
|
func TestPlacementSettingsClone(t *testing.T) {
|
|
settings := &PlacementSettings{}
|
|
clonedSettings := settings.Clone()
|
|
clonedSettings.PrimaryRegion = "r1"
|
|
clonedSettings.Regions = "r1,r2"
|
|
clonedSettings.Followers = 1
|
|
clonedSettings.Voters = 2
|
|
clonedSettings.Followers = 3
|
|
clonedSettings.Constraints = "[+zone=z1]"
|
|
clonedSettings.LearnerConstraints = "[+region=r1]"
|
|
clonedSettings.FollowerConstraints = "[+disk=ssd]"
|
|
clonedSettings.LeaderConstraints = "[+region=r2]"
|
|
clonedSettings.VoterConstraints = "[+zone=z2]"
|
|
clonedSettings.Schedule = "even"
|
|
require.Equal(t, PlacementSettings{}, *settings)
|
|
}
|
|
|
|
func TestPlacementPolicyClone(t *testing.T) {
|
|
policy := &PolicyInfo{
|
|
PlacementSettings: &PlacementSettings{},
|
|
}
|
|
clonedPolicy := policy.Clone()
|
|
clonedPolicy.ID = 100
|
|
clonedPolicy.Name = NewCIStr("p2")
|
|
clonedPolicy.State = StateDeleteOnly
|
|
clonedPolicy.PlacementSettings.Followers = 10
|
|
|
|
require.Equal(t, int64(0), policy.ID)
|
|
require.Equal(t, NewCIStr(""), policy.Name)
|
|
require.Equal(t, StateNone, policy.State)
|
|
require.Equal(t, PlacementSettings{}, *(policy.PlacementSettings))
|
|
}
|
|
|
|
func TestLocation(t *testing.T) {
|
|
// test offset = 0
|
|
loc := &TimeZoneLocation{}
|
|
nLoc, err := loc.GetLocation()
|
|
require.NoError(t, err)
|
|
require.Equal(t, nLoc.String(), "UTC")
|
|
// test loc.location != nil
|
|
loc.Name = "Asia/Shanghai"
|
|
nLoc, err = loc.GetLocation()
|
|
require.NoError(t, err)
|
|
require.Equal(t, nLoc.String(), "UTC")
|
|
// timezone +05:00
|
|
loc1 := &TimeZoneLocation{Name: "UTC", Offset: 18000}
|
|
loc1Byte, err := json.Marshal(loc1)
|
|
require.NoError(t, err)
|
|
loc2 := &TimeZoneLocation{}
|
|
err = json.Unmarshal(loc1Byte, loc2)
|
|
require.NoError(t, err)
|
|
require.Equal(t, loc2.Offset, loc1.Offset)
|
|
require.Equal(t, loc2.Name, loc1.Name)
|
|
nLoc, err = loc2.GetLocation()
|
|
require.NoError(t, err)
|
|
require.Equal(t, nLoc.String(), "UTC")
|
|
location := time.FixedZone("UTC", loc1.Offset)
|
|
require.Equal(t, nLoc, location)
|
|
}
|
|
|
|
func TestIsIndexPrefixCovered(t *testing.T) {
|
|
c0 := newColumnForTest(0, 0)
|
|
c1 := newColumnForTest(1, 1)
|
|
c2 := newColumnForTest(2, 2)
|
|
c3 := newColumnForTest(3, 3)
|
|
c4 := newColumnForTest(4, 4)
|
|
|
|
i0 := newIndexForTest(0, c0, c1, c2)
|
|
i1 := newIndexForTest(1, c4, c2)
|
|
|
|
tbl := &TableInfo{
|
|
ID: 1,
|
|
Name: NewCIStr("t"),
|
|
Columns: []*ColumnInfo{c0, c1, c2, c3, c4},
|
|
Indices: []*IndexInfo{i0, i1},
|
|
}
|
|
require.Equal(t, true, IsIndexPrefixCovered(tbl, i0, NewCIStr("c_0")))
|
|
require.Equal(t, true, IsIndexPrefixCovered(tbl, i0, NewCIStr("c_0"), NewCIStr("c_1"), NewCIStr("c_2")))
|
|
require.Equal(t, false, IsIndexPrefixCovered(tbl, i0, NewCIStr("c_1")))
|
|
require.Equal(t, false, IsIndexPrefixCovered(tbl, i0, NewCIStr("c_2")))
|
|
require.Equal(t, false, IsIndexPrefixCovered(tbl, i0, NewCIStr("c_1"), NewCIStr("c_2")))
|
|
require.Equal(t, false, IsIndexPrefixCovered(tbl, i0, NewCIStr("c_0"), NewCIStr("c_2")))
|
|
|
|
require.Equal(t, true, IsIndexPrefixCovered(tbl, i1, NewCIStr("c_4")))
|
|
require.Equal(t, true, IsIndexPrefixCovered(tbl, i1, NewCIStr("c_4"), NewCIStr("c_2")))
|
|
require.Equal(t, false, IsIndexPrefixCovered(tbl, i0, NewCIStr("c_2")))
|
|
}
|
|
|
|
func TestTTLInfoClone(t *testing.T) {
|
|
ttlInfo := &TTLInfo{
|
|
ColumnName: NewCIStr("test"),
|
|
IntervalExprStr: "test_expr",
|
|
IntervalTimeUnit: 5,
|
|
Enable: true,
|
|
}
|
|
|
|
clonedTTLInfo := ttlInfo.Clone()
|
|
clonedTTLInfo.ColumnName = NewCIStr("test_2")
|
|
clonedTTLInfo.IntervalExprStr = "test_expr_2"
|
|
clonedTTLInfo.IntervalTimeUnit = 9
|
|
clonedTTLInfo.Enable = false
|
|
|
|
require.Equal(t, "test", ttlInfo.ColumnName.O)
|
|
require.Equal(t, "test_expr", ttlInfo.IntervalExprStr)
|
|
require.Equal(t, 5, ttlInfo.IntervalTimeUnit)
|
|
require.Equal(t, true, ttlInfo.Enable)
|
|
}
|