func TestKeyRangeFilterPass(t *testing.T) { input := proto.BinlogTransaction{ Statements: []proto.Statement{ { Category: proto.BL_SET, Sql: []byte("set1"), }, { Category: proto.BL_DML, Sql: []byte("dml1 /* EMD keyspace_id:20 */"), }, { Category: proto.BL_DML, Sql: []byte("dml2 /* EMD keyspace_id:2 */"), }, }, GTIDField: myproto.GTIDField{Value: myproto.MustParseGTID("MariaDB", "0-41983-1")}, } var got string f := KeyRangeFilterFunc(key.KIT_UINT64, testKeyRange, func(reply *proto.BinlogTransaction) error { got = bltToString(reply) return nil }) f(&input) want := `statement: <6, "set1"> statement: <4, "dml2 /* EMD keyspace_id:2 */"> position: "0-41983-1" ` if want != got { t.Errorf("want %s, got %s", want, got) } }
func TestKeyRangeFilterMalformed(t *testing.T) { input := proto.BinlogTransaction{ Statements: []proto.Statement{ { Category: proto.BL_SET, Sql: []byte("set1"), }, { Category: proto.BL_DML, Sql: []byte("ddl"), }, { Category: proto.BL_DML, Sql: []byte("dml1 /* EMD keyspace_id:20*/"), }, { Category: proto.BL_DML, Sql: []byte("dml1 /* EMD keyspace_id:2a */"), }, }, GTIDField: myproto.GTIDField{Value: myproto.MustParseGTID(blsMysqlFlavor, "41983-1")}, } var got string f := KeyRangeFilterFunc(key.KIT_UINT64, testKeyRange, func(reply *proto.BinlogTransaction) error { got = bltToString(reply) return nil }) f(&input) want := `position: "41983-1" ` if want != got { t.Errorf("want %s, got %s", want, got) } }
func TestUpdateBlpCheckpointTimestamp(t *testing.T) { gtid := myproto.MustParseGTID("MariaDB", "0-2-582") want := "UPDATE _vt.blp_checkpoint " + "SET pos='MariaDB/0-2-582', time_updated=88822, transaction_timestamp=481828 " + "WHERE source_shard_uid=78522" got := UpdateBlpCheckpoint(78522, myproto.ReplicationPosition{GTIDSet: gtid.GTIDSet()}, 88822, 481828) if got != want { t.Errorf("UpdateBlpCheckpoint() = %#v, want %#v", got, want) } }
func TestPopulateBlpCheckpoint(t *testing.T) { gtid := myproto.MustParseGTID("MariaDB", "0-1-1083") want := "INSERT INTO _vt.blp_checkpoint " + "(source_shard_uid, pos, time_updated, transaction_timestamp, flags) " + "VALUES (18372, 'MariaDB/0-1-1083', 481823, 0, 'myflags')" got := PopulateBlpCheckpoint(18372, myproto.ReplicationPosition{GTIDSet: gtid.GTIDSet()}, 481823, "myflags") if got != want { t.Errorf("PopulateBlpCheckpoint() = %#v, want %#v", got, want) } }
func TestUpdateBlpCheckpoint(t *testing.T) { gtid := myproto.MustParseGTID("GoogleMysql", "41983-58283") want := "UPDATE _vt.blp_checkpoint " + "SET pos='GoogleMysql/41983-58283', time_updated=88822 " + "WHERE source_shard_uid=78522" got := UpdateBlpCheckpoint(78522, myproto.ReplicationPosition{GTIDSet: gtid.GTIDSet()}, 88822, 0) if got != want { t.Errorf("UpdateBlpCheckpoint() = %#v, want %#v", got, want) } }
func TestUpdateBlpCheckpointTimestamp(t *testing.T) { gtid := myproto.MustParseGTID("GoogleMysql", "58283") want := "UPDATE _vt.blp_checkpoint " + "SET gtid='GoogleMysql/58283', time_updated=88822, transaction_timestamp=481828 " + "WHERE source_shard_uid=78522" got := UpdateBlpCheckpoint(78522, gtid, 88822, 481828) if got != want { t.Errorf("UpdateBlpCheckpoint() = %#v, want %#v", got, want) } }
func TestPopulateBlpCheckpoint(t *testing.T) { gtid := myproto.MustParseGTID("GoogleMysql", "19283") want := "INSERT INTO _vt.blp_checkpoint " + "(source_shard_uid, gtid, time_updated, transaction_timestamp, flags) " + "VALUES (18372, 'GoogleMysql/19283', 481823, 0, 'myflags')" got := PopulateBlpCheckpoint(18372, gtid, 481823, "myflags") if got != want { t.Errorf("PopulateBlpCheckpoint() = %#v, want %#v", got, want) } }
func TestDMLEvent(t *testing.T) { trans := &proto.BinlogTransaction{ Statements: []proto.Statement{ { Category: proto.BL_SET, Sql: []byte("SET TIMESTAMP=2"), }, { Category: proto.BL_SET, Sql: []byte("SET INSERT_ID=10"), }, { Category: proto.BL_DML, Sql: []byte("query /* _stream vtocc_e (eid id name) (null -1 'bmFtZQ==' ) (null 18446744073709551615 'bmFtZQ==' ); */"), }, { Category: proto.BL_DML, Sql: []byte("query"), }, }, Timestamp: 1, GTIDField: myproto.GTIDField{Value: myproto.MustParseGTID("MariaDB", "0-41983-20")}, } evs := &EventStreamer{ sendEvent: func(event *proto.StreamEvent) error { switch event.Category { case "DML": want := `&{DML vtocc_e [eid id name] [[10 -1 [110 97 109 101]] [11 18446744073709551615 [110 97 109 101]]] 1 <nil>}` got := fmt.Sprintf("%v", event) if got != want { t.Errorf("got \n%s, want \n%s", got, want) } case "ERR": want := `&{ERR [] [] query 1 <nil>}` got := fmt.Sprintf("%v", event) if got != want { t.Errorf("got %s, want %s", got, want) } case "POS": want := `&{POS [] [] 1 0-41983-20}` got := fmt.Sprintf("%v", event) if got != want { t.Errorf("got %s, want %s", got, want) } default: t.Errorf("unexppected: %#v", event) } return nil }, } err := evs.transactionToEvent(trans) if err != nil { t.Error(err) } }
// nextStatement returns the next statement encountered in the binlog stream. If there are // positional comments, it updates the binlogFileStreamer state. It also ignores events that // are not material. If it returns nil, it's the end of stream. If err is also nil, then // it was due to a normal termination. func (bls *binlogFileStreamer) nextStatement(ctx *sync2.ServiceContext, bufReader *bufio.Reader) (stmt []byte, err error) { eventLoop: for { // Stop processing if we're shutting down if !ctx.IsRunning() { return nil, io.EOF } event, err := bls.readEvent(bufReader) if err != nil { if err == io.EOF { return nil, nil } return nil, err } values := posRE.FindSubmatch(event) if values != nil { bls.blPos.ServerId = mustParseInt64(values[1]) bls.file.Set(mustParseInt64(values[2])) // Make the fake Google GTID format we invented. gtid := string(values[1]) + "-" + string(values[3]) bls.blPos.GTID = myproto.MustParseGTID(blsMysqlFlavor, gtid) continue } values = rotateRE.FindSubmatch(event) if values != nil { err = bls.file.Rotate(path.Join(bls.dir, string(values[1])), mustParseInt64(values[2])) if err != nil { return nil, err } return nil, nil } if bytes.HasPrefix(event, eolfPrefix) { return nil, nil } values = delimRE.FindSubmatch(event) if values != nil { bls.delim = values[1] continue } for _, ignorePrefix := range ignorePrefixes { if bytes.HasPrefix(event, ignorePrefix) { continue eventLoop } } return event, nil } }
// nextStatement returns the next statement encountered in the binlog stream. If there are // positional comments, it updates the binlogFileStreamer state. It also ignores events that // are not material. If it returns nil, it's the end of stream. If err is also nil, then // it was due to a normal termination. func (bls *binlogFileStreamer) nextStatement(bufReader *bufio.Reader) (stmt []byte, err error) { eventLoop: for { // Stop processing if we're shutting down if bls.svm.State() != sync2.SERVICE_RUNNING { return nil, io.EOF } event, err := bls.readEvent(bufReader) if err != nil { if err == io.EOF { return nil, nil } return nil, err } values := posRE.FindSubmatch(event) if values != nil { bls.blPos.ServerId = mustParseInt64(values[1]) bls.file.Set(mustParseInt64(values[2])) bls.blPos.GTID = myproto.MustParseGTID(blsMysqlFlavor, string(values[3])) continue } values = rotateRE.FindSubmatch(event) if values != nil { err = bls.file.Rotate(path.Join(bls.dir, string(values[1])), mustParseInt64(values[2])) if err != nil { return nil, err } return nil, nil } if bytes.HasPrefix(event, eolfPrefix) { return nil, nil } values = delimRE.FindSubmatch(event) if values != nil { bls.delim = values[1] continue } for _, ignorePrefix := range ignorePrefixes { if bytes.HasPrefix(event, ignorePrefix) { continue eventLoop } } return event, nil } }
func TestDDLEvent(t *testing.T) { trans := &proto.BinlogTransaction{ Statements: []proto.Statement{ { Category: proto.BL_SET, Sql: []byte("SET TIMESTAMP=2"), }, { Category: proto.BL_DDL, Sql: []byte("DDL"), }, }, Timestamp: 1, GTIDField: myproto.GTIDField{Value: myproto.MustParseGTID("MariaDB", "0-41983-20")}, } evs := &EventStreamer{ sendEvent: func(event *proto.StreamEvent) error { switch event.Category { case "DDL": want := `&{DDL [] [] DDL 1 <nil>}` got := fmt.Sprintf("%v", event) if got != want { t.Errorf("got %s, want %s", got, want) } case "POS": want := `&{POS [] [] 1 0-41983-20}` got := fmt.Sprintf("%v", event) if got != want { t.Errorf("got %s, want %s", got, want) } default: t.Errorf("unexppected: %#v", event) } return nil }, } err := evs.transactionToEvent(trans) if err != nil { t.Error(err) } }