@@ -21,6 +21,7 @@ const (
21
21
LogicalTimestampTypeCode = 2
22
22
PartLogicalTimestampLength = 8
23
23
BinlogChecksumLength = 4
24
+ UndefinedServerVer = 999999 // UNDEFINED_SERVER_VERSION
24
25
)
25
26
26
27
type BinlogEvent struct {
@@ -225,31 +226,31 @@ type PreviousGTIDsEvent struct {
225
226
func (e * PreviousGTIDsEvent ) Decode (data []byte ) error {
226
227
var previousGTIDSets []string
227
228
pos := 0
228
- uuidCount := binary .LittleEndian .Uint16 (data [pos : pos + 8 ])
229
+ uuidCount := binary .LittleEndian .Uint16 (data [pos : pos + 8 ])
229
230
pos += 8
230
231
231
- for i := uint16 (0 );i < uuidCount ; i ++ {
232
- uuid := e .decodeUuid (data [pos : pos + 16 ])
232
+ for i := uint16 (0 ); i < uuidCount ; i ++ {
233
+ uuid := e .decodeUuid (data [pos : pos + 16 ])
233
234
pos += 16
234
- sliceCount := binary .LittleEndian .Uint16 (data [pos : pos + 8 ])
235
+ sliceCount := binary .LittleEndian .Uint16 (data [pos : pos + 8 ])
235
236
pos += 8
236
237
var intervals []string
237
- for i := uint16 (0 );i < sliceCount ; i ++ {
238
- start := e .decodeInterval (data [pos : pos + 8 ])
238
+ for i := uint16 (0 ); i < sliceCount ; i ++ {
239
+ start := e .decodeInterval (data [pos : pos + 8 ])
239
240
pos += 8
240
- stop := e .decodeInterval (data [pos : pos + 8 ])
241
+ stop := e .decodeInterval (data [pos : pos + 8 ])
241
242
pos += 8
242
243
interval := ""
243
244
if stop == start + 1 {
244
- interval = fmt .Sprintf ("%d" ,start )
245
- }else {
246
- interval = fmt .Sprintf ("%d-%d" ,start ,stop - 1 )
245
+ interval = fmt .Sprintf ("%d" , start )
246
+ } else {
247
+ interval = fmt .Sprintf ("%d-%d" , start , stop - 1 )
247
248
}
248
- intervals = append (intervals ,interval )
249
+ intervals = append (intervals , interval )
249
250
}
250
- previousGTIDSets = append (previousGTIDSets ,fmt .Sprintf ("%s:%s" ,uuid ,strings .Join (intervals ,":" )))
251
+ previousGTIDSets = append (previousGTIDSets , fmt .Sprintf ("%s:%s" , uuid , strings .Join (intervals , ":" )))
251
252
}
252
- e .GTIDSets = fmt .Sprintf ("%s" ,strings .Join (previousGTIDSets ,"," ))
253
+ e .GTIDSets = fmt .Sprintf ("%s" , strings .Join (previousGTIDSets , "," ))
253
254
return nil
254
255
}
255
256
@@ -259,8 +260,8 @@ func (e *PreviousGTIDsEvent) Dump(w io.Writer) {
259
260
}
260
261
261
262
func (e * PreviousGTIDsEvent ) decodeUuid (data []byte ) string {
262
- return fmt .Sprintf ("%s-%s-%s-%s-%s" ,hex .EncodeToString (data [0 :4 ]),hex .EncodeToString (data [4 :6 ]),
263
- hex .EncodeToString (data [6 :8 ]),hex .EncodeToString (data [8 :10 ]),hex .EncodeToString (data [10 :]))
263
+ return fmt .Sprintf ("%s-%s-%s-%s-%s" , hex .EncodeToString (data [0 :4 ]), hex .EncodeToString (data [4 :6 ]),
264
+ hex .EncodeToString (data [6 :8 ]), hex .EncodeToString (data [8 :10 ]), hex .EncodeToString (data [10 :]))
264
265
}
265
266
266
267
func (e * PreviousGTIDsEvent ) decodeInterval (data []byte ) uint64 {
@@ -349,6 +350,20 @@ type GTIDEvent struct {
349
350
GNO int64
350
351
LastCommitted int64
351
352
SequenceNumber int64
353
+
354
+ // ImmediateCommitTimestamp/OriginalCommitTimestamp are introduced in MySQL-8.0.1, see:
355
+ // https://mysqlhighavailability.com/replication-features-in-mysql-8-0-1/
356
+ ImmediateCommitTimestamp uint64
357
+ OriginalCommitTimestamp uint64
358
+
359
+ // Total transaction length (including this GTIDEvent), introduced in MySQL-8.0.2, see:
360
+ // https://mysqlhighavailability.com/taking-advantage-of-new-transaction-length-metadata/
361
+ TransactionLength uint64
362
+
363
+ // ImmediateServerVersion/OriginalServerVersion are introduced in MySQL-8.0.14, see
364
+ // https://dev.mysql.com/doc/refman/8.0/en/replication-compatibility.html
365
+ ImmediateServerVersion uint32
366
+ OriginalServerVersion uint32
352
367
}
353
368
354
369
func (e * GTIDEvent ) Decode (data []byte ) error {
@@ -359,26 +374,99 @@ func (e *GTIDEvent) Decode(data []byte) error {
359
374
pos += SidLength
360
375
e .GNO = int64 (binary .LittleEndian .Uint64 (data [pos :]))
361
376
pos += 8
377
+
362
378
if len (data ) >= 42 {
363
379
if uint8 (data [pos ]) == LogicalTimestampTypeCode {
364
380
pos ++
365
381
e .LastCommitted = int64 (binary .LittleEndian .Uint64 (data [pos :]))
366
382
pos += PartLogicalTimestampLength
367
383
e .SequenceNumber = int64 (binary .LittleEndian .Uint64 (data [pos :]))
384
+ pos += 8
385
+
386
+ // IMMEDIATE_COMMIT_TIMESTAMP_LENGTH = 7
387
+ if len (data )- pos < 7 {
388
+ return nil
389
+ }
390
+ e .ImmediateCommitTimestamp = FixedLengthInt (data [pos : pos + 7 ])
391
+ pos += 7
392
+ if (e .ImmediateCommitTimestamp & (uint64 (1 ) << 55 )) != 0 {
393
+ // If the most significant bit set, another 7 byte follows representing OriginalCommitTimestamp
394
+ e .ImmediateCommitTimestamp &= ^ (uint64 (1 ) << 55 )
395
+ e .OriginalCommitTimestamp = FixedLengthInt (data [pos : pos + 7 ])
396
+ pos += 7
397
+
398
+ } else {
399
+ // Otherwise OriginalCommitTimestamp == ImmediateCommitTimestamp
400
+ e .OriginalCommitTimestamp = e .ImmediateCommitTimestamp
401
+
402
+ }
403
+
404
+ // TRANSACTION_LENGTH_MIN_LENGTH = 1
405
+ if len (data )- pos < 1 {
406
+ return nil
407
+ }
408
+ var n int
409
+ e .TransactionLength , _ , n = LengthEncodedInt (data [pos :])
410
+ pos += n
411
+
412
+ // IMMEDIATE_SERVER_VERSION_LENGTH = 4
413
+ e .ImmediateServerVersion = UndefinedServerVer
414
+ e .OriginalServerVersion = UndefinedServerVer
415
+ if len (data )- pos < 4 {
416
+ return nil
417
+ }
418
+ e .ImmediateServerVersion = binary .LittleEndian .Uint32 (data [pos :])
419
+ pos += 4
420
+ if (e .ImmediateServerVersion & (uint32 (1 ) << 31 )) != 0 {
421
+ // If the most significant bit set, another 4 byte follows representing OriginalServerVersion
422
+ e .ImmediateServerVersion &= ^ (uint32 (1 ) << 31 )
423
+ e .OriginalServerVersion = binary .LittleEndian .Uint32 (data [pos :])
424
+ pos += 4
425
+
426
+ } else {
427
+ // Otherwise OriginalServerVersion == ImmediateServerVersion
428
+ e .OriginalServerVersion = e .ImmediateServerVersion
429
+
430
+ }
431
+
368
432
}
369
433
}
370
434
return nil
371
435
}
372
436
373
437
func (e * GTIDEvent ) Dump (w io.Writer ) {
438
+ fmtTime := func (t time.Time ) string {
439
+ if t .IsZero () {
440
+ return "<n/a>"
441
+ }
442
+ return t .Format (time .RFC3339Nano )
443
+ }
444
+
374
445
fmt .Fprintf (w , "Commit flag: %d\n " , e .CommitFlag )
375
446
u , _ := uuid .FromBytes (e .SID )
376
447
fmt .Fprintf (w , "GTID_NEXT: %s:%d\n " , u .String (), e .GNO )
377
448
fmt .Fprintf (w , "LAST_COMMITTED: %d\n " , e .LastCommitted )
378
449
fmt .Fprintf (w , "SEQUENCE_NUMBER: %d\n " , e .SequenceNumber )
450
+ fmt .Fprintf (w , "Immediate commmit timestamp: %d (%s)\n " , e .ImmediateCommitTimestamp , fmtTime (e .ImmediateCommitTime ()))
451
+ fmt .Fprintf (w , "Orignal commmit timestamp: %d (%s)\n " , e .OriginalCommitTimestamp , fmtTime (e .OriginalCommitTime ()))
452
+ fmt .Fprintf (w , "Transaction length: %d\n " , e .TransactionLength )
453
+ fmt .Fprintf (w , "Immediate server version: %d\n " , e .ImmediateServerVersion )
454
+ fmt .Fprintf (w , "Orignal server version: %d\n " , e .OriginalServerVersion )
379
455
fmt .Fprintln (w )
380
456
}
381
457
458
+ // ImmediateCommitTime returns the commit time of this trx on the immediate server
459
+ // or zero time if not available.
460
+ func (e * GTIDEvent ) ImmediateCommitTime () time.Time {
461
+ return microSecTimestampToTime (e .ImmediateCommitTimestamp )
462
+ }
463
+
464
+ // OriginalCommitTime returns the commit time of this trx on the original server
465
+ // or zero time if not available.
466
+ func (e * GTIDEvent ) OriginalCommitTime () time.Time {
467
+ return microSecTimestampToTime (e .OriginalCommitTimestamp )
468
+ }
469
+
382
470
type BeginLoadQueryEvent struct {
383
471
FileID uint32
384
472
BlockData []byte
0 commit comments