github.com/pingcap/tidb-lightning@v5.0.0-rc.0.20210428090220-84b649866577+incompatible/lightning/mydump/loader_test.go (about) 1 // Copyright 2019 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 mydump_test 15 16 import ( 17 "context" 18 "io/ioutil" 19 "os" 20 "path/filepath" 21 "testing" 22 23 . "github.com/pingcap/check" 24 filter "github.com/pingcap/tidb-tools/pkg/table-filter" 25 router "github.com/pingcap/tidb-tools/pkg/table-router" 26 27 "github.com/pingcap/tidb-lightning/lightning/config" 28 md "github.com/pingcap/tidb-lightning/lightning/mydump" 29 ) 30 31 var _ = Suite(&testMydumpLoaderSuite{}) 32 33 func TestMydumps(t *testing.T) { 34 TestingT(t) 35 } 36 37 type testMydumpLoaderSuite struct { 38 cfg *config.Config 39 sourceDir string 40 } 41 42 func (s *testMydumpLoaderSuite) SetUpSuite(c *C) {} 43 func (s *testMydumpLoaderSuite) TearDownSuite(c *C) {} 44 45 func newConfigWithSourceDir(sourceDir string) *config.Config { 46 path, _ := filepath.Abs(sourceDir) 47 return &config.Config{ 48 Mydumper: config.MydumperRuntime{ 49 SourceDir: "file://" + filepath.ToSlash(path), 50 Filter: []string{"*.*"}, 51 DefaultFileRules: true, 52 }, 53 } 54 } 55 56 func (s *testMydumpLoaderSuite) SetUpTest(c *C) { 57 s.sourceDir = c.MkDir() 58 s.cfg = newConfigWithSourceDir(s.sourceDir) 59 } 60 61 func (s *testMydumpLoaderSuite) touch(c *C, filename ...string) string { 62 components := make([]string, len(filename)+1) 63 components = append(components, s.sourceDir) 64 components = append(components, filename...) 65 path := filepath.Join(components...) 66 err := ioutil.WriteFile(path, nil, 0644) 67 c.Assert(err, IsNil) 68 return path 69 } 70 71 func (s *testMydumpLoaderSuite) mkdir(c *C, dirname string) { 72 path := filepath.Join(s.sourceDir, dirname) 73 err := os.Mkdir(path, 0755) 74 c.Assert(err, IsNil) 75 } 76 77 func (s *testMydumpLoaderSuite) TestLoader(c *C) { 78 ctx := context.Background() 79 cfg := newConfigWithSourceDir("./not-exists") 80 _, err := md.NewMyDumpLoader(ctx, cfg) 81 c.Assert(err, NotNil) 82 83 cfg = newConfigWithSourceDir("./examples") 84 mdl, err := md.NewMyDumpLoader(ctx, cfg) 85 c.Assert(err, IsNil) 86 87 dbMetas := mdl.GetDatabases() 88 c.Assert(len(dbMetas), Equals, 1) 89 dbMeta := dbMetas[0] 90 c.Assert(dbMeta.Name, Equals, "mocker_test") 91 c.Assert(len(dbMeta.Tables), Equals, 4) 92 93 expected := []struct { 94 name string 95 dataFiles int 96 }{ 97 {name: "i", dataFiles: 1}, 98 {name: "report_case_high_risk", dataFiles: 1}, 99 {name: "tbl_multi_index", dataFiles: 1}, 100 {name: "tbl_autoid", dataFiles: 1}, 101 } 102 103 for i, table := range expected { 104 c.Assert(dbMeta.Tables[i].Name, Equals, table.name) 105 c.Assert(len(dbMeta.Tables[i].DataFiles), Equals, table.dataFiles) 106 } 107 } 108 109 func (s *testMydumpLoaderSuite) TestEmptyDB(c *C) { 110 _, err := md.NewMyDumpLoader(context.Background(), s.cfg) 111 c.Assert(err, ErrorMatches, "no schema create sql files found. Please either set `mydumper.no-schema` to true or add schema sql file for each database.") 112 } 113 114 func (s *testMydumpLoaderSuite) TestDuplicatedDB(c *C) { 115 /* 116 Path/ 117 a/ 118 db-schema-create.sql 119 b/ 120 db-schema-create.sql 121 */ 122 s.mkdir(c, "a") 123 s.touch(c, "a", "db-schema-create.sql") 124 s.mkdir(c, "b") 125 s.touch(c, "b", "db-schema-create.sql") 126 127 _, err := md.NewMyDumpLoader(context.Background(), s.cfg) 128 c.Assert(err, ErrorMatches, `invalid database schema file, duplicated item - .*[/\\]db-schema-create\.sql`) 129 } 130 131 func (s *testMydumpLoaderSuite) TestTableNoHostDB(c *C) { 132 /* 133 Path/ 134 notdb-schema-create.sql 135 db.tbl-schema.sql 136 */ 137 138 dir := s.sourceDir 139 err := ioutil.WriteFile(filepath.Join(dir, "notdb-schema-create.sql"), nil, 0644) 140 c.Assert(err, IsNil) 141 err = ioutil.WriteFile(filepath.Join(dir, "db.tbl-schema.sql"), nil, 0644) 142 c.Assert(err, IsNil) 143 144 _, err = md.NewMyDumpLoader(context.Background(), s.cfg) 145 c.Assert(err, ErrorMatches, `invalid table schema file, cannot find db 'db' - .*db\.tbl-schema\.sql`) 146 } 147 148 func (s *testMydumpLoaderSuite) TestDuplicatedTable(c *C) { 149 /* 150 Path/ 151 db-schema-create.sql 152 a/ 153 db.tbl-schema.sql 154 b/ 155 db.tbl-schema.sql 156 */ 157 158 s.touch(c, "db-schema-create.sql") 159 s.mkdir(c, "a") 160 s.touch(c, "a", "db.tbl-schema.sql") 161 s.mkdir(c, "b") 162 s.touch(c, "b", "db.tbl-schema.sql") 163 164 _, err := md.NewMyDumpLoader(context.Background(), s.cfg) 165 c.Assert(err, ErrorMatches, `invalid table schema file, duplicated item - .*db\.tbl-schema\.sql`) 166 } 167 168 func (s *testMydumpLoaderSuite) TestDataNoHostDB(c *C) { 169 /* 170 Path/ 171 notdb-schema-create.sql 172 db.tbl.sql 173 */ 174 175 s.touch(c, "notdb-schema-create.sql") 176 s.touch(c, "db.tbl.sql") 177 178 _, err := md.NewMyDumpLoader(context.Background(), s.cfg) 179 c.Assert(err, ErrorMatches, `invalid data file, miss host db 'db' - .*[/\\]?db\.tbl\.sql`) 180 } 181 182 func (s *testMydumpLoaderSuite) TestDataNoHostTable(c *C) { 183 /* 184 Path/ 185 db-schema-create.sql 186 db.tbl.sql 187 */ 188 189 s.touch(c, "db-schema-create.sql") 190 s.touch(c, "db.tbl.sql") 191 192 _, err := md.NewMyDumpLoader(context.Background(), s.cfg) 193 c.Assert(err, ErrorMatches, `invalid data file, miss host table 'tbl' - .*[/\\]?db\.tbl\.sql`) 194 } 195 196 func (s *testMydumpLoaderSuite) TestViewNoHostDB(c *C) { 197 /* 198 Path/ 199 notdb-schema-create.sql 200 db.tbl-schema-view.sql 201 */ 202 s.touch(c, "notdb-schema-create.sql") 203 s.touch(c, "db.tbl-schema-view.sql") 204 205 _, err := md.NewMyDumpLoader(context.Background(), s.cfg) 206 c.Assert(err, ErrorMatches, `invalid table schema file, cannot find db 'db' - .*[/\\]?db\.tbl-schema-view\.sql`) 207 } 208 209 func (s *testMydumpLoaderSuite) TestViewNoHostTable(c *C) { 210 /* 211 Path/ 212 db-schema-create.sql 213 db.tbl-schema-view.sql 214 */ 215 216 s.touch(c, "db-schema-create.sql") 217 s.touch(c, "db.tbl-schema-view.sql") 218 219 _, err := md.NewMyDumpLoader(context.Background(), s.cfg) 220 c.Assert(err, ErrorMatches, `invalid view schema file, miss host table schema for view 'tbl'`) 221 } 222 223 func (s *testMydumpLoaderSuite) TestDataWithoutSchema(c *C) { 224 dir := s.sourceDir 225 p := filepath.Join(dir, "db.tbl.sql") 226 err := ioutil.WriteFile(p, nil, 0644) 227 c.Assert(err, IsNil) 228 229 s.cfg.Mydumper.NoSchema = true 230 231 mdl, err := md.NewMyDumpLoader(context.Background(), s.cfg) 232 c.Assert(err, IsNil) 233 c.Assert(mdl.GetDatabases(), DeepEquals, []*md.MDDatabaseMeta{{ 234 Name: "db", 235 SchemaFile: "", 236 Tables: []*md.MDTableMeta{{ 237 DB: "db", 238 Name: "tbl", 239 SchemaFile: md.FileInfo{TableName: filter.Table{Schema: "db", Name: "tbl"}}, 240 DataFiles: []md.FileInfo{{TableName: filter.Table{Schema: "db", Name: "tbl"}, FileMeta: md.SourceFileMeta{Path: "db.tbl.sql", Type: md.SourceTypeSQL}}}, 241 }}, 242 }}) 243 } 244 245 func (s *testMydumpLoaderSuite) TestTablesWithDots(c *C) { 246 s.touch(c, "db-schema-create.sql") 247 s.touch(c, "db.tbl.with.dots-schema.sql") 248 s.touch(c, "db.tbl.with.dots.0001.sql") 249 s.touch(c, "db.0002-schema.sql") 250 s.touch(c, "db.0002.sql") 251 252 // insert some tables with file name structures which we're going to ignore. 253 s.touch(c, "db.v-schema-trigger.sql") 254 s.touch(c, "db.v-schema-post.sql") 255 s.touch(c, "db.sql") 256 s.touch(c, "db-schema.sql") 257 258 mdl, err := md.NewMyDumpLoader(context.Background(), s.cfg) 259 c.Assert(err, IsNil) 260 c.Assert(mdl.GetDatabases(), DeepEquals, []*md.MDDatabaseMeta{{ 261 Name: "db", 262 SchemaFile: "db-schema-create.sql", 263 Tables: []*md.MDTableMeta{ 264 { 265 DB: "db", 266 Name: "0002", 267 SchemaFile: md.FileInfo{TableName: filter.Table{Schema: "db", Name: "0002"}, FileMeta: md.SourceFileMeta{Path: "db.0002-schema.sql", Type: md.SourceTypeTableSchema}}, 268 DataFiles: []md.FileInfo{{TableName: filter.Table{Schema: "db", Name: "0002"}, FileMeta: md.SourceFileMeta{Path: "db.0002.sql", Type: md.SourceTypeSQL}}}, 269 }, 270 { 271 DB: "db", 272 Name: "tbl.with.dots", 273 SchemaFile: md.FileInfo{TableName: filter.Table{Schema: "db", Name: "tbl.with.dots"}, FileMeta: md.SourceFileMeta{Path: "db.tbl.with.dots-schema.sql", Type: md.SourceTypeTableSchema}}, 274 DataFiles: []md.FileInfo{{TableName: filter.Table{Schema: "db", Name: "tbl.with.dots"}, FileMeta: md.SourceFileMeta{Path: "db.tbl.with.dots.0001.sql", Type: md.SourceTypeSQL, SortKey: "0001"}}}, 275 }, 276 }, 277 }}) 278 } 279 280 func (s *testMydumpLoaderSuite) TestRouter(c *C) { 281 s.cfg.Routes = []*router.TableRule{ 282 { 283 SchemaPattern: "a*", 284 TablePattern: "t*", 285 TargetSchema: "b", 286 TargetTable: "u", 287 }, 288 { 289 SchemaPattern: "c*", 290 TargetSchema: "c", 291 }, 292 { 293 SchemaPattern: "e*", 294 TablePattern: "f*", 295 TargetSchema: "v", 296 TargetTable: "vv", 297 }, 298 } 299 300 /* 301 Path/ 302 a0-schema-create.sql 303 a0.t0-schema.sql 304 a0.t0.1.sql 305 a0.t1-schema.sql 306 a0.t1.1.sql 307 a1-schema-create.sql 308 a1.s1-schema.sql 309 a1.s1.1.schema.sql 310 a1.t2-schema.sql 311 a1.t2.1.sql 312 a1.v1-schema.sql 313 a1.v1-schema-view.sql 314 c0-schema-create.sql 315 c0.t3-schema.sql 316 c0.t3.1.sql 317 d0-schema-create.sql 318 e0-schema-create.sql 319 e0.f0-schema.sql 320 e0.f0-schema-view.sql 321 */ 322 323 s.touch(c, "a0-schema-create.sql") 324 s.touch(c, "a0.t0-schema.sql") 325 s.touch(c, "a0.t0.1.sql") 326 s.touch(c, "a0.t1-schema.sql") 327 s.touch(c, "a0.t1.1.sql") 328 329 s.touch(c, "a1-schema-create.sql") 330 s.touch(c, "a1.s1-schema.sql") 331 s.touch(c, "a1.s1.1.sql") 332 s.touch(c, "a1.t2-schema.sql") 333 s.touch(c, "a1.t2.1.sql") 334 s.touch(c, "a1.v1-schema.sql") 335 s.touch(c, "a1.v1-schema-view.sql") 336 337 s.touch(c, "c0-schema-create.sql") 338 s.touch(c, "c0.t3-schema.sql") 339 s.touch(c, "c0.t3.1.sql") 340 341 s.touch(c, "d0-schema-create.sql") 342 343 s.touch(c, "e0-schema-create.sql") 344 s.touch(c, "e0.f0-schema.sql") 345 s.touch(c, "e0.f0-schema-view.sql") 346 347 mdl, err := md.NewMyDumpLoader(context.Background(), s.cfg) 348 c.Assert(err, IsNil) 349 c.Assert(mdl.GetDatabases(), DeepEquals, []*md.MDDatabaseMeta{ 350 { 351 Name: "a1", 352 SchemaFile: "a1-schema-create.sql", 353 Tables: []*md.MDTableMeta{ 354 { 355 DB: "a1", 356 Name: "s1", 357 SchemaFile: md.FileInfo{TableName: filter.Table{Schema: "a1", Name: "s1"}, FileMeta: md.SourceFileMeta{Path: "a1.s1-schema.sql", Type: md.SourceTypeTableSchema}}, 358 DataFiles: []md.FileInfo{{TableName: filter.Table{Schema: "a1", Name: "s1"}, FileMeta: md.SourceFileMeta{Path: "a1.s1.1.sql", Type: md.SourceTypeSQL, SortKey: "1"}}}, 359 }, 360 { 361 DB: "a1", 362 Name: "v1", 363 SchemaFile: md.FileInfo{TableName: filter.Table{Schema: "a1", Name: "v1"}, FileMeta: md.SourceFileMeta{Path: "a1.v1-schema.sql", Type: md.SourceTypeTableSchema}}, 364 DataFiles: []md.FileInfo{}, 365 }, 366 }, 367 Views: []*md.MDTableMeta{ 368 { 369 DB: "a1", 370 Name: "v1", 371 SchemaFile: md.FileInfo{TableName: filter.Table{Schema: "a1", Name: "v1"}, FileMeta: md.SourceFileMeta{Path: "a1.v1-schema-view.sql", Type: md.SourceTypeViewSchema}}, 372 }, 373 }, 374 }, 375 { 376 Name: "d0", 377 SchemaFile: "d0-schema-create.sql", 378 }, 379 { 380 Name: "b", 381 SchemaFile: "a0-schema-create.sql", 382 Tables: []*md.MDTableMeta{ 383 { 384 DB: "b", 385 Name: "u", 386 SchemaFile: md.FileInfo{TableName: filter.Table{Schema: "b", Name: "u"}, FileMeta: md.SourceFileMeta{Path: "a0.t0-schema.sql", Type: md.SourceTypeTableSchema}}, 387 DataFiles: []md.FileInfo{ 388 {TableName: filter.Table{Schema: "b", Name: "u"}, FileMeta: md.SourceFileMeta{Path: "a0.t0.1.sql", Type: md.SourceTypeSQL, SortKey: "1"}}, 389 {TableName: filter.Table{Schema: "b", Name: "u"}, FileMeta: md.SourceFileMeta{Path: "a0.t1.1.sql", Type: md.SourceTypeSQL, SortKey: "1"}}, 390 {TableName: filter.Table{Schema: "b", Name: "u"}, FileMeta: md.SourceFileMeta{Path: "a1.t2.1.sql", Type: md.SourceTypeSQL, SortKey: "1"}}, 391 }, 392 }, 393 }, 394 }, 395 { 396 Name: "c", 397 SchemaFile: "c0-schema-create.sql", 398 Tables: []*md.MDTableMeta{ 399 { 400 DB: "c", 401 Name: "t3", 402 SchemaFile: md.FileInfo{TableName: filter.Table{Schema: "c", Name: "t3"}, FileMeta: md.SourceFileMeta{Path: "c0.t3-schema.sql", Type: md.SourceTypeTableSchema}}, 403 DataFiles: []md.FileInfo{{TableName: filter.Table{Schema: "c", Name: "t3"}, FileMeta: md.SourceFileMeta{Path: "c0.t3.1.sql", Type: md.SourceTypeSQL, SortKey: "1"}}}, 404 }, 405 }, 406 }, 407 { 408 Name: "v", 409 SchemaFile: "e0-schema-create.sql", 410 Tables: []*md.MDTableMeta{ 411 { 412 DB: "v", 413 Name: "vv", 414 SchemaFile: md.FileInfo{TableName: filter.Table{Schema: "v", Name: "vv"}, FileMeta: md.SourceFileMeta{Path: "e0.f0-schema.sql", Type: md.SourceTypeTableSchema}}, 415 DataFiles: []md.FileInfo{}, 416 }, 417 }, 418 Views: []*md.MDTableMeta{ 419 { 420 DB: "v", 421 Name: "vv", 422 SchemaFile: md.FileInfo{TableName: filter.Table{Schema: "v", Name: "vv"}, FileMeta: md.SourceFileMeta{Path: "e0.f0-schema-view.sql", Type: md.SourceTypeViewSchema}}, 423 }, 424 }, 425 }, 426 }) 427 } 428 429 func (s *testMydumpLoaderSuite) TestBadRouterRule(c *C) { 430 s.cfg.Routes = []*router.TableRule{{ 431 SchemaPattern: "a*b", 432 TargetSchema: "ab", 433 }} 434 435 s.touch(c, "a1b-schema-create.sql") 436 437 _, err := md.NewMyDumpLoader(context.Background(), s.cfg) 438 c.Assert(err, ErrorMatches, `.*pattern a\*b not valid`) 439 } 440 441 func (s *testMydumpLoaderSuite) TestFileRouting(c *C) { 442 s.cfg.Mydumper.DefaultFileRules = false 443 s.cfg.Mydumper.FileRouters = []*config.FileRouteRule{ 444 { 445 Pattern: `(?i)^(?:[^./]*/)*([a-z0-9_]+)/schema\.sql$`, 446 Schema: "$1", 447 Type: "schema-schema", 448 }, 449 { 450 Pattern: `(?i)^(?:[^./]*/)*([a-z0-9]+)/([a-z0-9_]+)-table\.sql$`, 451 Schema: "$1", 452 Table: "$2", 453 Type: "table-schema", 454 }, 455 { 456 Pattern: `(?i)^(?:[^./]*/)*([a-z0-9]+)/([a-z0-9_]+)-view\.sql$`, 457 Schema: "$1", 458 Table: "$2", 459 Type: "view-schema", 460 }, 461 { 462 Pattern: `(?i)^(?:[^./]*/)*([a-z][a-z0-9_]*)/([a-z]+)[0-9]*(?:\.([0-9]+))?\.(sql|csv)$`, 463 Schema: "$1", 464 Table: "$2", 465 Type: "$4", 466 }, 467 { 468 Pattern: `^(?:[^./]*/)*([a-z]+)(?:\.([0-9]+))?\.(sql|csv)$`, 469 Schema: "d2", 470 Table: "$1", 471 Type: "$3", 472 }, 473 } 474 475 s.mkdir(c, "d1") 476 s.mkdir(c, "d2") 477 s.touch(c, "d1/schema.sql") 478 s.touch(c, "d1/test-table.sql") 479 s.touch(c, "d1/test0.sql") 480 s.touch(c, "d1/test1.sql") 481 s.touch(c, "d1/test2.001.sql") 482 s.touch(c, "d1/v1-table.sql") 483 s.touch(c, "d1/v1-view.sql") 484 _ = s.touch(c, "d1/t1-schema-create.sql") 485 s.touch(c, "d2/schema.sql") 486 s.touch(c, "d2/abc-table.sql") 487 s.touch(c, "abc.1.sql") 488 489 mdl, err := md.NewMyDumpLoader(context.Background(), s.cfg) 490 c.Assert(err, IsNil) 491 c.Assert(mdl.GetDatabases(), DeepEquals, []*md.MDDatabaseMeta{ 492 { 493 Name: "d1", 494 SchemaFile: filepath.FromSlash("d1/schema.sql"), 495 Tables: []*md.MDTableMeta{ 496 { 497 DB: "d1", 498 Name: "test", 499 SchemaFile: md.FileInfo{ 500 TableName: filter.Table{Schema: "d1", Name: "test"}, 501 FileMeta: md.SourceFileMeta{Path: filepath.FromSlash("d1/test-table.sql"), Type: md.SourceTypeTableSchema}, 502 }, 503 DataFiles: []md.FileInfo{ 504 { 505 TableName: filter.Table{Schema: "d1", Name: "test"}, 506 FileMeta: md.SourceFileMeta{Path: filepath.FromSlash("d1/test0.sql"), Type: md.SourceTypeSQL}, 507 }, 508 { 509 TableName: filter.Table{Schema: "d1", Name: "test"}, 510 FileMeta: md.SourceFileMeta{Path: filepath.FromSlash("d1/test1.sql"), Type: md.SourceTypeSQL}, 511 }, 512 { 513 TableName: filter.Table{Schema: "d1", Name: "test"}, 514 FileMeta: md.SourceFileMeta{Path: filepath.FromSlash("d1/test2.001.sql"), Type: md.SourceTypeSQL}, 515 }, 516 }, 517 }, 518 { 519 DB: "d1", 520 Name: "v1", 521 SchemaFile: md.FileInfo{ 522 TableName: filter.Table{Schema: "d1", Name: "v1"}, 523 FileMeta: md.SourceFileMeta{Path: filepath.FromSlash("d1/v1-table.sql"), Type: md.SourceTypeTableSchema}, 524 }, 525 DataFiles: []md.FileInfo{}, 526 }, 527 }, 528 Views: []*md.MDTableMeta{ 529 { 530 DB: "d1", 531 Name: "v1", 532 SchemaFile: md.FileInfo{ 533 TableName: filter.Table{Schema: "d1", Name: "v1"}, 534 FileMeta: md.SourceFileMeta{Path: filepath.FromSlash("d1/v1-view.sql"), Type: md.SourceTypeViewSchema}, 535 }, 536 }, 537 }, 538 }, 539 { 540 Name: "d2", 541 SchemaFile: filepath.FromSlash("d2/schema.sql"), 542 Tables: []*md.MDTableMeta{ 543 { 544 DB: "d2", 545 Name: "abc", 546 SchemaFile: md.FileInfo{ 547 TableName: filter.Table{Schema: "d2", Name: "abc"}, 548 FileMeta: md.SourceFileMeta{Path: filepath.FromSlash("d2/abc-table.sql"), Type: md.SourceTypeTableSchema}, 549 }, 550 DataFiles: []md.FileInfo{{TableName: filter.Table{Schema: "d2", Name: "abc"}, FileMeta: md.SourceFileMeta{Path: "abc.1.sql", Type: md.SourceTypeSQL}}}, 551 }, 552 }, 553 }, 554 }) 555 }