github.com/pingcap/tidb/parser@v0.0.0-20231013125129-93a834a6bf8d/model/model_test.go (about) 1 // Copyright 2015 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package model 15 16 import ( 17 "encoding/json" 18 "fmt" 19 "testing" 20 "time" 21 22 "github.com/pingcap/tidb/parser/charset" 23 "github.com/pingcap/tidb/parser/mysql" 24 "github.com/pingcap/tidb/parser/types" 25 "github.com/stretchr/testify/require" 26 ) 27 28 func TestT(t *testing.T) { 29 abc := NewCIStr("aBC") 30 require.Equal(t, "aBC", abc.O) 31 require.Equal(t, "abc", abc.L) 32 require.Equal(t, "aBC", abc.String()) 33 } 34 35 func newColumnForTest(id int64, offset int) *ColumnInfo { 36 return &ColumnInfo{ 37 ID: id, 38 Name: NewCIStr(fmt.Sprintf("c_%d", id)), 39 Offset: offset, 40 } 41 } 42 43 func newIndexForTest(id int64, cols ...*ColumnInfo) *IndexInfo { 44 idxCols := make([]*IndexColumn, 0, len(cols)) 45 for _, c := range cols { 46 idxCols = append(idxCols, &IndexColumn{Offset: c.Offset, Name: c.Name}) 47 } 48 return &IndexInfo{ 49 ID: id, 50 Name: NewCIStr(fmt.Sprintf("i_%d", id)), 51 Columns: idxCols, 52 } 53 } 54 55 func checkOffsets(t *testing.T, tbl *TableInfo, ids ...int) { 56 require.Equal(t, len(ids), len(tbl.Columns)) 57 for i := 0; i < len(ids); i++ { 58 expected := fmt.Sprintf("c_%d", ids[i]) 59 require.Equal(t, expected, tbl.Columns[i].Name.L) 60 require.Equal(t, i, tbl.Columns[i].Offset) 61 } 62 for _, col := range tbl.Columns { 63 for _, idx := range tbl.Indices { 64 for _, idxCol := range idx.Columns { 65 if col.Name.L != idxCol.Name.L { 66 continue 67 } 68 // Columns with the same name should have a same offset. 69 require.Equal(t, col.Offset, idxCol.Offset) 70 } 71 } 72 } 73 } 74 75 func TestMoveColumnInfo(t *testing.T) { 76 c0 := newColumnForTest(0, 0) 77 c1 := newColumnForTest(1, 1) 78 c2 := newColumnForTest(2, 2) 79 c3 := newColumnForTest(3, 3) 80 c4 := newColumnForTest(4, 4) 81 82 i0 := newIndexForTest(0, c0, c1, c2, c3, c4) 83 i1 := newIndexForTest(1, c4, c2) 84 i2 := newIndexForTest(2, c0, c4) 85 i3 := newIndexForTest(3, c1, c2, c3) 86 i4 := newIndexForTest(4, c3, c2, c1) 87 88 tbl := &TableInfo{ 89 ID: 1, 90 Name: NewCIStr("t"), 91 Columns: []*ColumnInfo{c0, c1, c2, c3, c4}, 92 Indices: []*IndexInfo{i0, i1, i2, i3, i4}, 93 } 94 95 // Original offsets: [0, 1, 2, 3, 4] 96 tbl.MoveColumnInfo(4, 0) 97 checkOffsets(t, tbl, 4, 0, 1, 2, 3) 98 tbl.MoveColumnInfo(2, 3) 99 checkOffsets(t, tbl, 4, 0, 2, 1, 3) 100 tbl.MoveColumnInfo(3, 2) 101 checkOffsets(t, tbl, 4, 0, 1, 2, 3) 102 tbl.MoveColumnInfo(0, 4) 103 checkOffsets(t, tbl, 0, 1, 2, 3, 4) 104 tbl.MoveColumnInfo(2, 2) 105 checkOffsets(t, tbl, 0, 1, 2, 3, 4) 106 tbl.MoveColumnInfo(0, 0) 107 checkOffsets(t, tbl, 0, 1, 2, 3, 4) 108 tbl.MoveColumnInfo(1, 4) 109 checkOffsets(t, tbl, 0, 2, 3, 4, 1) 110 tbl.MoveColumnInfo(3, 0) 111 checkOffsets(t, tbl, 4, 0, 2, 3, 1) 112 } 113 114 func TestModelBasic(t *testing.T) { 115 column := &ColumnInfo{ 116 ID: 1, 117 Name: NewCIStr("c"), 118 Offset: 0, 119 DefaultValue: 0, 120 FieldType: *types.NewFieldType(0), 121 Hidden: true, 122 } 123 column.AddFlag(mysql.PriKeyFlag) 124 125 index := &IndexInfo{ 126 Name: NewCIStr("key"), 127 Table: NewCIStr("t"), 128 Columns: []*IndexColumn{ 129 { 130 Name: NewCIStr("c"), 131 Offset: 0, 132 Length: 10, 133 }}, 134 Unique: true, 135 Primary: true, 136 } 137 138 fk := &FKInfo{ 139 RefCols: []CIStr{NewCIStr("a")}, 140 Cols: []CIStr{NewCIStr("a")}, 141 } 142 143 seq := &SequenceInfo{ 144 Increment: 1, 145 MinValue: 1, 146 MaxValue: 100, 147 } 148 149 table := &TableInfo{ 150 ID: 1, 151 Name: NewCIStr("t"), 152 Charset: "utf8", 153 Collate: "utf8_bin", 154 Columns: []*ColumnInfo{column}, 155 Indices: []*IndexInfo{index}, 156 ForeignKeys: []*FKInfo{fk}, 157 PKIsHandle: true, 158 } 159 160 table2 := &TableInfo{ 161 ID: 2, 162 Name: NewCIStr("s"), 163 Sequence: seq, 164 } 165 166 dbInfo := &DBInfo{ 167 ID: 1, 168 Name: NewCIStr("test"), 169 Charset: "utf8", 170 Collate: "utf8_bin", 171 Tables: []*TableInfo{table}, 172 } 173 174 n := dbInfo.Clone() 175 require.Equal(t, dbInfo, n) 176 177 pkName := table.GetPkName() 178 require.Equal(t, NewCIStr("c"), pkName) 179 newColumn := table.GetPkColInfo() 180 require.Equal(t, true, newColumn.Hidden) 181 require.Equal(t, column, newColumn) 182 inIdx := table.ColumnIsInIndex(column) 183 require.Equal(t, true, inIdx) 184 tp := IndexTypeBtree 185 require.Equal(t, "BTREE", tp.String()) 186 tp = IndexTypeHash 187 require.Equal(t, "HASH", tp.String()) 188 tp = 1e5 189 require.Equal(t, "", tp.String()) 190 has := index.HasPrefixIndex() 191 require.Equal(t, true, has) 192 require.Equal(t, TSConvert2Time(table.UpdateTS), table.GetUpdateTime()) 193 require.True(t, table2.IsSequence()) 194 require.False(t, table2.IsBaseTable()) 195 196 // Corner cases 197 column.ToggleFlag(mysql.PriKeyFlag) 198 pkName = table.GetPkName() 199 require.Equal(t, NewCIStr(""), pkName) 200 newColumn = table.GetPkColInfo() 201 require.Nil(t, newColumn) 202 anCol := &ColumnInfo{ 203 Name: NewCIStr("d"), 204 } 205 exIdx := table.ColumnIsInIndex(anCol) 206 require.Equal(t, false, exIdx) 207 anIndex := &IndexInfo{ 208 Columns: []*IndexColumn{}, 209 } 210 no := anIndex.HasPrefixIndex() 211 require.Equal(t, false, no) 212 213 extraPK := NewExtraHandleColInfo() 214 require.Equal(t, mysql.NotNullFlag|mysql.PriKeyFlag, extraPK.GetFlag()) 215 require.Equal(t, charset.CharsetBin, extraPK.GetCharset()) 216 require.Equal(t, charset.CollationBin, extraPK.GetCollate()) 217 } 218 219 func TestJobStartTime(t *testing.T) { 220 job := &Job{ 221 ID: 123, 222 BinlogInfo: &HistoryInfo{}, 223 } 224 require.Equal(t, TSConvert2Time(job.StartTS), time.Unix(0, 0)) 225 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()) 226 } 227 228 func TestJobCodec(t *testing.T) { 229 type A struct { 230 Name string 231 } 232 tzName, tzOffset := time.Now().In(time.UTC).Zone() 233 job := &Job{ 234 ID: 1, 235 TableID: 2, 236 SchemaID: 1, 237 BinlogInfo: &HistoryInfo{}, 238 Args: []interface{}{NewCIStr("a"), A{Name: "abc"}}, 239 ReorgMeta: &DDLReorgMeta{ 240 Location: &TimeZoneLocation{Name: tzName, Offset: tzOffset}, 241 }, 242 } 243 job.BinlogInfo.AddDBInfo(123, &DBInfo{ID: 1, Name: NewCIStr("test_history_db")}) 244 job.BinlogInfo.AddTableInfo(123, &TableInfo{ID: 1, Name: NewCIStr("test_history_tbl")}) 245 246 // Test IsDependentOn. 247 // job: table ID is 2 248 // job1: table ID is 2 249 var err error 250 job1 := &Job{ 251 ID: 2, 252 TableID: 2, 253 SchemaID: 1, 254 Type: ActionRenameTable, 255 BinlogInfo: &HistoryInfo{}, 256 Args: []interface{}{int64(3), NewCIStr("new_table_name")}, 257 } 258 job1.RawArgs, err = json.Marshal(job1.Args) 259 require.NoError(t, err) 260 isDependent, err := job.IsDependentOn(job1) 261 require.NoError(t, err) 262 require.True(t, isDependent) 263 // job1: rename table, old schema ID is 3 264 // job2: create schema, schema ID is 3 265 job2 := &Job{ 266 ID: 3, 267 TableID: 3, 268 SchemaID: 3, 269 Type: ActionCreateSchema, 270 BinlogInfo: &HistoryInfo{}, 271 } 272 isDependent, err = job2.IsDependentOn(job1) 273 require.NoError(t, err) 274 require.True(t, isDependent) 275 276 // Test IsDependentOn for exchange partition with table. 277 // test ActionCreateSchema and ActionExchangeTablePartition is dependent. 278 job3 := &Job{ 279 ID: 4, 280 TableID: 4, 281 SchemaID: 4, 282 Type: ActionExchangeTablePartition, 283 BinlogInfo: &HistoryInfo{}, 284 Args: []interface{}{int64(6), int64(3), int64(5), "pt", true}, 285 } 286 job3.RawArgs, err = json.Marshal(job3.Args) 287 require.NoError(t, err) 288 isDependent, err = job3.IsDependentOn(job2) 289 require.NoError(t, err) 290 require.True(t, isDependent) 291 292 // test random and ActionExchangeTablePartition is dependent because TableID is same. 293 job4 := &Job{ 294 ID: 5, 295 TableID: 5, 296 SchemaID: 3, 297 Type: ActionExchangeTablePartition, 298 BinlogInfo: &HistoryInfo{}, 299 Args: []interface{}{6, 4, 2, "pt", true}, 300 } 301 job4.RawArgs, err = json.Marshal(job4.Args) 302 require.NoError(t, err) 303 isDependent, err = job4.IsDependentOn(job) 304 require.NoError(t, err) 305 require.True(t, isDependent) 306 307 // test ActionExchangeTablePartition and ActionExchangeTablePartition is dependent. 308 job5 := &Job{ 309 ID: 6, 310 TableID: 6, 311 SchemaID: 6, 312 Type: ActionExchangeTablePartition, 313 BinlogInfo: &HistoryInfo{}, 314 Args: []interface{}{2, 6, 5, "pt", true}, 315 } 316 job5.RawArgs, err = json.Marshal(job5.Args) 317 require.NoError(t, err) 318 isDependent, err = job5.IsDependentOn(job4) 319 require.NoError(t, err) 320 require.True(t, isDependent) 321 322 job6 := &Job{ 323 ID: 7, 324 TableID: 7, 325 SchemaID: 7, 326 Type: ActionExchangeTablePartition, 327 BinlogInfo: &HistoryInfo{}, 328 Args: []interface{}{6, 4, 2, "pt", true}, 329 } 330 job6.RawArgs, err = json.Marshal(job6.Args) 331 require.NoError(t, err) 332 isDependent, err = job6.IsDependentOn(job5) 333 require.NoError(t, err) 334 require.True(t, isDependent) 335 336 job7 := &Job{ 337 ID: 8, 338 TableID: 8, 339 SchemaID: 8, 340 Type: ActionExchangeTablePartition, 341 BinlogInfo: &HistoryInfo{}, 342 Args: []interface{}{8, 4, 6, "pt", true}, 343 } 344 job7.RawArgs, err = json.Marshal(job7.Args) 345 require.NoError(t, err) 346 isDependent, err = job7.IsDependentOn(job6) 347 require.NoError(t, err) 348 require.True(t, isDependent) 349 350 job8 := &Job{ 351 ID: 9, 352 TableID: 9, 353 SchemaID: 9, 354 Type: ActionExchangeTablePartition, 355 BinlogInfo: &HistoryInfo{}, 356 Args: []interface{}{8, 9, 9, "pt", true}, 357 } 358 job8.RawArgs, err = json.Marshal(job8.Args) 359 require.NoError(t, err) 360 isDependent, err = job8.IsDependentOn(job7) 361 require.NoError(t, err) 362 require.True(t, isDependent) 363 364 job9 := &Job{ 365 ID: 10, 366 TableID: 10, 367 SchemaID: 10, 368 Type: ActionExchangeTablePartition, 369 BinlogInfo: &HistoryInfo{}, 370 Args: []interface{}{10, 10, 8, "pt", true}, 371 } 372 job9.RawArgs, err = json.Marshal(job9.Args) 373 require.NoError(t, err) 374 isDependent, err = job9.IsDependentOn(job8) 375 require.NoError(t, err) 376 require.True(t, isDependent) 377 378 // test ActionDropSchema and ActionExchangeTablePartition is dependent. 379 job10 := &Job{ 380 ID: 11, 381 TableID: 11, 382 SchemaID: 11, 383 Type: ActionDropSchema, 384 BinlogInfo: &HistoryInfo{}, 385 } 386 job10.RawArgs, err = json.Marshal(job10.Args) 387 require.NoError(t, err) 388 389 job11 := &Job{ 390 ID: 12, 391 TableID: 12, 392 SchemaID: 11, 393 Type: ActionExchangeTablePartition, 394 BinlogInfo: &HistoryInfo{}, 395 Args: []interface{}{10, 10, 8, "pt", true}, 396 } 397 job11.RawArgs, err = json.Marshal(job11.Args) 398 require.NoError(t, err) 399 isDependent, err = job11.IsDependentOn(job10) 400 require.NoError(t, err) 401 require.True(t, isDependent) 402 403 // test ActionDropTable and ActionExchangeTablePartition is dependent. 404 job12 := &Job{ 405 ID: 13, 406 TableID: 13, 407 SchemaID: 11, 408 Type: ActionDropTable, 409 BinlogInfo: &HistoryInfo{}, 410 } 411 job12.RawArgs, err = json.Marshal(job12.Args) 412 require.NoError(t, err) 413 isDependent, err = job11.IsDependentOn(job12) 414 require.NoError(t, err) 415 require.False(t, isDependent) 416 417 job13 := &Job{ 418 ID: 14, 419 TableID: 12, 420 SchemaID: 14, 421 Type: ActionDropTable, 422 BinlogInfo: &HistoryInfo{}, 423 } 424 job13.RawArgs, err = json.Marshal(job13.Args) 425 require.NoError(t, err) 426 isDependent, err = job11.IsDependentOn(job13) 427 require.NoError(t, err) 428 require.True(t, isDependent) 429 430 // test ActionDropTable and ActionExchangeTablePartition is dependent. 431 job14 := &Job{ 432 ID: 15, 433 TableID: 15, 434 SchemaID: 15, 435 Type: ActionExchangeTablePartition, 436 BinlogInfo: &HistoryInfo{}, 437 Args: []interface{}{16, 17, 12, "pt", true}, 438 } 439 job14.RawArgs, err = json.Marshal(job14.Args) 440 require.NoError(t, err) 441 isDependent, err = job13.IsDependentOn(job14) 442 require.NoError(t, err) 443 require.True(t, isDependent) 444 445 // test ActionFlashbackCluster with other ddl jobs are dependent. 446 job15 := &Job{ 447 ID: 16, 448 Type: ActionFlashbackCluster, 449 BinlogInfo: &HistoryInfo{}, 450 Args: []interface{}{0, map[string]interface{}{}, "ON", true}, 451 } 452 job15.RawArgs, err = json.Marshal(job15.Args) 453 require.NoError(t, err) 454 isDependent, err = job.IsDependentOn(job15) 455 require.NoError(t, err) 456 require.True(t, isDependent) 457 458 require.Equal(t, false, job.IsCancelled()) 459 b, err := job.Encode(false) 460 require.NoError(t, err) 461 newJob := &Job{} 462 err = newJob.Decode(b) 463 require.NoError(t, err) 464 require.Equal(t, job.BinlogInfo, newJob.BinlogInfo) 465 name := CIStr{} 466 a := A{} 467 err = newJob.DecodeArgs(&name, &a) 468 require.NoError(t, err) 469 require.Equal(t, NewCIStr(""), name) 470 require.Equal(t, A{Name: ""}, a) 471 require.Greater(t, len(newJob.String()), 0) 472 require.Equal(t, newJob.ReorgMeta.Location.Name, tzName) 473 require.Equal(t, newJob.ReorgMeta.Location.Offset, tzOffset) 474 475 job.BinlogInfo.Clean() 476 b1, err := job.Encode(true) 477 require.NoError(t, err) 478 newJob = &Job{} 479 err = newJob.Decode(b1) 480 require.NoError(t, err) 481 require.Equal(t, &HistoryInfo{}, newJob.BinlogInfo) 482 name = CIStr{} 483 a = A{} 484 err = newJob.DecodeArgs(&name, &a) 485 require.NoError(t, err) 486 require.Equal(t, NewCIStr("a"), name) 487 require.Equal(t, A{Name: "abc"}, a) 488 require.Greater(t, len(newJob.String()), 0) 489 490 b2, err := job.Encode(true) 491 require.NoError(t, err) 492 newJob = &Job{} 493 err = newJob.Decode(b2) 494 require.NoError(t, err) 495 name = CIStr{} 496 // Don't decode to a here. 497 err = newJob.DecodeArgs(&name) 498 require.NoError(t, err) 499 require.Equal(t, NewCIStr("a"), name) 500 require.Greater(t, len(newJob.String()), 0) 501 502 job.State = JobStateDone 503 require.True(t, job.IsDone()) 504 require.True(t, job.IsFinished()) 505 require.False(t, job.IsRunning()) 506 require.False(t, job.IsSynced()) 507 require.False(t, job.IsRollbackDone()) 508 job.SetRowCount(3) 509 require.Equal(t, int64(3), job.GetRowCount()) 510 } 511 512 func TestState(t *testing.T) { 513 schemaTbl := []SchemaState{ 514 StateDeleteOnly, 515 StateWriteOnly, 516 StateWriteReorganization, 517 StateDeleteReorganization, 518 StatePublic, 519 StateGlobalTxnOnly, 520 } 521 522 for _, state := range schemaTbl { 523 require.Greater(t, len(state.String()), 0) 524 } 525 526 jobTbl := []JobState{ 527 JobStateRunning, 528 JobStateDone, 529 JobStateCancelled, 530 JobStateRollingback, 531 JobStateRollbackDone, 532 JobStateSynced, 533 } 534 535 for _, state := range jobTbl { 536 require.Greater(t, len(state.String()), 0) 537 } 538 } 539 540 func TestString(t *testing.T) { 541 acts := []struct { 542 act ActionType 543 result string 544 }{ 545 {ActionNone, "none"}, 546 {ActionAddForeignKey, "add foreign key"}, 547 {ActionDropForeignKey, "drop foreign key"}, 548 {ActionTruncateTable, "truncate table"}, 549 {ActionModifyColumn, "modify column"}, 550 {ActionRenameTable, "rename table"}, 551 {ActionRenameTables, "rename tables"}, 552 {ActionSetDefaultValue, "set default value"}, 553 {ActionCreateSchema, "create schema"}, 554 {ActionDropSchema, "drop schema"}, 555 {ActionCreateTable, "create table"}, 556 {ActionDropTable, "drop table"}, 557 {ActionAddIndex, "add index"}, 558 {ActionDropIndex, "drop index"}, 559 {ActionAddColumn, "add column"}, 560 {ActionDropColumn, "drop column"}, 561 {ActionModifySchemaCharsetAndCollate, "modify schema charset and collate"}, 562 {ActionAlterTablePlacement, "alter table placement"}, 563 {ActionAlterTablePartitionPlacement, "alter table partition placement"}, 564 {ActionAlterNoCacheTable, "alter table nocache"}, 565 } 566 567 for _, v := range acts { 568 str := v.act.String() 569 require.Equal(t, v.result, str) 570 } 571 } 572 573 func TestUnmarshalCIStr(t *testing.T) { 574 var ci CIStr 575 576 // Test unmarshal CIStr from a single string. 577 str := "aaBB" 578 buf, err := json.Marshal(str) 579 require.NoError(t, err) 580 require.NoError(t, ci.UnmarshalJSON(buf)) 581 require.Equal(t, str, ci.O) 582 require.Equal(t, "aabb", ci.L) 583 584 buf, err = json.Marshal(ci) 585 require.NoError(t, err) 586 require.Equal(t, `{"O":"aaBB","L":"aabb"}`, string(buf)) 587 require.NoError(t, ci.UnmarshalJSON(buf)) 588 require.Equal(t, str, ci.O) 589 require.Equal(t, "aabb", ci.L) 590 } 591 592 func TestDefaultValue(t *testing.T) { 593 srcCol := &ColumnInfo{ 594 ID: 1, 595 } 596 randPlainStr := "random_plain_string" 597 598 oldPlainCol := srcCol.Clone() 599 oldPlainCol.Name = NewCIStr("oldPlainCol") 600 oldPlainCol.FieldType = *types.NewFieldType(mysql.TypeLong) 601 oldPlainCol.DefaultValue = randPlainStr 602 oldPlainCol.OriginDefaultValue = randPlainStr 603 604 newPlainCol := srcCol.Clone() 605 newPlainCol.Name = NewCIStr("newPlainCol") 606 newPlainCol.FieldType = *types.NewFieldType(mysql.TypeLong) 607 err := newPlainCol.SetDefaultValue(1) 608 require.NoError(t, err) 609 require.Equal(t, 1, newPlainCol.GetDefaultValue()) 610 err = newPlainCol.SetDefaultValue(randPlainStr) 611 require.NoError(t, err) 612 require.Equal(t, randPlainStr, newPlainCol.GetDefaultValue()) 613 614 randBitStr := string([]byte{25, 185}) 615 616 oldBitCol := srcCol.Clone() 617 oldBitCol.Name = NewCIStr("oldBitCol") 618 oldBitCol.FieldType = *types.NewFieldType(mysql.TypeBit) 619 oldBitCol.DefaultValue = randBitStr 620 oldBitCol.OriginDefaultValue = randBitStr 621 622 newBitCol := srcCol.Clone() 623 newBitCol.Name = NewCIStr("newBitCol") 624 newBitCol.FieldType = *types.NewFieldType(mysql.TypeBit) 625 err = newBitCol.SetDefaultValue(1) 626 // Only string type is allowed in BIT column. 627 require.Error(t, err) 628 require.Contains(t, err.Error(), "Invalid default value") 629 require.Equal(t, 1, newBitCol.GetDefaultValue()) 630 err = newBitCol.SetDefaultValue(randBitStr) 631 require.NoError(t, err) 632 require.Equal(t, randBitStr, newBitCol.GetDefaultValue()) 633 634 nullBitCol := srcCol.Clone() 635 nullBitCol.Name = NewCIStr("nullBitCol") 636 nullBitCol.FieldType = *types.NewFieldType(mysql.TypeBit) 637 err = nullBitCol.SetOriginDefaultValue(nil) 638 require.NoError(t, err) 639 require.Nil(t, nullBitCol.GetOriginDefaultValue()) 640 641 testCases := []struct { 642 col *ColumnInfo 643 isConsistent bool 644 }{ 645 {oldPlainCol, true}, 646 {oldBitCol, false}, 647 {newPlainCol, true}, 648 {newBitCol, true}, 649 {nullBitCol, true}, 650 } 651 for _, tc := range testCases { 652 col, isConsistent := tc.col, tc.isConsistent 653 comment := fmt.Sprintf("%s assertion failed", col.Name.O) 654 bytes, err := json.Marshal(col) 655 require.NoError(t, err, comment) 656 var newCol ColumnInfo 657 err = json.Unmarshal(bytes, &newCol) 658 require.NoError(t, err, comment) 659 if isConsistent { 660 require.Equal(t, col.GetDefaultValue(), newCol.GetDefaultValue(), comment) 661 require.Equal(t, col.GetOriginDefaultValue(), newCol.GetOriginDefaultValue(), comment) 662 } else { 663 require.NotEqual(t, col.GetDefaultValue(), newCol.GetDefaultValue(), comment) 664 require.NotEqual(t, col.GetOriginDefaultValue(), newCol.GetOriginDefaultValue(), comment) 665 } 666 } 667 } 668 669 func TestPlacementSettingsString(t *testing.T) { 670 settings := &PlacementSettings{ 671 PrimaryRegion: "us-east-1", 672 Regions: "us-east-1,us-east-2", 673 Schedule: "EVEN", 674 } 675 require.Equal(t, "PRIMARY_REGION=\"us-east-1\" REGIONS=\"us-east-1,us-east-2\" SCHEDULE=\"EVEN\"", settings.String()) 676 677 settings = &PlacementSettings{ 678 LeaderConstraints: "[+region=bj]", 679 } 680 require.Equal(t, "LEADER_CONSTRAINTS=\"[+region=bj]\"", settings.String()) 681 682 settings = &PlacementSettings{ 683 Voters: 1, 684 VoterConstraints: "[+region=us-east-1]", 685 Followers: 2, 686 FollowerConstraints: "[+disk=ssd]", 687 Learners: 3, 688 LearnerConstraints: "[+region=us-east-2]", 689 } 690 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()) 691 692 settings = &PlacementSettings{ 693 Voters: 3, 694 Followers: 2, 695 Learners: 1, 696 Constraints: "{\"+us-east-1\":1,+us-east-2:1}", 697 } 698 require.Equal(t, "CONSTRAINTS=\"{\\\"+us-east-1\\\":1,+us-east-2:1}\" VOTERS=3 FOLLOWERS=2 LEARNERS=1", settings.String()) 699 } 700 701 func TestPlacementSettingsClone(t *testing.T) { 702 settings := &PlacementSettings{} 703 clonedSettings := settings.Clone() 704 clonedSettings.PrimaryRegion = "r1" 705 clonedSettings.Regions = "r1,r2" 706 clonedSettings.Followers = 1 707 clonedSettings.Voters = 2 708 clonedSettings.Followers = 3 709 clonedSettings.Constraints = "[+zone=z1]" 710 clonedSettings.LearnerConstraints = "[+region=r1]" 711 clonedSettings.FollowerConstraints = "[+disk=ssd]" 712 clonedSettings.LeaderConstraints = "[+region=r2]" 713 clonedSettings.VoterConstraints = "[+zone=z2]" 714 clonedSettings.Schedule = "even" 715 require.Equal(t, PlacementSettings{}, *settings) 716 } 717 718 func TestPlacementPolicyClone(t *testing.T) { 719 policy := &PolicyInfo{ 720 PlacementSettings: &PlacementSettings{}, 721 } 722 clonedPolicy := policy.Clone() 723 clonedPolicy.ID = 100 724 clonedPolicy.Name = NewCIStr("p2") 725 clonedPolicy.State = StateDeleteOnly 726 clonedPolicy.PlacementSettings.Followers = 10 727 728 require.Equal(t, int64(0), policy.ID) 729 require.Equal(t, NewCIStr(""), policy.Name) 730 require.Equal(t, StateNone, policy.State) 731 require.Equal(t, PlacementSettings{}, *(policy.PlacementSettings)) 732 } 733 734 func TestLocation(t *testing.T) { 735 // test offset = 0 736 loc := &TimeZoneLocation{} 737 nLoc, err := loc.GetLocation() 738 require.NoError(t, err) 739 require.Equal(t, nLoc.String(), "UTC") 740 // test loc.location != nil 741 loc.Name = "Asia/Shanghai" 742 nLoc, err = loc.GetLocation() 743 require.NoError(t, err) 744 require.Equal(t, nLoc.String(), "UTC") 745 // timezone +05:00 746 loc1 := &TimeZoneLocation{Name: "UTC", Offset: 18000} 747 loc1Byte, err := json.Marshal(loc1) 748 require.NoError(t, err) 749 loc2 := &TimeZoneLocation{} 750 err = json.Unmarshal(loc1Byte, loc2) 751 require.NoError(t, err) 752 require.Equal(t, loc2.Offset, loc1.Offset) 753 require.Equal(t, loc2.Name, loc1.Name) 754 nLoc, err = loc2.GetLocation() 755 require.NoError(t, err) 756 require.Equal(t, nLoc.String(), "UTC") 757 location := time.FixedZone("UTC", loc1.Offset) 758 require.Equal(t, nLoc, location) 759 } 760 761 func TestIsIndexPrefixCovered(t *testing.T) { 762 c0 := newColumnForTest(0, 0) 763 c1 := newColumnForTest(1, 1) 764 c2 := newColumnForTest(2, 2) 765 c3 := newColumnForTest(3, 3) 766 c4 := newColumnForTest(4, 4) 767 768 i0 := newIndexForTest(0, c0, c1, c2) 769 i1 := newIndexForTest(1, c4, c2) 770 771 tbl := &TableInfo{ 772 ID: 1, 773 Name: NewCIStr("t"), 774 Columns: []*ColumnInfo{c0, c1, c2, c3, c4}, 775 Indices: []*IndexInfo{i0, i1}, 776 } 777 require.Equal(t, true, IsIndexPrefixCovered(tbl, i0, NewCIStr("c_0"))) 778 require.Equal(t, true, IsIndexPrefixCovered(tbl, i0, NewCIStr("c_0"), NewCIStr("c_1"), NewCIStr("c_2"))) 779 require.Equal(t, false, IsIndexPrefixCovered(tbl, i0, NewCIStr("c_1"))) 780 require.Equal(t, false, IsIndexPrefixCovered(tbl, i0, NewCIStr("c_2"))) 781 require.Equal(t, false, IsIndexPrefixCovered(tbl, i0, NewCIStr("c_1"), NewCIStr("c_2"))) 782 require.Equal(t, false, IsIndexPrefixCovered(tbl, i0, NewCIStr("c_0"), NewCIStr("c_2"))) 783 784 require.Equal(t, true, IsIndexPrefixCovered(tbl, i1, NewCIStr("c_4"))) 785 require.Equal(t, true, IsIndexPrefixCovered(tbl, i1, NewCIStr("c_4"), NewCIStr("c_2"))) 786 require.Equal(t, false, IsIndexPrefixCovered(tbl, i0, NewCIStr("c_2"))) 787 } 788 789 func TestTTLInfoClone(t *testing.T) { 790 ttlInfo := &TTLInfo{ 791 ColumnName: NewCIStr("test"), 792 IntervalExprStr: "test_expr", 793 IntervalTimeUnit: 5, 794 Enable: true, 795 } 796 797 clonedTTLInfo := ttlInfo.Clone() 798 clonedTTLInfo.ColumnName = NewCIStr("test_2") 799 clonedTTLInfo.IntervalExprStr = "test_expr_2" 800 clonedTTLInfo.IntervalTimeUnit = 9 801 clonedTTLInfo.Enable = false 802 803 require.Equal(t, "test", ttlInfo.ColumnName.O) 804 require.Equal(t, "test_expr", ttlInfo.IntervalExprStr) 805 require.Equal(t, 5, ttlInfo.IntervalTimeUnit) 806 require.Equal(t, true, ttlInfo.Enable) 807 } 808 809 func TestTTLJobInterval(t *testing.T) { 810 ttlInfo := &TTLInfo{} 811 812 interval, err := ttlInfo.GetJobInterval() 813 require.NoError(t, err) 814 require.Equal(t, time.Hour, interval) 815 816 ttlInfo = &TTLInfo{JobInterval: "200h"} 817 interval, err = ttlInfo.GetJobInterval() 818 require.NoError(t, err) 819 require.Equal(t, time.Hour*200, interval) 820 }