// Copyright 2024 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 model import ( "fmt" "testing" "time" "github.com/pingcap/tidb/pkg/parser/ast" "github.com/pingcap/tidb/pkg/parser/charset" "github.com/pingcap/tidb/pkg/parser/duration" "github.com/pingcap/tidb/pkg/parser/mysql" "github.com/pingcap/tidb/pkg/parser/types" "github.com/stretchr/testify/require" ) func checkOffsets(t *testing.T, tbl *TableInfo, ids ...int) { require.Equal(t, len(ids), len(tbl.Columns)) for i := range ids { 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: ast.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: ast.NewCIStr("c"), Offset: 0, DefaultValue: 0, FieldType: *types.NewFieldType(0), Hidden: true, } column.AddFlag(mysql.PriKeyFlag) index := &IndexInfo{ Name: ast.NewCIStr("key"), Table: ast.NewCIStr("t"), Columns: []*IndexColumn{ { Name: ast.NewCIStr("c"), Offset: 0, Length: 10, }}, Unique: true, Primary: true, } fk := &FKInfo{ RefCols: []ast.CIStr{ast.NewCIStr("a")}, Cols: []ast.CIStr{ast.NewCIStr("a")}, } seq := &SequenceInfo{ Increment: 1, MinValue: 1, MaxValue: 100, } table := &TableInfo{ ID: 1, Name: ast.NewCIStr("t"), Charset: "utf8", Collate: "utf8_bin", Columns: []*ColumnInfo{column}, Indices: []*IndexInfo{index}, ForeignKeys: []*FKInfo{fk}, PKIsHandle: true, } table2 := &TableInfo{ ID: 2, Name: ast.NewCIStr("s"), Sequence: seq, } dbInfo := &DBInfo{ ID: 1, Name: ast.NewCIStr("test"), Charset: "utf8", Collate: "utf8_bin", } dbInfo.Deprecated.Tables = []*TableInfo{table} n := dbInfo.Clone() require.Equal(t, dbInfo, n) pkName := table.GetPkName() require.Equal(t, ast.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 := ast.IndexTypeBtree require.Equal(t, "BTREE", tp.String()) tp = ast.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, ast.NewCIStr(""), pkName) newColumn = table.GetPkColInfo() require.Nil(t, newColumn) anCol := &ColumnInfo{ Name: ast.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 TestTTLInfoClone(t *testing.T) { ttlInfo := &TTLInfo{ ColumnName: ast.NewCIStr("test"), IntervalExprStr: "test_expr", IntervalTimeUnit: 5, Enable: true, } clonedTTLInfo := ttlInfo.Clone() clonedTTLInfo.ColumnName = ast.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) } func TestTTLJobInterval(t *testing.T) { ttlInfo := &TTLInfo{} interval, err := ttlInfo.GetJobInterval() require.NoError(t, err) require.Equal(t, time.Hour, interval) ttlInfo = &TTLInfo{JobInterval: "200h"} interval, err = ttlInfo.GetJobInterval() require.NoError(t, err) require.Equal(t, time.Hour*200, interval) } func TestClearReorgIntermediateInfo(t *testing.T) { ptInfo := &PartitionInfo{} ptInfo.DDLType = ast.PartitionTypeHash ptInfo.DDLExpr = "Test DDL Expr" ptInfo.NewTableID = 1111 ptInfo.ClearReorgIntermediateInfo() require.Equal(t, ast.PartitionTypeNone, ptInfo.DDLType) require.Equal(t, "", ptInfo.DDLExpr) require.Equal(t, true, ptInfo.DDLColumns == nil) require.Equal(t, int64(0), ptInfo.NewTableID) } func TestTTLDefaultJobInterval(t *testing.T) { // test default value of `DefaultTTLJobInterval` is valid. d, err := duration.ParseDuration(DefaultTTLJobInterval) require.NoError(t, err) require.Equal(t, 24*time.Hour, d) // test default value of `OldDefaultTTLJobInterval` is valid. d, err = duration.ParseDuration(OldDefaultTTLJobInterval) require.NoError(t, err) require.Equal(t, time.Hour, d) }