github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/binlog/event/event_test.go (about) 1 // Copyright 2019 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 // binlog events generator for MySQL used to generate some binlog events for tests. 15 // Readability takes precedence over performance. 16 17 package event 18 19 import ( 20 "os" 21 "path/filepath" 22 "strings" 23 "testing" 24 "time" 25 26 gmysql "github.com/go-mysql-org/go-mysql/mysql" 27 "github.com/go-mysql-org/go-mysql/replication" 28 "github.com/pingcap/tiflow/dm/pkg/gtid" 29 "github.com/stretchr/testify/require" 30 ) 31 32 func TestGenEventHeader(t *testing.T) { 33 t.Parallel() 34 var ( 35 latestPos uint32 = 4 36 header = &replication.EventHeader{ 37 Timestamp: uint32(time.Now().Unix()), 38 EventType: replication.FORMAT_DESCRIPTION_EVENT, 39 ServerID: 11, 40 Flags: 0x01, 41 LogPos: latestPos + 109, 42 EventSize: 109, // current binlog version, 109, 43 } 44 ) 45 46 data, err := GenEventHeader(header) 47 require.Nil(t, err) 48 require.Equal(t, eventHeaderLen, uint8(len(data))) 49 50 header2 := &replication.EventHeader{} 51 err = header2.Decode(data) 52 require.Nil(t, err) 53 verifyHeader(t, header2, header, header.EventType, latestPos, header.EventSize) 54 } 55 56 func verifyHeader(t *testing.T, obtained, excepted *replication.EventHeader, eventType replication.EventType, latestPos, eventSize uint32) { 57 t.Helper() 58 require.Equal(t, excepted.Timestamp, obtained.Timestamp) 59 require.Equal(t, excepted.ServerID, obtained.ServerID) 60 require.Equal(t, excepted.Flags, obtained.Flags) 61 require.Equal(t, eventType, obtained.EventType) 62 require.Equal(t, eventSize, obtained.EventSize) 63 require.Equal(t, eventSize+latestPos, obtained.LogPos) 64 } 65 66 func TestGenFormatDescriptionEvent(t *testing.T) { 67 t.Parallel() 68 var ( 69 header = &replication.EventHeader{ 70 Timestamp: uint32(time.Now().Unix()), 71 ServerID: 11, 72 Flags: 0x01, 73 } 74 latestPos uint32 = 4 75 ) 76 ev, err := GenFormatDescriptionEvent(header, latestPos) 77 require.Nil(t, err) 78 79 // verify the header 80 verifyHeader(t, ev.Header, header, replication.FORMAT_DESCRIPTION_EVENT, latestPos, uint32(len(ev.RawData))) 81 82 // some fields of FormatDescriptionEvent are a little hard to test, so we try to parse a binlog file. 83 dir := t.TempDir() 84 name := filepath.Join(dir, "mysql-bin-test.000001") 85 f, err := os.Create(name) 86 require.Nil(t, err) 87 defer f.Close() 88 89 // write a binlog file header 90 _, err = f.Write(replication.BinLogFileHeader) 91 require.Nil(t, err) 92 93 // write the FormatDescriptionEvent 94 _, err = f.Write(ev.RawData) 95 require.Nil(t, err) 96 97 // should only receive one FormatDescriptionEvent 98 onEventFunc := func(e *replication.BinlogEvent) error { 99 require.Equal(t, ev.Header, e.Header) 100 require.Equal(t, ev.Event, e.Event) 101 require.Equal(t, ev.RawData, e.RawData) 102 return nil 103 } 104 105 parser2 := replication.NewBinlogParser() 106 parser2.SetVerifyChecksum(true) 107 err = parser2.ParseFile(name, 0, onEventFunc) 108 require.Nil(t, err) 109 } 110 111 func TestGenRotateEvent(t *testing.T) { 112 t.Parallel() 113 var ( 114 header = &replication.EventHeader{ 115 Timestamp: uint32(time.Now().Unix()), 116 ServerID: 11, 117 Flags: 0x01, 118 } 119 latestPos uint32 = 4 120 nextLogName []byte // nil 121 position uint64 = 123 122 ) 123 124 // empty nextLogName, invalid 125 rotateEv, err := GenRotateEvent(header, latestPos, nextLogName, position) 126 require.NotNil(t, err) 127 require.Nil(t, rotateEv) 128 129 // valid nextLogName 130 nextLogName = []byte("mysql-bin.000010") 131 rotateEv, err = GenRotateEvent(header, latestPos, nextLogName, position) 132 require.Nil(t, err) 133 require.NotNil(t, rotateEv) 134 135 // verify the header 136 verifyHeader(t, rotateEv.Header, header, replication.ROTATE_EVENT, latestPos, uint32(len(rotateEv.RawData))) 137 138 // verify the body 139 rotateEvBody, ok := rotateEv.Event.(*replication.RotateEvent) 140 require.True(t, ok) 141 require.NotNil(t, rotateEvBody) 142 require.Equal(t, nextLogName, rotateEvBody.NextLogName) 143 require.Equal(t, position, rotateEvBody.Position) 144 } 145 146 func TestGenPreviousGTIDsEvent(t *testing.T) { 147 var ( 148 header = &replication.EventHeader{ 149 Timestamp: uint32(time.Now().Unix()), 150 ServerID: 11, 151 Flags: 0x01, 152 } 153 latestPos uint32 = 4 154 str = "9f61c5f9-1eef-11e9-b6cf-0242ac140003:1-5" 155 ) 156 157 // always needing a FormatDescriptionEvent in the binlog file. 158 formatDescEv, err := GenFormatDescriptionEvent(header, latestPos) 159 require.Nil(t, err) 160 161 // update latestPos 162 latestPos = formatDescEv.Header.LogPos 163 164 // generate a PreviousGTIDsEvent 165 gSet, err := gtid.ParserGTID(gmysql.MySQLFlavor, str) 166 require.Nil(t, err) 167 168 previousGTIDsEv, err := GenPreviousGTIDsEvent(header, latestPos, gSet) 169 require.Nil(t, err) 170 171 dir := t.TempDir() 172 name1 := filepath.Join(dir, "mysql-bin-test.000001") 173 f1, err := os.Create(name1) 174 require.Nil(t, err) 175 defer f1.Close() 176 177 // write a binlog file header 178 _, err = f1.Write(replication.BinLogFileHeader) 179 require.Nil(t, err) 180 181 // write a FormatDescriptionEvent event 182 _, err = f1.Write(formatDescEv.RawData) 183 require.Nil(t, err) 184 185 // write the PreviousGTIDsEvent 186 _, err = f1.Write(previousGTIDsEv.RawData) 187 require.Nil(t, err) 188 189 count := 0 190 onEventFunc := func(e *replication.BinlogEvent) error { 191 count++ 192 switch count { 193 case 1: // FormatDescriptionEvent 194 require.Equal(t, formatDescEv.Header, e.Header) 195 require.Equal(t, formatDescEv.Event, e.Event) 196 require.Equal(t, formatDescEv.RawData, e.RawData) 197 case 2: // PreviousGTIDsEvent 198 require.Equal(t, previousGTIDsEv.Header, e.Header) 199 require.Equal(t, previousGTIDsEv.Event, e.Event) 200 require.Equal(t, previousGTIDsEv.RawData, e.RawData) 201 default: 202 t.Fatalf("too many binlog events got, current is %+v", e.Header) 203 } 204 return nil 205 } 206 207 parser2 := replication.NewBinlogParser() 208 parser2.SetVerifyChecksum(true) 209 err = parser2.ParseFile(name1, 0, onEventFunc) 210 require.Nil(t, err) 211 212 // multi GTID 213 str = "3ccc475b-2343-11e7-be21-6c0b84d59f30:1-14,406a3f61-690d-11e7-87c5-6c92bf46f384:1-94321383,53bfca22-690d-11e7-8a62-18ded7a37b78:1-495,686e1ab6-c47e-11e7-a42c-6c92bf46f384:1-34981190,03fc0263-28c7-11e7-a653-6c0b84d59f30:1-7041423,05474d3c-28c7-11e7-8352-203db246dd3d:1-170,10b039fc-c843-11e7-8f6a-1866daf8d810:1-308290454" 214 gSet, err = gtid.ParserGTID(gmysql.MySQLFlavor, str) 215 require.Nil(t, err) 216 217 previousGTIDsEv, err = GenPreviousGTIDsEvent(header, latestPos, gSet) 218 require.Nil(t, err) 219 220 // write another file 221 name2 := filepath.Join(dir, "mysql-bin-test.000002") 222 f2, err := os.Create(name2) 223 require.Nil(t, err) 224 defer f2.Close() 225 226 // write a binlog file header 227 _, err = f2.Write(replication.BinLogFileHeader) 228 require.Nil(t, err) 229 230 // write a FormatDescriptionEvent event 231 _, err = f2.Write(formatDescEv.RawData) 232 require.Nil(t, err) 233 234 // write the PreviousGTIDsEvent 235 _, err = f2.Write(previousGTIDsEv.RawData) 236 require.Nil(t, err) 237 238 count = 0 // reset count 239 err = parser2.ParseFile(name2, 0, onEventFunc) 240 require.Nil(t, err) 241 } 242 243 func TestGenGTIDEvent(t *testing.T) { 244 var ( 245 header = &replication.EventHeader{ 246 Timestamp: uint32(time.Now().Unix()), 247 ServerID: 11, 248 Flags: 0x01, 249 } 250 latestPos uint32 = 4 251 gtidFlags = GTIDFlagsCommitYes 252 prevGTIDsStr = "9f61c5f9-1eef-11e9-b6cf-0242ac140003:1-5" 253 uuid = "9f61c5f9-1eef-11e9-b6cf-0242ac140003" 254 gno int64 = 6 255 lastCommitted int64 256 ) 257 sid, err := ParseSID(uuid) 258 require.Nil(t, err) 259 260 // always needing a FormatDescriptionEvent in the binlog file. 261 formatDescEv, err := GenFormatDescriptionEvent(header, latestPos) 262 require.Nil(t, err) 263 latestPos = formatDescEv.Header.LogPos // update latestPos 264 265 // also needing a PreviousGTIDsEvent after FormatDescriptionEvent 266 gSet, err := gtid.ParserGTID(gmysql.MySQLFlavor, prevGTIDsStr) 267 require.Nil(t, err) 268 previousGTIDsEv, err := GenPreviousGTIDsEvent(header, latestPos, gSet) 269 require.Nil(t, err) 270 latestPos = previousGTIDsEv.Header.LogPos // update latestPos 271 272 gtidEv, err := GenGTIDEvent(header, latestPos, gtidFlags, uuid, gno, lastCommitted, lastCommitted+1) 273 require.Nil(t, err) 274 275 // verify the header 276 verifyHeader(t, gtidEv.Header, header, replication.GTID_EVENT, latestPos, uint32(len(gtidEv.RawData))) 277 278 // verify the body 279 gtidEvBody, ok := gtidEv.Event.(*replication.GTIDEvent) 280 require.True(t, ok) 281 require.NotNil(t, gtidEvBody) 282 require.Equal(t, gtidFlags, gtidEvBody.CommitFlag) 283 require.Equal(t, sid.Bytes(), gtidEvBody.SID) 284 require.Equal(t, gno, gtidEvBody.GNO) 285 require.Equal(t, lastCommitted, gtidEvBody.LastCommitted) 286 require.Equal(t, lastCommitted+1, gtidEvBody.SequenceNumber) 287 288 // write a binlog file, then try to parse it 289 dir := t.TempDir() 290 name := filepath.Join(dir, "mysql-bin-test.000001") 291 f, err := os.Create(name) 292 require.Nil(t, err) 293 defer f.Close() 294 295 // write a binlog file. 296 _, err = f.Write(replication.BinLogFileHeader) 297 require.Nil(t, err) 298 _, err = f.Write(formatDescEv.RawData) 299 require.Nil(t, err) 300 _, err = f.Write(previousGTIDsEv.RawData) 301 require.Nil(t, err) 302 303 // write GTIDEvent. 304 _, err = f.Write(gtidEv.RawData) 305 require.Nil(t, err) 306 307 count := 0 308 onEventFunc := func(e *replication.BinlogEvent) error { 309 count++ 310 switch count { 311 case 1: // FormatDescriptionEvent 312 require.Equal(t, formatDescEv.Header, e.Header) 313 require.Equal(t, formatDescEv.Event, e.Event) 314 require.Equal(t, formatDescEv.RawData, e.RawData) 315 case 2: // PreviousGTIDsEvent 316 require.Equal(t, previousGTIDsEv.Header, e.Header) 317 require.Equal(t, previousGTIDsEv.Event, e.Event) 318 require.Equal(t, previousGTIDsEv.RawData, e.RawData) 319 case 3: // GTIDEvent 320 require.Equal(t, replication.GTID_EVENT, e.Header.EventType) 321 require.Equal(t, gtidEv.RawData, e.RawData) 322 default: 323 t.Fatalf("too many binlog events got, current is %+v", e.Header) 324 } 325 return nil 326 } 327 parser2 := replication.NewBinlogParser() 328 parser2.SetVerifyChecksum(true) 329 err = parser2.ParseFile(name, 0, onEventFunc) 330 require.Nil(t, err) 331 } 332 333 func TestGenQueryEvent(t *testing.T) { 334 var ( 335 header = &replication.EventHeader{ 336 Timestamp: uint32(time.Now().Unix()), 337 ServerID: 11, 338 Flags: 0x01, 339 } 340 latestPos uint32 = 4 341 slaveProxyID uint32 = 2 342 executionTime uint32 = 12 343 errorCode uint16 = 13 344 statusVars []byte // nil 345 schema []byte // nil 346 query []byte // nil 347 ) 348 349 // empty query, invalid 350 queryEv, err := GenQueryEvent(header, latestPos, slaveProxyID, executionTime, errorCode, statusVars, schema, query) 351 require.NotNil(t, err) 352 require.Nil(t, queryEv) 353 354 // valid query 355 query = []byte("BEGIN") 356 queryEv, err = GenQueryEvent(header, latestPos, slaveProxyID, executionTime, errorCode, statusVars, schema, query) 357 require.Nil(t, err) 358 require.NotNil(t, queryEv) 359 360 // verify the header 361 verifyHeader(t, queryEv.Header, header, replication.QUERY_EVENT, latestPos, uint32(len(queryEv.RawData))) 362 363 // verify the body 364 queryEvBody, ok := queryEv.Event.(*replication.QueryEvent) 365 require.True(t, ok) 366 require.NotNil(t, queryEvBody) 367 require.Equal(t, slaveProxyID, queryEvBody.SlaveProxyID) 368 require.Equal(t, executionTime, queryEvBody.ExecutionTime) 369 require.Equal(t, errorCode, queryEvBody.ErrorCode) 370 require.Equal(t, []byte{}, queryEvBody.StatusVars) 371 require.Equal(t, []byte{}, queryEvBody.Schema) 372 require.Equal(t, query, queryEvBody.Query) 373 374 // non-empty schema 375 schema = []byte("db") 376 query = []byte("CREATE TABLE db.tbl (c1 int)") 377 queryEv, err = GenQueryEvent(header, latestPos, slaveProxyID, executionTime, errorCode, statusVars, schema, query) 378 require.Nil(t, err) 379 require.NotNil(t, queryEv) 380 381 // verify the body 382 queryEvBody, ok = queryEv.Event.(*replication.QueryEvent) 383 require.True(t, ok) 384 require.NotNil(t, queryEvBody) 385 require.Equal(t, schema, queryEvBody.Schema) 386 require.Equal(t, query, queryEvBody.Query) 387 388 // non-empty statusVars 389 statusVars = []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0xa0, 0x55, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x73, 0x74, 0x64, 0x04, 0x21, 0x00, 0x21, 0x00, 0x08, 0x00, 0x0c, 0x01, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x64, 0x62, 0x5f, 0x31, 0x00} 390 queryEv, err = GenQueryEvent(header, latestPos, slaveProxyID, executionTime, errorCode, statusVars, schema, query) 391 require.Nil(t, err) 392 require.NotNil(t, queryEv) 393 394 // verify the body 395 queryEvBody, ok = queryEv.Event.(*replication.QueryEvent) 396 require.True(t, ok) 397 require.NotNil(t, queryEvBody) 398 require.Equal(t, statusVars, queryEvBody.StatusVars) 399 } 400 401 func TestGenTableMapEvent(t *testing.T) { 402 var ( 403 header = &replication.EventHeader{ 404 Timestamp: uint32(time.Now().Unix()), 405 ServerID: 11, 406 Flags: 0x01, 407 } 408 latestPos uint32 = 123 409 tableID uint64 = 108 410 schema []byte // nil 411 table []byte // nil 412 columnType []byte // nil 413 ) 414 415 // invalid schema, table and columnType 416 tableMapEv, err := GenTableMapEvent(header, latestPos, tableID, schema, table, columnType) 417 require.NotNil(t, err) 418 require.Nil(t, tableMapEv) 419 420 // valid schema, invalid table and columnType 421 schema = []byte("db") 422 tableMapEv, err = GenTableMapEvent(header, latestPos, tableID, schema, table, columnType) 423 require.NotNil(t, err) 424 require.Nil(t, tableMapEv) 425 426 // valid schema and table, invalid columnType 427 table = []byte("tbl") 428 tableMapEv, err = GenTableMapEvent(header, latestPos, tableID, schema, table, columnType) 429 require.NotNil(t, err) 430 require.Nil(t, tableMapEv) 431 432 // all valid 433 columnType = []byte{gmysql.MYSQL_TYPE_LONG} 434 tableMapEv, err = GenTableMapEvent(header, latestPos, tableID, schema, table, columnType) 435 require.Nil(t, err) 436 require.NotNil(t, tableMapEv) 437 438 // verify the header 439 verifyHeader(t, tableMapEv.Header, header, replication.TABLE_MAP_EVENT, latestPos, uint32(len(tableMapEv.RawData))) 440 441 // verify the body 442 tableMapEvBody, ok := tableMapEv.Event.(*replication.TableMapEvent) 443 require.True(t, ok) 444 require.NotNil(t, tableMapEvBody) 445 require.Equal(t, tableID, tableMapEvBody.TableID) 446 require.Equal(t, tableMapFlags, tableMapEvBody.Flags) 447 require.Equal(t, schema, tableMapEvBody.Schema) 448 require.Equal(t, table, tableMapEvBody.Table) 449 require.Equal(t, uint64(len(columnType)), tableMapEvBody.ColumnCount) 450 require.Equal(t, columnType, tableMapEvBody.ColumnType) 451 452 // multi column type 453 columnType = []byte{gmysql.MYSQL_TYPE_STRING, gmysql.MYSQL_TYPE_NEWDECIMAL, gmysql.MYSQL_TYPE_VAR_STRING, gmysql.MYSQL_TYPE_BLOB} 454 tableMapEv, err = GenTableMapEvent(header, latestPos, tableID, schema, table, columnType) 455 require.Nil(t, err) 456 require.NotNil(t, tableMapEv) 457 458 // verify the body 459 tableMapEvBody, ok = tableMapEv.Event.(*replication.TableMapEvent) 460 require.True(t, ok) 461 require.NotNil(t, tableMapEvBody) 462 require.Equal(t, uint64(len(columnType)), tableMapEvBody.ColumnCount) 463 require.Equal(t, columnType, tableMapEvBody.ColumnType) 464 465 // unsupported column type 466 columnType = []byte{gmysql.MYSQL_TYPE_NEWDATE} 467 tableMapEv, err = GenTableMapEvent(header, latestPos, tableID, schema, table, columnType) 468 require.NotNil(t, err) 469 require.Nil(t, tableMapEv) 470 } 471 472 func TestGenRowsEvent(t *testing.T) { 473 var ( 474 header = &replication.EventHeader{ 475 Timestamp: uint32(time.Now().Unix()), 476 ServerID: 11, 477 Flags: 0x01, 478 } 479 latestPos uint32 = 123 480 tableID uint64 = 108 481 eventType = replication.TABLE_MAP_EVENT 482 rowsFlag = RowFlagsEndOfStatement 483 rows [][]interface{} 484 columnType []byte // nil 485 ) 486 487 // invalid eventType, rows and columnType 488 rowsEv, err := GenRowsEvent(header, latestPos, eventType, tableID, rowsFlag, rows, columnType, nil) 489 require.NotNil(t, err) 490 require.Nil(t, rowsEv) 491 492 // valid eventType, invalid rows and columnType 493 eventType = replication.WRITE_ROWS_EVENTv0 494 rowsEv, err = GenRowsEvent(header, latestPos, eventType, tableID, rowsFlag, rows, columnType, nil) 495 require.NotNil(t, err) 496 require.Nil(t, rowsEv) 497 498 // valid eventType and rows, invalid columnType 499 row := []interface{}{int32(1)} 500 rows = append(rows, row) 501 rowsEv, err = GenRowsEvent(header, latestPos, eventType, tableID, rowsFlag, rows, columnType, nil) 502 require.NotNil(t, err) 503 require.Nil(t, rowsEv) 504 505 // all valid 506 columnType = []byte{gmysql.MYSQL_TYPE_LONG} 507 rowsEv, err = GenRowsEvent(header, latestPos, eventType, tableID, rowsFlag, rows, columnType, nil) 508 require.Nil(t, err) 509 require.NotNil(t, rowsEv) 510 511 // verify the header 512 verifyHeader(t, rowsEv.Header, header, eventType, latestPos, uint32(len(rowsEv.RawData))) 513 514 // verify the body 515 rowsEvBody, ok := rowsEv.Event.(*replication.RowsEvent) 516 require.True(t, ok) 517 require.NotNil(t, rowsEvBody) 518 require.Equal(t, rowsFlag, rowsEvBody.Flags) 519 require.Equal(t, tableID, rowsEvBody.TableID) 520 require.Equal(t, uint64(len(rows[0])), rowsEvBody.ColumnCount) 521 require.Equal(t, 0, rowsEvBody.Version) // WRITE_ROWS_EVENTv0 522 require.Equal(t, rows, rowsEvBody.Rows) 523 524 // multi rows, with different length, invalid 525 rows = append(rows, []interface{}{int32(1), int32(2)}) 526 rowsEv, err = GenRowsEvent(header, latestPos, eventType, tableID, rowsFlag, rows, columnType, nil) 527 require.NotNil(t, err) 528 require.Nil(t, rowsEv) 529 530 // multi rows, multi columns, valid 531 rows = make([][]interface{}, 0, 2) 532 rows = append(rows, []interface{}{int32(1), int32(2)}) 533 rows = append(rows, []interface{}{int32(3), int32(4)}) 534 columnType = []byte{gmysql.MYSQL_TYPE_LONG, gmysql.MYSQL_TYPE_LONG} 535 rowsEv, err = GenRowsEvent(header, latestPos, eventType, tableID, rowsFlag, rows, columnType, nil) 536 require.Nil(t, err) 537 require.NotNil(t, rowsEv) 538 // verify the body 539 rowsEvBody, ok = rowsEv.Event.(*replication.RowsEvent) 540 require.True(t, ok) 541 require.NotNil(t, rowsEvBody) 542 require.Equal(t, uint64(len(rows[0])), rowsEvBody.ColumnCount) 543 require.Equal(t, rows, rowsEvBody.Rows) 544 545 // all valid event-type 546 evTypes := []replication.EventType{ 547 replication.WRITE_ROWS_EVENTv0, replication.WRITE_ROWS_EVENTv1, replication.WRITE_ROWS_EVENTv2, 548 replication.UPDATE_ROWS_EVENTv0, replication.UPDATE_ROWS_EVENTv1, replication.UPDATE_ROWS_EVENTv2, 549 replication.DELETE_ROWS_EVENTv0, replication.DELETE_ROWS_EVENTv1, replication.DELETE_ROWS_EVENTv2, 550 } 551 for _, eventType = range evTypes { 552 rowsEv, err = GenRowsEvent(header, latestPos, eventType, tableID, rowsFlag, rows, columnType, nil) 553 require.Nil(t, err) 554 require.NotNil(t, rowsEv) 555 require.Equal(t, eventType, rowsEv.Header.EventType) 556 } 557 558 // more column types 559 rows = make([][]interface{}, 0, 1) 560 rows = append(rows, []interface{}{ 561 int32(1), int8(2), int16(3), int32(4), int64(5), 562 float32(1.23), float64(4.56), "string with type STRING", 563 }) 564 columnType = []byte{ 565 gmysql.MYSQL_TYPE_LONG, gmysql.MYSQL_TYPE_TINY, gmysql.MYSQL_TYPE_SHORT, gmysql.MYSQL_TYPE_INT24, gmysql.MYSQL_TYPE_LONGLONG, 566 gmysql.MYSQL_TYPE_FLOAT, gmysql.MYSQL_TYPE_DOUBLE, gmysql.MYSQL_TYPE_STRING, 567 } 568 rowsEv, err = GenRowsEvent(header, latestPos, eventType, tableID, rowsFlag, rows, columnType, nil) 569 require.Nil(t, err) 570 require.NotNil(t, rowsEv) 571 // verify the body 572 rowsEvBody, ok = rowsEv.Event.(*replication.RowsEvent) 573 require.True(t, ok) 574 require.NotNil(t, rowsEvBody) 575 require.Equal(t, uint64(len(rows[0])), rowsEvBody.ColumnCount) 576 require.Equal(t, rows, rowsEvBody.Rows) 577 578 // column type mismatch 579 rows[0][0] = int8(1) 580 rowsEv, err = GenRowsEvent(header, latestPos, eventType, tableID, rowsFlag, rows, columnType, nil) 581 require.NotNil(t, err) 582 require.Nil(t, rowsEv) 583 584 // NotSupported column type 585 rows = make([][]interface{}, 0, 1) 586 rows = append(rows, []interface{}{int32(1)}) 587 unsupportedTypes := []byte{ 588 gmysql.MYSQL_TYPE_VARCHAR, gmysql.MYSQL_TYPE_VAR_STRING, 589 gmysql.MYSQL_TYPE_NEWDECIMAL, gmysql.MYSQL_TYPE_BIT, 590 gmysql.MYSQL_TYPE_TIMESTAMP, gmysql.MYSQL_TYPE_TIMESTAMP2, 591 gmysql.MYSQL_TYPE_DATETIME, gmysql.MYSQL_TYPE_DATETIME2, 592 gmysql.MYSQL_TYPE_TIME, gmysql.MYSQL_TYPE_TIME2, 593 gmysql.MYSQL_TYPE_YEAR, gmysql.MYSQL_TYPE_ENUM, gmysql.MYSQL_TYPE_SET, 594 gmysql.MYSQL_TYPE_BLOB, gmysql.MYSQL_TYPE_JSON, gmysql.MYSQL_TYPE_GEOMETRY, 595 } 596 for i := range unsupportedTypes { 597 columnType = unsupportedTypes[i : i+1] 598 rowsEv, err = GenRowsEvent(header, latestPos, eventType, tableID, rowsFlag, rows, columnType, nil) 599 require.NotNil(t, err) 600 require.True(t, strings.Contains(err.Error(), "not supported")) 601 require.Nil(t, rowsEv) 602 } 603 } 604 605 func TestGenXIDEvent(t *testing.T) { 606 var ( 607 header = &replication.EventHeader{ 608 Timestamp: uint32(time.Now().Unix()), 609 ServerID: 11, 610 Flags: 0x01, 611 } 612 latestPos uint32 = 4 613 xid uint64 = 123 614 ) 615 616 xidEv, err := GenXIDEvent(header, latestPos, xid) 617 require.Nil(t, err) 618 require.NotNil(t, xidEv) 619 620 // verify the header 621 verifyHeader(t, xidEv.Header, header, replication.XID_EVENT, latestPos, uint32(len(xidEv.RawData))) 622 623 // verify the body 624 xidEvBody, ok := xidEv.Event.(*replication.XIDEvent) 625 require.True(t, ok) 626 require.NotNil(t, xidEvBody) 627 require.Equal(t, xid, xidEvBody.XID) 628 } 629 630 func TestGenMariaDBGTIDListEvent(t *testing.T) { 631 var ( 632 header = &replication.EventHeader{ 633 Timestamp: uint32(time.Now().Unix()), 634 ServerID: 11, 635 Flags: 0x01, 636 } 637 latestPos uint32 = 4 638 gSet gmysql.GTIDSet // invalid 639 ) 640 641 // invalid gSet 642 gtidListEv, err := GenMariaDBGTIDListEvent(header, latestPos, gSet) 643 require.NotNil(t, err) 644 require.Nil(t, gtidListEv) 645 646 // valid gSet with single GTID 647 gSet, err = gtid.ParserGTID(gmysql.MariaDBFlavor, "1-2-3") 648 require.Nil(t, err) 649 require.NotNil(t, gSet) 650 mGSet, ok := gSet.(*gmysql.MariadbGTIDSet) 651 require.True(t, ok) 652 require.NotNil(t, mGSet) 653 654 gtidListEv, err = GenMariaDBGTIDListEvent(header, latestPos, gSet) 655 require.Nil(t, err) 656 require.NotNil(t, gtidListEv) 657 658 // verify the header 659 verifyHeader(t, gtidListEv.Header, header, replication.MARIADB_GTID_LIST_EVENT, latestPos, uint32(len(gtidListEv.RawData))) 660 661 // verify the body 662 gtidListEvBody, ok := gtidListEv.Event.(*replication.MariadbGTIDListEvent) 663 require.True(t, ok) 664 require.NotNil(t, gtidListEvBody) 665 require.Len(t, gtidListEvBody.GTIDs, 1) 666 require.Equal(t, *mGSet.Sets[gtidListEvBody.GTIDs[0].DomainID][gtidListEvBody.GTIDs[0].ServerID], gtidListEvBody.GTIDs[0]) 667 668 // valid gSet with multi GTIDs 669 gSet, err = gtid.ParserGTID(gmysql.MariaDBFlavor, "1-2-12,2-2-3,3-3-8,3-4-4") 670 require.Nil(t, err) 671 require.NotNil(t, gSet) 672 mGSet, ok = gSet.(*gmysql.MariadbGTIDSet) 673 require.True(t, ok) 674 require.NotNil(t, mGSet) 675 676 gtidListEv, err = GenMariaDBGTIDListEvent(header, latestPos, gSet) 677 require.Nil(t, err) 678 require.NotNil(t, gtidListEv) 679 680 // verify the body 681 gtidListEvBody, ok = gtidListEv.Event.(*replication.MariadbGTIDListEvent) 682 require.True(t, ok) 683 require.NotNil(t, gtidListEvBody) 684 require.Len(t, gtidListEvBody.GTIDs, 4) 685 for _, mGTID := range gtidListEvBody.GTIDs { 686 set, ok := mGSet.Sets[mGTID.DomainID] 687 require.True(t, ok) 688 mGTID2, ok := set[mGTID.ServerID] 689 require.True(t, ok) 690 require.Equal(t, *mGTID2, mGTID) 691 } 692 } 693 694 func TestGenMariaDBGTIDEvent(t *testing.T) { 695 var ( 696 header = &replication.EventHeader{ 697 Timestamp: uint32(time.Now().Unix()), 698 ServerID: 11, 699 Flags: 0x01, 700 } 701 latestPos uint32 = 4 702 seqNum uint64 = 123 703 domainID uint32 = 456 704 ) 705 706 gtidEv, err := GenMariaDBGTIDEvent(header, latestPos, seqNum, domainID) 707 require.Nil(t, err) 708 require.NotNil(t, gtidEv) 709 710 // verify the header 711 verifyHeader(t, gtidEv.Header, header, replication.MARIADB_GTID_EVENT, latestPos, uint32(len(gtidEv.RawData))) 712 713 // verify the body 714 gtidEvBody, ok := gtidEv.Event.(*replication.MariadbGTIDEvent) 715 require.True(t, ok) 716 require.NotNil(t, gtidEvBody) 717 require.Equal(t, seqNum, gtidEvBody.GTID.SequenceNumber) 718 require.Equal(t, domainID, gtidEvBody.GTID.DomainID) 719 } 720 721 func TestGenDummyEvent(t *testing.T) { 722 var ( 723 header = &replication.EventHeader{ 724 Timestamp: uint32(time.Now().Unix()), 725 ServerID: 11, 726 Flags: replication.LOG_EVENT_THREAD_SPECIFIC_F | replication.LOG_EVENT_BINLOG_IN_USE_F, 727 } 728 expectedHeader = &replication.EventHeader{ 729 Timestamp: uint32(time.Now().Unix()), 730 ServerID: 11, 731 Flags: replication.LOG_EVENT_SUPPRESS_USE_F | replication.LOG_EVENT_RELAY_LOG_F | replication.LOG_EVENT_BINLOG_IN_USE_F, 732 } 733 latestPos uint32 = 4 734 ) 735 736 // too small event size 737 eventSize := MinUserVarEventLen - 1 738 userVarEv, err := GenDummyEvent(header, latestPos, eventSize) 739 require.Regexp(t, ".*is too small.*", err) 740 require.Nil(t, userVarEv) 741 742 // minimum event size, USER_VAR_EVENT with name-length==1 743 eventSize = MinUserVarEventLen 744 userVarEv, err = GenDummyEvent(header, latestPos, eventSize) 745 require.Nil(t, err) 746 require.NotNil(t, userVarEv) 747 // verify the header 748 verifyHeader(t, userVarEv.Header, expectedHeader, replication.USER_VAR_EVENT, latestPos, uint32(len(userVarEv.RawData))) 749 // verify the body 750 nameStart := uint32(eventHeaderLen + 4) 751 nameEnd := eventSize - 1 - crc32Len 752 nameLen := nameEnd - nameStart 753 require.Equal(t, uint32(1), nameLen) // name-length==1 754 require.Equal(t, dummyUserVarName[:nameLen], userVarEv.RawData[nameStart:nameEnd]) 755 require.Equal(t, []byte{0x01}, userVarEv.RawData[nameEnd:nameEnd+1]) // is-null always 1 756 757 // minimum, .., equal dummy query, longer, ... 758 dummyQueryLen := uint32(len(dummyQuery)) 759 eventSizeList := []uint32{ 760 MinQueryEventLen, MinQueryEventLen + 5, 761 MinQueryEventLen + dummyQueryLen - 1, MinQueryEventLen + dummyQueryLen, MinQueryEventLen + dummyQueryLen + 10, 762 } 763 for _, eventSize = range eventSizeList { 764 queryEv, err := GenDummyEvent(header, latestPos, eventSize) 765 require.Nil(t, err) 766 require.NotNil(t, queryEv) 767 // verify the header 768 verifyHeader(t, queryEv.Header, expectedHeader, replication.QUERY_EVENT, latestPos, uint32(len(queryEv.RawData))) 769 // verify the body 770 queryEvBody, ok := queryEv.Event.(*replication.QueryEvent) 771 require.True(t, ok) 772 require.NotNil(t, queryEvBody) 773 require.Equal(t, uint32(0), queryEvBody.SlaveProxyID) 774 require.Equal(t, uint32(0), queryEvBody.ExecutionTime) 775 require.Equal(t, uint16(0), queryEvBody.ErrorCode) 776 require.Equal(t, []byte{}, queryEvBody.StatusVars) 777 require.Equal(t, []byte{}, queryEvBody.Schema) 778 queryStart := uint32(eventHeaderLen + 4 + 4 + 1 + 2 + 2 + 1) 779 queryEnd := eventSize - crc32Len 780 queryLen := queryEnd - queryStart 781 require.Len(t, queryEvBody.Query, int(queryLen)) 782 if queryLen <= dummyQueryLen { 783 require.Equal(t, dummyQuery[:queryLen], queryEvBody.Query) 784 } else { 785 require.Equal(t, dummyQuery, queryEvBody.Query[:dummyQueryLen]) 786 zeroTail := make([]byte, queryLen-dummyQueryLen) 787 require.Equal(t, zeroTail, queryEvBody.Query[dummyQueryLen:]) 788 } 789 } 790 }