2018-11-06 21:51:56 +00:00
|
|
|
package reader
|
|
|
|
|
|
|
|
import (
|
|
|
|
"github.com/juju/errors"
|
2018-11-06 22:17:48 +00:00
|
|
|
"github.com/localhots/bocadillo/binlog"
|
2018-11-09 00:13:36 +00:00
|
|
|
"github.com/localhots/bocadillo/reader/schema"
|
2018-11-11 14:26:40 +00:00
|
|
|
"github.com/localhots/bocadillo/reader/slave"
|
2018-11-06 21:51:56 +00:00
|
|
|
)
|
|
|
|
|
2018-11-11 14:26:40 +00:00
|
|
|
// Reader is a binary log reader.
|
2018-11-06 21:51:56 +00:00
|
|
|
type Reader struct {
|
2018-11-11 14:26:40 +00:00
|
|
|
conn *slave.Conn
|
2018-11-06 21:51:56 +00:00
|
|
|
state binlog.Position
|
|
|
|
format binlog.FormatDescription
|
|
|
|
tableMap map[uint64]binlog.TableDescription
|
|
|
|
schema *schema.Schema
|
|
|
|
}
|
|
|
|
|
2018-11-11 14:26:40 +00:00
|
|
|
// Event contains binlog event details.
|
2018-11-06 21:51:56 +00:00
|
|
|
type Event struct {
|
2018-11-11 13:24:45 +00:00
|
|
|
Format binlog.FormatDescription
|
2018-11-06 21:51:56 +00:00
|
|
|
Header binlog.EventHeader
|
2018-11-11 13:24:45 +00:00
|
|
|
Buffer []byte
|
|
|
|
|
|
|
|
// Table is not empty for rows events
|
|
|
|
Table *binlog.TableDescription
|
2018-11-06 21:51:56 +00:00
|
|
|
}
|
|
|
|
|
2018-11-11 14:26:40 +00:00
|
|
|
// New creates a new binary log reader.
|
|
|
|
func New(dsn string, sc slave.Config) (*Reader, error) {
|
|
|
|
conn, err := slave.Connect(dsn, sc)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Annotate(err, "establish slave connection")
|
|
|
|
}
|
|
|
|
|
2018-11-06 21:51:56 +00:00
|
|
|
r := &Reader{
|
|
|
|
conn: conn,
|
|
|
|
tableMap: make(map[uint64]binlog.TableDescription),
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := conn.DisableChecksum(); err != nil {
|
|
|
|
return nil, errors.Annotate(err, "disable binlog checksum")
|
|
|
|
}
|
|
|
|
if err := conn.RegisterSlave(); err != nil {
|
|
|
|
return nil, errors.Annotate(err, "register slave server")
|
|
|
|
}
|
|
|
|
if err := conn.StartBinlogDump(); err != nil {
|
|
|
|
return nil, errors.Annotate(err, "start binlog dump")
|
|
|
|
}
|
|
|
|
|
|
|
|
return r, nil
|
|
|
|
}
|
|
|
|
|
2018-11-11 14:26:40 +00:00
|
|
|
// ReadEvent reads next event from the binary log.
|
2018-11-06 21:51:56 +00:00
|
|
|
func (r *Reader) ReadEvent() (*Event, error) {
|
|
|
|
connBuff, err := r.conn.ReadPacket()
|
|
|
|
if err != nil {
|
2018-11-11 13:24:45 +00:00
|
|
|
return nil, errors.Annotate(err, "read next event")
|
2018-11-06 21:51:56 +00:00
|
|
|
}
|
|
|
|
|
2018-11-11 13:24:45 +00:00
|
|
|
evt := Event{Format: r.format}
|
2018-11-06 21:51:56 +00:00
|
|
|
if err := evt.Header.Decode(connBuff, r.format); err != nil {
|
|
|
|
return nil, errors.Annotate(err, "decode event header")
|
|
|
|
}
|
|
|
|
if evt.Header.NextOffset > 0 {
|
|
|
|
r.state.Offset = uint64(evt.Header.NextOffset)
|
|
|
|
}
|
|
|
|
|
2018-11-11 13:24:45 +00:00
|
|
|
evt.Buffer = connBuff[r.format.HeaderLen():]
|
2018-11-06 21:51:56 +00:00
|
|
|
csa := r.format.ServerDetails.ChecksumAlgorithm
|
|
|
|
if evt.Header.Type != binlog.EventTypeFormatDescription && csa == binlog.ChecksumAlgorithmCRC32 {
|
2018-11-11 13:24:45 +00:00
|
|
|
// Remove trailing CRC32 checksum, we're not going to verify it
|
|
|
|
evt.Buffer = evt.Buffer[:len(evt.Buffer)-4]
|
2018-11-06 21:51:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch evt.Header.Type {
|
|
|
|
case binlog.EventTypeFormatDescription:
|
|
|
|
var fde binlog.FormatDescriptionEvent
|
2018-11-11 13:47:33 +00:00
|
|
|
err := fde.Decode(evt.Buffer)
|
2018-11-11 13:24:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Annotate(err, "decode format description event")
|
2018-11-06 21:51:56 +00:00
|
|
|
}
|
2018-11-11 13:24:45 +00:00
|
|
|
r.format = fde.FormatDescription
|
|
|
|
evt.Format = fde.FormatDescription
|
2018-11-11 13:47:33 +00:00
|
|
|
|
2018-11-06 21:51:56 +00:00
|
|
|
case binlog.EventTypeRotate:
|
|
|
|
var re binlog.RotateEvent
|
2018-11-11 13:47:33 +00:00
|
|
|
err := re.Decode(evt.Buffer, r.format)
|
2018-11-11 13:24:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Annotate(err, "decode rotate event")
|
2018-11-06 21:51:56 +00:00
|
|
|
}
|
2018-11-11 13:24:45 +00:00
|
|
|
r.state = re.NextFile
|
2018-11-11 13:47:33 +00:00
|
|
|
|
2018-11-06 21:51:56 +00:00
|
|
|
case binlog.EventTypeTableMap:
|
|
|
|
var tme binlog.TableMapEvent
|
2018-11-11 13:47:33 +00:00
|
|
|
err := tme.Decode(evt.Buffer, r.format)
|
2018-11-11 13:24:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Annotate(err, "decode table map event")
|
2018-11-06 21:51:56 +00:00
|
|
|
}
|
2018-11-11 13:24:45 +00:00
|
|
|
r.tableMap[tme.TableID] = tme.TableDescription
|
2018-11-11 13:47:33 +00:00
|
|
|
|
2018-11-06 21:51:56 +00:00
|
|
|
case binlog.EventTypeWriteRowsV0,
|
|
|
|
binlog.EventTypeWriteRowsV1,
|
|
|
|
binlog.EventTypeWriteRowsV2,
|
|
|
|
binlog.EventTypeUpdateRowsV0,
|
|
|
|
binlog.EventTypeUpdateRowsV1,
|
|
|
|
binlog.EventTypeUpdateRowsV2,
|
|
|
|
binlog.EventTypeDeleteRowsV0,
|
|
|
|
binlog.EventTypeDeleteRowsV1,
|
|
|
|
binlog.EventTypeDeleteRowsV2:
|
|
|
|
|
|
|
|
re := binlog.RowsEvent{Type: evt.Header.Type}
|
2018-11-11 13:24:45 +00:00
|
|
|
tableID := re.PeekTableID(evt.Buffer, r.format)
|
2018-11-06 21:51:56 +00:00
|
|
|
td, ok := r.tableMap[tableID]
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Unknown table ID")
|
|
|
|
}
|
2018-11-08 19:47:49 +00:00
|
|
|
evt.Table = &td
|
2018-11-11 13:47:33 +00:00
|
|
|
|
2018-11-11 13:24:45 +00:00
|
|
|
case binlog.EventTypeQuery:
|
|
|
|
// Can be decoded by the receiver
|
2018-11-06 21:51:56 +00:00
|
|
|
case binlog.EventTypeXID:
|
2018-11-11 13:24:45 +00:00
|
|
|
// Can be decoded by the receiver
|
2018-11-06 21:51:56 +00:00
|
|
|
case binlog.EventTypeGTID:
|
|
|
|
// TODO: Add support
|
|
|
|
}
|
|
|
|
|
|
|
|
return &evt, err
|
|
|
|
}
|
2018-11-11 13:33:26 +00:00
|
|
|
|
|
|
|
// DecodeRows decodes buffer into a rows event.
|
|
|
|
func (e Event) DecodeRows() (binlog.RowsEvent, error) {
|
|
|
|
re := binlog.RowsEvent{Type: e.Header.Type}
|
|
|
|
if binlog.RowsEventVersion(e.Header.Type) < 0 {
|
|
|
|
return re, errors.New("invalid rows event")
|
|
|
|
}
|
|
|
|
return re, re.Decode(e.Buffer, e.Format, *e.Table)
|
|
|
|
}
|