github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/cdc/entry/schema_storage_test.go (about) 1 // Copyright 2020 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 entry 15 16 import ( 17 "context" 18 "fmt" 19 "sort" 20 21 "github.com/google/go-cmp/cmp" 22 "github.com/pingcap/check" 23 "github.com/pingcap/errors" 24 timodel "github.com/pingcap/parser/model" 25 "github.com/pingcap/parser/mysql" 26 "github.com/pingcap/ticdc/cdc/kv" 27 "github.com/pingcap/ticdc/cdc/model" 28 "github.com/pingcap/ticdc/pkg/util/testleak" 29 ticonfig "github.com/pingcap/tidb/config" 30 "github.com/pingcap/tidb/domain" 31 tidbkv "github.com/pingcap/tidb/kv" 32 timeta "github.com/pingcap/tidb/meta" 33 "github.com/pingcap/tidb/session" 34 "github.com/pingcap/tidb/sessionctx" 35 "github.com/pingcap/tidb/store/mockstore" 36 "github.com/pingcap/tidb/store/tikv/oracle" 37 "github.com/pingcap/tidb/types" 38 "github.com/pingcap/tidb/util/testkit" 39 ) 40 41 type schemaSuite struct{} 42 43 var _ = check.Suite(&schemaSuite{}) 44 45 func (t *schemaSuite) TestSchema(c *check.C) { 46 defer testleak.AfterTest(c)() 47 dbName := timodel.NewCIStr("Test") 48 // db and ignoreDB info 49 dbInfo := &timodel.DBInfo{ 50 ID: 1, 51 Name: dbName, 52 State: timodel.StatePublic, 53 } 54 // `createSchema` job1 55 job := &timodel.Job{ 56 ID: 3, 57 State: timodel.JobStateSynced, 58 SchemaID: 1, 59 Type: timodel.ActionCreateSchema, 60 BinlogInfo: &timodel.HistoryInfo{SchemaVersion: 1, DBInfo: dbInfo, FinishedTS: 123}, 61 Query: "create database test", 62 } 63 // reconstruct the local schema 64 snap := newEmptySchemaSnapshot(false) 65 err := snap.handleDDL(job) 66 c.Assert(err, check.IsNil) 67 _, exist := snap.SchemaByID(job.SchemaID) 68 c.Assert(exist, check.IsTrue) 69 70 // test drop schema 71 job = &timodel.Job{ 72 ID: 6, 73 State: timodel.JobStateSynced, 74 SchemaID: 1, 75 Type: timodel.ActionDropSchema, 76 BinlogInfo: &timodel.HistoryInfo{SchemaVersion: 3, DBInfo: dbInfo, FinishedTS: 124}, 77 Query: "drop database test", 78 } 79 err = snap.handleDDL(job) 80 c.Assert(err, check.IsNil) 81 _, exist = snap.SchemaByID(job.SchemaID) 82 c.Assert(exist, check.IsFalse) 83 84 job = &timodel.Job{ 85 ID: 3, 86 State: timodel.JobStateSynced, 87 SchemaID: 1, 88 Type: timodel.ActionCreateSchema, 89 BinlogInfo: &timodel.HistoryInfo{SchemaVersion: 2, DBInfo: dbInfo, FinishedTS: 124}, 90 Query: "create database test", 91 } 92 93 err = snap.handleDDL(job) 94 c.Assert(err, check.IsNil) 95 err = snap.handleDDL(job) 96 c.Log(err) 97 c.Assert(errors.IsAlreadyExists(err), check.IsTrue) 98 99 // test schema drop schema error 100 job = &timodel.Job{ 101 ID: 9, 102 State: timodel.JobStateSynced, 103 SchemaID: 1, 104 Type: timodel.ActionDropSchema, 105 BinlogInfo: &timodel.HistoryInfo{SchemaVersion: 1, DBInfo: dbInfo, FinishedTS: 123}, 106 Query: "drop database test", 107 } 108 err = snap.handleDDL(job) 109 c.Assert(err, check.IsNil) 110 err = snap.handleDDL(job) 111 c.Assert(errors.IsNotFound(err), check.IsTrue) 112 } 113 114 func (*schemaSuite) TestTable(c *check.C) { 115 defer testleak.AfterTest(c)() 116 var jobs []*timodel.Job 117 dbName := timodel.NewCIStr("Test") 118 tbName := timodel.NewCIStr("T") 119 colName := timodel.NewCIStr("A") 120 idxName := timodel.NewCIStr("idx") 121 // column info 122 colInfo := &timodel.ColumnInfo{ 123 ID: 1, 124 Name: colName, 125 Offset: 0, 126 FieldType: *types.NewFieldType(mysql.TypeLonglong), 127 State: timodel.StatePublic, 128 } 129 // index info 130 idxInfo := &timodel.IndexInfo{ 131 Name: idxName, 132 Table: tbName, 133 Columns: []*timodel.IndexColumn{ 134 { 135 Name: colName, 136 Offset: 0, 137 Length: 10, 138 }, 139 }, 140 Unique: true, 141 Primary: true, 142 State: timodel.StatePublic, 143 } 144 // table info 145 tblInfo := &timodel.TableInfo{ 146 ID: 2, 147 Name: tbName, 148 State: timodel.StatePublic, 149 } 150 // db info 151 dbInfo := &timodel.DBInfo{ 152 ID: 3, 153 Name: dbName, 154 State: timodel.StatePublic, 155 } 156 157 // `createSchema` job 158 job := &timodel.Job{ 159 ID: 5, 160 State: timodel.JobStateSynced, 161 SchemaID: 3, 162 Type: timodel.ActionCreateSchema, 163 BinlogInfo: &timodel.HistoryInfo{SchemaVersion: 1, DBInfo: dbInfo, FinishedTS: 123}, 164 Query: "create database " + dbName.O, 165 } 166 jobs = append(jobs, job) 167 168 // `createTable` job 169 job = &timodel.Job{ 170 ID: 6, 171 State: timodel.JobStateSynced, 172 SchemaID: 3, 173 TableID: 2, 174 Type: timodel.ActionCreateTable, 175 BinlogInfo: &timodel.HistoryInfo{SchemaVersion: 2, TableInfo: tblInfo, FinishedTS: 124}, 176 Query: "create table " + tbName.O, 177 } 178 jobs = append(jobs, job) 179 180 // `addColumn` job 181 tblInfo.Columns = []*timodel.ColumnInfo{colInfo} 182 job = &timodel.Job{ 183 ID: 7, 184 State: timodel.JobStateSynced, 185 SchemaID: 3, 186 TableID: 2, 187 Type: timodel.ActionAddColumn, 188 BinlogInfo: &timodel.HistoryInfo{SchemaVersion: 3, TableInfo: tblInfo, FinishedTS: 125}, 189 Query: "alter table " + tbName.O + " add column " + colName.O, 190 } 191 jobs = append(jobs, job) 192 193 // construct a historical `addIndex` job 194 tblInfo = tblInfo.Clone() 195 tblInfo.Indices = []*timodel.IndexInfo{idxInfo} 196 job = &timodel.Job{ 197 ID: 8, 198 State: timodel.JobStateSynced, 199 SchemaID: 3, 200 TableID: 2, 201 Type: timodel.ActionAddIndex, 202 BinlogInfo: &timodel.HistoryInfo{SchemaVersion: 4, TableInfo: tblInfo, FinishedTS: 126}, 203 Query: fmt.Sprintf("alter table %s add index %s(%s)", tbName, idxName, colName), 204 } 205 jobs = append(jobs, job) 206 207 // reconstruct the local schema 208 snap := newEmptySchemaSnapshot(false) 209 for _, job := range jobs { 210 err := snap.handleDDL(job) 211 c.Assert(err, check.IsNil) 212 } 213 214 // check the historical db that constructed above whether in the schema list of local schema 215 _, ok := snap.SchemaByID(dbInfo.ID) 216 c.Assert(ok, check.IsTrue) 217 // check the historical table that constructed above whether in the table list of local schema 218 table, ok := snap.TableByID(tblInfo.ID) 219 c.Assert(ok, check.IsTrue) 220 c.Assert(table.Columns, check.HasLen, 1) 221 c.Assert(table.Indices, check.HasLen, 1) 222 223 // test ineligible tables 224 c.Assert(snap.IsIneligibleTableID(2), check.IsTrue) 225 226 // check truncate table 227 tblInfo1 := &timodel.TableInfo{ 228 ID: 9, 229 Name: tbName, 230 State: timodel.StatePublic, 231 } 232 job = &timodel.Job{ 233 ID: 9, 234 State: timodel.JobStateSynced, 235 SchemaID: 3, 236 TableID: 2, 237 Type: timodel.ActionTruncateTable, 238 BinlogInfo: &timodel.HistoryInfo{SchemaVersion: 5, TableInfo: tblInfo1, FinishedTS: 127}, 239 Query: "truncate table " + tbName.O, 240 } 241 preTableInfo, err := snap.PreTableInfo(job) 242 c.Assert(err, check.IsNil) 243 c.Assert(preTableInfo.TableName, check.Equals, model.TableName{Schema: "Test", Table: "T"}) 244 c.Assert(preTableInfo.ID, check.Equals, int64(2)) 245 246 err = snap.handleDDL(job) 247 c.Assert(err, check.IsNil) 248 249 _, ok = snap.TableByID(tblInfo1.ID) 250 c.Assert(ok, check.IsTrue) 251 252 _, ok = snap.TableByID(2) 253 c.Assert(ok, check.IsFalse) 254 255 // test ineligible tables 256 c.Assert(snap.IsIneligibleTableID(9), check.IsTrue) 257 c.Assert(snap.IsIneligibleTableID(2), check.IsFalse) 258 // check drop table 259 job = &timodel.Job{ 260 ID: 9, 261 State: timodel.JobStateSynced, 262 SchemaID: 3, 263 TableID: 9, 264 Type: timodel.ActionDropTable, 265 BinlogInfo: &timodel.HistoryInfo{SchemaVersion: 6, FinishedTS: 128}, 266 Query: "drop table " + tbName.O, 267 } 268 preTableInfo, err = snap.PreTableInfo(job) 269 c.Assert(err, check.IsNil) 270 c.Assert(preTableInfo.TableName, check.Equals, model.TableName{Schema: "Test", Table: "T"}) 271 c.Assert(preTableInfo.ID, check.Equals, int64(9)) 272 273 err = snap.handleDDL(job) 274 c.Assert(err, check.IsNil) 275 276 _, ok = snap.TableByID(tblInfo.ID) 277 c.Assert(ok, check.IsFalse) 278 279 // test ineligible tables 280 c.Assert(snap.IsIneligibleTableID(9), check.IsFalse) 281 282 // drop schema 283 err = snap.dropSchema(3) 284 c.Assert(err, check.IsNil) 285 } 286 287 func (t *schemaSuite) TestHandleDDL(c *check.C) { 288 defer testleak.AfterTest(c)() 289 290 snap := newEmptySchemaSnapshot(false) 291 dbName := timodel.NewCIStr("Test") 292 colName := timodel.NewCIStr("A") 293 tbName := timodel.NewCIStr("T") 294 newTbName := timodel.NewCIStr("RT") 295 296 // db info 297 dbInfo := &timodel.DBInfo{ 298 ID: 2, 299 Name: dbName, 300 State: timodel.StatePublic, 301 } 302 // table Info 303 tblInfo := &timodel.TableInfo{ 304 ID: 6, 305 Name: tbName, 306 State: timodel.StatePublic, 307 } 308 // column info 309 colInfo := &timodel.ColumnInfo{ 310 ID: 8, 311 Name: colName, 312 Offset: 0, 313 FieldType: *types.NewFieldType(mysql.TypeLonglong), 314 State: timodel.StatePublic, 315 } 316 tblInfo.Columns = []*timodel.ColumnInfo{colInfo} 317 318 testCases := []struct { 319 name string 320 jobID int64 321 schemaID int64 322 tableID int64 323 jobType timodel.ActionType 324 binlogInfo *timodel.HistoryInfo 325 query string 326 resultQuery string 327 schemaName string 328 tableName string 329 }{ 330 {name: "createSchema", jobID: 3, schemaID: 2, tableID: 0, jobType: timodel.ActionCreateSchema, binlogInfo: &timodel.HistoryInfo{SchemaVersion: 1, DBInfo: dbInfo, TableInfo: nil, FinishedTS: 123}, query: "create database Test", resultQuery: "create database Test", schemaName: dbInfo.Name.O, tableName: ""}, 331 {name: "updateSchema", jobID: 4, schemaID: 2, tableID: 0, jobType: timodel.ActionModifySchemaCharsetAndCollate, binlogInfo: &timodel.HistoryInfo{SchemaVersion: 8, DBInfo: dbInfo, TableInfo: nil, FinishedTS: 123}, query: "ALTER DATABASE Test CHARACTER SET utf8mb4;", resultQuery: "ALTER DATABASE Test CHARACTER SET utf8mb4;", schemaName: dbInfo.Name.O}, 332 {name: "createTable", jobID: 7, schemaID: 2, tableID: 6, jobType: timodel.ActionCreateTable, binlogInfo: &timodel.HistoryInfo{SchemaVersion: 3, DBInfo: nil, TableInfo: tblInfo, FinishedTS: 123}, query: "create table T(id int);", resultQuery: "create table T(id int);", schemaName: dbInfo.Name.O, tableName: tblInfo.Name.O}, 333 {name: "addColumn", jobID: 9, schemaID: 2, tableID: 6, jobType: timodel.ActionAddColumn, binlogInfo: &timodel.HistoryInfo{SchemaVersion: 4, DBInfo: nil, TableInfo: tblInfo, FinishedTS: 123}, query: "alter table T add a varchar(45);", resultQuery: "alter table T add a varchar(45);", schemaName: dbInfo.Name.O, tableName: tblInfo.Name.O}, 334 {name: "truncateTable", jobID: 10, schemaID: 2, tableID: 6, jobType: timodel.ActionTruncateTable, binlogInfo: &timodel.HistoryInfo{SchemaVersion: 5, DBInfo: nil, TableInfo: tblInfo, FinishedTS: 123}, query: "truncate table T;", resultQuery: "truncate table T;", schemaName: dbInfo.Name.O, tableName: tblInfo.Name.O}, 335 {name: "renameTable", jobID: 11, schemaID: 2, tableID: 10, jobType: timodel.ActionRenameTable, binlogInfo: &timodel.HistoryInfo{SchemaVersion: 6, DBInfo: nil, TableInfo: tblInfo, FinishedTS: 123}, query: "rename table T to RT;", resultQuery: "rename table T to RT;", schemaName: dbInfo.Name.O, tableName: newTbName.O}, 336 {name: "dropTable", jobID: 12, schemaID: 2, tableID: 12, jobType: timodel.ActionDropTable, binlogInfo: &timodel.HistoryInfo{SchemaVersion: 7, DBInfo: nil, TableInfo: nil, FinishedTS: 123}, query: "drop table RT;", resultQuery: "drop table RT;", schemaName: dbInfo.Name.O, tableName: newTbName.O}, 337 {name: "dropSchema", jobID: 13, schemaID: 2, tableID: 0, jobType: timodel.ActionDropSchema, binlogInfo: &timodel.HistoryInfo{SchemaVersion: 8, DBInfo: dbInfo, TableInfo: nil, FinishedTS: 123}, query: "drop database test;", resultQuery: "drop database test;", schemaName: dbInfo.Name.O, tableName: ""}, 338 } 339 340 for _, testCase := range testCases { 341 // prepare for ddl 342 switch testCase.name { 343 case "addColumn": 344 tblInfo.Columns = []*timodel.ColumnInfo{colInfo} 345 case "truncateTable": 346 tblInfo.ID = 10 347 case "renameTable": 348 tblInfo.ID = 12 349 tblInfo.Name = newTbName 350 } 351 352 job := &timodel.Job{ 353 ID: testCase.jobID, 354 State: timodel.JobStateDone, 355 SchemaID: testCase.schemaID, 356 TableID: testCase.tableID, 357 Type: testCase.jobType, 358 BinlogInfo: testCase.binlogInfo, 359 Query: testCase.query, 360 } 361 testDoDDLAndCheck(c, snap, job, false) 362 363 // custom check after ddl 364 switch testCase.name { 365 case "createSchema": 366 _, ok := snap.SchemaByID(dbInfo.ID) 367 c.Assert(ok, check.IsTrue) 368 case "createTable": 369 _, ok := snap.TableByID(tblInfo.ID) 370 c.Assert(ok, check.IsTrue) 371 case "renameTable": 372 tb, ok := snap.TableByID(tblInfo.ID) 373 c.Assert(ok, check.IsTrue) 374 c.Assert(tblInfo.Name, check.Equals, tb.Name) 375 case "addColumn", "truncateTable": 376 tb, ok := snap.TableByID(tblInfo.ID) 377 c.Assert(ok, check.IsTrue) 378 c.Assert(tb.Columns, check.HasLen, 1) 379 case "dropTable": 380 _, ok := snap.TableByID(tblInfo.ID) 381 c.Assert(ok, check.IsFalse) 382 case "dropSchema": 383 _, ok := snap.SchemaByID(job.SchemaID) 384 c.Assert(ok, check.IsFalse) 385 } 386 } 387 } 388 389 func testDoDDLAndCheck(c *check.C, snap *schemaSnapshot, job *timodel.Job, isErr bool) { 390 err := snap.handleDDL(job) 391 c.Assert(err != nil, check.Equals, isErr) 392 } 393 394 type getUniqueKeysSuite struct{} 395 396 var _ = check.Suite(&getUniqueKeysSuite{}) 397 398 func (s *getUniqueKeysSuite) TestPKShouldBeInTheFirstPlaceWhenPKIsNotHandle(c *check.C) { 399 defer testleak.AfterTest(c)() 400 t := timodel.TableInfo{ 401 Columns: []*timodel.ColumnInfo{ 402 { 403 Name: timodel.CIStr{O: "name"}, 404 FieldType: types.FieldType{ 405 Flag: mysql.NotNullFlag, 406 }, 407 }, 408 {Name: timodel.CIStr{O: "id"}}, 409 }, 410 Indices: []*timodel.IndexInfo{ 411 { 412 Name: timodel.CIStr{ 413 O: "name", 414 }, 415 Columns: []*timodel.IndexColumn{ 416 { 417 Name: timodel.CIStr{O: "name"}, 418 Offset: 0, 419 }, 420 }, 421 Unique: true, 422 }, 423 { 424 Name: timodel.CIStr{ 425 O: "PRIMARY", 426 }, 427 Columns: []*timodel.IndexColumn{ 428 { 429 Name: timodel.CIStr{O: "id"}, 430 Offset: 1, 431 }, 432 }, 433 Primary: true, 434 }, 435 }, 436 PKIsHandle: false, 437 } 438 info := model.WrapTableInfo(1, "", 0, &t) 439 cols := info.GetUniqueKeys() 440 c.Assert(cols, check.DeepEquals, [][]string{ 441 {"id"}, {"name"}, 442 }) 443 } 444 445 func (s *getUniqueKeysSuite) TestPKShouldBeInTheFirstPlaceWhenPKIsHandle(c *check.C) { 446 defer testleak.AfterTest(c)() 447 t := timodel.TableInfo{ 448 Indices: []*timodel.IndexInfo{ 449 { 450 Name: timodel.CIStr{ 451 O: "uniq_job", 452 }, 453 Columns: []*timodel.IndexColumn{ 454 {Name: timodel.CIStr{O: "job"}}, 455 }, 456 Unique: true, 457 }, 458 }, 459 Columns: []*timodel.ColumnInfo{ 460 { 461 Name: timodel.CIStr{ 462 O: "job", 463 }, 464 FieldType: types.FieldType{ 465 Flag: mysql.NotNullFlag, 466 }, 467 }, 468 { 469 Name: timodel.CIStr{ 470 O: "uid", 471 }, 472 FieldType: types.FieldType{ 473 Flag: mysql.PriKeyFlag, 474 }, 475 }, 476 }, 477 PKIsHandle: true, 478 } 479 info := model.WrapTableInfo(1, "", 0, &t) 480 cols := info.GetUniqueKeys() 481 c.Assert(cols, check.DeepEquals, [][]string{ 482 {"uid"}, {"job"}, 483 }) 484 } 485 486 func (t *schemaSuite) TestMultiVersionStorage(c *check.C) { 487 defer testleak.AfterTest(c)() 488 ctx, cancel := context.WithCancel(context.Background()) 489 dbName := timodel.NewCIStr("Test") 490 tbName := timodel.NewCIStr("T1") 491 // db and ignoreDB info 492 dbInfo := &timodel.DBInfo{ 493 ID: 1, 494 Name: dbName, 495 State: timodel.StatePublic, 496 } 497 var jobs []*timodel.Job 498 // `createSchema` job1 499 job := &timodel.Job{ 500 ID: 3, 501 State: timodel.JobStateSynced, 502 SchemaID: 1, 503 Type: timodel.ActionCreateSchema, 504 BinlogInfo: &timodel.HistoryInfo{SchemaVersion: 1, DBInfo: dbInfo, FinishedTS: 100}, 505 Query: "create database test", 506 } 507 jobs = append(jobs, job) 508 509 // table info 510 tblInfo := &timodel.TableInfo{ 511 ID: 2, 512 Name: tbName, 513 State: timodel.StatePublic, 514 } 515 516 // `createTable` job 517 job = &timodel.Job{ 518 ID: 6, 519 State: timodel.JobStateSynced, 520 SchemaID: 1, 521 TableID: 2, 522 Type: timodel.ActionCreateTable, 523 BinlogInfo: &timodel.HistoryInfo{SchemaVersion: 2, TableInfo: tblInfo, FinishedTS: 110}, 524 Query: "create table " + tbName.O, 525 } 526 527 jobs = append(jobs, job) 528 529 tbName = timodel.NewCIStr("T2") 530 // table info 531 tblInfo = &timodel.TableInfo{ 532 ID: 3, 533 Name: tbName, 534 State: timodel.StatePublic, 535 } 536 // `createTable` job 537 job = &timodel.Job{ 538 ID: 6, 539 State: timodel.JobStateSynced, 540 SchemaID: 1, 541 TableID: 3, 542 Type: timodel.ActionCreateTable, 543 BinlogInfo: &timodel.HistoryInfo{SchemaVersion: 2, TableInfo: tblInfo, FinishedTS: 120}, 544 Query: "create table " + tbName.O, 545 } 546 547 jobs = append(jobs, job) 548 storage, err := NewSchemaStorage(nil, 0, nil, false) 549 c.Assert(err, check.IsNil) 550 for _, job := range jobs { 551 err := storage.HandleDDLJob(job) 552 c.Assert(err, check.IsNil) 553 } 554 555 // `dropTable` job 556 job = &timodel.Job{ 557 ID: 6, 558 State: timodel.JobStateSynced, 559 SchemaID: 1, 560 TableID: 2, 561 Type: timodel.ActionDropTable, 562 BinlogInfo: &timodel.HistoryInfo{FinishedTS: 130}, 563 } 564 565 err = storage.HandleDDLJob(job) 566 c.Assert(err, check.IsNil) 567 568 // `dropSchema` job 569 job = &timodel.Job{ 570 ID: 6, 571 State: timodel.JobStateSynced, 572 SchemaID: 1, 573 Type: timodel.ActionDropSchema, 574 BinlogInfo: &timodel.HistoryInfo{FinishedTS: 140, DBInfo: dbInfo}, 575 } 576 577 err = storage.HandleDDLJob(job) 578 c.Assert(err, check.IsNil) 579 580 c.Assert(storage.(*schemaStorageImpl).resolvedTs, check.Equals, uint64(140)) 581 snap, err := storage.GetSnapshot(ctx, 100) 582 c.Assert(err, check.IsNil) 583 _, exist := snap.SchemaByID(1) 584 c.Assert(exist, check.IsTrue) 585 _, exist = snap.TableByID(2) 586 c.Assert(exist, check.IsFalse) 587 _, exist = snap.TableByID(3) 588 c.Assert(exist, check.IsFalse) 589 590 snap, err = storage.GetSnapshot(ctx, 115) 591 c.Assert(err, check.IsNil) 592 _, exist = snap.SchemaByID(1) 593 c.Assert(exist, check.IsTrue) 594 _, exist = snap.TableByID(2) 595 c.Assert(exist, check.IsTrue) 596 _, exist = snap.TableByID(3) 597 c.Assert(exist, check.IsFalse) 598 599 snap, err = storage.GetSnapshot(ctx, 125) 600 c.Assert(err, check.IsNil) 601 _, exist = snap.SchemaByID(1) 602 c.Assert(exist, check.IsTrue) 603 _, exist = snap.TableByID(2) 604 c.Assert(exist, check.IsTrue) 605 _, exist = snap.TableByID(3) 606 c.Assert(exist, check.IsTrue) 607 608 snap, err = storage.GetSnapshot(ctx, 135) 609 c.Assert(err, check.IsNil) 610 _, exist = snap.SchemaByID(1) 611 c.Assert(exist, check.IsTrue) 612 _, exist = snap.TableByID(2) 613 c.Assert(exist, check.IsFalse) 614 _, exist = snap.TableByID(3) 615 c.Assert(exist, check.IsTrue) 616 617 snap, err = storage.GetSnapshot(ctx, 140) 618 c.Assert(err, check.IsNil) 619 _, exist = snap.SchemaByID(1) 620 c.Assert(exist, check.IsFalse) 621 _, exist = snap.TableByID(2) 622 c.Assert(exist, check.IsFalse) 623 _, exist = snap.TableByID(3) 624 c.Assert(exist, check.IsFalse) 625 626 storage.DoGC(0) 627 snap, err = storage.GetSnapshot(ctx, 100) 628 c.Assert(err, check.IsNil) 629 _, exist = snap.SchemaByID(1) 630 c.Assert(exist, check.IsTrue) 631 _, exist = snap.TableByID(2) 632 c.Assert(exist, check.IsFalse) 633 _, exist = snap.TableByID(3) 634 c.Assert(exist, check.IsFalse) 635 storage.DoGC(115) 636 _, err = storage.GetSnapshot(ctx, 100) 637 c.Assert(err, check.NotNil) 638 snap, err = storage.GetSnapshot(ctx, 115) 639 c.Assert(err, check.IsNil) 640 _, exist = snap.SchemaByID(1) 641 c.Assert(exist, check.IsTrue) 642 _, exist = snap.TableByID(2) 643 c.Assert(exist, check.IsTrue) 644 _, exist = snap.TableByID(3) 645 c.Assert(exist, check.IsFalse) 646 647 storage.DoGC(155) 648 storage.AdvanceResolvedTs(185) 649 650 snap, err = storage.GetSnapshot(ctx, 180) 651 c.Assert(err, check.IsNil) 652 _, exist = snap.SchemaByID(1) 653 c.Assert(exist, check.IsFalse) 654 _, exist = snap.TableByID(2) 655 c.Assert(exist, check.IsFalse) 656 _, exist = snap.TableByID(3) 657 c.Assert(exist, check.IsFalse) 658 _, err = storage.GetSnapshot(ctx, 130) 659 c.Assert(err, check.NotNil) 660 661 cancel() 662 _, err = storage.GetSnapshot(ctx, 200) 663 c.Assert(errors.Cause(err), check.Equals, context.Canceled) 664 } 665 666 func (t *schemaSuite) TestCreateSnapFromMeta(c *check.C) { 667 defer testleak.AfterTest(c)() 668 store, err := mockstore.NewMockStore() 669 c.Assert(err, check.IsNil) 670 defer store.Close() //nolint:errcheck 671 672 session.SetSchemaLease(0) 673 session.DisableStats4Test() 674 domain, err := session.BootstrapSession(store) 675 c.Assert(err, check.IsNil) 676 defer domain.Close() 677 domain.SetStatsUpdating(true) 678 tk := testkit.NewTestKit(c, store) 679 tk.MustExec("create database test2") 680 tk.MustExec("create table test.simple_test1 (id bigint primary key)") 681 tk.MustExec("create table test.simple_test2 (id bigint primary key)") 682 tk.MustExec("create table test2.simple_test3 (id bigint primary key)") 683 tk.MustExec("create table test2.simple_test4 (id bigint primary key)") 684 tk.MustExec("create table test2.simple_test5 (a bigint)") 685 ver, err := store.CurrentVersion(oracle.GlobalTxnScope) 686 c.Assert(err, check.IsNil) 687 meta, err := kv.GetSnapshotMeta(store, ver.Ver) 688 c.Assert(err, check.IsNil) 689 snap, err := newSchemaSnapshotFromMeta(meta, ver.Ver, false) 690 c.Assert(err, check.IsNil) 691 _, ok := snap.GetTableByName("test", "simple_test1") 692 c.Assert(ok, check.IsTrue) 693 tableID, ok := snap.GetTableIDByName("test2", "simple_test5") 694 c.Assert(ok, check.IsTrue) 695 c.Assert(snap.IsIneligibleTableID(tableID), check.IsTrue) 696 dbInfo, ok := snap.SchemaByTableID(tableID) 697 c.Assert(ok, check.IsTrue) 698 c.Assert(dbInfo.Name.O, check.Equals, "test2") 699 c.Assert(len(snap.tableInSchema), check.Equals, 3) 700 } 701 702 func (t *schemaSuite) TestSnapshotClone(c *check.C) { 703 defer testleak.AfterTest(c)() 704 store, err := mockstore.NewMockStore() 705 c.Assert(err, check.IsNil) 706 defer store.Close() //nolint:errcheck 707 708 session.SetSchemaLease(0) 709 session.DisableStats4Test() 710 domain, err := session.BootstrapSession(store) 711 c.Assert(err, check.IsNil) 712 defer domain.Close() 713 domain.SetStatsUpdating(true) 714 tk := testkit.NewTestKit(c, store) 715 tk.MustExec("create database test2") 716 tk.MustExec("create table test.simple_test1 (id bigint primary key)") 717 tk.MustExec("create table test.simple_test2 (id bigint primary key)") 718 tk.MustExec("create table test2.simple_test3 (id bigint primary key)") 719 tk.MustExec("create table test2.simple_test4 (id bigint primary key)") 720 tk.MustExec("create table test2.simple_test5 (a bigint)") 721 ver, err := store.CurrentVersion(oracle.GlobalTxnScope) 722 c.Assert(err, check.IsNil) 723 meta, err := kv.GetSnapshotMeta(store, ver.Ver) 724 c.Assert(err, check.IsNil) 725 snap, err := newSchemaSnapshotFromMeta(meta, ver.Ver, false /* explicitTables */) 726 c.Assert(err, check.IsNil) 727 728 clone := snap.Clone() 729 c.Assert(clone.tableNameToID, check.DeepEquals, snap.tableNameToID) 730 c.Assert(clone.schemaNameToID, check.DeepEquals, snap.schemaNameToID) 731 c.Assert(clone.truncateTableID, check.DeepEquals, snap.truncateTableID) 732 c.Assert(clone.ineligibleTableID, check.DeepEquals, snap.ineligibleTableID) 733 c.Assert(clone.currentTs, check.Equals, snap.currentTs) 734 c.Assert(clone.explicitTables, check.Equals, snap.explicitTables) 735 c.Assert(len(clone.tables), check.Equals, len(snap.tables)) 736 c.Assert(len(clone.schemas), check.Equals, len(snap.schemas)) 737 c.Assert(len(clone.partitionTable), check.Equals, len(snap.partitionTable)) 738 739 tableCount := len(snap.tables) 740 clone.tables = make(map[int64]*model.TableInfo) 741 c.Assert(len(snap.tables), check.Equals, tableCount) 742 } 743 744 func (t *schemaSuite) TestExplicitTables(c *check.C) { 745 defer testleak.AfterTest(c)() 746 store, err := mockstore.NewMockStore() 747 c.Assert(err, check.IsNil) 748 defer store.Close() //nolint:errcheck 749 750 session.SetSchemaLease(0) 751 session.DisableStats4Test() 752 domain, err := session.BootstrapSession(store) 753 c.Assert(err, check.IsNil) 754 defer domain.Close() 755 domain.SetStatsUpdating(true) 756 tk := testkit.NewTestKit(c, store) 757 ver1, err := store.CurrentVersion(oracle.GlobalTxnScope) 758 c.Assert(err, check.IsNil) 759 tk.MustExec("create database test2") 760 tk.MustExec("create table test.simple_test1 (id bigint primary key)") 761 tk.MustExec("create table test.simple_test2 (id bigint unique key)") 762 tk.MustExec("create table test2.simple_test3 (a bigint)") 763 tk.MustExec("create table test2.simple_test4 (a varchar(20) unique key)") 764 tk.MustExec("create table test2.simple_test5 (a varchar(20))") 765 ver2, err := store.CurrentVersion(oracle.GlobalTxnScope) 766 c.Assert(err, check.IsNil) 767 meta1, err := kv.GetSnapshotMeta(store, ver1.Ver) 768 c.Assert(err, check.IsNil) 769 snap1, err := newSchemaSnapshotFromMeta(meta1, ver1.Ver, true /* explicitTables */) 770 c.Assert(err, check.IsNil) 771 meta2, err := kv.GetSnapshotMeta(store, ver2.Ver) 772 c.Assert(err, check.IsNil) 773 snap2, err := newSchemaSnapshotFromMeta(meta2, ver2.Ver, false /* explicitTables */) 774 c.Assert(err, check.IsNil) 775 snap3, err := newSchemaSnapshotFromMeta(meta2, ver2.Ver, true /* explicitTables */) 776 c.Assert(err, check.IsNil) 777 778 c.Assert(len(snap2.tables)-len(snap1.tables), check.Equals, 5) 779 // some system tables are also ineligible 780 c.Assert(len(snap2.ineligibleTableID), check.GreaterEqual, 4) 781 782 c.Assert(len(snap3.tables)-len(snap1.tables), check.Equals, 5) 783 c.Assert(snap3.ineligibleTableID, check.HasLen, 0) 784 } 785 786 /* 787 TODO: Untested Action: 788 789 ActionAddForeignKey ActionType = 9 790 ActionDropForeignKey ActionType = 10 791 ActionRebaseAutoID ActionType = 13 792 ActionShardRowID ActionType = 16 793 ActionLockTable ActionType = 27 794 ActionUnlockTable ActionType = 28 795 ActionRepairTable ActionType = 29 796 ActionSetTiFlashReplica ActionType = 30 797 ActionUpdateTiFlashReplicaStatus ActionType = 31 798 ActionCreateSequence ActionType = 34 799 ActionAlterSequence ActionType = 35 800 ActionDropSequence ActionType = 36 801 ActionModifyTableAutoIdCache ActionType = 39 802 ActionRebaseAutoRandomBase ActionType = 40 803 ActionExchangeTablePartition ActionType = 42 804 ActionAddCheckConstraint ActionType = 43 805 ActionDropCheckConstraint ActionType = 44 806 ActionAlterCheckConstraint ActionType = 45 807 ActionAlterTableAlterPartition ActionType = 46 808 809 ... Any Action which of value is greater than 46 ... 810 */ 811 func (t *schemaSuite) TestSchemaStorage(c *check.C) { 812 defer testleak.AfterTest(c)() 813 ctx := context.Background() 814 testCases := [][]string{{ 815 "create database test_ddl1", // ActionCreateSchema 816 "create table test_ddl1.simple_test1 (id bigint primary key)", // ActionCreateTable 817 "create table test_ddl1.simple_test2 (id bigint)", // ActionCreateTable 818 "create table test_ddl1.simple_test3 (id bigint primary key)", // ActionCreateTable 819 "create table test_ddl1.simple_test4 (id bigint primary key)", // ActionCreateTable 820 "DROP TABLE test_ddl1.simple_test3", // ActionDropTable 821 "ALTER TABLE test_ddl1.simple_test1 ADD COLUMN c1 INT NOT NULL", // ActionAddColumn 822 "ALTER TABLE test_ddl1.simple_test1 ADD c2 INT NOT NULL AFTER id", // ActionAddColumn 823 "ALTER TABLE test_ddl1.simple_test1 ADD c3 INT NOT NULL, ADD c4 INT NOT NULL", // ActionAddColumns 824 "ALTER TABLE test_ddl1.simple_test1 DROP c1", // ActionDropColumn 825 "ALTER TABLE test_ddl1.simple_test1 DROP c2, DROP c3", // ActionDropColumns 826 "ALTER TABLE test_ddl1.simple_test1 ADD INDEX (c4)", // ActionAddIndex 827 "ALTER TABLE test_ddl1.simple_test1 DROP INDEX c4", // ActionDropIndex 828 "TRUNCATE test_ddl1.simple_test1", // ActionTruncateTable 829 "ALTER DATABASE test_ddl1 CHARACTER SET = binary COLLATE binary", // ActionModifySchemaCharsetAndCollate 830 "ALTER TABLE test_ddl1.simple_test2 ADD c1 INT NOT NULL, ADD c2 INT NOT NULL", // ActionAddColumns 831 "ALTER TABLE test_ddl1.simple_test2 ADD INDEX (c1)", // ActionAddIndex 832 "ALTER TABLE test_ddl1.simple_test2 ALTER INDEX c1 INVISIBLE", // ActionAlterIndexVisibility 833 "ALTER TABLE test_ddl1.simple_test2 RENAME INDEX c1 TO idx_c1", // ActionRenameIndex 834 "ALTER TABLE test_ddl1.simple_test2 MODIFY c2 BIGINT", // ActionModifyColumn 835 "CREATE VIEW test_ddl1.view_test2 AS SELECT * FROM test_ddl1.simple_test2 WHERE id > 2", // ActionCreateView 836 "DROP VIEW test_ddl1.view_test2", // ActionDropView 837 "RENAME TABLE test_ddl1.simple_test2 TO test_ddl1.simple_test5", // ActionRenameTable 838 "DROP DATABASE test_ddl1", // ActionDropSchema 839 "create database test_ddl2", // ActionCreateSchema 840 "create table test_ddl2.simple_test1 (id bigint primary key, c1 int not null unique key)", // ActionCreateTable 841 `CREATE TABLE test_ddl2.employees ( 842 id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 843 fname VARCHAR(25) NOT NULL, 844 lname VARCHAR(25) NOT NULL, 845 store_id INT NOT NULL, 846 department_id INT NOT NULL 847 ) 848 849 PARTITION BY RANGE(id) ( 850 PARTITION p0 VALUES LESS THAN (5), 851 PARTITION p1 VALUES LESS THAN (10), 852 PARTITION p2 VALUES LESS THAN (15), 853 PARTITION p3 VALUES LESS THAN (20) 854 )`, // ActionCreateTable 855 "ALTER TABLE test_ddl2.employees DROP PARTITION p2", // ActionDropTablePartition 856 "ALTER TABLE test_ddl2.employees ADD PARTITION (PARTITION p4 VALUES LESS THAN (25))", // ActionAddTablePartition 857 "ALTER TABLE test_ddl2.employees TRUNCATE PARTITION p3", // ActionTruncateTablePartition 858 "alter table test_ddl2.employees comment='modify comment'", // ActionModifyTableComment 859 "alter table test_ddl2.simple_test1 drop primary key", // ActionDropPrimaryKey 860 "alter table test_ddl2.simple_test1 add primary key pk(id)", // ActionAddPrimaryKey 861 "ALTER TABLE test_ddl2.simple_test1 ALTER id SET DEFAULT 18", // ActionSetDefaultValue 862 "ALTER TABLE test_ddl2.simple_test1 CHARACTER SET = utf8mb4", // ActionModifyTableCharsetAndCollate 863 // "recover table test_ddl2.employees", // ActionRecoverTable this ddl can't work on mock tikv 864 865 "DROP TABLE test_ddl2.employees", 866 `CREATE TABLE test_ddl2.employees2 ( 867 id INT NOT NULL, 868 fname VARCHAR(25) NOT NULL, 869 lname VARCHAR(25) NOT NULL, 870 store_id INT NOT NULL, 871 department_id INT NOT NULL 872 ) 873 874 PARTITION BY RANGE(id) ( 875 PARTITION p0 VALUES LESS THAN (5), 876 PARTITION p1 VALUES LESS THAN (10), 877 PARTITION p2 VALUES LESS THAN (15), 878 PARTITION p3 VALUES LESS THAN (20) 879 )`, 880 "ALTER TABLE test_ddl2.employees2 CHARACTER SET = utf8mb4", 881 "DROP DATABASE test_ddl2", 882 }} 883 884 testOneGroup := func(tc []string) { 885 store, err := mockstore.NewMockStore() 886 c.Assert(err, check.IsNil) 887 defer store.Close() //nolint:errcheck 888 ticonfig.UpdateGlobal(func(conf *ticonfig.Config) { 889 conf.AlterPrimaryKey = true 890 }) 891 session.SetSchemaLease(0) 892 session.DisableStats4Test() 893 domain, err := session.BootstrapSession(store) 894 c.Assert(err, check.IsNil) 895 defer domain.Close() 896 domain.SetStatsUpdating(true) 897 tk := testkit.NewTestKit(c, store) 898 899 for _, ddlSQL := range tc { 900 tk.MustExec(ddlSQL) 901 } 902 903 jobs, err := getAllHistoryDDLJob(store) 904 c.Assert(err, check.IsNil) 905 scheamStorage, err := NewSchemaStorage(nil, 0, nil, false) 906 c.Assert(err, check.IsNil) 907 for _, job := range jobs { 908 err := scheamStorage.HandleDDLJob(job) 909 c.Assert(err, check.IsNil) 910 } 911 912 for _, job := range jobs { 913 ts := job.BinlogInfo.FinishedTS 914 meta, err := kv.GetSnapshotMeta(store, ts) 915 c.Assert(err, check.IsNil) 916 snapFromMeta, err := newSchemaSnapshotFromMeta(meta, ts, false) 917 c.Assert(err, check.IsNil) 918 snapFromSchemaStore, err := scheamStorage.GetSnapshot(ctx, ts) 919 c.Assert(err, check.IsNil) 920 921 tidySchemaSnapshot(snapFromMeta) 922 tidySchemaSnapshot(snapFromSchemaStore) 923 c.Assert(snapFromMeta, check.DeepEquals, snapFromSchemaStore, 924 check.Commentf("%s", cmp.Diff(snapFromMeta, snapFromSchemaStore, cmp.AllowUnexported(schemaSnapshot{}, model.TableInfo{})))) 925 } 926 } 927 928 for _, tc := range testCases { 929 testOneGroup(tc) 930 } 931 } 932 933 func tidySchemaSnapshot(snap *schemaSnapshot) { 934 for _, dbInfo := range snap.schemas { 935 if len(dbInfo.Tables) == 0 { 936 dbInfo.Tables = nil 937 } 938 } 939 for _, tableInfo := range snap.tables { 940 tableInfo.TableInfoVersion = 0 941 if len(tableInfo.Columns) == 0 { 942 tableInfo.Columns = nil 943 } 944 if len(tableInfo.Indices) == 0 { 945 tableInfo.Indices = nil 946 } 947 if len(tableInfo.ForeignKeys) == 0 { 948 tableInfo.ForeignKeys = nil 949 } 950 } 951 // the snapshot from meta doesn't know which ineligible tables that have existed in history 952 // so we delete the ineligible tables which are already not exist 953 for tableID := range snap.ineligibleTableID { 954 if _, ok := snap.tables[tableID]; !ok { 955 delete(snap.ineligibleTableID, tableID) 956 } 957 } 958 // the snapshot from meta doesn't know which tables are truncated, so we just ignore it 959 snap.truncateTableID = nil 960 for _, v := range snap.tableInSchema { 961 sort.Slice(v, func(i, j int) bool { return v[i] < v[j] }) 962 } 963 } 964 965 func getAllHistoryDDLJob(storage tidbkv.Storage) ([]*timodel.Job, error) { 966 s, err := session.CreateSession(storage) 967 if err != nil { 968 return nil, errors.Trace(err) 969 } 970 971 if s != nil { 972 defer s.Close() 973 } 974 975 store := domain.GetDomain(s.(sessionctx.Context)).Store() 976 txn, err := store.Begin() 977 if err != nil { 978 return nil, errors.Trace(err) 979 } 980 defer txn.Rollback() //nolint:errcheck 981 txnMeta := timeta.NewMeta(txn) 982 983 jobs, err := txnMeta.GetAllHistoryDDLJobs() 984 if err != nil { 985 return nil, errors.Trace(err) 986 } 987 return jobs, nil 988 }