vitess.io/vitess@v0.16.2/go/vt/wrangler/materializer_test.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package wrangler 18 19 import ( 20 "context" 21 "fmt" 22 "sort" 23 "strings" 24 "testing" 25 "time" 26 27 "github.com/stretchr/testify/require" 28 "google.golang.org/protobuf/proto" 29 30 "vitess.io/vitess/go/sqltypes" 31 "vitess.io/vitess/go/test/utils" 32 "vitess.io/vitess/go/vt/logutil" 33 binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" 34 querypb "vitess.io/vitess/go/vt/proto/query" 35 tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" 36 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 37 vschemapb "vitess.io/vitess/go/vt/proto/vschema" 38 vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" 39 "vitess.io/vitess/go/vt/topo/memorytopo" 40 ) 41 42 const mzUpdateQuery = "update _vt.vreplication set state='Running' where db_name='vt_targetks' and workflow='workflow'" 43 const mzSelectIDQuery = "select id from _vt.vreplication where db_name='vt_targetks' and workflow='workflow'" 44 const mzSelectFrozenQuery = "select 1 from _vt.vreplication where db_name='vt_targetks' and message='FROZEN' and workflow_sub_type != 1" 45 const mzCheckJournal = "/select val from _vt.resharding_journal where id=" 46 47 var defaultOnDDL = binlogdatapb.OnDDLAction_name[int32(binlogdatapb.OnDDLAction_IGNORE)] 48 49 func TestMigrateTables(t *testing.T) { 50 ms := &vtctldatapb.MaterializeSettings{ 51 Workflow: "workflow", 52 SourceKeyspace: "sourceks", 53 TargetKeyspace: "targetks", 54 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 55 TargetTable: "t1", 56 SourceExpression: "select * from t1", 57 }}, 58 } 59 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"}) 60 defer env.close() 61 62 env.tmc.expectVRQuery(100, mzCheckJournal, &sqltypes.Result{}) 63 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 64 env.tmc.expectVRQuery(200, insertPrefix, &sqltypes.Result{}) 65 env.tmc.expectVRQuery(200, mzSelectIDQuery, &sqltypes.Result{}) 66 env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{}) 67 68 ctx := context.Background() 69 err := env.wr.MoveTables(ctx, "workflow", "sourceks", "targetks", "t1", "", "", false, "", true, false, "", false, false, "", defaultOnDDL, nil) 70 require.NoError(t, err) 71 vschema, err := env.wr.ts.GetSrvVSchema(ctx, env.cell) 72 require.NoError(t, err) 73 got := fmt.Sprintf("%v", vschema) 74 want := []string{ 75 `keyspaces:{key:"sourceks" value:{}} keyspaces:{key:"targetks" value:{tables:{key:"t1" value:{}}}}`, 76 `rules:{from_table:"t1" to_tables:"sourceks.t1"}`, 77 `rules:{from_table:"targetks.t1" to_tables:"sourceks.t1"}`, 78 } 79 for _, wantstr := range want { 80 require.Contains(t, got, wantstr) 81 } 82 } 83 84 func TestMissingTables(t *testing.T) { 85 ms := &vtctldatapb.MaterializeSettings{ 86 Workflow: "workflow", 87 SourceKeyspace: "sourceks", 88 TargetKeyspace: "targetks", 89 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 90 TargetTable: "t1", 91 SourceExpression: "select * from t1", 92 }, { 93 TargetTable: "t2", 94 SourceExpression: "select * from t2", 95 }, { 96 TargetTable: "t3", 97 SourceExpression: "select * from t3", 98 }}, 99 } 100 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"}) 101 defer env.close() 102 103 env.tmc.expectVRQuery(100, mzCheckJournal, &sqltypes.Result{}) 104 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 105 env.tmc.expectVRQuery(200, insertPrefix, &sqltypes.Result{}) 106 env.tmc.expectVRQuery(200, mzSelectIDQuery, &sqltypes.Result{}) 107 env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{}) 108 109 ctx := context.Background() 110 err := env.wr.MoveTables(ctx, "workflow", "sourceks", "targetks", "t1,tyt", "", "", false, "", true, false, "", false, false, "", defaultOnDDL, nil) 111 require.EqualError(t, err, "table(s) not found in source keyspace sourceks: tyt") 112 err = env.wr.MoveTables(ctx, "workflow", "sourceks", "targetks", "t1,tyt,t2,txt", "", "", false, "", true, false, "", false, false, "", defaultOnDDL, nil) 113 require.EqualError(t, err, "table(s) not found in source keyspace sourceks: tyt,txt") 114 err = env.wr.MoveTables(ctx, "workflow", "sourceks", "targetks", "t1", "", "", false, "", true, false, "", false, false, "", defaultOnDDL, nil) 115 require.NoError(t, err) 116 } 117 118 func TestMoveTablesAllAndExclude(t *testing.T) { 119 ms := &vtctldatapb.MaterializeSettings{ 120 Workflow: "workflow", 121 SourceKeyspace: "sourceks", 122 TargetKeyspace: "targetks", 123 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 124 TargetTable: "t1", 125 SourceExpression: "select * from t1", 126 }, { 127 TargetTable: "t2", 128 SourceExpression: "select * from t2", 129 }, { 130 TargetTable: "t3", 131 SourceExpression: "select * from t3", 132 }}, 133 } 134 135 ctx := context.Background() 136 var err error 137 138 var targetTables = func(env *testMaterializerEnv) []string { 139 vschema, err := env.wr.ts.GetSrvVSchema(ctx, env.cell) 140 require.NoError(t, err) 141 var targetTables []string 142 for table := range vschema.Keyspaces["targetks"].Tables { 143 targetTables = append(targetTables, table) 144 } 145 sort.Strings(targetTables) 146 return targetTables 147 } 148 allTables := []string{"t1", "t2", "t3"} 149 sort.Strings(allTables) 150 151 type testCase struct { 152 allTables bool 153 excludeTables string 154 want []string 155 } 156 testCases := []*testCase{ 157 {true, "", allTables}, 158 {true, "t2,t3", []string{"t1"}}, 159 {true, "t1", []string{"t2", "t3"}}, 160 } 161 for _, tcase := range testCases { 162 t.Run("", func(t *testing.T) { 163 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"}) 164 defer env.close() 165 env.tmc.expectVRQuery(100, mzCheckJournal, &sqltypes.Result{}) 166 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 167 env.tmc.expectVRQuery(200, insertPrefix, &sqltypes.Result{}) 168 env.tmc.expectVRQuery(200, mzSelectIDQuery, &sqltypes.Result{}) 169 env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{}) 170 err = env.wr.MoveTables(ctx, "workflow", "sourceks", "targetks", "", "", "", tcase.allTables, tcase.excludeTables, true, false, "", false, false, "", defaultOnDDL, nil) 171 require.NoError(t, err) 172 require.EqualValues(t, tcase.want, targetTables(env)) 173 }) 174 175 } 176 177 } 178 179 func TestMoveTablesStopFlags(t *testing.T) { 180 ms := &vtctldatapb.MaterializeSettings{ 181 Workflow: "workflow", 182 SourceKeyspace: "sourceks", 183 TargetKeyspace: "targetks", 184 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 185 TargetTable: "t1", 186 SourceExpression: "select * from t1", 187 }}, 188 } 189 190 ctx := context.Background() 191 var err error 192 t.Run("StopStartedAndStopAfterCopyFlags", func(t *testing.T) { 193 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"}) 194 defer env.close() 195 env.tmc.expectVRQuery(100, mzCheckJournal, &sqltypes.Result{}) 196 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 197 // insert expects flag stop_after_copy to be true 198 insert := `/insert into _vt.vreplication\(workflow, source, pos, max_tps, max_replication_lag, cell, tablet_types.*stop_after_copy:true.*` 199 200 env.tmc.expectVRQuery(200, insert, &sqltypes.Result{}) 201 env.tmc.expectVRQuery(200, mzSelectIDQuery, &sqltypes.Result{}) 202 // -auto_start=false is tested by NOT expecting the update query which sets state to RUNNING 203 err = env.wr.MoveTables(ctx, "workflow", "sourceks", "targetks", "t1", "", 204 "", false, "", false, true, "", false, false, "", defaultOnDDL, nil) 205 require.NoError(t, err) 206 env.tmc.verifyQueries(t) 207 }) 208 } 209 210 func TestMigrateVSchema(t *testing.T) { 211 ms := &vtctldatapb.MaterializeSettings{ 212 Workflow: "workflow", 213 SourceKeyspace: "sourceks", 214 TargetKeyspace: "targetks", 215 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 216 TargetTable: "t1", 217 SourceExpression: "select * from t1", 218 }}, 219 } 220 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"}) 221 defer env.close() 222 223 env.tmc.expectVRQuery(100, mzCheckJournal, &sqltypes.Result{}) 224 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 225 env.tmc.expectVRQuery(200, insertPrefix, &sqltypes.Result{}) 226 env.tmc.expectVRQuery(200, mzSelectIDQuery, &sqltypes.Result{}) 227 env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{}) 228 229 ctx := context.Background() 230 err := env.wr.MoveTables(ctx, "workflow", "sourceks", "targetks", `{"t1":{}}`, "", "", false, "", true, false, "", false, false, "", defaultOnDDL, nil) 231 require.NoError(t, err) 232 vschema, err := env.wr.ts.GetSrvVSchema(ctx, env.cell) 233 require.NoError(t, err) 234 got := fmt.Sprintf("%v", vschema) 235 want := []string{`keyspaces:{key:"sourceks" value:{}}`, 236 `keyspaces:{key:"sourceks" value:{}} keyspaces:{key:"targetks" value:{tables:{key:"t1" value:{}}}}`, 237 `rules:{from_table:"t1" to_tables:"sourceks.t1"}`, 238 `rules:{from_table:"targetks.t1" to_tables:"sourceks.t1"}`, 239 } 240 for _, wantstr := range want { 241 require.Contains(t, got, wantstr) 242 } 243 } 244 245 func TestCreateLookupVindexFull(t *testing.T) { 246 ms := &vtctldatapb.MaterializeSettings{ 247 Workflow: "lkp_vdx", 248 SourceKeyspace: "sourceks", 249 TargetKeyspace: "targetks", 250 } 251 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"}) 252 defer env.close() 253 254 specs := &vschemapb.Keyspace{ 255 Vindexes: map[string]*vschemapb.Vindex{ 256 "v": { 257 Type: "lookup_unique", 258 Params: map[string]string{ 259 "table": "targetks.lkp", 260 "from": "c1", 261 "to": "c2", 262 }, 263 Owner: "t1", 264 }, 265 }, 266 Tables: map[string]*vschemapb.Table{ 267 "t1": { 268 ColumnVindexes: []*vschemapb.ColumnVindex{{ 269 Name: "v", 270 Column: "col2", 271 }}, 272 }, 273 }, 274 } 275 // Dummy sourceSchema 276 sourceSchema := "CREATE TABLE `t1` (\n" + 277 " `col1` int(11) NOT NULL AUTO_INCREMENT,\n" + 278 " `col2` int(11) DEFAULT NULL,\n" + 279 " PRIMARY KEY (`id`)\n" + 280 ") ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1" 281 282 sourceVSchema := &vschemapb.Keyspace{ 283 Sharded: true, 284 Vindexes: map[string]*vschemapb.Vindex{ 285 "hash": { 286 Type: "hash", 287 }, 288 }, 289 Tables: map[string]*vschemapb.Table{ 290 "t1": { 291 ColumnVindexes: []*vschemapb.ColumnVindex{{ 292 Name: "hash", 293 Column: "col1", 294 }}, 295 }, 296 }, 297 } 298 env.tmc.schema[ms.SourceKeyspace+".t1"] = &tabletmanagerdatapb.SchemaDefinition{ 299 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{{ 300 Fields: []*querypb.Field{{ 301 Name: "col1", 302 Type: querypb.Type_INT64, 303 }, { 304 Name: "col2", 305 Type: querypb.Type_INT64, 306 }}, 307 Schema: sourceSchema, 308 }}, 309 } 310 if err := env.topoServ.SaveVSchema(context.Background(), ms.TargetKeyspace, &vschemapb.Keyspace{}); err != nil { 311 t.Fatal(err) 312 } 313 if err := env.topoServ.SaveVSchema(context.Background(), ms.SourceKeyspace, sourceVSchema); err != nil { 314 t.Fatal(err) 315 } 316 317 env.tmc.expectVRQuery(100, mzCheckJournal, &sqltypes.Result{}) 318 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 319 env.tmc.expectVRQuery(200, "/CREATE TABLE `lkp`", &sqltypes.Result{}) 320 env.tmc.expectVRQuery(200, insertPrefix, &sqltypes.Result{}) 321 env.tmc.expectVRQuery(200, "update _vt.vreplication set state='Running' where db_name='vt_targetks' and workflow='lkp_vdx'", &sqltypes.Result{}) 322 323 ctx := context.Background() 324 err := env.wr.CreateLookupVindex(ctx, ms.SourceKeyspace, specs, "cell", "PRIMARY", false) 325 require.NoError(t, err) 326 327 wantvschema := &vschemapb.Keyspace{ 328 Sharded: true, 329 Vindexes: map[string]*vschemapb.Vindex{ 330 "hash": { 331 Type: "hash", 332 }, 333 "v": { 334 Type: "lookup_unique", 335 Params: map[string]string{ 336 "table": "targetks.lkp", 337 "from": "c1", 338 "to": "c2", 339 "write_only": "true", 340 }, 341 Owner: "t1", 342 }, 343 }, 344 Tables: map[string]*vschemapb.Table{ 345 "t1": { 346 ColumnVindexes: []*vschemapb.ColumnVindex{{ 347 Name: "hash", 348 Column: "col1", 349 }, { 350 Name: "v", 351 Column: "col2", 352 }}, 353 }, 354 }, 355 } 356 vschema, err := env.topoServ.GetVSchema(ctx, ms.SourceKeyspace) 357 require.NoError(t, err) 358 utils.MustMatch(t, wantvschema, vschema) 359 360 wantvschema = &vschemapb.Keyspace{ 361 Tables: map[string]*vschemapb.Table{ 362 "lkp": {}, 363 }, 364 } 365 vschema, err = env.topoServ.GetVSchema(ctx, ms.TargetKeyspace) 366 require.NoError(t, err) 367 utils.MustMatch(t, wantvschema, vschema) 368 } 369 370 func TestCreateLookupVindexCreateDDL(t *testing.T) { 371 ms := &vtctldatapb.MaterializeSettings{ 372 SourceKeyspace: "sourceks", 373 TargetKeyspace: "targetks", 374 } 375 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"}) 376 defer env.close() 377 vs := &vschemapb.Keyspace{ 378 Sharded: true, 379 Vindexes: map[string]*vschemapb.Vindex{ 380 "hash": { 381 Type: "hash", 382 }, 383 }, 384 Tables: map[string]*vschemapb.Table{ 385 "t1": { 386 ColumnVindexes: []*vschemapb.ColumnVindex{{ 387 Column: "col1", 388 Name: "hash", 389 }}, 390 }, 391 }, 392 } 393 if err := env.topoServ.SaveVSchema(context.Background(), ms.SourceKeyspace, vs); err != nil { 394 t.Fatal(err) 395 } 396 397 testcases := []struct { 398 description string 399 specs *vschemapb.Keyspace 400 sourceSchema string 401 out string 402 err string 403 }{{ 404 description: "unique lookup", 405 specs: &vschemapb.Keyspace{ 406 Vindexes: map[string]*vschemapb.Vindex{ 407 "v": { 408 Type: "lookup_unique", 409 Params: map[string]string{ 410 "table": fmt.Sprintf("%s.lkp", ms.TargetKeyspace), 411 "from": "c1", 412 "to": "c2", 413 }, 414 Owner: "t1", 415 }, 416 }, 417 Tables: map[string]*vschemapb.Table{ 418 "t1": { 419 ColumnVindexes: []*vschemapb.ColumnVindex{{ 420 Name: "v", 421 Column: "col2", 422 }}, 423 }, 424 }, 425 }, 426 sourceSchema: "CREATE TABLE `t1` (\n" + 427 " `col1` int(11) NOT NULL AUTO_INCREMENT,\n" + 428 " `col2` int(11) DEFAULT NULL,\n" + 429 " `col3` int(11) DEFAULT NULL,\n" + 430 " PRIMARY KEY (`id`)\n" + 431 ") ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1", 432 out: "CREATE TABLE `lkp` (\n" + 433 " `c1` int(11),\n" + 434 " `c2` varbinary(128),\n" + 435 " PRIMARY KEY (`c1`)\n" + 436 ")", 437 }, { 438 description: "unique lookup, also pk", 439 specs: &vschemapb.Keyspace{ 440 Vindexes: map[string]*vschemapb.Vindex{ 441 "v": { 442 Type: "lookup_unique", 443 Params: map[string]string{ 444 "table": fmt.Sprintf("%s.lkp", ms.TargetKeyspace), 445 "from": "c1", 446 "to": "c2", 447 }, 448 Owner: "t1", 449 }, 450 }, 451 Tables: map[string]*vschemapb.Table{ 452 "t1": { 453 ColumnVindexes: []*vschemapb.ColumnVindex{{ 454 Name: "v", 455 Column: "col2", 456 }}, 457 }, 458 }, 459 }, 460 sourceSchema: "CREATE TABLE `t1` (\n" + 461 " `col2` int(11) NOT NULL AUTO_INCREMENT,\n" + 462 " `col1` int(11) DEFAULT NULL,\n" + 463 " `col4` int(11) DEFAULT NULL,\n" + 464 " PRIMARY KEY (`id`)\n" + 465 ") ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1", 466 out: "CREATE TABLE `lkp` (\n" + 467 " `c1` int(11) NOT NULL,\n" + 468 " `c2` varbinary(128),\n" + 469 " PRIMARY KEY (`c1`)\n" + 470 ")", 471 }, { 472 description: "non-unique lookup, also pk", 473 specs: &vschemapb.Keyspace{ 474 Vindexes: map[string]*vschemapb.Vindex{ 475 "v": { 476 Type: "lookup", 477 Params: map[string]string{ 478 "table": fmt.Sprintf("%s.lkp", ms.TargetKeyspace), 479 "from": "c1,c2", 480 "to": "c3", 481 }, 482 Owner: "t1", 483 }, 484 }, 485 Tables: map[string]*vschemapb.Table{ 486 "t1": { 487 ColumnVindexes: []*vschemapb.ColumnVindex{{ 488 Name: "v", 489 Columns: []string{"col2", "col1"}, 490 }}, 491 }, 492 }, 493 }, 494 sourceSchema: "CREATE TABLE `t1` (\n" + 495 " `col1` int(11) NOT NULL AUTO_INCREMENT,\n" + 496 " `col2` int(11) NOT NULL,\n" + 497 " `col3` int(11) DEFAULT NULL,\n" + 498 " PRIMARY KEY (`id`)\n" + 499 ") ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1", 500 out: "CREATE TABLE `lkp` (\n" + 501 " `c1` int(11) NOT NULL,\n" + 502 " `c2` int(11) NOT NULL,\n" + 503 " `c3` varbinary(128),\n" + 504 " PRIMARY KEY (`c1`, `c2`)\n" + 505 ")", 506 }, { 507 description: "column missing", 508 specs: &vschemapb.Keyspace{ 509 Vindexes: map[string]*vschemapb.Vindex{ 510 "v": { 511 Type: "lookup_unique", 512 Params: map[string]string{ 513 "table": fmt.Sprintf("%s.lkp", ms.TargetKeyspace), 514 "from": "c1", 515 "to": "c2", 516 }, 517 Owner: "t1", 518 }, 519 }, 520 Tables: map[string]*vschemapb.Table{ 521 "t1": { 522 ColumnVindexes: []*vschemapb.ColumnVindex{{ 523 Name: "v", 524 Column: "nocol", 525 }}, 526 }, 527 }, 528 }, 529 sourceSchema: "CREATE TABLE `t1` (\n" + 530 " `col1` int(11) NOT NULL AUTO_INCREMENT,\n" + 531 " `col2` int(11) NOT NULL,\n" + 532 " `col3` int(11) DEFAULT NULL,\n" + 533 " PRIMARY KEY (`id`)\n" + 534 ") ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1", 535 err: "column nocol not found in schema", 536 }, { 537 description: "no table in schema", 538 specs: &vschemapb.Keyspace{ 539 Vindexes: map[string]*vschemapb.Vindex{ 540 "v": { 541 Type: "lookup_unique", 542 Params: map[string]string{ 543 "table": fmt.Sprintf("%s.lkp", ms.TargetKeyspace), 544 "from": "c1", 545 "to": "c2", 546 }, 547 Owner: "t1", 548 }, 549 }, 550 Tables: map[string]*vschemapb.Table{ 551 "t1": { 552 ColumnVindexes: []*vschemapb.ColumnVindex{{ 553 Name: "v", 554 Column: "nocol", 555 }}, 556 }, 557 }, 558 }, 559 sourceSchema: "", 560 err: "unexpected number of tables returned from schema", 561 }} 562 for _, tcase := range testcases { 563 if tcase.sourceSchema != "" { 564 env.tmc.schema[ms.SourceKeyspace+".t1"] = &tabletmanagerdatapb.SchemaDefinition{ 565 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{{ 566 Schema: tcase.sourceSchema, 567 }}, 568 } 569 } else { 570 delete(env.tmc.schema, ms.SourceKeyspace+".t1") 571 } 572 573 outms, _, _, err := env.wr.prepareCreateLookup(context.Background(), ms.SourceKeyspace, tcase.specs, false) 574 if tcase.err != "" { 575 if err == nil || !strings.Contains(err.Error(), tcase.err) { 576 t.Errorf("prepareCreateLookup(%s) err: %v, must contain %v", tcase.description, err, tcase.err) 577 } 578 continue 579 } 580 require.NoError(t, err) 581 want := strings.Split(tcase.out, "\n") 582 got := strings.Split(outms.TableSettings[0].CreateDdl, "\n") 583 require.Equal(t, want, got, tcase.description) 584 } 585 } 586 587 func TestCreateLookupVindexSourceVSchema(t *testing.T) { 588 ms := &vtctldatapb.MaterializeSettings{ 589 SourceKeyspace: "sourceks", 590 TargetKeyspace: "targetks", 591 } 592 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"}) 593 defer env.close() 594 595 specs := &vschemapb.Keyspace{ 596 Vindexes: map[string]*vschemapb.Vindex{ 597 "v": { 598 Type: "lookup_unique", 599 Params: map[string]string{ 600 "table": "targetks.lkp", 601 "from": "c1", 602 "to": "c2", 603 }, 604 Owner: "t1", 605 }, 606 }, 607 Tables: map[string]*vschemapb.Table{ 608 "t1": { 609 ColumnVindexes: []*vschemapb.ColumnVindex{{ 610 Name: "v", 611 Column: "col2", 612 }}, 613 }, 614 }, 615 } 616 // Dummy sourceSchema 617 sourceSchema := "CREATE TABLE `t1` (\n" + 618 " `col1` int(11) NOT NULL AUTO_INCREMENT,\n" + 619 " `col2` int(11) DEFAULT NULL,\n" + 620 " PRIMARY KEY (`id`)\n" + 621 ") ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1" 622 623 testcases := []struct { 624 description string 625 sourceVSchema *vschemapb.Keyspace 626 out *vschemapb.Keyspace 627 }{{ 628 description: "source vschema has no prior info", 629 sourceVSchema: &vschemapb.Keyspace{ 630 Sharded: true, 631 Vindexes: map[string]*vschemapb.Vindex{ 632 "hash": { 633 Type: "hash", 634 }, 635 }, 636 Tables: map[string]*vschemapb.Table{ 637 "t1": { 638 ColumnVindexes: []*vschemapb.ColumnVindex{{ 639 Name: "hash", 640 Column: "col1", 641 }}, 642 }, 643 }, 644 }, 645 out: &vschemapb.Keyspace{ 646 Sharded: true, 647 Vindexes: map[string]*vschemapb.Vindex{ 648 "hash": { 649 Type: "hash", 650 }, 651 "v": { 652 Type: "lookup_unique", 653 Params: map[string]string{ 654 "table": "targetks.lkp", 655 "from": "c1", 656 "to": "c2", 657 "write_only": "true", 658 }, 659 Owner: "t1", 660 }, 661 }, 662 Tables: map[string]*vschemapb.Table{ 663 "t1": { 664 ColumnVindexes: []*vschemapb.ColumnVindex{{ 665 Name: "hash", 666 Column: "col1", 667 }, { 668 Name: "v", 669 Column: "col2", 670 }}, 671 }, 672 }, 673 }, 674 }, { 675 description: "source vschema has the lookup vindex", 676 sourceVSchema: &vschemapb.Keyspace{ 677 Sharded: true, 678 Vindexes: map[string]*vschemapb.Vindex{ 679 "hash": { 680 Type: "hash", 681 }, 682 "v": { 683 Type: "lookup_unique", 684 Params: map[string]string{ 685 "table": "targetks.lkp", 686 "from": "c1", 687 "to": "c2", 688 "write_only": "true", 689 }, 690 Owner: "t1", 691 }, 692 }, 693 Tables: map[string]*vschemapb.Table{ 694 "t1": { 695 ColumnVindexes: []*vschemapb.ColumnVindex{{ 696 Name: "hash", 697 Column: "col1", 698 }}, 699 }, 700 }, 701 }, 702 out: &vschemapb.Keyspace{ 703 Sharded: true, 704 Vindexes: map[string]*vschemapb.Vindex{ 705 "hash": { 706 Type: "hash", 707 }, 708 "v": { 709 Type: "lookup_unique", 710 Params: map[string]string{ 711 "table": "targetks.lkp", 712 "from": "c1", 713 "to": "c2", 714 "write_only": "true", 715 }, 716 Owner: "t1", 717 }, 718 }, 719 Tables: map[string]*vschemapb.Table{ 720 "t1": { 721 ColumnVindexes: []*vschemapb.ColumnVindex{{ 722 Name: "hash", 723 Column: "col1", 724 }, { 725 Name: "v", 726 Column: "col2", 727 }}, 728 }, 729 }, 730 }, 731 }, { 732 description: "source vschema table has a different vindex on same column", 733 sourceVSchema: &vschemapb.Keyspace{ 734 Sharded: true, 735 Vindexes: map[string]*vschemapb.Vindex{ 736 "hash": { 737 Type: "hash", 738 }, 739 "v": { 740 Type: "lookup_unique", 741 Params: map[string]string{ 742 "table": "targetks.lkp", 743 "from": "c1", 744 "to": "c2", 745 "write_only": "true", 746 }, 747 Owner: "t1", 748 }, 749 }, 750 Tables: map[string]*vschemapb.Table{ 751 "t1": { 752 ColumnVindexes: []*vschemapb.ColumnVindex{{ 753 Name: "hash", 754 Column: "col1", 755 }, { 756 Name: "hash", 757 Column: "col2", 758 }}, 759 }, 760 }, 761 }, 762 out: &vschemapb.Keyspace{ 763 Sharded: true, 764 Vindexes: map[string]*vschemapb.Vindex{ 765 "hash": { 766 Type: "hash", 767 }, 768 "v": { 769 Type: "lookup_unique", 770 Params: map[string]string{ 771 "table": "targetks.lkp", 772 "from": "c1", 773 "to": "c2", 774 "write_only": "true", 775 }, 776 Owner: "t1", 777 }, 778 }, 779 Tables: map[string]*vschemapb.Table{ 780 "t1": { 781 ColumnVindexes: []*vschemapb.ColumnVindex{{ 782 Name: "hash", 783 Column: "col1", 784 }, { 785 Name: "hash", 786 Column: "col2", 787 }, { 788 Name: "v", 789 Column: "col2", 790 }}, 791 }, 792 }, 793 }, 794 }} 795 for _, tcase := range testcases { 796 env.tmc.schema[ms.SourceKeyspace+".t1"] = &tabletmanagerdatapb.SchemaDefinition{ 797 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{{ 798 Fields: []*querypb.Field{{ 799 Name: "col1", 800 Type: querypb.Type_INT64, 801 }, { 802 Name: "col2", 803 Type: querypb.Type_INT64, 804 }}, 805 Schema: sourceSchema, 806 }}, 807 } 808 if err := env.topoServ.SaveVSchema(context.Background(), ms.TargetKeyspace, &vschemapb.Keyspace{}); err != nil { 809 t.Fatal(err) 810 } 811 if err := env.topoServ.SaveVSchema(context.Background(), ms.SourceKeyspace, tcase.sourceVSchema); err != nil { 812 t.Fatal(err) 813 } 814 815 _, got, _, err := env.wr.prepareCreateLookup(context.Background(), ms.SourceKeyspace, specs, false) 816 require.NoError(t, err) 817 if !proto.Equal(got, tcase.out) { 818 t.Errorf("%s: got:\n%v, want\n%v", tcase.description, got, tcase.out) 819 } 820 } 821 } 822 823 func TestCreateLookupVindexTargetVSchema(t *testing.T) { 824 ms := &vtctldatapb.MaterializeSettings{ 825 SourceKeyspace: "sourceks", 826 TargetKeyspace: "targetks", 827 } 828 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"}) 829 defer env.close() 830 sourcevs := &vschemapb.Keyspace{ 831 Sharded: true, 832 Vindexes: map[string]*vschemapb.Vindex{ 833 "hash": { 834 Type: "hash", 835 }, 836 }, 837 Tables: map[string]*vschemapb.Table{ 838 "t1": { 839 ColumnVindexes: []*vschemapb.ColumnVindex{{ 840 Column: "col1", 841 Name: "hash", 842 }}, 843 }, 844 }, 845 } 846 if err := env.topoServ.SaveVSchema(context.Background(), ms.SourceKeyspace, sourcevs); err != nil { 847 t.Fatal(err) 848 } 849 850 // withTable is a target vschema with a pre-existing table. 851 withTable := &vschemapb.Keyspace{ 852 Sharded: true, 853 Vindexes: map[string]*vschemapb.Vindex{ 854 "hash": { 855 Type: "hash", 856 }, 857 }, 858 Tables: map[string]*vschemapb.Table{ 859 "t2": { 860 ColumnVindexes: []*vschemapb.ColumnVindex{{ 861 Column: "c1", 862 Name: "hash", 863 }}, 864 }, 865 }, 866 } 867 868 specs := &vschemapb.Keyspace{ 869 Vindexes: map[string]*vschemapb.Vindex{ 870 "v": { 871 Type: "lookup_unique", 872 Params: map[string]string{ 873 "table": "will be set by the test case", 874 "from": "c1", 875 "to": "c2", 876 }, 877 Owner: "t1", 878 }, 879 }, 880 Tables: map[string]*vschemapb.Table{ 881 "t1": { 882 ColumnVindexes: []*vschemapb.ColumnVindex{{ 883 Name: "v", 884 Column: "col2", 885 }}, 886 }, 887 }, 888 } 889 // Dummy sourceSchema 890 sourceSchema := "CREATE TABLE `t1` (\n" + 891 " `col1` int(11) NOT NULL AUTO_INCREMENT,\n" + 892 " `col2` int(11) DEFAULT NULL,\n" + 893 " PRIMARY KEY (`id`)\n" + 894 ") ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1" 895 896 testcases := []struct { 897 description string 898 targetTable string 899 sourceFieldType querypb.Type 900 targetVSchema *vschemapb.Keyspace 901 out *vschemapb.Keyspace 902 err string 903 }{{ 904 description: "sharded, int64, empty target", 905 targetTable: "lkp", 906 sourceFieldType: querypb.Type_INT64, 907 targetVSchema: &vschemapb.Keyspace{Sharded: true}, 908 out: &vschemapb.Keyspace{ 909 Sharded: true, 910 Vindexes: map[string]*vschemapb.Vindex{ 911 "hash": { 912 Type: "hash", 913 }, 914 }, 915 Tables: map[string]*vschemapb.Table{ 916 "lkp": { 917 ColumnVindexes: []*vschemapb.ColumnVindex{{ 918 Column: "c1", 919 Name: "hash", 920 }}, 921 }, 922 }, 923 }, 924 }, { 925 description: "sharded, varchar, empty target", 926 targetTable: "lkp", 927 sourceFieldType: querypb.Type_VARCHAR, 928 targetVSchema: &vschemapb.Keyspace{Sharded: true}, 929 out: &vschemapb.Keyspace{ 930 Sharded: true, 931 Vindexes: map[string]*vschemapb.Vindex{ 932 "unicode_loose_md5": { 933 Type: "unicode_loose_md5", 934 }, 935 }, 936 Tables: map[string]*vschemapb.Table{ 937 "lkp": { 938 ColumnVindexes: []*vschemapb.ColumnVindex{{ 939 Column: "c1", 940 Name: "unicode_loose_md5", 941 }}, 942 }, 943 }, 944 }, 945 }, { 946 description: "sharded, int64, good vindex", 947 targetTable: "lkp", 948 sourceFieldType: querypb.Type_INT64, 949 targetVSchema: &vschemapb.Keyspace{ 950 Sharded: true, 951 Vindexes: map[string]*vschemapb.Vindex{ 952 // Create a misleading vindex name. 953 "hash": { 954 Type: "hash", 955 }, 956 }, 957 }, 958 out: &vschemapb.Keyspace{ 959 Sharded: true, 960 Vindexes: map[string]*vschemapb.Vindex{ 961 "hash": { 962 Type: "hash", 963 }, 964 }, 965 Tables: map[string]*vschemapb.Table{ 966 "lkp": { 967 ColumnVindexes: []*vschemapb.ColumnVindex{{ 968 Column: "c1", 969 Name: "hash", 970 }}, 971 }, 972 }, 973 }, 974 }, { 975 description: "sharded, int64, bad vindex", 976 targetTable: "lkp", 977 sourceFieldType: querypb.Type_INT64, 978 targetVSchema: &vschemapb.Keyspace{ 979 Sharded: true, 980 Vindexes: map[string]*vschemapb.Vindex{ 981 // Create a misleading vindex name. 982 "hash": { 983 Type: "unicode_loose_md5", 984 }, 985 }, 986 }, 987 err: "a conflicting vindex named hash already exists in the target vschema", 988 }, { 989 description: "sharded, int64, good table", 990 targetTable: "t2", 991 sourceFieldType: querypb.Type_INT64, 992 targetVSchema: withTable, 993 out: &vschemapb.Keyspace{ 994 Sharded: true, 995 Vindexes: map[string]*vschemapb.Vindex{ 996 "hash": { 997 Type: "hash", 998 }, 999 }, 1000 Tables: map[string]*vschemapb.Table{ 1001 "t2": { 1002 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1003 Column: "c1", 1004 Name: "hash", 1005 }}, 1006 }, 1007 }, 1008 }, 1009 }, { 1010 description: "sharded, int64, table mismatch", 1011 targetTable: "t2", 1012 sourceFieldType: querypb.Type_VARCHAR, 1013 targetVSchema: withTable, 1014 err: "a conflicting table named t2 already exists in the target vschema", 1015 }, { 1016 description: "unsharded", 1017 targetTable: "lkp", 1018 sourceFieldType: querypb.Type_INT64, 1019 targetVSchema: &vschemapb.Keyspace{}, 1020 out: &vschemapb.Keyspace{ 1021 Vindexes: map[string]*vschemapb.Vindex{}, 1022 Tables: map[string]*vschemapb.Table{ 1023 "lkp": {}, 1024 }, 1025 }, 1026 }, { 1027 description: "invalid column type", 1028 targetTable: "lkp", 1029 sourceFieldType: querypb.Type_SET, 1030 targetVSchema: &vschemapb.Keyspace{Sharded: true}, 1031 err: "type SET is not recommended for a vindex", 1032 }} 1033 for _, tcase := range testcases { 1034 env.tmc.schema[ms.SourceKeyspace+".t1"] = &tabletmanagerdatapb.SchemaDefinition{ 1035 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{{ 1036 Fields: []*querypb.Field{{ 1037 Name: "col2", 1038 Type: tcase.sourceFieldType, 1039 }}, 1040 Schema: sourceSchema, 1041 }}, 1042 } 1043 specs.Vindexes["v"].Params["table"] = fmt.Sprintf("%s.%s", ms.TargetKeyspace, tcase.targetTable) 1044 if err := env.topoServ.SaveVSchema(context.Background(), ms.TargetKeyspace, tcase.targetVSchema); err != nil { 1045 t.Fatal(err) 1046 } 1047 1048 _, _, got, err := env.wr.prepareCreateLookup(context.Background(), ms.SourceKeyspace, specs, false) 1049 if tcase.err != "" { 1050 if err == nil || !strings.Contains(err.Error(), tcase.err) { 1051 t.Errorf("prepareCreateLookup(%s) err: %v, must contain %v", tcase.description, err, tcase.err) 1052 } 1053 continue 1054 } 1055 require.NoError(t, err) 1056 utils.MustMatch(t, tcase.out, got, tcase.description) 1057 } 1058 } 1059 1060 func TestCreateLookupVindexSameKeyspace(t *testing.T) { 1061 ms := &vtctldatapb.MaterializeSettings{ 1062 SourceKeyspace: "ks", 1063 TargetKeyspace: "ks", 1064 } 1065 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"}) 1066 defer env.close() 1067 1068 specs := &vschemapb.Keyspace{ 1069 Vindexes: map[string]*vschemapb.Vindex{ 1070 "v": { 1071 Type: "lookup_unique", 1072 Params: map[string]string{ 1073 "table": "ks.lkp", 1074 "from": "c1", 1075 "to": "c2", 1076 }, 1077 Owner: "t1", 1078 }, 1079 }, 1080 Tables: map[string]*vschemapb.Table{ 1081 "t1": { 1082 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1083 Name: "v", 1084 Column: "col2", 1085 }}, 1086 }, 1087 }, 1088 } 1089 // Dummy sourceSchema 1090 sourceSchema := "CREATE TABLE `t1` (\n" + 1091 " `col1` int(11) NOT NULL AUTO_INCREMENT,\n" + 1092 " `col2` int(11) DEFAULT NULL,\n" + 1093 " PRIMARY KEY (`id`)\n" + 1094 ") ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1" 1095 1096 vschema := &vschemapb.Keyspace{ 1097 Sharded: true, 1098 Vindexes: map[string]*vschemapb.Vindex{ 1099 "hash": { 1100 Type: "hash", 1101 }, 1102 }, 1103 Tables: map[string]*vschemapb.Table{ 1104 "t1": { 1105 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1106 Name: "hash", 1107 Column: "col1", 1108 }}, 1109 }, 1110 }, 1111 } 1112 want := &vschemapb.Keyspace{ 1113 Sharded: true, 1114 Vindexes: map[string]*vschemapb.Vindex{ 1115 "hash": { 1116 Type: "hash", 1117 }, 1118 "v": { 1119 Type: "lookup_unique", 1120 Params: map[string]string{ 1121 "table": "ks.lkp", 1122 "from": "c1", 1123 "to": "c2", 1124 "write_only": "true", 1125 }, 1126 Owner: "t1", 1127 }, 1128 }, 1129 Tables: map[string]*vschemapb.Table{ 1130 "t1": { 1131 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1132 Name: "hash", 1133 Column: "col1", 1134 }, { 1135 Name: "v", 1136 Column: "col2", 1137 }}, 1138 }, 1139 "lkp": { 1140 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1141 Column: "c1", 1142 Name: "hash", 1143 }}, 1144 }, 1145 }, 1146 } 1147 env.tmc.schema[ms.SourceKeyspace+".t1"] = &tabletmanagerdatapb.SchemaDefinition{ 1148 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{{ 1149 Fields: []*querypb.Field{{ 1150 Name: "col1", 1151 Type: querypb.Type_INT64, 1152 }, { 1153 Name: "col2", 1154 Type: querypb.Type_INT64, 1155 }}, 1156 Schema: sourceSchema, 1157 }}, 1158 } 1159 if err := env.topoServ.SaveVSchema(context.Background(), ms.SourceKeyspace, vschema); err != nil { 1160 t.Fatal(err) 1161 } 1162 1163 _, got, _, err := env.wr.prepareCreateLookup(context.Background(), ms.SourceKeyspace, specs, false) 1164 require.NoError(t, err) 1165 if !proto.Equal(got, want) { 1166 t.Errorf("same keyspace: got:\n%v, want\n%v", got, want) 1167 } 1168 } 1169 func TestCreateCustomizedVindex(t *testing.T) { 1170 ms := &vtctldatapb.MaterializeSettings{ 1171 SourceKeyspace: "ks", 1172 TargetKeyspace: "ks", 1173 } 1174 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"}) 1175 defer env.close() 1176 1177 specs := &vschemapb.Keyspace{ 1178 Vindexes: map[string]*vschemapb.Vindex{ 1179 "v": { 1180 Type: "lookup_unique", 1181 Params: map[string]string{ 1182 "table": "ks.lkp", 1183 "from": "c1", 1184 "to": "col2", 1185 "data_type": "bigint(20)", 1186 }, 1187 Owner: "t1", 1188 }, 1189 }, 1190 Tables: map[string]*vschemapb.Table{ 1191 "t1": { 1192 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1193 Name: "v", 1194 Column: "col2", 1195 }}, 1196 }, 1197 }, 1198 } 1199 // Dummy sourceSchema 1200 sourceSchema := "CREATE TABLE `t1` (\n" + 1201 " `col1` int(11) NOT NULL AUTO_INCREMENT,\n" + 1202 " `col2` int(11) DEFAULT NULL,\n" + 1203 " PRIMARY KEY (`id`)\n" + 1204 ") ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1" 1205 1206 vschema := &vschemapb.Keyspace{ 1207 Sharded: true, 1208 Vindexes: map[string]*vschemapb.Vindex{ 1209 "hash": { 1210 Type: "hash", 1211 }, 1212 }, 1213 Tables: map[string]*vschemapb.Table{ 1214 "t1": { 1215 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1216 Name: "hash", 1217 Column: "col1", 1218 }}, 1219 }, 1220 }, 1221 } 1222 want := &vschemapb.Keyspace{ 1223 Sharded: true, 1224 Vindexes: map[string]*vschemapb.Vindex{ 1225 "hash": { 1226 Type: "hash", 1227 }, 1228 "v": { 1229 Type: "lookup_unique", 1230 Params: map[string]string{ 1231 "table": "ks.lkp", 1232 "from": "c1", 1233 "to": "col2", 1234 "data_type": "bigint(20)", 1235 "write_only": "true", 1236 }, 1237 Owner: "t1", 1238 }, 1239 }, 1240 Tables: map[string]*vschemapb.Table{ 1241 "t1": { 1242 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1243 Name: "hash", 1244 Column: "col1", 1245 }, { 1246 Name: "v", 1247 Column: "col2", 1248 }}, 1249 }, 1250 "lkp": { 1251 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1252 Column: "c1", 1253 Name: "hash", 1254 }}, 1255 }, 1256 }, 1257 } 1258 env.tmc.schema[ms.SourceKeyspace+".t1"] = &tabletmanagerdatapb.SchemaDefinition{ 1259 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{{ 1260 Fields: []*querypb.Field{{ 1261 Name: "col1", 1262 Type: querypb.Type_INT64, 1263 }, { 1264 Name: "col2", 1265 Type: querypb.Type_INT64, 1266 }}, 1267 Schema: sourceSchema, 1268 }}, 1269 } 1270 if err := env.topoServ.SaveVSchema(context.Background(), ms.SourceKeyspace, vschema); err != nil { 1271 t.Fatal(err) 1272 } 1273 1274 _, got, _, err := env.wr.prepareCreateLookup(context.Background(), ms.SourceKeyspace, specs, false) 1275 require.NoError(t, err) 1276 if !proto.Equal(got, want) { 1277 t.Errorf("customize create lookup error same: got:\n%v, want\n%v", got, want) 1278 } 1279 } 1280 1281 func TestStopAfterCopyFlag(t *testing.T) { 1282 ms := &vtctldatapb.MaterializeSettings{ 1283 SourceKeyspace: "ks", 1284 TargetKeyspace: "ks", 1285 } 1286 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"}) 1287 defer env.close() 1288 specs := &vschemapb.Keyspace{ 1289 Vindexes: map[string]*vschemapb.Vindex{ 1290 "v": { 1291 Type: "lookup_unique", 1292 Params: map[string]string{ 1293 "table": "ks.lkp", 1294 "from": "c1", 1295 "to": "col2", 1296 }, 1297 Owner: "t1", 1298 }, 1299 }, 1300 Tables: map[string]*vschemapb.Table{ 1301 "t1": { 1302 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1303 Name: "v", 1304 Column: "col2", 1305 }}, 1306 }, 1307 }, 1308 } 1309 // Dummy sourceSchema 1310 sourceSchema := "CREATE TABLE `t1` (\n" + 1311 " `col1` int(11) NOT NULL AUTO_INCREMENT,\n" + 1312 " `col2` int(11) DEFAULT NULL,\n" + 1313 " PRIMARY KEY (`id`)\n" + 1314 ") ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1" 1315 1316 vschema := &vschemapb.Keyspace{ 1317 Sharded: true, 1318 Vindexes: map[string]*vschemapb.Vindex{ 1319 "hash": { 1320 Type: "hash", 1321 }, 1322 }, 1323 Tables: map[string]*vschemapb.Table{ 1324 "t1": { 1325 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1326 Name: "hash", 1327 Column: "col1", 1328 }}, 1329 }, 1330 }, 1331 } 1332 env.tmc.schema[ms.SourceKeyspace+".t1"] = &tabletmanagerdatapb.SchemaDefinition{ 1333 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{{ 1334 Fields: []*querypb.Field{{ 1335 Name: "col1", 1336 Type: querypb.Type_INT64, 1337 }, { 1338 Name: "col2", 1339 Type: querypb.Type_INT64, 1340 }}, 1341 Schema: sourceSchema, 1342 }}, 1343 } 1344 if err := env.topoServ.SaveVSchema(context.Background(), ms.SourceKeyspace, vschema); err != nil { 1345 t.Fatal(err) 1346 } 1347 1348 ms1, _, _, err := env.wr.prepareCreateLookup(context.Background(), ms.SourceKeyspace, specs, false) 1349 require.NoError(t, err) 1350 require.Equal(t, ms1.StopAfterCopy, true) 1351 1352 ms2, _, _, err := env.wr.prepareCreateLookup(context.Background(), ms.SourceKeyspace, specs, true) 1353 require.NoError(t, err) 1354 require.Equal(t, ms2.StopAfterCopy, false) 1355 } 1356 1357 func TestCreateLookupVindexFailures(t *testing.T) { 1358 topoServ := memorytopo.NewServer("cell") 1359 wr := New(logutil.NewConsoleLogger(), topoServ, nil) 1360 1361 unique := map[string]*vschemapb.Vindex{ 1362 "v": { 1363 Type: "lookup_unique", 1364 Params: map[string]string{ 1365 "table": "targetks.t", 1366 "from": "c1", 1367 "to": "c2", 1368 }, 1369 }, 1370 } 1371 1372 vs := &vschemapb.Keyspace{ 1373 Sharded: true, 1374 Vindexes: map[string]*vschemapb.Vindex{ 1375 "other": { 1376 Type: "hash", 1377 }, 1378 "v": { 1379 Type: "lookup_unique", 1380 Params: map[string]string{ 1381 "table": "targetks.t", 1382 "from": "c1", 1383 "to": "c2", 1384 "write_only": "true", 1385 }, 1386 }, 1387 }, 1388 Tables: map[string]*vschemapb.Table{ 1389 "t1": { 1390 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1391 Column: "c1", 1392 Name: "v", 1393 }}, 1394 }, 1395 }, 1396 } 1397 if err := topoServ.SaveVSchema(context.Background(), "sourceks", vs); err != nil { 1398 t.Fatal(err) 1399 } 1400 if err := topoServ.SaveVSchema(context.Background(), "targetks", &vschemapb.Keyspace{}); err != nil { 1401 t.Fatal(err) 1402 } 1403 1404 testcases := []struct { 1405 description string 1406 input *vschemapb.Keyspace 1407 err string 1408 }{{ 1409 description: "dup vindex", 1410 input: &vschemapb.Keyspace{ 1411 Vindexes: map[string]*vschemapb.Vindex{ 1412 "v1": { 1413 Type: "hash", 1414 }, 1415 "v2": { 1416 Type: "hash", 1417 }, 1418 }, 1419 }, 1420 err: "only one vindex must be specified in the specs", 1421 }, { 1422 description: "not a lookup", 1423 input: &vschemapb.Keyspace{ 1424 Vindexes: map[string]*vschemapb.Vindex{ 1425 "v": { 1426 Type: "hash", 1427 }, 1428 }, 1429 }, 1430 err: "vindex hash is not a lookup type", 1431 }, { 1432 description: "unqualified table", 1433 input: &vschemapb.Keyspace{ 1434 Vindexes: map[string]*vschemapb.Vindex{ 1435 "v": { 1436 Type: "lookup", 1437 Params: map[string]string{ 1438 "table": "t", 1439 }, 1440 }, 1441 }, 1442 }, 1443 err: "vindex table name must be in the form <keyspace>.<table>", 1444 }, { 1445 description: "unique lookup should have only one from column", 1446 input: &vschemapb.Keyspace{ 1447 Vindexes: map[string]*vschemapb.Vindex{ 1448 "v": { 1449 Type: "lookup_unique", 1450 Params: map[string]string{ 1451 "table": "targetks.t", 1452 "from": "c1,c2", 1453 }, 1454 }, 1455 }, 1456 }, 1457 err: "unique vindex 'from' should have only one column", 1458 }, { 1459 description: "non-unique lookup should have more than one column", 1460 input: &vschemapb.Keyspace{ 1461 Vindexes: map[string]*vschemapb.Vindex{ 1462 "v": { 1463 Type: "lookup", 1464 Params: map[string]string{ 1465 "table": "targetks.t", 1466 "from": "c1", 1467 }, 1468 }, 1469 }, 1470 }, 1471 err: "non-unique vindex 'from' should have more than one column", 1472 }, { 1473 description: "vindex not found", 1474 input: &vschemapb.Keyspace{ 1475 Vindexes: map[string]*vschemapb.Vindex{ 1476 "v": { 1477 Type: "lookup_noexist", 1478 Params: map[string]string{ 1479 "table": "targetks.t", 1480 "from": "c1,c2", 1481 }, 1482 }, 1483 }, 1484 }, 1485 err: `vindexType "lookup_noexist" not found`, 1486 }, { 1487 description: "only one table", 1488 input: &vschemapb.Keyspace{ 1489 Vindexes: unique, 1490 }, 1491 err: "exactly one table must be specified in the specs", 1492 }, { 1493 description: "only one colvindex", 1494 input: &vschemapb.Keyspace{ 1495 Vindexes: unique, 1496 Tables: map[string]*vschemapb.Table{ 1497 "t1": {}, 1498 }, 1499 }, 1500 err: "exactly one ColumnVindex must be specified for the table", 1501 }, { 1502 description: "vindex name must match", 1503 input: &vschemapb.Keyspace{ 1504 Vindexes: unique, 1505 Tables: map[string]*vschemapb.Table{ 1506 "t1": { 1507 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1508 Name: "other", 1509 }}, 1510 }, 1511 }, 1512 }, 1513 err: "ColumnVindex name must match vindex name: other vs v", 1514 }, { 1515 description: "owner must match", 1516 input: &vschemapb.Keyspace{ 1517 Vindexes: map[string]*vschemapb.Vindex{ 1518 "v": { 1519 Type: "lookup_unique", 1520 Params: map[string]string{ 1521 "table": "targetks.t", 1522 "from": "c1", 1523 }, 1524 Owner: "otherTable", 1525 }, 1526 }, 1527 Tables: map[string]*vschemapb.Table{ 1528 "t1": { 1529 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1530 Name: "v", 1531 }}, 1532 }, 1533 }, 1534 }, 1535 err: "vindex owner must match table name: otherTable vs t1", 1536 }, { 1537 description: "owner must match", 1538 input: &vschemapb.Keyspace{ 1539 Vindexes: unique, 1540 Tables: map[string]*vschemapb.Table{ 1541 "t1": { 1542 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1543 Name: "v", 1544 }}, 1545 }, 1546 }, 1547 }, 1548 err: "at least one column must be specified in ColumnVindexes", 1549 }, { 1550 description: "columnvindex length mismatch", 1551 input: &vschemapb.Keyspace{ 1552 Vindexes: unique, 1553 Tables: map[string]*vschemapb.Table{ 1554 "t1": { 1555 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1556 Name: "v", 1557 Columns: []string{"col1", "col2"}, 1558 }}, 1559 }, 1560 }, 1561 }, 1562 err: "length of table columns differes from length of vindex columns", 1563 }, { 1564 description: "vindex mismatches with what's in vschema", 1565 input: &vschemapb.Keyspace{ 1566 Vindexes: map[string]*vschemapb.Vindex{ 1567 "other": { 1568 Type: "lookup_unique", 1569 Params: map[string]string{ 1570 "table": "targetks.t", 1571 "from": "c1", 1572 }, 1573 Owner: "t1", 1574 }, 1575 }, 1576 Tables: map[string]*vschemapb.Table{ 1577 "t1": { 1578 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1579 Name: "other", 1580 Column: "col", 1581 }}, 1582 }, 1583 }, 1584 }, 1585 err: "a conflicting vindex named other already exists in the source vschema", 1586 }, { 1587 description: "source table not in vschema", 1588 input: &vschemapb.Keyspace{ 1589 Vindexes: unique, 1590 Tables: map[string]*vschemapb.Table{ 1591 "other": { 1592 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1593 Name: "v", 1594 Column: "col", 1595 }}, 1596 }, 1597 }, 1598 }, 1599 err: "source table other not found in vschema", 1600 }, { 1601 description: "colvindex already exists in vschema", 1602 input: &vschemapb.Keyspace{ 1603 Vindexes: unique, 1604 Tables: map[string]*vschemapb.Table{ 1605 "t1": { 1606 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1607 Name: "v", 1608 Column: "c1", 1609 }}, 1610 }, 1611 }, 1612 }, 1613 err: "ColumnVindex for table t1 already exists: c1", 1614 }} 1615 for _, tcase := range testcases { 1616 err := wr.CreateLookupVindex(context.Background(), "sourceks", tcase.input, "", "", false) 1617 if !strings.Contains(err.Error(), tcase.err) { 1618 t.Errorf("CreateLookupVindex(%s) err: %v, must contain %v", tcase.description, err, tcase.err) 1619 } 1620 } 1621 } 1622 1623 func TestExternalizeVindex(t *testing.T) { 1624 ms := &vtctldatapb.MaterializeSettings{ 1625 SourceKeyspace: "sourceks", 1626 TargetKeyspace: "targetks", 1627 } 1628 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"-80", "80-"}) 1629 defer env.close() 1630 1631 sourceVSchema := &vschemapb.Keyspace{ 1632 Sharded: true, 1633 Vindexes: map[string]*vschemapb.Vindex{ 1634 "hash": { 1635 Type: "hash", 1636 }, 1637 "owned": { 1638 Type: "lookup_unique", 1639 Params: map[string]string{ 1640 "table": "targetks.lkp", 1641 "from": "c1", 1642 "to": "c2", 1643 "write_only": "true", 1644 }, 1645 Owner: "t1", 1646 }, 1647 "unowned": { 1648 Type: "lookup_unique", 1649 Params: map[string]string{ 1650 "table": "targetks.lkp", 1651 "from": "c1", 1652 "to": "c2", 1653 "write_only": "true", 1654 }, 1655 }, 1656 "bad": { 1657 Type: "lookup_unique", 1658 Params: map[string]string{ 1659 "table": "unqualified", 1660 "from": "c1", 1661 "to": "c2", 1662 }, 1663 }, 1664 }, 1665 Tables: map[string]*vschemapb.Table{ 1666 "t1": { 1667 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1668 Name: "hash", 1669 Column: "col1", 1670 }, { 1671 Name: "owned", 1672 Column: "col2", 1673 }}, 1674 }, 1675 }, 1676 } 1677 fields := sqltypes.MakeTestFields( 1678 "id|state|message|source", 1679 "int64|varbinary|varbinary|blob", 1680 ) 1681 sourceStopAfterCopy := `keyspace:"sourceKs",shard:"0",filter:{rules:{match:"owned" filter:"select * from t1 where in_keyrange(col1, 'sourceKs.hash', '-80')"}} stop_after_copy:true` 1682 sourceKeepRunningAfterCopy := `keyspace:"sourceKs",shard:"0",filter:{rules:{match:"owned" filter:"select * from t1 where in_keyrange(col1, 'sourceKs.hash', '-80')"}}` 1683 running := sqltypes.MakeTestResult(fields, "1|Running|msg|"+sourceKeepRunningAfterCopy) 1684 stopped := sqltypes.MakeTestResult(fields, "1|Stopped|Stopped after copy|"+sourceStopAfterCopy) 1685 testcases := []struct { 1686 input string 1687 vrResponse *sqltypes.Result 1688 expectDelete bool 1689 err string 1690 }{{ 1691 input: "sourceks.owned", 1692 vrResponse: stopped, 1693 expectDelete: true, 1694 }, { 1695 input: "sourceks.unowned", 1696 vrResponse: running, 1697 }, { 1698 input: "unqualified", 1699 err: "vindex name should be of the form keyspace.vindex: unqualified", 1700 }, { 1701 input: "sourceks.absent", 1702 err: "vindex sourceks.absent not found in vschema", 1703 }, { 1704 input: "sourceks.bad", 1705 err: "vindex table name must be in the form <keyspace>.<table>. Got: unqualified", 1706 }, { 1707 input: "sourceks.owned", 1708 vrResponse: running, 1709 expectDelete: true, 1710 }, { 1711 input: "sourceks.unowned", 1712 vrResponse: stopped, 1713 err: "is not in Running state", 1714 }} 1715 for _, tcase := range testcases { 1716 // Resave the source schema for every iteration. 1717 if err := env.topoServ.SaveVSchema(context.Background(), ms.SourceKeyspace, sourceVSchema); err != nil { 1718 t.Fatal(err) 1719 } 1720 if tcase.vrResponse != nil { 1721 validationQuery := "select id, state, message, source from _vt.vreplication where workflow='lkp_vdx' and db_name='vt_targetks'" 1722 env.tmc.expectVRQuery(200, validationQuery, tcase.vrResponse) 1723 env.tmc.expectVRQuery(210, validationQuery, tcase.vrResponse) 1724 } 1725 1726 if tcase.expectDelete { 1727 deleteQuery := "delete from _vt.vreplication where db_name='vt_targetks' and workflow='lkp_vdx'" 1728 env.tmc.expectVRQuery(200, deleteQuery, &sqltypes.Result{}) 1729 env.tmc.expectVRQuery(210, deleteQuery, &sqltypes.Result{}) 1730 } 1731 1732 err := env.wr.ExternalizeVindex(context.Background(), tcase.input) 1733 if tcase.err != "" { 1734 if err == nil || !strings.Contains(err.Error(), tcase.err) { 1735 t.Errorf("ExternalizeVindex(%s) err: %v, must contain %v", tcase.input, err, tcase.err) 1736 } 1737 continue 1738 } 1739 require.NoError(t, err) 1740 1741 outvschema, err := env.topoServ.GetVSchema(context.Background(), ms.SourceKeyspace) 1742 require.NoError(t, err) 1743 vindexName := strings.Split(tcase.input, ".")[1] 1744 require.NotContains(t, outvschema.Vindexes[vindexName].Params, "write_only", tcase.input) 1745 } 1746 } 1747 1748 func TestMaterializerOneToOne(t *testing.T) { 1749 ms := &vtctldatapb.MaterializeSettings{ 1750 Workflow: "workflow", 1751 SourceKeyspace: "sourceks", 1752 TargetKeyspace: "targetks", 1753 TableSettings: []*vtctldatapb.TableMaterializeSettings{ 1754 { 1755 TargetTable: "t1", 1756 SourceExpression: "select * from t1", 1757 CreateDdl: "t1ddl", 1758 }, 1759 { 1760 TargetTable: "t2", 1761 SourceExpression: "select * from t3", 1762 CreateDdl: "t2ddl", 1763 }, 1764 { 1765 TargetTable: "t4", 1766 SourceExpression: "", // empty 1767 CreateDdl: "t4ddl", 1768 }, 1769 }, 1770 Cell: "zone1", 1771 TabletTypes: "primary,rdonly", 1772 } 1773 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"}) 1774 defer env.close() 1775 1776 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 1777 env.tmc.expectVRQuery( 1778 200, 1779 insertPrefix+ 1780 `\(`+ 1781 `'workflow', `+ 1782 (`'keyspace:\\"sourceks\\" shard:\\"0\\" `+ 1783 `filter:{`+ 1784 `rules:{match:\\"t1\\" filter:\\"select.*t1\\"} `+ 1785 `rules:{match:\\"t2\\" filter:\\"select.*t3\\"} `+ 1786 `rules:{match:\\"t4\\"}`+ 1787 `}', `)+ 1788 `'', [0-9]*, [0-9]*, 'zone1', 'primary,rdonly', [0-9]*, 0, 'Stopped', 'vt_targetks', 0, 0, false`+ 1789 `\)`+eol, 1790 &sqltypes.Result{}, 1791 ) 1792 env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{}) 1793 1794 err := env.wr.Materialize(context.Background(), ms) 1795 require.NoError(t, err) 1796 env.tmc.verifyQueries(t) 1797 } 1798 1799 func TestMaterializerManyToOne(t *testing.T) { 1800 ms := &vtctldatapb.MaterializeSettings{ 1801 Workflow: "workflow", 1802 SourceKeyspace: "sourceks", 1803 TargetKeyspace: "targetks", 1804 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 1805 TargetTable: "t1", 1806 SourceExpression: "select * from t1", 1807 CreateDdl: "t1ddl", 1808 }, { 1809 TargetTable: "t2", 1810 SourceExpression: "select * from t3", 1811 CreateDdl: "t2ddl", 1812 }}, 1813 } 1814 env := newTestMaterializerEnv(t, ms, []string{"-80", "80-"}, []string{"0"}) 1815 defer env.close() 1816 1817 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 1818 env.tmc.expectVRQuery( 1819 200, 1820 insertPrefix+ 1821 `\('workflow', 'keyspace:\\"sourceks\\" shard:\\"-80\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1\\"} rules:{match:\\"t2\\" filter:\\"select.*t3\\"}}', '', [0-9]*, [0-9]*, '', '', [0-9]*, 0, 'Stopped', 'vt_targetks', 0, 0, false\)`+ 1822 `, `+ 1823 `\('workflow', 'keyspace:\\"sourceks\\" shard:\\"80-\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1\\"} rules:{match:\\"t2\\" filter:\\"select.*t3\\"}}', '', [0-9]*, [0-9]*, '', '', [0-9]*, 0, 'Stopped', 'vt_targetks', 0, 0, false\)`+ 1824 eol, 1825 &sqltypes.Result{}, 1826 ) 1827 env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{}) 1828 1829 err := env.wr.Materialize(context.Background(), ms) 1830 require.NoError(t, err) 1831 env.tmc.verifyQueries(t) 1832 } 1833 1834 func TestMaterializerOneToMany(t *testing.T) { 1835 ms := &vtctldatapb.MaterializeSettings{ 1836 Workflow: "workflow", 1837 SourceKeyspace: "sourceks", 1838 TargetKeyspace: "targetks", 1839 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 1840 TargetTable: "t1", 1841 SourceExpression: "select * from t1", 1842 CreateDdl: "t1ddl", 1843 }}, 1844 } 1845 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"-80", "80-"}) 1846 defer env.close() 1847 1848 vs := &vschemapb.Keyspace{ 1849 Sharded: true, 1850 Vindexes: map[string]*vschemapb.Vindex{ 1851 "hash": { 1852 Type: "hash", 1853 }, 1854 }, 1855 Tables: map[string]*vschemapb.Table{ 1856 "t1": { 1857 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1858 Column: "c1", 1859 Name: "hash", 1860 }}, 1861 }, 1862 }, 1863 } 1864 1865 if err := env.topoServ.SaveVSchema(context.Background(), "targetks", vs); err != nil { 1866 t.Fatal(err) 1867 } 1868 1869 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 1870 env.tmc.expectVRQuery(210, mzSelectFrozenQuery, &sqltypes.Result{}) 1871 env.tmc.expectVRQuery( 1872 200, 1873 insertPrefix+ 1874 `.*shard:\\"0\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c1.*targetks\.hash.*-80.*`, 1875 &sqltypes.Result{}, 1876 ) 1877 env.tmc.expectVRQuery( 1878 210, 1879 insertPrefix+ 1880 `.*shard:\\"0\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c1.*targetks\.hash.*80-.*`, 1881 &sqltypes.Result{}, 1882 ) 1883 env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{}) 1884 env.tmc.expectVRQuery(210, mzUpdateQuery, &sqltypes.Result{}) 1885 1886 err := env.wr.Materialize(context.Background(), ms) 1887 require.NoError(t, err) 1888 env.tmc.verifyQueries(t) 1889 } 1890 1891 func TestMaterializerManyToMany(t *testing.T) { 1892 ms := &vtctldatapb.MaterializeSettings{ 1893 Workflow: "workflow", 1894 SourceKeyspace: "sourceks", 1895 TargetKeyspace: "targetks", 1896 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 1897 TargetTable: "t1", 1898 SourceExpression: "select * from t1", 1899 CreateDdl: "t1ddl", 1900 }}, 1901 } 1902 env := newTestMaterializerEnv(t, ms, []string{"-40", "40-"}, []string{"-80", "80-"}) 1903 defer env.close() 1904 1905 vs := &vschemapb.Keyspace{ 1906 Sharded: true, 1907 Vindexes: map[string]*vschemapb.Vindex{ 1908 "hash": { 1909 Type: "hash", 1910 }, 1911 }, 1912 Tables: map[string]*vschemapb.Table{ 1913 "t1": { 1914 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1915 Column: "c1", 1916 Name: "hash", 1917 }}, 1918 }, 1919 }, 1920 } 1921 1922 if err := env.topoServ.SaveVSchema(context.Background(), "targetks", vs); err != nil { 1923 t.Fatal(err) 1924 } 1925 1926 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 1927 env.tmc.expectVRQuery(210, mzSelectFrozenQuery, &sqltypes.Result{}) 1928 env.tmc.expectVRQuery( 1929 200, 1930 insertPrefix+ 1931 `.*shard:\\"-40\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c1.*targetks\.hash.*-80.*`+ 1932 `.*shard:\\"40-\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c1.*targetks\.hash.*-80.*`, 1933 &sqltypes.Result{}, 1934 ) 1935 env.tmc.expectVRQuery( 1936 210, 1937 insertPrefix+ 1938 `.*shard:\\"-40\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c1.*targetks\.hash.*80-.*`+ 1939 `.*shard:\\"40-\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c1.*targetks\.hash.*80-.*`, 1940 &sqltypes.Result{}, 1941 ) 1942 env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{}) 1943 env.tmc.expectVRQuery(210, mzUpdateQuery, &sqltypes.Result{}) 1944 err := env.wr.Materialize(context.Background(), ms) 1945 require.NoError(t, err) 1946 env.tmc.verifyQueries(t) 1947 } 1948 1949 func TestMaterializerMulticolumnVindex(t *testing.T) { 1950 ms := &vtctldatapb.MaterializeSettings{ 1951 Workflow: "workflow", 1952 SourceKeyspace: "sourceks", 1953 TargetKeyspace: "targetks", 1954 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 1955 TargetTable: "t1", 1956 SourceExpression: "select * from t1", 1957 CreateDdl: "t1ddl", 1958 }}, 1959 } 1960 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"-80", "80-"}) 1961 defer env.close() 1962 1963 vs := &vschemapb.Keyspace{ 1964 Sharded: true, 1965 Vindexes: map[string]*vschemapb.Vindex{ 1966 "region": { 1967 Type: "region_experimental", 1968 Params: map[string]string{ 1969 "region_bytes": "1", 1970 }, 1971 }, 1972 }, 1973 Tables: map[string]*vschemapb.Table{ 1974 "t1": { 1975 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1976 Columns: []string{"c1", "c2"}, 1977 Name: "region", 1978 }}, 1979 }, 1980 }, 1981 } 1982 1983 if err := env.topoServ.SaveVSchema(context.Background(), "targetks", vs); err != nil { 1984 t.Fatal(err) 1985 } 1986 1987 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 1988 env.tmc.expectVRQuery(210, mzSelectFrozenQuery, &sqltypes.Result{}) 1989 env.tmc.expectVRQuery( 1990 200, 1991 insertPrefix+ 1992 `.*shard:\\"0\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c1, c2.*targetks\.region.*-80.*`, 1993 &sqltypes.Result{}, 1994 ) 1995 env.tmc.expectVRQuery( 1996 210, 1997 insertPrefix+ 1998 `.*shard:\\"0\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c1, c2.*targetks\.region.*80-.*`, 1999 &sqltypes.Result{}, 2000 ) 2001 env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{}) 2002 env.tmc.expectVRQuery(210, mzUpdateQuery, &sqltypes.Result{}) 2003 2004 err := env.wr.Materialize(context.Background(), ms) 2005 require.NoError(t, err) 2006 env.tmc.verifyQueries(t) 2007 } 2008 2009 func TestMaterializerDeploySchema(t *testing.T) { 2010 ms := &vtctldatapb.MaterializeSettings{ 2011 Workflow: "workflow", 2012 SourceKeyspace: "sourceks", 2013 TargetKeyspace: "targetks", 2014 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 2015 TargetTable: "t1", 2016 SourceExpression: "select * from t1", 2017 CreateDdl: "t1ddl", 2018 }, { 2019 TargetTable: "t2", 2020 SourceExpression: "select * from t3", 2021 CreateDdl: "t2ddl", 2022 }}, 2023 } 2024 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"}) 2025 defer env.close() 2026 2027 delete(env.tmc.schema, "targetks.t2") 2028 2029 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 2030 env.tmc.expectVRQuery(200, `t2ddl`, &sqltypes.Result{}) 2031 env.tmc.expectVRQuery( 2032 200, 2033 insertPrefix+ 2034 `\('workflow', 'keyspace:\\"sourceks\\" shard:\\"0\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1\\"} rules:{match:\\"t2\\" filter:\\"select.*t3\\"}}', '', [0-9]*, [0-9]*, '', '', [0-9]*, 0, 'Stopped', 'vt_targetks', 0, 0, false\)`+ 2035 eol, 2036 &sqltypes.Result{}, 2037 ) 2038 env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{}) 2039 2040 err := env.wr.Materialize(context.Background(), ms) 2041 require.NoError(t, err) 2042 env.tmc.verifyQueries(t) 2043 require.Equal(t, env.tmc.getSchemaRequestCount(100), 1) 2044 require.Equal(t, env.tmc.getSchemaRequestCount(200), 1) 2045 } 2046 2047 func TestMaterializerCopySchema(t *testing.T) { 2048 ms := &vtctldatapb.MaterializeSettings{ 2049 Workflow: "workflow", 2050 SourceKeyspace: "sourceks", 2051 TargetKeyspace: "targetks", 2052 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 2053 TargetTable: "t1", 2054 SourceExpression: "select * from t1", 2055 CreateDdl: "copy", 2056 }, { 2057 TargetTable: "t2", 2058 SourceExpression: "select * from t3", 2059 CreateDdl: "t2ddl", 2060 }}, 2061 } 2062 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"}) 2063 defer env.close() 2064 2065 delete(env.tmc.schema, "targetks.t1") 2066 2067 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 2068 env.tmc.expectVRQuery(200, `t1_schema`, &sqltypes.Result{}) 2069 env.tmc.expectVRQuery( 2070 200, 2071 insertPrefix+ 2072 `\('workflow', 'keyspace:\\"sourceks\\" shard:\\"0\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1\\"} rules:{match:\\"t2\\" filter:\\"select.*t3\\"}}', '', [0-9]*, [0-9]*, '', '', [0-9]*, 0, 'Stopped', 'vt_targetks', 0, 0, false\)`+ 2073 eol, 2074 &sqltypes.Result{}, 2075 ) 2076 env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{}) 2077 2078 err := env.wr.Materialize(context.Background(), ms) 2079 require.NoError(t, err) 2080 env.tmc.verifyQueries(t) 2081 require.Equal(t, env.tmc.getSchemaRequestCount(100), 1) 2082 require.Equal(t, env.tmc.getSchemaRequestCount(200), 1) 2083 2084 } 2085 2086 func TestMaterializerExplicitColumns(t *testing.T) { 2087 ms := &vtctldatapb.MaterializeSettings{ 2088 Workflow: "workflow", 2089 SourceKeyspace: "sourceks", 2090 TargetKeyspace: "targetks", 2091 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 2092 TargetTable: "t1", 2093 SourceExpression: "select c1, c1+c2, c2 from t1", 2094 CreateDdl: "t1ddl", 2095 }}, 2096 } 2097 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"-80", "80-"}) 2098 defer env.close() 2099 2100 vs := &vschemapb.Keyspace{ 2101 Sharded: true, 2102 Vindexes: map[string]*vschemapb.Vindex{ 2103 "region": { 2104 Type: "region_experimental", 2105 Params: map[string]string{ 2106 "region_bytes": "1", 2107 }, 2108 }, 2109 }, 2110 Tables: map[string]*vschemapb.Table{ 2111 "t1": { 2112 ColumnVindexes: []*vschemapb.ColumnVindex{{ 2113 Columns: []string{"c1", "c2"}, 2114 Name: "region", 2115 }}, 2116 }, 2117 }, 2118 } 2119 2120 if err := env.topoServ.SaveVSchema(context.Background(), "targetks", vs); err != nil { 2121 t.Fatal(err) 2122 } 2123 2124 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 2125 env.tmc.expectVRQuery(210, mzSelectFrozenQuery, &sqltypes.Result{}) 2126 env.tmc.expectVRQuery( 2127 200, 2128 insertPrefix+ 2129 `.*shard:\\"0\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c1, c2.*targetks\.region.*-80.*`, 2130 &sqltypes.Result{}, 2131 ) 2132 env.tmc.expectVRQuery( 2133 210, 2134 insertPrefix+ 2135 `.*shard:\\"0\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c1, c2.*targetks\.region.*80-.*`, 2136 &sqltypes.Result{}, 2137 ) 2138 env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{}) 2139 env.tmc.expectVRQuery(210, mzUpdateQuery, &sqltypes.Result{}) 2140 2141 err := env.wr.Materialize(context.Background(), ms) 2142 require.NoError(t, err) 2143 env.tmc.verifyQueries(t) 2144 } 2145 2146 func TestMaterializerRenamedColumns(t *testing.T) { 2147 ms := &vtctldatapb.MaterializeSettings{ 2148 Workflow: "workflow", 2149 SourceKeyspace: "sourceks", 2150 TargetKeyspace: "targetks", 2151 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 2152 TargetTable: "t1", 2153 SourceExpression: "select c3 as c1, c1+c2, c4 as c2 from t1", 2154 CreateDdl: "t1ddl", 2155 }}, 2156 } 2157 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"-80", "80-"}) 2158 defer env.close() 2159 2160 vs := &vschemapb.Keyspace{ 2161 Sharded: true, 2162 Vindexes: map[string]*vschemapb.Vindex{ 2163 "region": { 2164 Type: "region_experimental", 2165 Params: map[string]string{ 2166 "region_bytes": "1", 2167 }, 2168 }, 2169 }, 2170 Tables: map[string]*vschemapb.Table{ 2171 "t1": { 2172 ColumnVindexes: []*vschemapb.ColumnVindex{{ 2173 Columns: []string{"c1", "c2"}, 2174 Name: "region", 2175 }}, 2176 }, 2177 }, 2178 } 2179 2180 if err := env.topoServ.SaveVSchema(context.Background(), "targetks", vs); err != nil { 2181 t.Fatal(err) 2182 } 2183 2184 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 2185 env.tmc.expectVRQuery(210, mzSelectFrozenQuery, &sqltypes.Result{}) 2186 env.tmc.expectVRQuery( 2187 200, 2188 insertPrefix+ 2189 `.*shard:\\"0\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c3, c4.*targetks\.region.*-80.*`, 2190 &sqltypes.Result{}, 2191 ) 2192 env.tmc.expectVRQuery( 2193 210, 2194 insertPrefix+ 2195 `.*shard:\\"0\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c3, c4.*targetks\.region.*80-.*`, 2196 &sqltypes.Result{}, 2197 ) 2198 env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{}) 2199 env.tmc.expectVRQuery(210, mzUpdateQuery, &sqltypes.Result{}) 2200 2201 err := env.wr.Materialize(context.Background(), ms) 2202 require.NoError(t, err) 2203 env.tmc.verifyQueries(t) 2204 } 2205 2206 func TestMaterializerStopAfterCopy(t *testing.T) { 2207 ms := &vtctldatapb.MaterializeSettings{ 2208 Workflow: "workflow", 2209 SourceKeyspace: "sourceks", 2210 TargetKeyspace: "targetks", 2211 StopAfterCopy: true, 2212 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 2213 TargetTable: "t1", 2214 SourceExpression: "select * from t1", 2215 CreateDdl: "t1ddl", 2216 }, { 2217 TargetTable: "t2", 2218 SourceExpression: "select * from t3", 2219 CreateDdl: "t2ddl", 2220 }}, 2221 } 2222 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"}) 2223 defer env.close() 2224 2225 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 2226 env.tmc.expectVRQuery(200, insertPrefix+`.*stop_after_copy:true`, &sqltypes.Result{}) 2227 env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{}) 2228 2229 err := env.wr.Materialize(context.Background(), ms) 2230 require.NoError(t, err) 2231 env.tmc.verifyQueries(t) 2232 } 2233 2234 func TestMaterializerNoTargetVSchema(t *testing.T) { 2235 ms := &vtctldatapb.MaterializeSettings{ 2236 Workflow: "workflow", 2237 SourceKeyspace: "sourceks", 2238 TargetKeyspace: "targetks", 2239 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 2240 TargetTable: "t1", 2241 SourceExpression: "select * from t1", 2242 CreateDdl: "t1ddl", 2243 }}, 2244 } 2245 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"-80", "80-"}) 2246 defer env.close() 2247 2248 vs := &vschemapb.Keyspace{ 2249 Sharded: true, 2250 } 2251 2252 if err := env.topoServ.SaveVSchema(context.Background(), "targetks", vs); err != nil { 2253 t.Fatal(err) 2254 } 2255 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 2256 env.tmc.expectVRQuery(210, mzSelectFrozenQuery, &sqltypes.Result{}) 2257 err := env.wr.Materialize(context.Background(), ms) 2258 require.EqualError(t, err, "table t1 not found in vschema for keyspace targetks") 2259 } 2260 2261 func TestMaterializerNoDDL(t *testing.T) { 2262 ms := &vtctldatapb.MaterializeSettings{ 2263 Workflow: "workflow", 2264 SourceKeyspace: "sourceks", 2265 TargetKeyspace: "targetks", 2266 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 2267 TargetTable: "t1", 2268 SourceExpression: "select * from t1", 2269 CreateDdl: "", 2270 }}, 2271 } 2272 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"}) 2273 defer env.close() 2274 2275 delete(env.tmc.schema, "targetks.t1") 2276 2277 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 2278 err := env.wr.Materialize(context.Background(), ms) 2279 require.EqualError(t, err, "target table t1 does not exist and there is no create ddl defined") 2280 require.Equal(t, env.tmc.getSchemaRequestCount(100), 0) 2281 require.Equal(t, env.tmc.getSchemaRequestCount(200), 1) 2282 2283 } 2284 2285 func TestMaterializerNoSourcePrimary(t *testing.T) { 2286 ms := &vtctldatapb.MaterializeSettings{ 2287 Workflow: "workflow", 2288 SourceKeyspace: "sourceks", 2289 TargetKeyspace: "targetks", 2290 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 2291 TargetTable: "t1", 2292 SourceExpression: "select * from t1", 2293 CreateDdl: "copy", 2294 }}, 2295 } 2296 sources := []string{"0"} 2297 targets := []string{"0"} 2298 2299 // Copied from newTestMaterializerEnv 2300 env := &testMaterializerEnv{ 2301 ms: ms, 2302 sources: sources, 2303 targets: targets, 2304 tablets: make(map[int]*topodatapb.Tablet), 2305 topoServ: memorytopo.NewServer("cell"), 2306 cell: "cell", 2307 tmc: newTestMaterializerTMClient(), 2308 } 2309 env.wr = New(logutil.NewConsoleLogger(), env.topoServ, env.tmc) 2310 defer env.close() 2311 2312 tabletID := 100 2313 for _, shard := range sources { 2314 _ = env.addTablet(tabletID, env.ms.SourceKeyspace, shard, topodatapb.TabletType_REPLICA) 2315 tabletID += 10 2316 } 2317 tabletID = 200 2318 for _, shard := range targets { 2319 _ = env.addTablet(tabletID, env.ms.TargetKeyspace, shard, topodatapb.TabletType_PRIMARY) 2320 tabletID += 10 2321 } 2322 2323 // Skip the schema creation part. 2324 2325 env.expectValidation() 2326 2327 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 2328 err := env.wr.Materialize(context.Background(), ms) 2329 require.EqualError(t, err, "source shard must have a primary for copying schema: 0") 2330 } 2331 2332 func TestMaterializerTableMismatchNonCopy(t *testing.T) { 2333 ms := &vtctldatapb.MaterializeSettings{ 2334 Workflow: "workflow", 2335 SourceKeyspace: "sourceks", 2336 TargetKeyspace: "targetks", 2337 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 2338 TargetTable: "t1", 2339 SourceExpression: "select * from t2", 2340 CreateDdl: "", 2341 }}, 2342 } 2343 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"}) 2344 defer env.close() 2345 2346 delete(env.tmc.schema, "targetks.t1") 2347 2348 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 2349 err := env.wr.Materialize(context.Background(), ms) 2350 require.EqualError(t, err, "target table t1 does not exist and there is no create ddl defined") 2351 } 2352 2353 func TestMaterializerTableMismatchCopy(t *testing.T) { 2354 ms := &vtctldatapb.MaterializeSettings{ 2355 Workflow: "workflow", 2356 SourceKeyspace: "sourceks", 2357 TargetKeyspace: "targetks", 2358 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 2359 TargetTable: "t1", 2360 SourceExpression: "select * from t2", 2361 CreateDdl: "copy", 2362 }}, 2363 } 2364 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"}) 2365 defer env.close() 2366 2367 delete(env.tmc.schema, "targetks.t1") 2368 2369 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 2370 err := env.wr.Materialize(context.Background(), ms) 2371 require.EqualError(t, err, "source and target table names must match for copying schema: t2 vs t1") 2372 } 2373 2374 func TestMaterializerNoSourceTable(t *testing.T) { 2375 ms := &vtctldatapb.MaterializeSettings{ 2376 Workflow: "workflow", 2377 SourceKeyspace: "sourceks", 2378 TargetKeyspace: "targetks", 2379 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 2380 TargetTable: "t1", 2381 SourceExpression: "select * from t1", 2382 CreateDdl: "copy", 2383 }}, 2384 } 2385 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"}) 2386 defer env.close() 2387 2388 delete(env.tmc.schema, "targetks.t1") 2389 delete(env.tmc.schema, "sourceks.t1") 2390 2391 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 2392 err := env.wr.Materialize(context.Background(), ms) 2393 require.EqualError(t, err, "source table t1 does not exist") 2394 } 2395 2396 func TestMaterializerSyntaxError(t *testing.T) { 2397 ms := &vtctldatapb.MaterializeSettings{ 2398 Workflow: "workflow", 2399 SourceKeyspace: "sourceks", 2400 TargetKeyspace: "targetks", 2401 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 2402 TargetTable: "t1", 2403 SourceExpression: "bad query", 2404 CreateDdl: "t1ddl", 2405 }}, 2406 } 2407 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"}) 2408 defer env.close() 2409 2410 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 2411 err := env.wr.Materialize(context.Background(), ms) 2412 require.EqualError(t, err, "syntax error at position 4 near 'bad'") 2413 } 2414 2415 func TestMaterializerNotASelect(t *testing.T) { 2416 ms := &vtctldatapb.MaterializeSettings{ 2417 Workflow: "workflow", 2418 SourceKeyspace: "sourceks", 2419 TargetKeyspace: "targetks", 2420 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 2421 TargetTable: "t1", 2422 SourceExpression: "update t1 set val=1", 2423 CreateDdl: "t1ddl", 2424 }}, 2425 } 2426 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"}) 2427 defer env.close() 2428 2429 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 2430 err := env.wr.Materialize(context.Background(), ms) 2431 require.EqualError(t, err, "unrecognized statement: update t1 set val=1") 2432 } 2433 2434 func TestMaterializerNoGoodVindex(t *testing.T) { 2435 ms := &vtctldatapb.MaterializeSettings{ 2436 Workflow: "workflow", 2437 SourceKeyspace: "sourceks", 2438 TargetKeyspace: "targetks", 2439 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 2440 TargetTable: "t1", 2441 SourceExpression: "select * from t1", 2442 CreateDdl: "t1ddl", 2443 }}, 2444 } 2445 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"-80", "80-"}) 2446 defer env.close() 2447 2448 vs := &vschemapb.Keyspace{ 2449 Sharded: true, 2450 Vindexes: map[string]*vschemapb.Vindex{ 2451 "lookup_unique": { 2452 Type: "lookup_unique", 2453 }, 2454 }, 2455 Tables: map[string]*vschemapb.Table{ 2456 "t1": { 2457 ColumnVindexes: []*vschemapb.ColumnVindex{{ 2458 Column: "c1", 2459 Name: "lookup_unique", 2460 }}, 2461 }, 2462 }, 2463 } 2464 2465 if err := env.topoServ.SaveVSchema(context.Background(), "targetks", vs); err != nil { 2466 t.Fatal(err) 2467 } 2468 2469 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 2470 env.tmc.expectVRQuery(210, mzSelectFrozenQuery, &sqltypes.Result{}) 2471 err := env.wr.Materialize(context.Background(), ms) 2472 require.EqualError(t, err, "could not find a vindex to compute keyspace id for table t1") 2473 } 2474 2475 func TestMaterializerComplexVindexExpression(t *testing.T) { 2476 ms := &vtctldatapb.MaterializeSettings{ 2477 Workflow: "workflow", 2478 SourceKeyspace: "sourceks", 2479 TargetKeyspace: "targetks", 2480 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 2481 TargetTable: "t1", 2482 SourceExpression: "select a+b as c1 from t1", 2483 CreateDdl: "t1ddl", 2484 }}, 2485 } 2486 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"-80", "80-"}) 2487 defer env.close() 2488 2489 vs := &vschemapb.Keyspace{ 2490 Sharded: true, 2491 Vindexes: map[string]*vschemapb.Vindex{ 2492 "hash": { 2493 Type: "hash", 2494 }, 2495 }, 2496 Tables: map[string]*vschemapb.Table{ 2497 "t1": { 2498 ColumnVindexes: []*vschemapb.ColumnVindex{{ 2499 Column: "c1", 2500 Name: "hash", 2501 }}, 2502 }, 2503 }, 2504 } 2505 2506 if err := env.topoServ.SaveVSchema(context.Background(), "targetks", vs); err != nil { 2507 t.Fatal(err) 2508 } 2509 2510 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 2511 env.tmc.expectVRQuery(210, mzSelectFrozenQuery, &sqltypes.Result{}) 2512 err := env.wr.Materialize(context.Background(), ms) 2513 require.EqualError(t, err, "vindex column cannot be a complex expression: a + b as c1") 2514 } 2515 2516 func TestMaterializerNoVindexInExpression(t *testing.T) { 2517 ms := &vtctldatapb.MaterializeSettings{ 2518 Workflow: "workflow", 2519 SourceKeyspace: "sourceks", 2520 TargetKeyspace: "targetks", 2521 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 2522 TargetTable: "t1", 2523 SourceExpression: "select c2 from t1", 2524 CreateDdl: "t1ddl", 2525 }}, 2526 } 2527 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"-80", "80-"}) 2528 defer env.close() 2529 2530 vs := &vschemapb.Keyspace{ 2531 Sharded: true, 2532 Vindexes: map[string]*vschemapb.Vindex{ 2533 "hash": { 2534 Type: "hash", 2535 }, 2536 }, 2537 Tables: map[string]*vschemapb.Table{ 2538 "t1": { 2539 ColumnVindexes: []*vschemapb.ColumnVindex{{ 2540 Column: "c1", 2541 Name: "hash", 2542 }}, 2543 }, 2544 }, 2545 } 2546 2547 if err := env.topoServ.SaveVSchema(context.Background(), "targetks", vs); err != nil { 2548 t.Fatal(err) 2549 } 2550 2551 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 2552 env.tmc.expectVRQuery(210, mzSelectFrozenQuery, &sqltypes.Result{}) 2553 err := env.wr.Materialize(context.Background(), ms) 2554 require.EqualError(t, err, "could not find vindex column c1") 2555 } 2556 2557 func TestStripForeignKeys(t *testing.T) { 2558 tcs := []struct { 2559 desc string 2560 ddl string 2561 2562 hasErr bool 2563 newDDL string 2564 }{ 2565 { 2566 desc: "has FK constraints", 2567 ddl: "CREATE TABLE `table1` (\n" + 2568 "`id` int(11) NOT NULL AUTO_INCREMENT,\n" + 2569 "`foreign_id` int(11) CHECK (foreign_id>10),\n" + 2570 "PRIMARY KEY (`id`),\n" + 2571 "KEY `fk_table1_ref_foreign_id` (`foreign_id`),\n" + 2572 "CONSTRAINT `fk_table1_ref_foreign_id` FOREIGN KEY (`foreign_id`) REFERENCES `foreign` (`id`)\n" + 2573 ") ENGINE=InnoDB DEFAULT CHARSET=latin1;", 2574 2575 newDDL: "create table table1 (\n" + 2576 "\tid int(11) not null auto_increment,\n" + 2577 "\tforeign_id int(11),\n" + 2578 "\tPRIMARY KEY (id),\n" + 2579 "\tKEY fk_table1_ref_foreign_id (foreign_id),\n" + 2580 "\tcheck (foreign_id > 10)\n" + 2581 ") ENGINE InnoDB,\n" + 2582 " CHARSET latin1", 2583 2584 hasErr: false, 2585 }, 2586 { 2587 desc: "no FK constraints", 2588 ddl: "CREATE TABLE `table1` (\n" + 2589 "`id` int(11) NOT NULL AUTO_INCREMENT,\n" + 2590 "`foreign_id` int(11) NOT NULL CHECK (foreign_id>10),\n" + 2591 "`user_id` int(11) NOT NULL,\n" + 2592 "PRIMARY KEY (`id`),\n" + 2593 "KEY `fk_table1_ref_foreign_id` (`foreign_id`),\n" + 2594 "KEY `fk_table1_ref_user_id` (`user_id`)\n" + 2595 ") ENGINE=InnoDB DEFAULT CHARSET=latin1;", 2596 2597 newDDL: "create table table1 (\n" + 2598 "\tid int(11) not null auto_increment,\n" + 2599 "\tforeign_id int(11) not null,\n" + 2600 "\tuser_id int(11) not null,\n" + 2601 "\tPRIMARY KEY (id),\n" + 2602 "\tKEY fk_table1_ref_foreign_id (foreign_id),\n" + 2603 "\tKEY fk_table1_ref_user_id (user_id),\n" + 2604 "\tcheck (foreign_id > 10)\n" + 2605 ") ENGINE InnoDB,\n" + 2606 " CHARSET latin1", 2607 }, 2608 } 2609 2610 for _, tc := range tcs { 2611 newDDL, err := stripTableForeignKeys(tc.ddl) 2612 if tc.hasErr != (err != nil) { 2613 t.Fatalf("hasErr does not match: err: %v, tc: %+v", err, tc) 2614 } 2615 2616 if newDDL != tc.newDDL { 2617 utils.MustMatch(t, tc.newDDL, newDDL, fmt.Sprintf("newDDL does not match. tc: %+v", tc)) 2618 } 2619 } 2620 } 2621 2622 func TestStripConstraints(t *testing.T) { 2623 tcs := []struct { 2624 desc string 2625 ddl string 2626 2627 hasErr bool 2628 newDDL string 2629 }{ 2630 { 2631 desc: "constraints", 2632 ddl: "CREATE TABLE `table1` (\n" + 2633 "`id` int(11) NOT NULL AUTO_INCREMENT,\n" + 2634 "`foreign_id` int(11) NOT NULL,\n" + 2635 "`user_id` int(11) NOT NULL,\n" + 2636 "PRIMARY KEY (`id`),\n" + 2637 "KEY `fk_table1_ref_foreign_id` (`foreign_id`),\n" + 2638 "KEY `fk_table1_ref_user_id` (`user_id`),\n" + 2639 "CONSTRAINT `fk_table1_ref_foreign_id` FOREIGN KEY (`foreign_id`) REFERENCES `foreign` (`id`),\n" + 2640 "CONSTRAINT `fk_table1_ref_user_id` FOREIGN KEY (`user_id`) REFERENCES `core_user` (`id`)\n" + 2641 ") ENGINE=InnoDB DEFAULT CHARSET=latin1;", 2642 2643 newDDL: "create table table1 (\n" + 2644 "\tid int(11) not null auto_increment,\n" + 2645 "\tforeign_id int(11) not null,\n" + 2646 "\tuser_id int(11) not null,\n" + 2647 "\tPRIMARY KEY (id),\n" + 2648 "\tKEY fk_table1_ref_foreign_id (foreign_id),\n" + 2649 "\tKEY fk_table1_ref_user_id (user_id)\n" + 2650 ") ENGINE InnoDB,\n" + 2651 " CHARSET latin1", 2652 2653 hasErr: false, 2654 }, 2655 { 2656 desc: "no constraints", 2657 ddl: "CREATE TABLE `table1` (\n" + 2658 "`id` int(11) NOT NULL AUTO_INCREMENT,\n" + 2659 "`foreign_id` int(11) NOT NULL,\n" + 2660 "`user_id` int(11) NOT NULL,\n" + 2661 "PRIMARY KEY (`id`),\n" + 2662 "KEY `fk_table1_ref_foreign_id` (`foreign_id`),\n" + 2663 "KEY `fk_table1_ref_user_id` (`user_id`)\n" + 2664 ") ENGINE=InnoDB DEFAULT CHARSET=latin1;", 2665 2666 newDDL: "create table table1 (\n" + 2667 "\tid int(11) not null auto_increment,\n" + 2668 "\tforeign_id int(11) not null,\n" + 2669 "\tuser_id int(11) not null,\n" + 2670 "\tPRIMARY KEY (id),\n" + 2671 "\tKEY fk_table1_ref_foreign_id (foreign_id),\n" + 2672 "\tKEY fk_table1_ref_user_id (user_id)\n" + 2673 ") ENGINE InnoDB,\n" + 2674 " CHARSET latin1", 2675 }, 2676 { 2677 desc: "bad ddl has error", 2678 ddl: "bad ddl", 2679 2680 hasErr: true, 2681 }, 2682 } 2683 2684 for _, tc := range tcs { 2685 newDDL, err := stripTableConstraints(tc.ddl) 2686 if tc.hasErr != (err != nil) { 2687 t.Fatalf("hasErr does not match: err: %v, tc: %+v", err, tc) 2688 } 2689 2690 if newDDL != tc.newDDL { 2691 utils.MustMatch(t, tc.newDDL, newDDL, fmt.Sprintf("newDDL does not match. tc: %+v", tc)) 2692 } 2693 } 2694 } 2695 2696 func TestMaterializerManyToManySomeUnreachable(t *testing.T) { 2697 ms := &vtctldatapb.MaterializeSettings{ 2698 Workflow: "workflow", 2699 SourceKeyspace: "sourceks", 2700 TargetKeyspace: "targetks", 2701 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 2702 TargetTable: "t1", 2703 SourceExpression: "select * from t1", 2704 CreateDdl: "t1ddl", 2705 }}, 2706 } 2707 2708 vs := &vschemapb.Keyspace{ 2709 Sharded: true, 2710 Vindexes: map[string]*vschemapb.Vindex{ 2711 "hash": { 2712 Type: "hash", 2713 }, 2714 }, 2715 Tables: map[string]*vschemapb.Table{ 2716 "t1": { 2717 ColumnVindexes: []*vschemapb.ColumnVindex{{ 2718 Column: "c1", 2719 Name: "hash", 2720 }}, 2721 }, 2722 }, 2723 } 2724 type testcase struct { 2725 targetShards, sourceShards []string 2726 insertMap map[string][]string 2727 } 2728 testcases := []testcase{ 2729 { 2730 targetShards: []string{"-40", "40-80", "80-c0", "c0-"}, 2731 sourceShards: []string{"-80", "80-"}, 2732 insertMap: map[string][]string{"-40": {"-80"}, "40-80": {"-80"}, "80-c0": {"80-"}, "c0-": {"80-"}}, 2733 }, 2734 { 2735 targetShards: []string{"-20", "20-40", "40-a0", "a0-f0", "f0-"}, 2736 sourceShards: []string{"-40", "40-80", "80-c0", "c0-"}, 2737 insertMap: map[string][]string{"-20": {"-40"}, "20-40": {"-40"}, "40-a0": {"40-80", "80-c0"}, "a0-f0": {"80-c0", "c0-"}, "f0-": {"c0-"}}, 2738 }, 2739 { 2740 targetShards: []string{"-40", "40-80", "80-"}, 2741 sourceShards: []string{"-80", "80-"}, 2742 insertMap: map[string][]string{"-40": {"-80"}, "40-80": {"-80"}, "80-": {"80-"}}, 2743 }, 2744 { 2745 targetShards: []string{"-80", "80-"}, 2746 sourceShards: []string{"-40", "40-80", "80-c0", "c0-"}, 2747 insertMap: map[string][]string{"-80": {"-40", "40-80"}, "80-": {"80-c0", "c0-"}}, 2748 }, 2749 { 2750 targetShards: []string{"0"}, 2751 sourceShards: []string{"-80", "80-"}, 2752 insertMap: map[string][]string{"0": {"-80", "80-"}}, 2753 }, 2754 { 2755 targetShards: []string{"-80", "80-"}, 2756 sourceShards: []string{"0"}, 2757 insertMap: map[string][]string{"-80": {"0"}, "80-": {"0"}}, 2758 }, 2759 } 2760 2761 getStreamInsert := func(sourceShard, targetShard string) string { 2762 return fmt.Sprintf(`.*shard:\\"%s\\" filter:{rules:{match:\\"t1\\" filter:\\"select.*t1 where in_keyrange\(c1.*targetks\.hash.*%s.*`, sourceShard, targetShard) 2763 } 2764 2765 for _, tcase := range testcases { 2766 t.Run("", func(t *testing.T) { 2767 env := newTestMaterializerEnv(t, ms, tcase.sourceShards, tcase.targetShards) 2768 if err := env.topoServ.SaveVSchema(context.Background(), "targetks", vs); err != nil { 2769 t.Fatal(err) 2770 } 2771 defer env.close() 2772 for i, targetShard := range tcase.targetShards { 2773 tabletID := 200 + i*10 2774 env.tmc.expectVRQuery(tabletID, mzSelectFrozenQuery, &sqltypes.Result{}) 2775 2776 streamsInsert := "" 2777 sourceShards := tcase.insertMap[targetShard] 2778 for _, sourceShard := range sourceShards { 2779 streamsInsert += getStreamInsert(sourceShard, targetShard) 2780 } 2781 env.tmc.expectVRQuery( 2782 tabletID, 2783 insertPrefix+streamsInsert, 2784 &sqltypes.Result{}, 2785 ) 2786 env.tmc.expectVRQuery(tabletID, mzUpdateQuery, &sqltypes.Result{}) 2787 } 2788 err := env.wr.Materialize(context.Background(), ms) 2789 require.NoError(t, err) 2790 env.tmc.verifyQueries(t) 2791 }) 2792 } 2793 } 2794 2795 // TestMoveTablesDDLFlag tests that we save the on-ddl flag value in the workflow. 2796 // Note: 2797 // - TestPlayerDDL tests that the vplayer correctly implements the ddl behavior 2798 // - We have a manual e2e test for the full behavior: TestVReplicationDDLHandling 2799 func TestMoveTablesDDLFlag(t *testing.T) { 2800 ms := &vtctldatapb.MaterializeSettings{ 2801 Workflow: "workflow", 2802 SourceKeyspace: "sourceks", 2803 TargetKeyspace: "targetks", 2804 TableSettings: []*vtctldatapb.TableMaterializeSettings{{ 2805 TargetTable: "t1", 2806 SourceExpression: "select * from t1", 2807 }}, 2808 } 2809 ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) 2810 defer cancel() 2811 2812 for onDDLAction := range binlogdatapb.OnDDLAction_value { 2813 t.Run(fmt.Sprintf("OnDDL Flag:%v", onDDLAction), func(t *testing.T) { 2814 env := newTestMaterializerEnv(t, ms, []string{"0"}, []string{"0"}) 2815 defer env.close() 2816 2817 env.tmc.expectVRQuery(100, mzCheckJournal, &sqltypes.Result{}) 2818 env.tmc.expectVRQuery(200, mzSelectFrozenQuery, &sqltypes.Result{}) 2819 if onDDLAction == binlogdatapb.OnDDLAction_name[int32(binlogdatapb.OnDDLAction_IGNORE)] { 2820 // This is the default and go does not marshal defaults 2821 // for prototext fields so we use the default insert stmt. 2822 env.tmc.expectVRQuery(200, insertPrefix, &sqltypes.Result{}) 2823 } else { 2824 env.tmc.expectVRQuery(200, fmt.Sprintf(`/insert into _vt.vreplication\(.*on_ddl:%s.*`, onDDLAction), 2825 &sqltypes.Result{}) 2826 } 2827 env.tmc.expectVRQuery(200, mzSelectIDQuery, &sqltypes.Result{}) 2828 env.tmc.expectVRQuery(200, mzUpdateQuery, &sqltypes.Result{}) 2829 2830 err := env.wr.MoveTables(ctx, "workflow", "sourceks", "targetks", "t1", "", 2831 "", false, "", false, true, "", false, false, "", onDDLAction, nil) 2832 require.NoError(t, err) 2833 }) 2834 } 2835 }