vitess.io/vitess@v0.16.2/go/vt/binlog/binlog_streamer_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 binlog 18 19 import ( 20 "fmt" 21 "io" 22 "strings" 23 "testing" 24 "time" 25 26 "github.com/stretchr/testify/require" 27 "google.golang.org/protobuf/proto" 28 29 "context" 30 31 "vitess.io/vitess/go/mysql" 32 33 "vitess.io/vitess/go/vt/dbconfigs" 34 binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" 35 querypb "vitess.io/vitess/go/vt/proto/query" 36 ) 37 38 // fullBinlogTransaction is a helper type for tests. 39 type fullBinlogTransaction struct { 40 eventToken *querypb.EventToken 41 statements []FullBinlogStatement 42 } 43 44 type binlogStatements []*binlogdatapb.BinlogTransaction 45 46 func (bs *binlogStatements) sendTransaction(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { 47 var s []*binlogdatapb.BinlogTransaction_Statement 48 if len(statements) > 0 { 49 s = make([]*binlogdatapb.BinlogTransaction_Statement, len(statements)) 50 for i, statement := range statements { 51 s[i] = statement.Statement 52 } 53 } 54 *bs = append(*bs, &binlogdatapb.BinlogTransaction{ 55 Statements: s, 56 EventToken: eventToken, 57 }) 58 return nil 59 } 60 61 func (bs *binlogStatements) equal(bts []*binlogdatapb.BinlogTransaction) bool { 62 if len(*bs) != len(bts) { 63 return false 64 } 65 for i, s := range *bs { 66 if !proto.Equal(s, bts[i]) { 67 return false 68 } 69 } 70 return true 71 } 72 73 func sendTestEvents(channel chan<- mysql.BinlogEvent, events []mysql.BinlogEvent) { 74 for _, ev := range events { 75 channel <- ev 76 } 77 close(channel) 78 } 79 80 func TestStreamerParseEventsXID(t *testing.T) { 81 f := mysql.NewMySQL56BinlogFormat() 82 s := mysql.NewFakeBinlogStream() 83 s.ServerID = 62344 84 85 input := []mysql.BinlogEvent{ 86 mysql.NewRotateEvent(f, s, 0, ""), 87 mysql.NewFormatDescriptionEvent(f, s), 88 mysql.NewMariaDBGTIDEvent(f, s, mysql.MariadbGTID{Domain: 0, Sequence: 0xd}, false /* hasBegin */), 89 mysql.NewQueryEvent(f, s, mysql.Query{ 90 Database: "vt_test_keyspace", 91 SQL: "BEGIN"}), 92 mysql.NewQueryEvent(f, s, mysql.Query{ 93 Database: "vt_test_keyspace", 94 SQL: "insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */"}), 95 mysql.NewXIDEvent(f, s), 96 } 97 98 events := make(chan mysql.BinlogEvent) 99 errs := make(chan error) 100 101 want := []*binlogdatapb.BinlogTransaction{ 102 { 103 Statements: []*binlogdatapb.BinlogTransaction_Statement{ 104 {Category: binlogdatapb.BinlogTransaction_Statement_BL_SET, Sql: []byte("SET TIMESTAMP=1407805592")}, 105 {Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT, Sql: []byte("insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */")}, 106 }, 107 EventToken: &querypb.EventToken{ 108 Timestamp: 1407805592, 109 Position: mysql.EncodePosition(mysql.Position{ 110 GTIDSet: mysql.MariadbGTIDSet{ 111 0: mysql.MariadbGTID{ 112 Domain: 0, 113 Server: 62344, 114 Sequence: 0x0d, 115 }, 116 }, 117 }), 118 }, 119 }, 120 } 121 var got binlogStatements 122 123 // Set mock mysql.ConnParams and dbconfig 124 mcp := &mysql.ConnParams{ 125 DbName: "vt_test_keyspace", 126 } 127 dbcfgs := dbconfigs.New(mcp) 128 129 bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) 130 131 go sendTestEvents(events, input) 132 _, err := bls.parseEvents(context.Background(), events, errs) 133 if err != ErrServerEOF { 134 t.Errorf("unexpected error: %v", err) 135 } 136 137 if !got.equal(want) { 138 t.Errorf("binlogConnStreamer.parseEvents(): got:\n%v\nwant:\n%v", got, want) 139 } 140 } 141 142 func TestStreamerParseEventsCommit(t *testing.T) { 143 f := mysql.NewMySQL56BinlogFormat() 144 s := mysql.NewFakeBinlogStream() 145 s.ServerID = 62344 146 147 input := []mysql.BinlogEvent{ 148 mysql.NewRotateEvent(f, s, 0, ""), 149 mysql.NewFormatDescriptionEvent(f, s), 150 mysql.NewMariaDBGTIDEvent(f, s, mysql.MariadbGTID{Domain: 0, Sequence: 0xd}, false /* hasBegin */), 151 mysql.NewQueryEvent(f, s, mysql.Query{ 152 Database: "vt_test_keyspace", 153 SQL: "BEGIN"}), 154 mysql.NewQueryEvent(f, s, mysql.Query{ 155 Database: "vt_test_keyspace", 156 SQL: "insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */"}), 157 mysql.NewQueryEvent(f, s, mysql.Query{ 158 Database: "vt_test_keyspace", 159 SQL: "COMMIT"}), 160 } 161 162 events := make(chan mysql.BinlogEvent) 163 errs := make(chan error) 164 165 want := []*binlogdatapb.BinlogTransaction{ 166 { 167 Statements: []*binlogdatapb.BinlogTransaction_Statement{ 168 {Category: binlogdatapb.BinlogTransaction_Statement_BL_SET, Sql: []byte("SET TIMESTAMP=1407805592")}, 169 {Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT, Sql: []byte("insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */")}, 170 }, 171 EventToken: &querypb.EventToken{ 172 Timestamp: 1407805592, 173 Position: mysql.EncodePosition(mysql.Position{ 174 GTIDSet: mysql.MariadbGTIDSet{ 175 0: mysql.MariadbGTID{ 176 Domain: 0, 177 Server: 62344, 178 Sequence: 0x0d, 179 }, 180 }, 181 }), 182 }, 183 }, 184 } 185 // Set mock mysql.ConnParams and dbconfig 186 mcp := &mysql.ConnParams{ 187 DbName: "vt_test_keyspace", 188 } 189 dbcfgs := dbconfigs.New(mcp) 190 191 var got binlogStatements 192 bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) 193 194 go sendTestEvents(events, input) 195 _, err := bls.parseEvents(context.Background(), events, errs) 196 if err != ErrServerEOF { 197 t.Errorf("unexpected error: %v", err) 198 } 199 200 if !got.equal(want) { 201 t.Errorf("binlogConnStreamer.parseEvents(): got %v, want %v", got, want) 202 } 203 } 204 205 func TestStreamerStop(t *testing.T) { 206 events := make(chan mysql.BinlogEvent) 207 errs := make(chan error) 208 209 sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { 210 return nil 211 } 212 213 // Set mock mysql.ConnParams and dbconfig 214 mcp := &mysql.ConnParams{ 215 DbName: "vt_test_keyspace", 216 } 217 dbcfgs := dbconfigs.New(mcp) 218 219 bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, sendTransaction) 220 221 // Start parseEvents(), but don't send it anything, so it just waits. 222 ctx, cancel := context.WithCancel(context.Background()) 223 done := make(chan error) 224 go func() { 225 _, err := bls.parseEvents(ctx, events, errs) 226 done <- err 227 }() 228 229 // close the context, expect the parser to return 230 cancel() 231 232 select { 233 case err := <-done: 234 if err != context.Canceled { 235 t.Errorf("wrong context interruption returned value: %v", err) 236 } 237 case <-time.After(1 * time.Second): 238 t.Errorf("timed out waiting for binlogConnStreamer.Stop()") 239 } 240 } 241 242 func TestStreamerParseEventsClientEOF(t *testing.T) { 243 f := mysql.NewMySQL56BinlogFormat() 244 s := mysql.NewFakeBinlogStream() 245 246 input := []mysql.BinlogEvent{ 247 mysql.NewRotateEvent(f, s, 0, ""), 248 mysql.NewFormatDescriptionEvent(f, s), 249 mysql.NewQueryEvent(f, s, mysql.Query{ 250 Database: "vt_test_keyspace", 251 SQL: "BEGIN"}), 252 mysql.NewQueryEvent(f, s, mysql.Query{ 253 Database: "vt_test_keyspace", 254 SQL: "insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */"}), 255 mysql.NewXIDEvent(f, s), 256 } 257 want := ErrClientEOF 258 259 events := make(chan mysql.BinlogEvent) 260 errs := make(chan error) 261 262 sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { 263 return io.EOF 264 } 265 266 // Set mock mysql.ConnParams and dbconfig 267 mcp := &mysql.ConnParams{ 268 DbName: "vt_test_keyspace", 269 } 270 dbcfgs := dbconfigs.New(mcp) 271 272 bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, sendTransaction) 273 274 go sendTestEvents(events, input) 275 _, err := bls.parseEvents(context.Background(), events, errs) 276 if err != want { 277 t.Errorf("wrong error, got %#v, want %#v", err, want) 278 } 279 } 280 281 func TestStreamerParseEventsServerEOF(t *testing.T) { 282 want := ErrServerEOF 283 284 events := make(chan mysql.BinlogEvent) 285 errs := make(chan error) 286 close(events) 287 288 sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { 289 return nil 290 } 291 // Set mock mysql.ConnParams and dbconfig 292 mcp := &mysql.ConnParams{ 293 DbName: "vt_test_keyspace", 294 } 295 dbcfgs := dbconfigs.New(mcp) 296 297 bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, sendTransaction) 298 _, err := bls.parseEvents(context.Background(), events, errs) 299 if err != want { 300 t.Errorf("wrong error, got %#v, want %#v", err, want) 301 } 302 } 303 304 // TestStreamerParseEventsGTIDPurged tests binlog streamer error 305 // propagation generally, as well as testing specifically for 306 // the error seen when the client needs GTIDs that have been 307 // purged on the source. 308 func TestStreamerParseEventsGTIDPurged(t *testing.T) { 309 events := make(chan mysql.BinlogEvent) 310 errs := make(chan error) 311 expectedStreamErr := mysql.NewSQLError(mysql.ERMasterFatalReadingBinlog, mysql.SSUnknownSQLState, 312 "Cannot replicate because the master purged required binary logs.") 313 314 sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { 315 return nil 316 } 317 // Set mock mysql.ConnParams and dbconfig 318 mcp := &mysql.ConnParams{ 319 DbName: "vt_test_keyspace", 320 } 321 dbcfgs := dbconfigs.New(mcp) 322 323 go func() { 324 tmr := time.NewTimer(10 * time.Second) 325 defer tmr.Stop() 326 select { 327 case errs <- expectedStreamErr: 328 case <-tmr.C: 329 require.FailNow(t, "timed out sending error message") 330 } 331 }() 332 333 bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, sendTransaction) 334 _, err := bls.parseEvents(context.Background(), events, errs) 335 require.Error(t, err) 336 sqlErr, ok := err.(*mysql.SQLError) 337 require.True(t, ok, "expected SQLError, got %T", err) 338 require.True(t, sqlErr.Num == mysql.ERMasterFatalReadingBinlog, "expected ERMasterFatalReadingBinlog (%d), got %d", 339 mysql.ERMasterFatalReadingBinlog, sqlErr.Num) 340 } 341 342 func TestStreamerParseEventsSendErrorXID(t *testing.T) { 343 f := mysql.NewMySQL56BinlogFormat() 344 s := mysql.NewFakeBinlogStream() 345 346 input := []mysql.BinlogEvent{ 347 mysql.NewRotateEvent(f, s, 0, ""), 348 mysql.NewFormatDescriptionEvent(f, s), 349 mysql.NewQueryEvent(f, s, mysql.Query{ 350 Database: "vt_test_keyspace", 351 SQL: "BEGIN"}), 352 mysql.NewQueryEvent(f, s, mysql.Query{ 353 Database: "vt_test_keyspace", 354 SQL: "insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */"}), 355 mysql.NewXIDEvent(f, s), 356 } 357 want := "send reply error: foobar" 358 359 events := make(chan mysql.BinlogEvent) 360 errs := make(chan error) 361 362 sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { 363 return fmt.Errorf("foobar") 364 } 365 366 // Set mock mysql.ConnParams and dbconfig 367 mcp := &mysql.ConnParams{ 368 DbName: "vt_test_keyspace", 369 } 370 dbcfgs := dbconfigs.New(mcp) 371 372 bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, sendTransaction) 373 374 go sendTestEvents(events, input) 375 376 _, err := bls.parseEvents(context.Background(), events, errs) 377 if err == nil { 378 t.Errorf("expected error, got none") 379 return 380 } 381 if got := err.Error(); got != want { 382 t.Errorf("wrong error, got %#v, want %#v", got, want) 383 } 384 } 385 386 func TestStreamerParseEventsSendErrorCommit(t *testing.T) { 387 f := mysql.NewMySQL56BinlogFormat() 388 s := mysql.NewFakeBinlogStream() 389 390 input := []mysql.BinlogEvent{ 391 mysql.NewRotateEvent(f, s, 0, ""), 392 mysql.NewFormatDescriptionEvent(f, s), 393 mysql.NewQueryEvent(f, s, mysql.Query{ 394 Database: "vt_test_keyspace", 395 SQL: "BEGIN"}), 396 mysql.NewQueryEvent(f, s, mysql.Query{ 397 Database: "vt_test_keyspace", 398 SQL: "insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */"}), 399 mysql.NewQueryEvent(f, s, mysql.Query{ 400 Database: "vt_test_keyspace", 401 SQL: "COMMIT"}), 402 } 403 want := "send reply error: foobar" 404 405 events := make(chan mysql.BinlogEvent) 406 errs := make(chan error) 407 408 sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { 409 return fmt.Errorf("foobar") 410 } 411 412 // Set mock mysql.ConnParams and dbconfig 413 mcp := &mysql.ConnParams{ 414 DbName: "vt_test_keyspace", 415 } 416 dbcfgs := dbconfigs.New(mcp) 417 418 bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, sendTransaction) 419 420 go sendTestEvents(events, input) 421 _, err := bls.parseEvents(context.Background(), events, errs) 422 if err == nil { 423 t.Errorf("expected error, got none") 424 return 425 } 426 if got := err.Error(); got != want { 427 t.Errorf("wrong error, got %#v, want %#v", got, want) 428 } 429 } 430 431 func TestStreamerParseEventsInvalid(t *testing.T) { 432 f := mysql.NewMySQL56BinlogFormat() 433 s := mysql.NewFakeBinlogStream() 434 435 input := []mysql.BinlogEvent{ 436 mysql.NewRotateEvent(f, s, 0, ""), 437 mysql.NewFormatDescriptionEvent(f, s), 438 mysql.NewQueryEvent(f, s, mysql.Query{ 439 Database: "vt_test_keyspace", 440 SQL: "BEGIN"}), 441 mysql.NewInvalidEvent(), 442 mysql.NewXIDEvent(f, s), 443 } 444 want := "can't parse binlog event, invalid data:" 445 446 events := make(chan mysql.BinlogEvent) 447 errs := make(chan error) 448 449 sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { 450 return nil 451 } 452 453 // Set mock mysql.ConnParams and dbconfig 454 mcp := &mysql.ConnParams{ 455 DbName: "vt_test_keyspace", 456 } 457 dbcfgs := dbconfigs.New(mcp) 458 459 bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, sendTransaction) 460 461 go sendTestEvents(events, input) 462 _, err := bls.parseEvents(context.Background(), events, errs) 463 if err == nil { 464 t.Errorf("expected error, got none") 465 return 466 } 467 if got := err.Error(); !strings.HasPrefix(got, want) { 468 t.Errorf("wrong error, got %#v, want %#v", got, want) 469 } 470 } 471 472 func TestStreamerParseEventsInvalidFormat(t *testing.T) { 473 f := mysql.NewMySQL56BinlogFormat() 474 s := mysql.NewFakeBinlogStream() 475 476 input := []mysql.BinlogEvent{ 477 mysql.NewRotateEvent(f, s, 0, ""), 478 mysql.NewInvalidFormatDescriptionEvent(f, s), 479 mysql.NewQueryEvent(f, s, mysql.Query{ 480 Database: "vt_test_keyspace", 481 SQL: "BEGIN"}), 482 mysql.NewQueryEvent(f, s, mysql.Query{ 483 Database: "vt_test_keyspace", 484 SQL: "insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */"}), 485 mysql.NewXIDEvent(f, s), 486 } 487 want := "can't parse FORMAT_DESCRIPTION_EVENT:" 488 489 events := make(chan mysql.BinlogEvent) 490 errs := make(chan error) 491 492 sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { 493 return nil 494 } 495 496 // Set mock mysql.ConnParams and dbconfig 497 mcp := &mysql.ConnParams{ 498 DbName: "vt_test_keyspace", 499 } 500 dbcfgs := dbconfigs.New(mcp) 501 502 bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, sendTransaction) 503 504 go sendTestEvents(events, input) 505 _, err := bls.parseEvents(context.Background(), events, errs) 506 if err == nil { 507 t.Errorf("expected error, got none") 508 return 509 } 510 if got := err.Error(); !strings.HasPrefix(got, want) { 511 t.Errorf("wrong error, got %#v, want %#v", got, want) 512 } 513 } 514 515 func TestStreamerParseEventsNoFormat(t *testing.T) { 516 f := mysql.NewMySQL56BinlogFormat() 517 s := mysql.NewFakeBinlogStream() 518 519 input := []mysql.BinlogEvent{ 520 mysql.NewRotateEvent(f, s, 0, ""), 521 //mysql.NewFormatDescriptionEvent(f, s), 522 mysql.NewQueryEvent(f, s, mysql.Query{ 523 Database: "vt_test_keyspace", 524 SQL: "BEGIN"}), 525 mysql.NewQueryEvent(f, s, mysql.Query{ 526 Database: "vt_test_keyspace", 527 SQL: "insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */"}), 528 mysql.NewXIDEvent(f, s), 529 } 530 want := "got a real event before FORMAT_DESCRIPTION_EVENT:" 531 532 events := make(chan mysql.BinlogEvent) 533 errs := make(chan error) 534 535 sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { 536 return nil 537 } 538 539 // Set mock mysql.ConnParams and dbconfig 540 mcp := &mysql.ConnParams{ 541 DbName: "vt_test_keyspace", 542 } 543 dbcfgs := dbconfigs.New(mcp) 544 545 bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, sendTransaction) 546 547 go sendTestEvents(events, input) 548 _, err := bls.parseEvents(context.Background(), events, errs) 549 if err == nil { 550 t.Errorf("expected error, got none") 551 return 552 } 553 if got := err.Error(); !strings.HasPrefix(got, want) { 554 t.Errorf("wrong error, got %#v, want %#v", got, want) 555 } 556 } 557 558 func TestStreamerParseEventsInvalidQuery(t *testing.T) { 559 f := mysql.NewMySQL56BinlogFormat() 560 s := mysql.NewFakeBinlogStream() 561 562 input := []mysql.BinlogEvent{ 563 mysql.NewRotateEvent(f, s, 0, ""), 564 mysql.NewFormatDescriptionEvent(f, s), 565 mysql.NewQueryEvent(f, s, mysql.Query{ 566 Database: "vt_test_keyspace", 567 SQL: "BEGIN"}), 568 mysql.NewInvalidQueryEvent(f, s), 569 mysql.NewXIDEvent(f, s), 570 } 571 want := "can't get query from binlog event:" 572 573 events := make(chan mysql.BinlogEvent) 574 errs := make(chan error) 575 576 sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { 577 return nil 578 } 579 580 // Set mock mysql.ConnParams and dbconfig 581 mcp := &mysql.ConnParams{ 582 DbName: "vt_test_keyspace", 583 } 584 dbcfgs := dbconfigs.New(mcp) 585 586 bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, sendTransaction) 587 588 go sendTestEvents(events, input) 589 _, err := bls.parseEvents(context.Background(), events, errs) 590 if err == nil { 591 t.Errorf("expected error, got none") 592 return 593 } 594 if got := err.Error(); !strings.HasPrefix(got, want) { 595 t.Errorf("wrong error, got %#v, want %#v", got, want) 596 } 597 } 598 599 func TestStreamerParseEventsRollback(t *testing.T) { 600 f := mysql.NewMySQL56BinlogFormat() 601 s := mysql.NewFakeBinlogStream() 602 s.ServerID = 62344 603 604 input := []mysql.BinlogEvent{ 605 mysql.NewRotateEvent(f, s, 0, ""), 606 mysql.NewFormatDescriptionEvent(f, s), 607 mysql.NewMariaDBGTIDEvent(f, s, mysql.MariadbGTID{Domain: 0, Sequence: 0xd}, false /* hasBegin */), 608 mysql.NewQueryEvent(f, s, mysql.Query{ 609 Database: "vt_test_keyspace", 610 SQL: "BEGIN"}), 611 mysql.NewQueryEvent(f, s, mysql.Query{ 612 Database: "vt_test_keyspace", 613 SQL: "insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */"}), 614 mysql.NewQueryEvent(f, s, mysql.Query{ 615 Database: "vt_test_keyspace", 616 SQL: "insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */"}), 617 mysql.NewQueryEvent(f, s, mysql.Query{ 618 Database: "vt_test_keyspace", 619 SQL: "ROLLBACK"}), 620 mysql.NewQueryEvent(f, s, mysql.Query{ 621 Database: "vt_test_keyspace", 622 SQL: "BEGIN"}), 623 mysql.NewQueryEvent(f, s, mysql.Query{ 624 Database: "vt_test_keyspace", 625 SQL: "insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */"}), 626 mysql.NewXIDEvent(f, s), 627 } 628 629 events := make(chan mysql.BinlogEvent) 630 errs := make(chan error) 631 632 want := []*binlogdatapb.BinlogTransaction{ 633 { 634 Statements: nil, 635 EventToken: &querypb.EventToken{ 636 Timestamp: 1407805592, 637 Position: mysql.EncodePosition(mysql.Position{ 638 GTIDSet: mysql.MariadbGTIDSet{ 639 0: mysql.MariadbGTID{ 640 Domain: 0, 641 Server: 62344, 642 Sequence: 0x0d, 643 }, 644 }, 645 }), 646 }, 647 }, 648 { 649 Statements: []*binlogdatapb.BinlogTransaction_Statement{ 650 {Category: binlogdatapb.BinlogTransaction_Statement_BL_SET, Sql: []byte("SET TIMESTAMP=1407805592")}, 651 {Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT, Sql: []byte("insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */")}, 652 }, 653 EventToken: &querypb.EventToken{ 654 Timestamp: 1407805592, 655 Position: mysql.EncodePosition(mysql.Position{ 656 GTIDSet: mysql.MariadbGTIDSet{ 657 0: mysql.MariadbGTID{ 658 Domain: 0, 659 Server: 62344, 660 Sequence: 0x0d, 661 }, 662 }, 663 }), 664 }, 665 }, 666 } 667 var got binlogStatements 668 // Set mock mysql.ConnParams and dbconfig 669 mcp := &mysql.ConnParams{ 670 DbName: "vt_test_keyspace", 671 } 672 dbcfgs := dbconfigs.New(mcp) 673 674 bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) 675 676 go sendTestEvents(events, input) 677 if _, err := bls.parseEvents(context.Background(), events, errs); err != ErrServerEOF { 678 t.Errorf("unexpected error: %v", err) 679 } 680 681 if !got.equal(want) { 682 t.Errorf("binlogConnStreamer.parseEvents(): got:\n%v\nwant:\n%v", got, want) 683 } 684 } 685 686 func TestStreamerParseEventsDMLWithoutBegin(t *testing.T) { 687 f := mysql.NewMySQL56BinlogFormat() 688 s := mysql.NewFakeBinlogStream() 689 s.ServerID = 62344 690 691 input := []mysql.BinlogEvent{ 692 mysql.NewRotateEvent(f, s, 0, ""), 693 mysql.NewFormatDescriptionEvent(f, s), 694 mysql.NewMariaDBGTIDEvent(f, s, mysql.MariadbGTID{Domain: 0, Sequence: 0xd}, false /* hasBegin */), 695 mysql.NewQueryEvent(f, s, mysql.Query{ 696 Database: "vt_test_keyspace", 697 SQL: "insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */"}), 698 mysql.NewXIDEvent(f, s), 699 } 700 701 events := make(chan mysql.BinlogEvent) 702 errs := make(chan error) 703 704 want := []*binlogdatapb.BinlogTransaction{ 705 { 706 Statements: []*binlogdatapb.BinlogTransaction_Statement{ 707 {Category: binlogdatapb.BinlogTransaction_Statement_BL_SET, Sql: []byte("SET TIMESTAMP=1407805592")}, 708 {Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT, Sql: []byte("insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */")}, 709 }, 710 EventToken: &querypb.EventToken{ 711 Timestamp: 1407805592, 712 Position: mysql.EncodePosition(mysql.Position{ 713 GTIDSet: mysql.MariadbGTIDSet{ 714 0: mysql.MariadbGTID{ 715 Domain: 0, 716 Server: 62344, 717 Sequence: 0x0d, 718 }, 719 }, 720 }), 721 }, 722 }, 723 { 724 Statements: nil, 725 EventToken: &querypb.EventToken{ 726 Timestamp: 1407805592, 727 Position: mysql.EncodePosition(mysql.Position{ 728 GTIDSet: mysql.MariadbGTIDSet{ 729 0: mysql.MariadbGTID{ 730 Domain: 0, 731 Server: 62344, 732 Sequence: 0x0d, 733 }, 734 }, 735 }), 736 }, 737 }, 738 } 739 var got binlogStatements 740 741 // Set mock mysql.ConnParams and dbconfig 742 mcp := &mysql.ConnParams{ 743 DbName: "vt_test_keyspace", 744 } 745 dbcfgs := dbconfigs.New(mcp) 746 747 bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) 748 749 go sendTestEvents(events, input) 750 if _, err := bls.parseEvents(context.Background(), events, errs); err != ErrServerEOF { 751 t.Errorf("unexpected error: %v", err) 752 } 753 754 if !got.equal(want) { 755 t.Errorf("binlogConnStreamer.parseEvents(): got:\n%v\nwant:\n%v", got, want) 756 } 757 } 758 759 func TestStreamerParseEventsBeginWithoutCommit(t *testing.T) { 760 f := mysql.NewMySQL56BinlogFormat() 761 s := mysql.NewFakeBinlogStream() 762 s.ServerID = 62344 763 764 input := []mysql.BinlogEvent{ 765 mysql.NewRotateEvent(f, s, 0, ""), 766 mysql.NewFormatDescriptionEvent(f, s), 767 mysql.NewMariaDBGTIDEvent(f, s, mysql.MariadbGTID{Domain: 0, Sequence: 0xd}, false /* hasBegin */), 768 mysql.NewQueryEvent(f, s, mysql.Query{ 769 Database: "vt_test_keyspace", 770 SQL: "insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */"}), 771 mysql.NewQueryEvent(f, s, mysql.Query{ 772 Database: "vt_test_keyspace", 773 SQL: "BEGIN"}), 774 mysql.NewXIDEvent(f, s), 775 } 776 777 events := make(chan mysql.BinlogEvent) 778 errs := make(chan error) 779 780 want := []*binlogdatapb.BinlogTransaction{ 781 { 782 Statements: []*binlogdatapb.BinlogTransaction_Statement{ 783 {Category: binlogdatapb.BinlogTransaction_Statement_BL_SET, Sql: []byte("SET TIMESTAMP=1407805592")}, 784 {Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT, Sql: []byte("insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */")}, 785 }, 786 EventToken: &querypb.EventToken{ 787 Timestamp: 1407805592, 788 Position: mysql.EncodePosition(mysql.Position{ 789 GTIDSet: mysql.MariadbGTIDSet{ 790 0: mysql.MariadbGTID{ 791 Domain: 0, 792 Server: 62344, 793 Sequence: 0x0d, 794 }, 795 }, 796 }), 797 }, 798 }, 799 { 800 Statements: nil, 801 EventToken: &querypb.EventToken{ 802 Timestamp: 1407805592, 803 Position: mysql.EncodePosition(mysql.Position{ 804 GTIDSet: mysql.MariadbGTIDSet{ 805 0: mysql.MariadbGTID{ 806 Domain: 0, 807 Server: 62344, 808 Sequence: 0x0d, 809 }, 810 }, 811 }), 812 }, 813 }, 814 } 815 var got binlogStatements 816 817 // Set mock mysql.ConnParams and dbconfig 818 mcp := &mysql.ConnParams{ 819 DbName: "vt_test_keyspace", 820 } 821 dbcfgs := dbconfigs.New(mcp) 822 823 bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) 824 825 go sendTestEvents(events, input) 826 if _, err := bls.parseEvents(context.Background(), events, errs); err != ErrServerEOF { 827 t.Errorf("unexpected error: %v", err) 828 } 829 830 if !got.equal(want) { 831 t.Errorf("binlogConnStreamer.parseEvents(): got:\n%v\nwant:\n%v", got, want) 832 } 833 } 834 835 func TestStreamerParseEventsSetInsertID(t *testing.T) { 836 f := mysql.NewMySQL56BinlogFormat() 837 s := mysql.NewFakeBinlogStream() 838 s.ServerID = 62344 839 840 input := []mysql.BinlogEvent{ 841 mysql.NewRotateEvent(f, s, 0, ""), 842 mysql.NewFormatDescriptionEvent(f, s), 843 mysql.NewMariaDBGTIDEvent(f, s, mysql.MariadbGTID{Domain: 0, Sequence: 0xd}, false /* hasBegin */), 844 mysql.NewQueryEvent(f, s, mysql.Query{ 845 Database: "vt_test_keyspace", 846 SQL: "BEGIN"}), 847 mysql.NewIntVarEvent(f, s, mysql.IntVarInsertID, 101), 848 mysql.NewQueryEvent(f, s, mysql.Query{ 849 Database: "vt_test_keyspace", 850 SQL: "insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */"}), 851 mysql.NewXIDEvent(f, s), 852 } 853 854 events := make(chan mysql.BinlogEvent) 855 errs := make(chan error) 856 857 want := []*binlogdatapb.BinlogTransaction{ 858 { 859 Statements: []*binlogdatapb.BinlogTransaction_Statement{ 860 {Category: binlogdatapb.BinlogTransaction_Statement_BL_SET, Sql: []byte("SET INSERT_ID=101")}, 861 {Category: binlogdatapb.BinlogTransaction_Statement_BL_SET, Sql: []byte("SET TIMESTAMP=1407805592")}, 862 {Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT, Sql: []byte("insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */")}, 863 }, 864 EventToken: &querypb.EventToken{ 865 Timestamp: 1407805592, 866 Position: mysql.EncodePosition(mysql.Position{ 867 GTIDSet: mysql.MariadbGTIDSet{ 868 0: mysql.MariadbGTID{ 869 Domain: 0, 870 Server: 62344, 871 Sequence: 0x0d, 872 }, 873 }, 874 }), 875 }, 876 }, 877 } 878 var got binlogStatements 879 // Set mock mysql.ConnParams and dbconfig 880 mcp := &mysql.ConnParams{ 881 DbName: "vt_test_keyspace", 882 } 883 dbcfgs := dbconfigs.New(mcp) 884 885 bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) 886 887 go sendTestEvents(events, input) 888 if _, err := bls.parseEvents(context.Background(), events, errs); err != ErrServerEOF { 889 t.Errorf("unexpected error: %v", err) 890 } 891 892 if !got.equal(want) { 893 t.Errorf("binlogConnStreamer.parseEvents(): got %v, want %v", got, want) 894 } 895 } 896 897 func TestStreamerParseEventsInvalidIntVar(t *testing.T) { 898 f := mysql.NewMySQL56BinlogFormat() 899 s := mysql.NewFakeBinlogStream() 900 901 input := []mysql.BinlogEvent{ 902 mysql.NewRotateEvent(f, s, 0, ""), 903 mysql.NewFormatDescriptionEvent(f, s), 904 mysql.NewQueryEvent(f, s, mysql.Query{ 905 Database: "vt_test_keyspace", 906 SQL: "BEGIN"}), 907 mysql.NewIntVarEvent(f, s, mysql.IntVarInvalidInt, 0), // Invalid intvar. 908 mysql.NewQueryEvent(f, s, mysql.Query{ 909 Database: "vt_test_keyspace", 910 SQL: "insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */"}), 911 mysql.NewXIDEvent(f, s), 912 } 913 want := "can't parse INTVAR_EVENT:" 914 915 events := make(chan mysql.BinlogEvent) 916 errs := make(chan error) 917 918 sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { 919 return nil 920 } 921 // Set mock mysql.ConnParams and dbconfig 922 mcp := &mysql.ConnParams{ 923 DbName: "vt_test_keyspace", 924 } 925 dbcfgs := dbconfigs.New(mcp) 926 927 bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, sendTransaction) 928 929 go sendTestEvents(events, input) 930 _, err := bls.parseEvents(context.Background(), events, errs) 931 if err == nil { 932 t.Errorf("expected error, got none") 933 return 934 } 935 if got := err.Error(); !strings.HasPrefix(got, want) { 936 t.Errorf("wrong error, got %#v, want %#v", got, want) 937 } 938 } 939 940 func TestStreamerParseEventsOtherDB(t *testing.T) { 941 f := mysql.NewMySQL56BinlogFormat() 942 s := mysql.NewFakeBinlogStream() 943 s.ServerID = 62344 944 945 input := []mysql.BinlogEvent{ 946 mysql.NewRotateEvent(f, s, 0, ""), 947 mysql.NewFormatDescriptionEvent(f, s), 948 mysql.NewMariaDBGTIDEvent(f, s, mysql.MariadbGTID{Domain: 0, Sequence: 0xd}, false /* hasBegin */), 949 mysql.NewQueryEvent(f, s, mysql.Query{ 950 Database: "vt_test_keyspace", 951 SQL: "BEGIN"}), 952 mysql.NewQueryEvent(f, s, mysql.Query{ 953 Database: "other", 954 SQL: "INSERT INTO test values (3, 4)"}), 955 mysql.NewQueryEvent(f, s, mysql.Query{ 956 Database: "vt_test_keyspace", 957 SQL: "insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */"}), 958 mysql.NewXIDEvent(f, s), 959 } 960 961 events := make(chan mysql.BinlogEvent) 962 errs := make(chan error) 963 964 want := []*binlogdatapb.BinlogTransaction{ 965 { 966 Statements: []*binlogdatapb.BinlogTransaction_Statement{ 967 {Category: binlogdatapb.BinlogTransaction_Statement_BL_SET, Sql: []byte("SET TIMESTAMP=1407805592")}, 968 {Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT, Sql: []byte("insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */")}, 969 }, 970 EventToken: &querypb.EventToken{ 971 Timestamp: 1407805592, 972 Position: mysql.EncodePosition(mysql.Position{ 973 GTIDSet: mysql.MariadbGTIDSet{ 974 0: mysql.MariadbGTID{ 975 Domain: 0, 976 Server: 62344, 977 Sequence: 0x0d, 978 }, 979 }, 980 }), 981 }, 982 }, 983 } 984 var got binlogStatements 985 // Set mock mysql.ConnParams and dbconfig 986 mcp := &mysql.ConnParams{ 987 DbName: "vt_test_keyspace", 988 } 989 dbcfgs := dbconfigs.New(mcp) 990 991 bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) 992 993 go sendTestEvents(events, input) 994 if _, err := bls.parseEvents(context.Background(), events, errs); err != ErrServerEOF { 995 t.Errorf("unexpected error: %v", err) 996 } 997 998 if !got.equal(want) { 999 t.Errorf("binlogConnStreamer.parseEvents(): got %v, want %v", got, want) 1000 } 1001 } 1002 1003 func TestStreamerParseEventsOtherDBBegin(t *testing.T) { 1004 f := mysql.NewMySQL56BinlogFormat() 1005 s := mysql.NewFakeBinlogStream() 1006 s.ServerID = 62344 1007 1008 input := []mysql.BinlogEvent{ 1009 mysql.NewRotateEvent(f, s, 0, ""), 1010 mysql.NewFormatDescriptionEvent(f, s), 1011 mysql.NewMariaDBGTIDEvent(f, s, mysql.MariadbGTID{Domain: 0, Sequence: 0xd}, false /* hasBegin */), 1012 mysql.NewQueryEvent(f, s, mysql.Query{ 1013 Database: "other", 1014 SQL: "BEGIN"}), // Check that this doesn't get filtered out. 1015 mysql.NewQueryEvent(f, s, mysql.Query{ 1016 Database: "other", 1017 SQL: "INSERT INTO test values (3, 4)"}), 1018 mysql.NewQueryEvent(f, s, mysql.Query{ 1019 Database: "vt_test_keyspace", 1020 SQL: "insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */"}), 1021 mysql.NewXIDEvent(f, s), 1022 } 1023 1024 events := make(chan mysql.BinlogEvent) 1025 errs := make(chan error) 1026 1027 want := []*binlogdatapb.BinlogTransaction{ 1028 { 1029 Statements: []*binlogdatapb.BinlogTransaction_Statement{ 1030 {Category: binlogdatapb.BinlogTransaction_Statement_BL_SET, Sql: []byte("SET TIMESTAMP=1407805592")}, 1031 {Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT, Sql: []byte("insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */")}, 1032 }, 1033 EventToken: &querypb.EventToken{ 1034 Timestamp: 1407805592, 1035 Position: mysql.EncodePosition(mysql.Position{ 1036 GTIDSet: mysql.MariadbGTIDSet{ 1037 0: mysql.MariadbGTID{ 1038 Domain: 0, 1039 Server: 62344, 1040 Sequence: 0x0d, 1041 }, 1042 }, 1043 }), 1044 }, 1045 }, 1046 } 1047 var got binlogStatements 1048 // Set mock mysql.ConnParams and dbconfig 1049 mcp := &mysql.ConnParams{ 1050 DbName: "vt_test_keyspace", 1051 } 1052 dbcfgs := dbconfigs.New(mcp) 1053 1054 bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) 1055 1056 go sendTestEvents(events, input) 1057 if _, err := bls.parseEvents(context.Background(), events, errs); err != ErrServerEOF { 1058 t.Errorf("unexpected error: %v", err) 1059 } 1060 1061 if !got.equal(want) { 1062 t.Errorf("binlogConnStreamer.parseEvents(): got %v, want %v", got, want) 1063 } 1064 } 1065 1066 func TestStreamerParseEventsBeginAgain(t *testing.T) { 1067 f := mysql.NewMySQL56BinlogFormat() 1068 s := mysql.NewFakeBinlogStream() 1069 1070 input := []mysql.BinlogEvent{ 1071 mysql.NewRotateEvent(f, s, 0, ""), 1072 mysql.NewFormatDescriptionEvent(f, s), 1073 mysql.NewQueryEvent(f, s, mysql.Query{ 1074 Database: "vt_test_keyspace", 1075 SQL: "BEGIN"}), 1076 mysql.NewQueryEvent(f, s, mysql.Query{ 1077 Database: "vt_test_keyspace", 1078 SQL: "insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */"}), 1079 mysql.NewQueryEvent(f, s, mysql.Query{ 1080 Database: "vt_test_keyspace", 1081 SQL: "BEGIN"}), 1082 } 1083 1084 events := make(chan mysql.BinlogEvent) 1085 errs := make(chan error) 1086 1087 sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { 1088 return nil 1089 } 1090 // Set mock mysql.ConnParams and dbconfig 1091 mcp := &mysql.ConnParams{ 1092 DbName: "vt_test_keyspace", 1093 } 1094 dbcfgs := dbconfigs.New(mcp) 1095 1096 bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, sendTransaction) 1097 before := binlogStreamerErrors.Counts()["ParseEvents"] 1098 1099 go sendTestEvents(events, input) 1100 if _, err := bls.parseEvents(context.Background(), events, errs); err != ErrServerEOF { 1101 t.Errorf("unexpected error: %v", err) 1102 } 1103 after := binlogStreamerErrors.Counts()["ParseEvents"] 1104 if got := after - before; got != 1 { 1105 t.Errorf("error count change = %v, want 1", got) 1106 } 1107 } 1108 1109 // TestStreamerParseEventsMariadbStandaloneGTID tests a MariaDB server 1110 // with no checksum, using a GTID with a Begin. 1111 func TestStreamerParseEventsMariadbBeginGTID(t *testing.T) { 1112 f := mysql.NewMariaDBBinlogFormat() 1113 s := mysql.NewFakeBinlogStream() 1114 s.ServerID = 62344 1115 s.Timestamp = 1409892744 1116 1117 input := []mysql.BinlogEvent{ 1118 mysql.NewRotateEvent(f, s, 4, "filename.0001"), 1119 mysql.NewFormatDescriptionEvent(f, s), 1120 mysql.NewMariaDBGTIDEvent(f, s, mysql.MariadbGTID{Domain: 0, Sequence: 10}, true /* hasBegin */), 1121 mysql.NewQueryEvent(f, s, mysql.Query{ 1122 Charset: &binlogdatapb.Charset{Client: 33, Conn: 33, Server: 33}, 1123 SQL: "insert into vt_insert_test(msg) values ('test 0') /* _stream vt_insert_test (id ) (null ); */", 1124 }), 1125 mysql.NewXIDEvent(f, s), 1126 } 1127 1128 events := make(chan mysql.BinlogEvent) 1129 errs := make(chan error) 1130 1131 want := []*binlogdatapb.BinlogTransaction{ 1132 { 1133 Statements: []*binlogdatapb.BinlogTransaction_Statement{ 1134 { 1135 Category: binlogdatapb.BinlogTransaction_Statement_BL_SET, 1136 Charset: &binlogdatapb.Charset{Client: 33, Conn: 33, Server: 33}, 1137 Sql: []byte("SET TIMESTAMP=1409892744"), 1138 }, 1139 { 1140 Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT, 1141 Charset: &binlogdatapb.Charset{Client: 33, Conn: 33, Server: 33}, 1142 Sql: []byte("insert into vt_insert_test(msg) values ('test 0') /* _stream vt_insert_test (id ) (null ); */"), 1143 }, 1144 }, 1145 EventToken: &querypb.EventToken{ 1146 Timestamp: 1409892744, 1147 Position: mysql.EncodePosition(mysql.Position{ 1148 GTIDSet: mysql.MariadbGTIDSet{ 1149 0: mysql.MariadbGTID{ 1150 Domain: 0, 1151 Server: 62344, 1152 Sequence: 10, 1153 }, 1154 }, 1155 }), 1156 }, 1157 }, 1158 } 1159 var got binlogStatements 1160 // Set mock mysql.ConnParams and dbconfig 1161 mcp := &mysql.ConnParams{ 1162 DbName: "vt_test_keyspace", 1163 } 1164 dbcfgs := dbconfigs.New(mcp) 1165 1166 bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) 1167 1168 go sendTestEvents(events, input) 1169 if _, err := bls.parseEvents(context.Background(), events, errs); err != ErrServerEOF { 1170 t.Errorf("unexpected error: %v", err) 1171 } 1172 1173 if !got.equal(want) { 1174 t.Errorf("binlogConnStreamer.parseEvents(): got:\n%v\nwant:\n%v", got, want) 1175 } 1176 } 1177 1178 // TestStreamerParseEventsMariadbStandaloneGTID tests a MariaDB server 1179 // with no checksum, using a standalone GTID. 1180 func TestStreamerParseEventsMariadbStandaloneGTID(t *testing.T) { 1181 f := mysql.NewMariaDBBinlogFormat() 1182 s := mysql.NewFakeBinlogStream() 1183 s.ServerID = 62344 1184 s.Timestamp = 1409892744 1185 1186 input := []mysql.BinlogEvent{ 1187 mysql.NewRotateEvent(f, s, 4, "filename.0001"), 1188 mysql.NewFormatDescriptionEvent(f, s), 1189 mysql.NewMariaDBGTIDEvent(f, s, mysql.MariadbGTID{Domain: 0, Sequence: 9}, false /* hasBegin */), 1190 mysql.NewQueryEvent(f, s, mysql.Query{ 1191 Charset: &binlogdatapb.Charset{Client: 8, Conn: 8, Server: 33}, 1192 SQL: "create table if not exists vt_insert_test (\nid bigint auto_increment,\nmsg varchar(64),\nprimary key (id)\n) Engine=InnoDB", 1193 }), 1194 } 1195 1196 events := make(chan mysql.BinlogEvent) 1197 errs := make(chan error) 1198 1199 want := []*binlogdatapb.BinlogTransaction{ 1200 { 1201 Statements: []*binlogdatapb.BinlogTransaction_Statement{ 1202 {Category: binlogdatapb.BinlogTransaction_Statement_BL_SET, Charset: &binlogdatapb.Charset{Client: 8, Conn: 8, Server: 33}, Sql: []byte("SET TIMESTAMP=1409892744")}, 1203 {Category: binlogdatapb.BinlogTransaction_Statement_BL_DDL, Charset: &binlogdatapb.Charset{Client: 8, Conn: 8, Server: 33}, Sql: []byte("create table if not exists vt_insert_test (\nid bigint auto_increment,\nmsg varchar(64),\nprimary key (id)\n) Engine=InnoDB")}, 1204 }, 1205 EventToken: &querypb.EventToken{ 1206 Timestamp: 1409892744, 1207 Position: mysql.EncodePosition(mysql.Position{ 1208 GTIDSet: mysql.MariadbGTIDSet{ 1209 0: mysql.MariadbGTID{ 1210 Domain: 0, 1211 Server: 62344, 1212 Sequence: 9, 1213 }, 1214 }, 1215 }), 1216 }, 1217 }, 1218 } 1219 var got binlogStatements 1220 // Set mock mysql.ConnParams and dbconfig 1221 mcp := &mysql.ConnParams{ 1222 DbName: "vt_test_keyspace", 1223 } 1224 dbcfgs := dbconfigs.New(mcp) 1225 1226 bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) 1227 1228 go sendTestEvents(events, input) 1229 if _, err := bls.parseEvents(context.Background(), events, errs); err != ErrServerEOF { 1230 t.Errorf("unexpected error: %v", err) 1231 } 1232 1233 if !got.equal(want) { 1234 t.Errorf("binlogConnStreamer.parseEvents(): got:\n%v\nwant:\n%v", got, want) 1235 } 1236 } 1237 1238 func TestGetStatementCategory(t *testing.T) { 1239 table := map[string]binlogdatapb.BinlogTransaction_Statement_Category{ 1240 "": binlogdatapb.BinlogTransaction_Statement_BL_UNRECOGNIZED, 1241 " ": binlogdatapb.BinlogTransaction_Statement_BL_UNRECOGNIZED, 1242 " UPDATE we don't try to fix leading spaces": binlogdatapb.BinlogTransaction_Statement_BL_UNRECOGNIZED, 1243 "FOOBAR unknown query prefix": binlogdatapb.BinlogTransaction_Statement_BL_UNRECOGNIZED, 1244 1245 "BEGIN": binlogdatapb.BinlogTransaction_Statement_BL_BEGIN, 1246 "COMMIT": binlogdatapb.BinlogTransaction_Statement_BL_COMMIT, 1247 "ROLLBACK": binlogdatapb.BinlogTransaction_Statement_BL_ROLLBACK, 1248 "INSERT something (something, something)": binlogdatapb.BinlogTransaction_Statement_BL_INSERT, 1249 "UPDATE something SET something=nothing": binlogdatapb.BinlogTransaction_Statement_BL_UPDATE, 1250 "DELETE something": binlogdatapb.BinlogTransaction_Statement_BL_DELETE, 1251 "CREATE something": binlogdatapb.BinlogTransaction_Statement_BL_DDL, 1252 "ALTER something": binlogdatapb.BinlogTransaction_Statement_BL_DDL, 1253 "DROP something": binlogdatapb.BinlogTransaction_Statement_BL_DDL, 1254 "TRUNCATE something": binlogdatapb.BinlogTransaction_Statement_BL_DDL, 1255 "RENAME something": binlogdatapb.BinlogTransaction_Statement_BL_DDL, 1256 "SET something=nothing": binlogdatapb.BinlogTransaction_Statement_BL_SET, 1257 } 1258 1259 for input, want := range table { 1260 if got := getStatementCategory(input); got != want { 1261 t.Errorf("getStatementCategory(%v) = %v, want %v", input, got, want) 1262 } 1263 } 1264 }